mirror of
https://github.com/xen-project/xen.git
synced 2026-10-29 10:40:03 +01:00
Added blktap support. Includes kernel driver (enabled as CONFIG_XEN_BLKDEV_TAP=y) and userspace tools. The userspace deamon (blktapctrl) is enabled by default when xend is activated. For further information on using and configuring blktap see tools/blktap/README.
This commit is contained in:
1 parent
0929bd9fc0
commit
0da81aa1d4
133 files changed
+16928
-8
No files matched your search
@@ -1322,6 +1322,7 @@ CONFIG_XEN_PCIDEV_BACKEND=y
|
||||
CONFIG_XEN_PCIDEV_BACKEND_PASS=y
|
||||
# CONFIG_XEN_PCIDEV_BE_DEBUG is not set
|
||||
CONFIG_XEN_BLKDEV_BACKEND=y
|
||||
CONFIG_XEN_BLKDEV_TAP=y
|
||||
CONFIG_XEN_NETDEV_BACKEND=y
|
||||
# CONFIG_XEN_NETDEV_PIPELINED_TRANSMITTER is not set
|
||||
CONFIG_XEN_NETDEV_LOOPBACK=y
|
||||
|
||||
@@ -1263,6 +1263,7 @@ CONFIG_XEN_PCIDEV_BACKEND=y
|
||||
CONFIG_XEN_PCIDEV_BACKEND_PASS=y
|
||||
# CONFIG_XEN_PCIDEV_BE_DEBUG is not set
|
||||
CONFIG_XEN_BLKDEV_BACKEND=y
|
||||
CONFIG_XEN_BLKDEV_TAP=y
|
||||
CONFIG_XEN_NETDEV_BACKEND=y
|
||||
# CONFIG_XEN_NETDEV_PIPELINED_TRANSMITTER is not set
|
||||
CONFIG_XEN_NETDEV_LOOPBACK=y
|
||||
|
||||
@@ -3023,6 +3023,7 @@ CONFIG_XEN_PCIDEV_BACKEND_VPCI=y
|
||||
# CONFIG_XEN_PCIDEV_BACKEND_PASS is not set
|
||||
# CONFIG_XEN_PCIDEV_BE_DEBUG is not set
|
||||
CONFIG_XEN_BLKDEV_BACKEND=y
|
||||
CONFIG_XEN_BLKDEV_TAP=y
|
||||
CONFIG_XEN_NETDEV_BACKEND=y
|
||||
# CONFIG_XEN_NETDEV_PIPELINED_TRANSMITTER is not set
|
||||
CONFIG_XEN_NETDEV_LOOPBACK=y
|
||||
|
||||
@@ -2855,6 +2855,7 @@ CONFIG_XEN_PCIDEV_BACKEND=m
|
||||
CONFIG_XEN_PCIDEV_BACKEND_PASS=y
|
||||
# CONFIG_XEN_PCIDEV_BE_DEBUG is not set
|
||||
CONFIG_XEN_BLKDEV_BACKEND=y
|
||||
CONFIG_XEN_BLKDEV_TAP=y
|
||||
CONFIG_XEN_NETDEV_BACKEND=y
|
||||
# CONFIG_XEN_NETDEV_PIPELINED_TRANSMITTER is not set
|
||||
CONFIG_XEN_NETDEV_LOOPBACK=y
|
||||
|
||||
@@ -94,6 +94,18 @@ config XEN_XENBUS_DEV
|
||||
depends on PROC_FS
|
||||
default y
|
||||
|
||||
config XEN_BLKDEV_TAP
|
||||
tristate "Blockk device tap backend"
|
||||
depends on XEN_BACKEND
|
||||
default XEN_PRIVILEGED_GUEST
|
||||
help
|
||||
The block tap driver is an alternative to the block back driver
|
||||
and allows VM block requests to be redirected to userspace through
|
||||
a device interface. The tap allows user-space development of
|
||||
high-performance block backends, where disk images may be implemented
|
||||
as files, in memory, or on other hosts across the network. This
|
||||
driver can safely coexist with the existing blockback driver.
|
||||
|
||||
config XEN_NETDEV_BACKEND
|
||||
tristate "Network-device backend driver"
|
||||
depends on XEN_BACKEND && NET
|
||||
|
||||
@@ -8,6 +8,7 @@ obj-$(CONFIG_XEN_UTIL) += util.o
|
||||
obj-$(CONFIG_XEN_BALLOON) += balloon/
|
||||
obj-$(CONFIG_XEN_DEVMEM) += char/
|
||||
obj-$(CONFIG_XEN_BLKDEV_BACKEND) += blkback/
|
||||
obj-$(CONFIG_XEN_BLKDEV_TAP) += blktap/
|
||||
obj-$(CONFIG_XEN_NETDEV_BACKEND) += netback/
|
||||
obj-$(CONFIG_XEN_TPMDEV_BACKEND) += tpmback/
|
||||
obj-$(CONFIG_XEN_BLKDEV_FRONTEND) += blkfront/
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
LINUXINCLUDE += -I../xen/include/public/io
|
||||
obj-y := xenbus.o interface.o blktap.o
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* 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 __BLKIF__BACKEND__COMMON_H__
|
||||
#define __BLKIF__BACKEND__COMMON_H__
|
||||
|
||||
#include <linux/config.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/blkdev.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/setup.h>
|
||||
#include <asm/pgalloc.h>
|
||||
#include <xen/evtchn.h>
|
||||
#include <asm/hypervisor.h>
|
||||
#include <xen/interface/io/blkif.h>
|
||||
#include <xen/interface/io/ring.h>
|
||||
#include <xen/gnttab.h>
|
||||
#include <xen/driver_util.h>
|
||||
|
||||
#define DPRINTK(_f, _a...) pr_debug("(file=%s, line=%d) " _f, \
|
||||
__FILE__ , __LINE__ , ## _a )
|
||||
|
||||
#define WPRINTK(fmt, args...) printk(KERN_WARNING "blk_tap: " fmt, ##args)
|
||||
|
||||
struct backend_info;
|
||||
|
||||
typedef struct blkif_st {
|
||||
/* Unique identifier for this interface. */
|
||||
domid_t domid;
|
||||
unsigned int handle;
|
||||
/* Physical parameters of the comms window. */
|
||||
unsigned int evtchn;
|
||||
unsigned int irq;
|
||||
/* Comms information. */
|
||||
blkif_back_ring_t blk_ring;
|
||||
struct vm_struct *blk_ring_area;
|
||||
/* Back pointer to the backend_info. */
|
||||
struct backend_info *be;
|
||||
/* Private fields. */
|
||||
spinlock_t blk_ring_lock;
|
||||
atomic_t refcnt;
|
||||
|
||||
wait_queue_head_t wq;
|
||||
struct task_struct *xenblkd;
|
||||
unsigned int waiting_reqs;
|
||||
request_queue_t *plug;
|
||||
|
||||
/* statistics */
|
||||
unsigned long st_print;
|
||||
int st_rd_req;
|
||||
int st_wr_req;
|
||||
int st_oo_req;
|
||||
|
||||
wait_queue_head_t waiting_to_free;
|
||||
|
||||
grant_handle_t shmem_handle;
|
||||
grant_ref_t shmem_ref;
|
||||
|
||||
int dev_num;
|
||||
uint64_t sectors;
|
||||
} blkif_t;
|
||||
|
||||
blkif_t *tap_alloc_blkif(domid_t domid);
|
||||
void tap_blkif_free(blkif_t *blkif);
|
||||
int tap_blkif_map(blkif_t *blkif, unsigned long shared_page,
|
||||
unsigned int evtchn);
|
||||
|
||||
#define blkif_get(_b) (atomic_inc(&(_b)->refcnt))
|
||||
#define blkif_put(_b) \
|
||||
do { \
|
||||
if (atomic_dec_and_test(&(_b)->refcnt)) \
|
||||
wake_up(&(_b)->waiting_to_free);\
|
||||
} while (0)
|
||||
|
||||
|
||||
struct phys_req {
|
||||
unsigned short dev;
|
||||
unsigned short nr_sects;
|
||||
struct block_device *bdev;
|
||||
blkif_sector_t sector_number;
|
||||
};
|
||||
|
||||
void tap_blkif_interface_init(void);
|
||||
|
||||
void tap_blkif_xenbus_init(void);
|
||||
|
||||
irqreturn_t tap_blkif_be_int(int irq, void *dev_id, struct pt_regs *regs);
|
||||
int tap_blkif_schedule(void *arg);
|
||||
|
||||
int dom_to_devid(domid_t domid, int xenbus_id, blkif_t *blkif);
|
||||
void signal_tapdisk(int idx);
|
||||
|
||||
#endif /* __BLKIF__BACKEND__COMMON_H__ */
|
||||
@@ -0,0 +1,165 @@
|
||||
/******************************************************************************
|
||||
* drivers/xen/blktap/interface.c
|
||||
*
|
||||
* Block-device interface management.
|
||||
*
|
||||
* Copyright (c) 2004, Keir Fraser
|
||||
*
|
||||
* 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 "common.h"
|
||||
#include <xen/evtchn.h>
|
||||
|
||||
static kmem_cache_t *blkif_cachep;
|
||||
|
||||
blkif_t *tap_alloc_blkif(domid_t domid)
|
||||
{
|
||||
blkif_t *blkif;
|
||||
|
||||
blkif = kmem_cache_alloc(blkif_cachep, GFP_KERNEL);
|
||||
if (!blkif)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
memset(blkif, 0, sizeof(*blkif));
|
||||
blkif->domid = domid;
|
||||
spin_lock_init(&blkif->blk_ring_lock);
|
||||
atomic_set(&blkif->refcnt, 1);
|
||||
init_waitqueue_head(&blkif->wq);
|
||||
blkif->st_print = jiffies;
|
||||
init_waitqueue_head(&blkif->waiting_to_free);
|
||||
|
||||
return blkif;
|
||||
}
|
||||
|
||||
static int map_frontend_page(blkif_t *blkif, unsigned long shared_page)
|
||||
{
|
||||
struct gnttab_map_grant_ref op;
|
||||
int ret;
|
||||
|
||||
gnttab_set_map_op(&op, (unsigned long)blkif->blk_ring_area->addr,
|
||||
GNTMAP_host_map, shared_page, blkif->domid);
|
||||
|
||||
lock_vm_area(blkif->blk_ring_area);
|
||||
ret = HYPERVISOR_grant_table_op(GNTTABOP_map_grant_ref, &op, 1);
|
||||
unlock_vm_area(blkif->blk_ring_area);
|
||||
BUG_ON(ret);
|
||||
|
||||
if (op.status) {
|
||||
DPRINTK(" Grant table operation failure !\n");
|
||||
return op.status;
|
||||
}
|
||||
|
||||
blkif->shmem_ref = shared_page;
|
||||
blkif->shmem_handle = op.handle;
|
||||
|
||||
#ifdef CONFIG_XEN_IA64_DOM0_NON_VP
|
||||
/* on some arch's, map_grant_ref behaves like mmap, in that the
|
||||
* passed address is a hint and a different address may be returned */
|
||||
blkif->blk_ring_area->addr = gnttab_map_vaddr(op);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void unmap_frontend_page(blkif_t *blkif)
|
||||
{
|
||||
struct gnttab_unmap_grant_ref op;
|
||||
int ret;
|
||||
|
||||
gnttab_set_unmap_op(&op, (unsigned long)blkif->blk_ring_area->addr,
|
||||
GNTMAP_host_map, blkif->shmem_handle);
|
||||
|
||||
lock_vm_area(blkif->blk_ring_area);
|
||||
ret = HYPERVISOR_grant_table_op(GNTTABOP_unmap_grant_ref, &op, 1);
|
||||
unlock_vm_area(blkif->blk_ring_area);
|
||||
BUG_ON(ret);
|
||||
}
|
||||
|
||||
int tap_blkif_map(blkif_t *blkif, unsigned long shared_page,
|
||||
unsigned int evtchn)
|
||||
{
|
||||
blkif_sring_t *sring;
|
||||
int err;
|
||||
struct evtchn_bind_interdomain bind_interdomain;
|
||||
|
||||
/* Already connected through? */
|
||||
if (blkif->irq)
|
||||
return 0;
|
||||
|
||||
if ( (blkif->blk_ring_area = alloc_vm_area(PAGE_SIZE)) == NULL )
|
||||
return -ENOMEM;
|
||||
|
||||
err = map_frontend_page(blkif, shared_page);
|
||||
if (err) {
|
||||
free_vm_area(blkif->blk_ring_area);
|
||||
return err;
|
||||
}
|
||||
|
||||
bind_interdomain.remote_dom = blkif->domid;
|
||||
bind_interdomain.remote_port = evtchn;
|
||||
|
||||
err = HYPERVISOR_event_channel_op(EVTCHNOP_bind_interdomain,
|
||||
&bind_interdomain);
|
||||
if (err) {
|
||||
unmap_frontend_page(blkif);
|
||||
free_vm_area(blkif->blk_ring_area);
|
||||
return err;
|
||||
}
|
||||
|
||||
blkif->evtchn = bind_interdomain.local_port;
|
||||
|
||||
sring = (blkif_sring_t *)blkif->blk_ring_area->addr;
|
||||
BACK_RING_INIT(&blkif->blk_ring, sring, PAGE_SIZE);
|
||||
|
||||
blkif->irq = bind_evtchn_to_irqhandler(
|
||||
blkif->evtchn, tap_blkif_be_int, 0, "blkif-backend", blkif);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void tap_blkif_free(blkif_t *blkif)
|
||||
{
|
||||
atomic_dec(&blkif->refcnt);
|
||||
wait_event(blkif->waiting_to_free, atomic_read(&blkif->refcnt) == 0);
|
||||
|
||||
/* Already disconnected? */
|
||||
if (blkif->irq)
|
||||
unbind_from_irqhandler(blkif->irq, blkif);
|
||||
|
||||
if (blkif->blk_ring.sring) {
|
||||
unmap_frontend_page(blkif);
|
||||
free_vm_area(blkif->blk_ring_area);
|
||||
}
|
||||
|
||||
kmem_cache_free(blkif_cachep, blkif);
|
||||
}
|
||||
|
||||
void __init tap_blkif_interface_init(void)
|
||||
{
|
||||
blkif_cachep = kmem_cache_create("blktapif_cache", sizeof(blkif_t),
|
||||
0, 0, NULL, NULL);
|
||||
}
|
||||
@@ -0,0 +1,354 @@
|
||||
/* drivers/xen/blktap/xenbus.c
|
||||
*
|
||||
* Xenbus code for blktap
|
||||
*
|
||||
* Copyright (c) 2004-2005, Andrew Warfield and Julian Chesterfield
|
||||
*
|
||||
* Based on the blkback xenbus code:
|
||||
*
|
||||
* Copyright (C) 2005 Rusty Russell <rusty@rustcorp.com.au>
|
||||
* Copyright (C) 2005 XenSource Ltd
|
||||
*
|
||||
* 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 <stdarg.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <xen/xenbus.h>
|
||||
#include "common.h"
|
||||
|
||||
|
||||
struct backend_info
|
||||
{
|
||||
struct xenbus_device *dev;
|
||||
blkif_t *blkif;
|
||||
struct xenbus_watch backend_watch;
|
||||
int xenbus_id;
|
||||
};
|
||||
|
||||
|
||||
static void connect(struct backend_info *);
|
||||
static int connect_ring(struct backend_info *);
|
||||
static int blktap_remove(struct xenbus_device *dev);
|
||||
static int blktap_probe(struct xenbus_device *dev,
|
||||
const struct xenbus_device_id *id);
|
||||
static void tap_backend_changed(struct xenbus_watch *, const char **,
|
||||
unsigned int);
|
||||
static void tap_frontend_changed(struct xenbus_device *dev,
|
||||
enum xenbus_state frontend_state);
|
||||
|
||||
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 long get_id(const char *str)
|
||||
{
|
||||
int len,end;
|
||||
const char *ptr;
|
||||
char *tptr, num[10];
|
||||
|
||||
len = strsep_len(str, '/', 2);
|
||||
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';
|
||||
DPRINTK("Get_id called for %s (%s)\n",str,num);
|
||||
|
||||
return simple_strtol(num, NULL, 10);
|
||||
}
|
||||
|
||||
static void tap_update_blkif_status(blkif_t *blkif)
|
||||
{
|
||||
int err;
|
||||
|
||||
/* Not ready to connect? */
|
||||
if(!blkif->irq || !blkif->sectors) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Already connected? */
|
||||
if (blkif->be->dev->state == XenbusStateConnected)
|
||||
return;
|
||||
|
||||
/* Attempt to connect: exit if we fail to. */
|
||||
connect(blkif->be);
|
||||
if (blkif->be->dev->state != XenbusStateConnected)
|
||||
return;
|
||||
|
||||
blkif->xenblkd = kthread_run(tap_blkif_schedule, blkif,
|
||||
"xvd %d",
|
||||
blkif->domid);
|
||||
|
||||
if (IS_ERR(blkif->xenblkd)) {
|
||||
err = PTR_ERR(blkif->xenblkd);
|
||||
blkif->xenblkd = NULL;
|
||||
xenbus_dev_fatal(blkif->be->dev, err, "start xenblkd");
|
||||
WPRINTK("Error starting thread\n");
|
||||
}
|
||||
}
|
||||
|
||||
static int blktap_remove(struct xenbus_device *dev)
|
||||
{
|
||||
struct backend_info *be = dev->dev.driver_data;
|
||||
|
||||
if (be->backend_watch.node) {
|
||||
unregister_xenbus_watch(&be->backend_watch);
|
||||
kfree(be->backend_watch.node);
|
||||
be->backend_watch.node = NULL;
|
||||
}
|
||||
if (be->blkif) {
|
||||
if (be->blkif->xenblkd)
|
||||
kthread_stop(be->blkif->xenblkd);
|
||||
signal_tapdisk(be->blkif->dev_num);
|
||||
tap_blkif_free(be->blkif);
|
||||
be->blkif = NULL;
|
||||
}
|
||||
kfree(be);
|
||||
dev->dev.driver_data = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Entry point to this code when a new device is created. Allocate
|
||||
* the basic structures, and watch the store waiting for the
|
||||
* user-space program to tell us the physical device info. Switch to
|
||||
* InitWait.
|
||||
*/
|
||||
static int blktap_probe(struct xenbus_device *dev,
|
||||
const struct xenbus_device_id *id)
|
||||
{
|
||||
int err;
|
||||
struct backend_info *be = kzalloc(sizeof(struct backend_info),
|
||||
GFP_KERNEL);
|
||||
if (!be) {
|
||||
xenbus_dev_fatal(dev, -ENOMEM,
|
||||
"allocating backend structure");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
be->dev = dev;
|
||||
dev->dev.driver_data = be;
|
||||
be->xenbus_id = get_id(dev->nodename);
|
||||
|
||||
be->blkif = tap_alloc_blkif(dev->otherend_id);
|
||||
if (IS_ERR(be->blkif)) {
|
||||
err = PTR_ERR(be->blkif);
|
||||
be->blkif = NULL;
|
||||
xenbus_dev_fatal(dev, err, "creating block interface");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* setup back pointer */
|
||||
be->blkif->be = be;
|
||||
be->blkif->sectors = 0;
|
||||
|
||||
/* set a watch on disk info, waiting for userspace to update details*/
|
||||
err = xenbus_watch_path2(dev, dev->nodename, "info",
|
||||
&be->backend_watch, tap_backend_changed);
|
||||
if (err)
|
||||
goto fail;
|
||||
|
||||
err = xenbus_switch_state(dev, XenbusStateInitWait);
|
||||
if (err)
|
||||
goto fail;
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
DPRINTK("blktap probe failed");
|
||||
blktap_remove(dev);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Callback received when the user space code has placed the device
|
||||
* information in xenstore.
|
||||
*/
|
||||
static void tap_backend_changed(struct xenbus_watch *watch,
|
||||
const char **vec, unsigned int len)
|
||||
{
|
||||
int err;
|
||||
unsigned long info;
|
||||
struct backend_info *be
|
||||
= container_of(watch, struct backend_info, backend_watch);
|
||||
struct xenbus_device *dev = be->dev;
|
||||
|
||||
/**
|
||||
* Check to see whether userspace code has opened the image
|
||||
* and written sector
|
||||
* and disk info to xenstore
|
||||
*/
|
||||
err = xenbus_gather(XBT_NIL, dev->nodename, "info", "%lu", &info,
|
||||
NULL);
|
||||
if (err) {
|
||||
xenbus_dev_error(dev, err, "getting info");
|
||||
return;
|
||||
}
|
||||
|
||||
DPRINTK("Userspace update on disk info, %lu\n",info);
|
||||
|
||||
err = xenbus_gather(XBT_NIL, dev->nodename, "sectors", "%llu",
|
||||
&be->blkif->sectors, NULL);
|
||||
|
||||
/* Associate tap dev with domid*/
|
||||
be->blkif->dev_num = dom_to_devid(be->blkif->domid, be->xenbus_id,
|
||||
be->blkif);
|
||||
DPRINTK("Thread started for domid [%d], connecting disk\n",
|
||||
be->blkif->dev_num);
|
||||
|
||||
tap_update_blkif_status(be->blkif);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback received when the frontend's state changes.
|
||||
*/
|
||||
static void tap_frontend_changed(struct xenbus_device *dev,
|
||||
enum xenbus_state frontend_state)
|
||||
{
|
||||
struct backend_info *be = dev->dev.driver_data;
|
||||
int err;
|
||||
|
||||
DPRINTK("");
|
||||
|
||||
switch (frontend_state) {
|
||||
case XenbusStateInitialising:
|
||||
break;
|
||||
|
||||
case XenbusStateInitialised:
|
||||
case XenbusStateConnected:
|
||||
/* Ensure we connect even when two watches fire in
|
||||
close successsion and we miss the intermediate value
|
||||
of frontend_state. */
|
||||
if (dev->state == XenbusStateConnected)
|
||||
break;
|
||||
|
||||
err = connect_ring(be);
|
||||
if (err)
|
||||
break;
|
||||
tap_update_blkif_status(be->blkif);
|
||||
break;
|
||||
|
||||
case XenbusStateClosing:
|
||||
xenbus_switch_state(dev, XenbusStateClosing);
|
||||
break;
|
||||
|
||||
case XenbusStateClosed:
|
||||
device_unregister(&dev->dev);
|
||||
break;
|
||||
|
||||
case XenbusStateUnknown:
|
||||
case XenbusStateInitWait:
|
||||
default:
|
||||
xenbus_dev_fatal(dev, -EINVAL, "saw state %d at frontend",
|
||||
frontend_state);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Switch to Connected state.
|
||||
*/
|
||||
static void connect(struct backend_info *be)
|
||||
{
|
||||
int err;
|
||||
|
||||
struct xenbus_device *dev = be->dev;
|
||||
|
||||
err = xenbus_switch_state(dev, XenbusStateConnected);
|
||||
if (err)
|
||||
xenbus_dev_fatal(dev, err, "switching to Connected state",
|
||||
dev->nodename);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
static int connect_ring(struct backend_info *be)
|
||||
{
|
||||
struct xenbus_device *dev = be->dev;
|
||||
unsigned long ring_ref;
|
||||
unsigned int evtchn;
|
||||
int err;
|
||||
|
||||
DPRINTK("%s", dev->otherend);
|
||||
|
||||
err = xenbus_gather(XBT_NIL, dev->otherend, "ring-ref", "%lu",
|
||||
&ring_ref, "event-channel", "%u", &evtchn, NULL);
|
||||
if (err) {
|
||||
xenbus_dev_fatal(dev, err,
|
||||
"reading %s/ring-ref and event-channel",
|
||||
dev->otherend);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Map the shared frame, irq etc. */
|
||||
err = tap_blkif_map(be->blkif, ring_ref, evtchn);
|
||||
if (err) {
|
||||
xenbus_dev_fatal(dev, err, "mapping ring-ref %lu port %u",
|
||||
ring_ref, evtchn);
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* ** Driver Registration ** */
|
||||
|
||||
|
||||
static struct xenbus_device_id blktap_ids[] = {
|
||||
{ "tap" },
|
||||
{ "" }
|
||||
};
|
||||
|
||||
|
||||
static struct xenbus_driver blktap = {
|
||||
.name = "tap",
|
||||
.owner = THIS_MODULE,
|
||||
.ids = blktap_ids,
|
||||
.probe = blktap_probe,
|
||||
.remove = blktap_remove,
|
||||
.otherend_changed = tap_frontend_changed
|
||||
};
|
||||
|
||||
|
||||
void tap_blkif_xenbus_init(void)
|
||||
{
|
||||
xenbus_register_backend(&blktap);
|
||||
}
|
||||
@@ -0,0 +1,297 @@
|
||||
diff -pruN ../pristine-linux-2.6.16-rc5/fs/aio.c ./fs/aio.c
|
||||
--- ../pristine-linux-2.6.16-rc5/fs/aio.c 2006-03-14 14:10:10.827401387 +0000
|
||||
+++ ./fs/aio.c 2006-03-16 09:57:53.898316582 +0000
|
||||
@@ -34,6 +34,11 @@
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/mmu_context.h>
|
||||
|
||||
+#ifdef CONFIG_EPOLL
|
||||
+#include <linux/poll.h>
|
||||
+#include <linux/eventpoll.h>
|
||||
+#endif
|
||||
+
|
||||
#if DEBUG > 1
|
||||
#define dprintk printk
|
||||
#else
|
||||
@@ -1016,6 +1021,10 @@ put_rq:
|
||||
if (waitqueue_active(&ctx->wait))
|
||||
wake_up(&ctx->wait);
|
||||
|
||||
+#ifdef CONFIG_EPOLL
|
||||
+ if (ctx->file && waitqueue_active(&ctx->poll_wait))
|
||||
+ wake_up(&ctx->poll_wait);
|
||||
+#endif
|
||||
if (ret)
|
||||
put_ioctx(ctx);
|
||||
|
||||
@@ -1025,6 +1034,8 @@ put_rq:
|
||||
/* aio_read_evt
|
||||
* Pull an event off of the ioctx's event ring. Returns the number of
|
||||
* events fetched (0 or 1 ;-)
|
||||
+ * If ent parameter is 0, just returns the number of events that would
|
||||
+ * be fetched.
|
||||
* FIXME: make this use cmpxchg.
|
||||
* TODO: make the ringbuffer user mmap()able (requires FIXME).
|
||||
*/
|
||||
@@ -1047,13 +1058,18 @@ static int aio_read_evt(struct kioctx *i
|
||||
|
||||
head = ring->head % info->nr;
|
||||
if (head != ring->tail) {
|
||||
- struct io_event *evp = aio_ring_event(info, head, KM_USER1);
|
||||
- *ent = *evp;
|
||||
- head = (head + 1) % info->nr;
|
||||
- smp_mb(); /* finish reading the event before updatng the head */
|
||||
- ring->head = head;
|
||||
- ret = 1;
|
||||
- put_aio_ring_event(evp, KM_USER1);
|
||||
+ if (ent) { /* event requested */
|
||||
+ struct io_event *evp =
|
||||
+ aio_ring_event(info, head, KM_USER1);
|
||||
+ *ent = *evp;
|
||||
+ head = (head + 1) % info->nr;
|
||||
+ /* finish reading the event before updatng the head */
|
||||
+ smp_mb();
|
||||
+ ring->head = head;
|
||||
+ ret = 1;
|
||||
+ put_aio_ring_event(evp, KM_USER1);
|
||||
+ } else /* only need to know availability */
|
||||
+ ret = 1;
|
||||
}
|
||||
spin_unlock(&info->ring_lock);
|
||||
|
||||
@@ -1236,9 +1252,78 @@ static void io_destroy(struct kioctx *io
|
||||
|
||||
aio_cancel_all(ioctx);
|
||||
wait_for_all_aios(ioctx);
|
||||
+#ifdef CONFIG_EPOLL
|
||||
+ /* forget the poll file, but it's up to the user to close it */
|
||||
+ if (ioctx->file) {
|
||||
+ ioctx->file->private_data = 0;
|
||||
+ ioctx->file = 0;
|
||||
+ }
|
||||
+#endif
|
||||
put_ioctx(ioctx); /* once for the lookup */
|
||||
}
|
||||
|
||||
+#ifdef CONFIG_EPOLL
|
||||
+
|
||||
+static int aio_queue_fd_close(struct inode *inode, struct file *file)
|
||||
+{
|
||||
+ struct kioctx *ioctx = file->private_data;
|
||||
+ if (ioctx) {
|
||||
+ file->private_data = 0;
|
||||
+ spin_lock_irq(&ioctx->ctx_lock);
|
||||
+ ioctx->file = 0;
|
||||
+ spin_unlock_irq(&ioctx->ctx_lock);
|
||||
+ }
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static unsigned int aio_queue_fd_poll(struct file *file, poll_table *wait)
|
||||
+{ unsigned int pollflags = 0;
|
||||
+ struct kioctx *ioctx = file->private_data;
|
||||
+
|
||||
+ if (ioctx) {
|
||||
+
|
||||
+ spin_lock_irq(&ioctx->ctx_lock);
|
||||
+ /* Insert inside our poll wait queue */
|
||||
+ poll_wait(file, &ioctx->poll_wait, wait);
|
||||
+
|
||||
+ /* Check our condition */
|
||||
+ if (aio_read_evt(ioctx, 0))
|
||||
+ pollflags = POLLIN | POLLRDNORM;
|
||||
+ spin_unlock_irq(&ioctx->ctx_lock);
|
||||
+ }
|
||||
+
|
||||
+ return pollflags;
|
||||
+}
|
||||
+
|
||||
+static struct file_operations aioq_fops = {
|
||||
+ .release = aio_queue_fd_close,
|
||||
+ .poll = aio_queue_fd_poll
|
||||
+};
|
||||
+
|
||||
+/* make_aio_fd:
|
||||
+ * Create a file descriptor that can be used to poll the event queue.
|
||||
+ * Based and piggybacked on the excellent epoll code.
|
||||
+ */
|
||||
+
|
||||
+static int make_aio_fd(struct kioctx *ioctx)
|
||||
+{
|
||||
+ int error, fd;
|
||||
+ struct inode *inode;
|
||||
+ struct file *file;
|
||||
+
|
||||
+ error = ep_getfd(&fd, &inode, &file, NULL, &aioq_fops);
|
||||
+ if (error)
|
||||
+ return error;
|
||||
+
|
||||
+ /* associate the file with the IO context */
|
||||
+ file->private_data = ioctx;
|
||||
+ ioctx->file = file;
|
||||
+ init_waitqueue_head(&ioctx->poll_wait);
|
||||
+ return fd;
|
||||
+}
|
||||
+#endif
|
||||
+
|
||||
+
|
||||
/* sys_io_setup:
|
||||
* Create an aio_context capable of receiving at least nr_events.
|
||||
* ctxp must not point to an aio_context that already exists, and
|
||||
@@ -1251,18 +1336,30 @@ static void io_destroy(struct kioctx *io
|
||||
* resources are available. May fail with -EFAULT if an invalid
|
||||
* pointer is passed for ctxp. Will fail with -ENOSYS if not
|
||||
* implemented.
|
||||
+ *
|
||||
+ * To request a selectable fd, the user context has to be initialized
|
||||
+ * to 1, instead of 0, and the return value is the fd.
|
||||
+ * This keeps the system call compatible, since a non-zero value
|
||||
+ * was not allowed so far.
|
||||
*/
|
||||
asmlinkage long sys_io_setup(unsigned nr_events, aio_context_t __user *ctxp)
|
||||
{
|
||||
struct kioctx *ioctx = NULL;
|
||||
unsigned long ctx;
|
||||
long ret;
|
||||
+ int make_fd = 0;
|
||||
|
||||
ret = get_user(ctx, ctxp);
|
||||
if (unlikely(ret))
|
||||
goto out;
|
||||
|
||||
ret = -EINVAL;
|
||||
+#ifdef CONFIG_EPOLL
|
||||
+ if (ctx == 1) {
|
||||
+ make_fd = 1;
|
||||
+ ctx = 0;
|
||||
+ }
|
||||
+#endif
|
||||
if (unlikely(ctx || nr_events == 0)) {
|
||||
pr_debug("EINVAL: io_setup: ctx %lu nr_events %u\n",
|
||||
ctx, nr_events);
|
||||
@@ -1273,8 +1370,12 @@ asmlinkage long sys_io_setup(unsigned nr
|
||||
ret = PTR_ERR(ioctx);
|
||||
if (!IS_ERR(ioctx)) {
|
||||
ret = put_user(ioctx->user_id, ctxp);
|
||||
- if (!ret)
|
||||
- return 0;
|
||||
+#ifdef CONFIG_EPOLL
|
||||
+ if (make_fd && ret >= 0)
|
||||
+ ret = make_aio_fd(ioctx);
|
||||
+#endif
|
||||
+ if (ret >= 0)
|
||||
+ return ret;
|
||||
|
||||
get_ioctx(ioctx); /* io_destroy() expects us to hold a ref */
|
||||
io_destroy(ioctx);
|
||||
|
||||
diff -pruN ../pristine-linux-2.6.16-rc5/fs/eventpoll.c ./fs/eventpoll.c
|
||||
--- ../pristine-linux-2.6.16-rc5/fs/eventpoll.c 2006-01-03 03:21:10.000000000 +0000
|
||||
+++ ./fs/eventpoll.c 2006-03-16 10:04:35.469956167 +0000
|
||||
@@ -235,8 +235,6 @@ struct ep_pqueue {
|
||||
|
||||
static void ep_poll_safewake_init(struct poll_safewake *psw);
|
||||
static void ep_poll_safewake(struct poll_safewake *psw, wait_queue_head_t *wq);
|
||||
-static int ep_getfd(int *efd, struct inode **einode, struct file **efile,
|
||||
- struct eventpoll *ep);
|
||||
static int ep_alloc(struct eventpoll **pep);
|
||||
static void ep_free(struct eventpoll *ep);
|
||||
static struct epitem *ep_find(struct eventpoll *ep, struct file *file, int fd);
|
||||
@@ -266,7 +264,7 @@ static int ep_events_transfer(struct eve
|
||||
static int ep_poll(struct eventpoll *ep, struct epoll_event __user *events,
|
||||
int maxevents, long timeout);
|
||||
static int eventpollfs_delete_dentry(struct dentry *dentry);
|
||||
-static struct inode *ep_eventpoll_inode(void);
|
||||
+static struct inode *ep_eventpoll_inode(struct file_operations *fops);
|
||||
static struct super_block *eventpollfs_get_sb(struct file_system_type *fs_type,
|
||||
int flags, const char *dev_name,
|
||||
void *data);
|
||||
@@ -525,7 +523,7 @@ asmlinkage long sys_epoll_create(int siz
|
||||
* Creates all the items needed to setup an eventpoll file. That is,
|
||||
* a file structure, and inode and a free file descriptor.
|
||||
*/
|
||||
- error = ep_getfd(&fd, &inode, &file, ep);
|
||||
+ error = ep_getfd(&fd, &inode, &file, ep, &eventpoll_fops);
|
||||
if (error)
|
||||
goto eexit_2;
|
||||
|
||||
@@ -710,8 +708,8 @@ eexit_1:
|
||||
/*
|
||||
* Creates the file descriptor to be used by the epoll interface.
|
||||
*/
|
||||
-static int ep_getfd(int *efd, struct inode **einode, struct file **efile,
|
||||
- struct eventpoll *ep)
|
||||
+int ep_getfd(int *efd, struct inode **einode, struct file **efile,
|
||||
+ struct eventpoll *ep, struct file_operations *fops)
|
||||
{
|
||||
struct qstr this;
|
||||
char name[32];
|
||||
@@ -727,7 +725,7 @@ static int ep_getfd(int *efd, struct ino
|
||||
goto eexit_1;
|
||||
|
||||
/* Allocates an inode from the eventpoll file system */
|
||||
- inode = ep_eventpoll_inode();
|
||||
+ inode = ep_eventpoll_inode(fops);
|
||||
error = PTR_ERR(inode);
|
||||
if (IS_ERR(inode))
|
||||
goto eexit_2;
|
||||
@@ -758,7 +756,7 @@ static int ep_getfd(int *efd, struct ino
|
||||
|
||||
file->f_pos = 0;
|
||||
file->f_flags = O_RDONLY;
|
||||
- file->f_op = &eventpoll_fops;
|
||||
+ file->f_op = fops;
|
||||
file->f_mode = FMODE_READ;
|
||||
file->f_version = 0;
|
||||
file->private_data = ep;
|
||||
@@ -1574,7 +1572,7 @@ static int eventpollfs_delete_dentry(str
|
||||
}
|
||||
|
||||
|
||||
-static struct inode *ep_eventpoll_inode(void)
|
||||
+static struct inode *ep_eventpoll_inode(struct file_operations *fops)
|
||||
{
|
||||
int error = -ENOMEM;
|
||||
struct inode *inode = new_inode(eventpoll_mnt->mnt_sb);
|
||||
@@ -1582,7 +1580,7 @@ static struct inode *ep_eventpoll_inode(
|
||||
if (!inode)
|
||||
goto eexit_1;
|
||||
|
||||
- inode->i_fop = &eventpoll_fops;
|
||||
+ inode->i_fop = fops;
|
||||
|
||||
/*
|
||||
* Mark the inode dirty from the very beginning,
|
||||
|
||||
diff -pruN ../pristine-linux-2.6.16-rc5/include/linux/aio.h ./include/linux/aio.h
|
||||
--- ../pristine-linux-2.6.16-rc5/include/linux/aio.h 2006-03-14 14:10:21.597916731 +0000
|
||||
+++ ./include/linux/aio.h 2006-03-16 10:05:39.848833028 +0000
|
||||
@@ -191,6 +191,11 @@ struct kioctx {
|
||||
struct aio_ring_info ring_info;
|
||||
|
||||
struct work_struct wq;
|
||||
+#ifdef CONFIG_EPOLL
|
||||
+ // poll integration
|
||||
+ wait_queue_head_t poll_wait;
|
||||
+ struct file *file;
|
||||
+#endif
|
||||
};
|
||||
|
||||
/* prototypes */
|
||||
|
||||
diff -pruN ../pristine-linux-2.6.16-rc5/include/linux/eventpoll.h ./include/linux/eventpoll.h
|
||||
--- ../pristine-linux-2.6.16-rc5/include/linux/eventpoll.h 2006-01-03 03:21:10.000000000 +0000
|
||||
+++ ./include/linux/eventpoll.h 2006-03-16 10:08:51.577809317 +0000
|
||||
@@ -86,6 +86,12 @@ static inline void eventpoll_release(str
|
||||
}
|
||||
|
||||
|
||||
+/*
|
||||
+ * called by aio code to create fd that can poll the aio event queueQ
|
||||
+ */
|
||||
+struct eventpoll;
|
||||
+int ep_getfd(int *efd, struct inode **einode, struct file **efile,
|
||||
+ struct eventpoll *ep, struct file_operations *fops);
|
||||
#else
|
||||
|
||||
static inline void eventpoll_init_file(struct file *file) {}
|
||||
@@ -16,6 +16,8 @@ SUBDIRS-y += guest-headers
|
||||
SUBDIRS-$(VTPM_TOOLS) += vtpm_manager
|
||||
SUBDIRS-$(VTPM_TOOLS) += vtpm
|
||||
SUBDIRS-y += xenstat
|
||||
SUBDIRS-y += libaio
|
||||
SUBDIRS-y += blktap
|
||||
|
||||
# These don't cross-compile
|
||||
ifeq ($(XEN_COMPILE_ARCH),$(XEN_TARGET_ARCH))
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
XEN_ROOT = ../..
|
||||
include $(XEN_ROOT)/tools/Rules.mk
|
||||
|
||||
SUBDIRS-y :=
|
||||
SUBDIRS-y += lib
|
||||
SUBDIRS-y += drivers
|
||||
|
||||
.PHONY: all
|
||||
all: build
|
||||
|
||||
.PHONY: build
|
||||
build: mk-symlinks
|
||||
@set -e; for subdir in $(SUBDIRS-y); do \
|
||||
$(MAKE) -C $$subdir all; \
|
||||
done
|
||||
|
||||
.PHONY: install
|
||||
install:
|
||||
@set -e; for subdir in $(SUBDIRS-y); do \
|
||||
$(MAKE) -C $$subdir install; \
|
||||
done
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -rf *.a *.so *.o *.rpm $(LIB) *~ $(DEPS) xen TAGS
|
||||
@set -e; for subdir in $(SUBDIRS-y); do \
|
||||
$(MAKE) -C $$subdir clean; \
|
||||
done
|
||||
@@ -0,0 +1,122 @@
|
||||
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
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
XEN_ROOT = ../../..
|
||||
include $(XEN_ROOT)/tools/Rules.mk
|
||||
|
||||
INCLUDES += -I.. -I../lib
|
||||
|
||||
INSTALL = install
|
||||
INSTALL_PROG = $(INSTALL) -m0755
|
||||
IBIN = blktapctrl tapdisk
|
||||
QCOW_UTIL = img2qcow qcow2raw qcow-create
|
||||
INSTALL_DIR = /usr/sbin
|
||||
LIBAIO_DIR = ../../libaio/src
|
||||
|
||||
CFLAGS += -fPIC
|
||||
CFLAGS += -Wall
|
||||
CFLAGS += -Werror
|
||||
CFLAGS += -Wno-unused
|
||||
CFLAGS += -g3
|
||||
CFLAGS += -fno-strict-aliasing
|
||||
CFLAGS += -I $(XEN_LIBXC) -I $(LIBAIO_DIR)
|
||||
CFLAGS += $(INCLUDES) -I. -I../../xenstore
|
||||
CFLAGS += -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE
|
||||
CFLAGS += -D_GNU_SOURCE
|
||||
|
||||
# Get gcc to generate the dependencies for us.
|
||||
CFLAGS += -Wp,-MD,.$(@F).d
|
||||
DEPS = .*.d
|
||||
|
||||
THREADLIB := -lpthread -lz
|
||||
LIBS := -L. -L.. -L../lib
|
||||
LIBS += -L$(XEN_LIBXC)
|
||||
LIBS += -lblktap
|
||||
LIBS += -lcrypto
|
||||
LIBS += -lz
|
||||
LIBS += -L$(XEN_XENSTORE) -lxenstore
|
||||
|
||||
AIOLIBS := -L $(LIBAIO_DIR)
|
||||
AIOLIBS += -laio
|
||||
AIOLIBS += -static
|
||||
|
||||
BLK-OBJS := block-aio.o
|
||||
BLK-OBJS += block-sync.o
|
||||
BLK-OBJS += block-vmdk.o
|
||||
BLK-OBJS += block-ram.o
|
||||
BLK-OBJS += block-qcow.o
|
||||
BLK-OBJS += aes.o
|
||||
|
||||
all: $(IBIN) qcow-util
|
||||
|
||||
LINUX_ROOT := $(wildcard $(XEN_ROOT)/linux-2.6.*-xen-sparse)
|
||||
|
||||
|
||||
blktapctrl:
|
||||
$(CC) $(CFLAGS) -o blktapctrl $(LIBS) blktapctrl.c
|
||||
|
||||
tapdisk: $(BLK-OBJS)
|
||||
$(CC) $(CFLAGS) -o tapdisk $(BLK-OBJS) tapdisk.c \
|
||||
$(AIOLIBS) $(LIBS)
|
||||
|
||||
|
||||
qcow-util: $(BLK-OBJS)
|
||||
$(CC) $(CFLAGS) -o img2qcow $(BLK-OBJS) img2qcow.c \
|
||||
$(AIOLIBS) $(LIBS)
|
||||
$(CC) $(CFLAGS) -o qcow2raw $(BLK-OBJS) qcow2raw.c \
|
||||
$(AIOLIBS) $(LIBS)
|
||||
$(CC) $(CFLAGS) -o qcow-create $(BLK-OBJS) qcow-create.c \
|
||||
$(AIOLIBS) $(LIBS)
|
||||
|
||||
install: all
|
||||
$(INSTALL_PROG) $(IBIN) $(QCOW_UTIL) $(DESTDIR)$(INSTALL_DIR)
|
||||
|
||||
clean:
|
||||
rm -rf *.o *~ $(DEPS) xen TAGS $(IBIN) $(LIB) $(QCOW_UTIL)
|
||||
|
||||
.PHONY: clean install
|
||||
|
||||
-include $(DEPS)
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,26 @@
|
||||
#ifndef QEMU_AES_H
|
||||
#define QEMU_AES_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
|
||||
@@ -0,0 +1,704 @@
|
||||
/*
|
||||
* 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 <sys/user.h>
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
#include <linux/types.h>
|
||||
#include <signal.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/poll.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <xs.h>
|
||||
#include <printf.h>
|
||||
#include <sys/time.h>
|
||||
#include <syslog.h>
|
||||
|
||||
#include "blktaplib.h"
|
||||
#include "blktapctrl.h"
|
||||
#include "tapdisk.h"
|
||||
|
||||
#define NUM_POLL_FDS 2
|
||||
#define MSG_SIZE 4096
|
||||
#define MAX_TIMEOUT 10
|
||||
#define MAX_RAND_VAL 0xFFFF
|
||||
|
||||
int run = 1;
|
||||
int max_timeout = MAX_TIMEOUT;
|
||||
int ctlfd = 0;
|
||||
|
||||
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];
|
||||
|
||||
void sig_handler(int sig)
|
||||
{
|
||||
run = 0;
|
||||
}
|
||||
|
||||
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 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);
|
||||
}
|
||||
|
||||
static int get_new_dev(int *major, int *minor, blkif_t *blkif)
|
||||
{
|
||||
domid_translate_t tr;
|
||||
int ret;
|
||||
char *devname;
|
||||
|
||||
tr.domid = blkif->domid;
|
||||
tr.busid = (unsigned short)blkif->be_id;
|
||||
ret = ioctl(ctlfd, BLKTAP_IOCTL_NEWINTF, tr );
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
asprintf(&devname,"%s/%s%d",BLKTAP_DEV_DIR, BLKTAP_DEV_NAME, *minor);
|
||||
make_blktap_dev(devname,*major,*minor);
|
||||
DPRINTF("Received device id %d and major %d, "
|
||||
"sent domid %d and be_id %d\n",
|
||||
*minor, *major, tr.domid, tr.busid);
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static blkif_t *test_path(char *path, char **dev, int *type)
|
||||
{
|
||||
char *ptr, handle[10];
|
||||
int i, size;
|
||||
|
||||
size = sizeof(dtypes)/sizeof(disk_info_t *);
|
||||
*type = MAX_DISK_TYPES + 1;
|
||||
|
||||
if ( (ptr = strstr(path, ":"))!=NULL) {
|
||||
memcpy(handle, path, (ptr - path));
|
||||
*dev = ptr + 1;
|
||||
ptr = handle + (ptr - path);
|
||||
*ptr = '\0';
|
||||
DPRINTF("Detected handle: [%s]\n",handle);
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
if (strncmp(handle, dtypes[i]->handle, (ptr - path))
|
||||
==0) {
|
||||
*type = dtypes[i]->idnum;
|
||||
|
||||
if (dtypes[i]->single_handler == 1) {
|
||||
/* Check whether tapdisk process
|
||||
already exists */
|
||||
if (active_disks[dtypes[i]->idnum]
|
||||
== NULL) return NULL;
|
||||
else
|
||||
return active_disks[dtypes[i]->idnum]->blkif;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else *dev = NULL;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void add_disktype(blkif_t *blkif, int type)
|
||||
{
|
||||
driver_list_entry_t *entry, *ptr, *last;
|
||||
|
||||
if (type > MAX_DISK_TYPES) return;
|
||||
|
||||
entry = malloc(sizeof(driver_list_entry_t));
|
||||
entry->blkif = blkif;
|
||||
entry->next = NULL;
|
||||
ptr = active_disks[type];
|
||||
|
||||
if (ptr == NULL) {
|
||||
active_disks[type] = entry;
|
||||
entry->prev = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
while (ptr != NULL) {
|
||||
last = ptr;
|
||||
ptr = ptr->next;
|
||||
}
|
||||
|
||||
/*We've found the end of the list*/
|
||||
last->next = entry;
|
||||
entry->prev = last;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static int del_disktype(blkif_t *blkif)
|
||||
{
|
||||
driver_list_entry_t *ptr, *cur, *last;
|
||||
int type = blkif->drivertype, count = 0, close = 0;
|
||||
|
||||
if (type > MAX_DISK_TYPES) return 1;
|
||||
|
||||
ptr = active_disks[type];
|
||||
last = NULL;
|
||||
while (ptr != NULL) {
|
||||
count++;
|
||||
if (blkif == ptr->blkif) {
|
||||
cur = ptr;
|
||||
if (ptr->next != NULL) {
|
||||
/*There's more later in the chain*/
|
||||
if (!last) {
|
||||
/*We're first in the list*/
|
||||
active_disks[type] = ptr->next;
|
||||
ptr = ptr->next;
|
||||
ptr->prev = NULL;
|
||||
}
|
||||
else {
|
||||
/*We're sandwiched*/
|
||||
last->next = ptr->next;
|
||||
ptr = ptr->next;
|
||||
ptr->prev = last;
|
||||
}
|
||||
|
||||
} else if (last) {
|
||||
/*There's more earlier in the chain*/
|
||||
last->next = NULL;
|
||||
} else {
|
||||
/*We're the only entry*/
|
||||
active_disks[type] = NULL;
|
||||
if(dtypes[type]->single_handler == 1)
|
||||
close = 1;
|
||||
}
|
||||
DPRINTF("DEL_DISKTYPE: Freeing entry\n");
|
||||
free(cur);
|
||||
if (dtypes[type]->single_handler == 0) close = 1;
|
||||
|
||||
return close;
|
||||
}
|
||||
last = ptr;
|
||||
ptr = ptr->next;
|
||||
}
|
||||
DPRINTF("DEL_DISKTYPE: No match\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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: %lu, %lu, %lu\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;
|
||||
|
||||
}
|
||||
|
||||
int blktapctrl_new_blkif(blkif_t *blkif)
|
||||
{
|
||||
blkif_info_t *blk;
|
||||
int major, minor, fd_read, fd_write, type, new;
|
||||
char *rdctldev, *wrctldev, *cmd, *ptr;
|
||||
image_t *image;
|
||||
blkif_t *exist = NULL;
|
||||
|
||||
DPRINTF("Received a poll for a new vbd\n");
|
||||
if ( ((blk=blkif->info) != NULL) && (blk->params != NULL) ) {
|
||||
if (get_new_dev(&major, &minor, blkif)<0)
|
||||
return -1;
|
||||
|
||||
exist = test_path(blk->params, &ptr, &type);
|
||||
blkif->drivertype = type;
|
||||
blkif->cookie = lrand48() % MAX_RAND_VAL;
|
||||
|
||||
if (!exist) {
|
||||
DPRINTF("Process does not exist:\n");
|
||||
asprintf(&rdctldev, "/dev/xen/tapctrlread%d", minor);
|
||||
blkif->fds[READ] = open_ctrl_socket(rdctldev);
|
||||
|
||||
|
||||
asprintf(&wrctldev, "/dev/xen/tapctrlwrite%d", minor);
|
||||
blkif->fds[WRITE] = open_ctrl_socket(wrctldev);
|
||||
|
||||
if (blkif->fds[READ] == -1 || blkif->fds[WRITE] == -1)
|
||||
goto fail;
|
||||
|
||||
/*launch the new process*/
|
||||
asprintf(&cmd, "tapdisk %s %s", wrctldev, rdctldev);
|
||||
DPRINTF("Launching process, CMDLINE [%s]\n",cmd);
|
||||
if (system(cmd) == -1) {
|
||||
DPRINTF("Unable to fork, cmdline: [%s]\n",cmd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
free(rdctldev);
|
||||
free(wrctldev);
|
||||
free(cmd);
|
||||
} 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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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)) {
|
||||
close(blkif->fds[WRITE]);
|
||||
close(blkif->fds[READ]);
|
||||
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int open_ctrl_socket(char *devname)
|
||||
{
|
||||
int ret;
|
||||
int ipc_fd;
|
||||
char *cmd;
|
||||
fd_set socks;
|
||||
struct timeval timeout;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
char *devname;
|
||||
tapdev_info_t *ctlinfo;
|
||||
int tap_pfd, store_pfd, xs_fd, ret, timeout, pfd_count;
|
||||
struct xs_handle *h;
|
||||
struct pollfd pfd[NUM_POLL_FDS];
|
||||
pid_t process;
|
||||
|
||||
__init_blkif();
|
||||
openlog("BLKTAPCTRL", LOG_CONS|LOG_ODELAY, LOG_DAEMON);
|
||||
|
||||
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);
|
||||
|
||||
/*Attach to blktap0 */
|
||||
asprintf(&devname,"%s/%s0", BLKTAP_DEV_DIR, BLKTAP_DEV_NAME);
|
||||
make_blktap_dev(devname,254,0);
|
||||
ctlfd = open(devname, O_RDWR);
|
||||
if (ctlfd == -1) {
|
||||
DPRINTF("blktap0 open failed\n");
|
||||
goto open_failed;
|
||||
}
|
||||
|
||||
/* Set up store connection and watch. */
|
||||
h = xs_daemon_open();
|
||||
if (h == NULL) {
|
||||
DPRINTF("xs_daemon_open failed -- "
|
||||
"is xenstore running?\n");
|
||||
goto open_failed;
|
||||
}
|
||||
|
||||
ret = add_blockdevice_probe_watch(h, "Domain-0");
|
||||
if (ret != 0) {
|
||||
DPRINTF("adding device probewatch\n");
|
||||
goto open_failed;
|
||||
}
|
||||
|
||||
ioctl(ctlfd, BLKTAP_IOCTL_SETMODE, BLKTAP_MODE_INTERPOSE );
|
||||
|
||||
process = getpid();
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ioctl(ctlfd, BLKTAP_IOCTL_SETMODE, BLKTAP_MODE_PASSTHROUGH );
|
||||
close(ctlfd);
|
||||
closelog();
|
||||
|
||||
return 0;
|
||||
|
||||
open_failed:
|
||||
DPRINTF("Unable to start blktapctrl\n");
|
||||
closelog();
|
||||
return -1;
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
|
||||
static inline long int tapdisk_get_size(blkif_t *blkif)
|
||||
{
|
||||
image_t *img = (image_t *)blkif->prv;
|
||||
return img->size;
|
||||
}
|
||||
|
||||
static inline long int tapdisk_get_secsize(blkif_t *blkif)
|
||||
{
|
||||
image_t *img = (image_t *)blkif->prv;
|
||||
return img->secsize;
|
||||
}
|
||||
|
||||
static inline unsigned 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,
|
||||
};
|
||||
@@ -0,0 +1,327 @@
|
||||
/* 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 <linux/fs.h>
|
||||
#include "tapdisk.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
|
||||
|
||||
#define MAX_AIO_REQS (MAX_REQUESTS * MAX_SEGMENTS_PER_REQ * 8)
|
||||
|
||||
struct pending_aio {
|
||||
td_callback_t cb;
|
||||
int id;
|
||||
void *private;
|
||||
};
|
||||
|
||||
struct tdaio_state {
|
||||
int fd;
|
||||
|
||||
/* libaio state */
|
||||
io_context_t aio_ctx;
|
||||
struct iocb iocb_list [MAX_AIO_REQS];
|
||||
struct iocb *iocb_free [MAX_AIO_REQS];
|
||||
struct pending_aio pending_aio[MAX_AIO_REQS];
|
||||
int iocb_free_count;
|
||||
struct iocb *iocb_queue[MAX_AIO_REQS];
|
||||
int iocb_queued;
|
||||
int poll_fd; /* NB: we require aio_poll support */
|
||||
struct io_event aio_events[MAX_AIO_REQS];
|
||||
};
|
||||
|
||||
#define IOCB_IDX(_s, _io) ((_io) - (_s)->iocb_list)
|
||||
|
||||
/*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*/
|
||||
s->size = 0;
|
||||
if (ioctl(fd,BLKGETSIZE,&s->size)!=0) {
|
||||
DPRINTF("ERR: BLKGETSIZE failed, couldn't stat image");
|
||||
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 defined(BLKSSZGET)
|
||||
{
|
||||
int arg;
|
||||
s->sector_size = DEFAULT_SECTOR_SIZE;
|
||||
ioctl(fd, BLKSSZGET, &s->sector_size);
|
||||
|
||||
if (s->sector_size != DEFAULT_SECTOR_SIZE)
|
||||
DPRINTF("Note: sector size is %ld (not %d)\n",
|
||||
s->sector_size, DEFAULT_SECTOR_SIZE);
|
||||
}
|
||||
#else
|
||||
s->sector_size = DEFAULT_SECTOR_SIZE;
|
||||
#endif
|
||||
|
||||
} 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;
|
||||
}
|
||||
|
||||
/* Open the disk file and initialize aio state. */
|
||||
int tdaio_open (struct td_state *s, const char *name)
|
||||
{
|
||||
int i, fd, ret = 0;
|
||||
struct tdaio_state *prv = (struct tdaio_state *)s->private;
|
||||
s->private = prv;
|
||||
|
||||
DPRINTF("XXX: block-aio open('%s')", name);
|
||||
/* Initialize AIO */
|
||||
prv->iocb_free_count = MAX_AIO_REQS;
|
||||
prv->iocb_queued = 0;
|
||||
|
||||
prv->aio_ctx = (io_context_t) REQUEST_ASYNC_FD;
|
||||
prv->poll_fd = io_setup(MAX_AIO_REQS, &prv->aio_ctx);
|
||||
|
||||
if (prv->poll_fd < 0) {
|
||||
ret = prv->poll_fd;
|
||||
DPRINTF("Couldn't get fd for AIO poll support. This is "
|
||||
"probably because your kernel does not have the "
|
||||
"aio-poll patch applied.\n");
|
||||
goto done;
|
||||
}
|
||||
|
||||
for (i=0;i<MAX_AIO_REQS;i++)
|
||||
prv->iocb_free[i] = &prv->iocb_list[i];
|
||||
|
||||
/* Open the file */
|
||||
fd = open(name, O_RDWR | O_DIRECT | O_LARGEFILE);
|
||||
|
||||
if ( (fd == -1) && (errno == EINVAL) ) {
|
||||
|
||||
/* Maybe O_DIRECT isn't supported. */
|
||||
fd = open(name, O_RDWR | O_LARGEFILE);
|
||||
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;
|
||||
|
||||
ret = get_image_info(s, fd);
|
||||
done:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int tdaio_queue_read(struct td_state *s, uint64_t sector,
|
||||
int nb_sectors, char *buf, td_callback_t cb,
|
||||
int id, void *private)
|
||||
{
|
||||
struct iocb *io;
|
||||
struct pending_aio *pio;
|
||||
struct tdaio_state *prv = (struct tdaio_state *)s->private;
|
||||
int size = nb_sectors * s->sector_size;
|
||||
uint64_t offset = sector * (uint64_t)s->sector_size;
|
||||
long ioidx;
|
||||
|
||||
if (prv->iocb_free_count == 0)
|
||||
return -ENOMEM;
|
||||
io = prv->iocb_free[--prv->iocb_free_count];
|
||||
|
||||
ioidx = IOCB_IDX(prv, io);
|
||||
pio = &prv->pending_aio[ioidx];
|
||||
pio->cb = cb;
|
||||
pio->id = id;
|
||||
pio->private = private;
|
||||
|
||||
io_prep_pread(io, prv->fd, buf, size, offset);
|
||||
io->data = (void *)ioidx;
|
||||
|
||||
prv->iocb_queue[prv->iocb_queued++] = io;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tdaio_queue_write(struct td_state *s, uint64_t sector,
|
||||
int nb_sectors, char *buf, td_callback_t cb,
|
||||
int id, void *private)
|
||||
{
|
||||
struct iocb *io;
|
||||
struct pending_aio *pio;
|
||||
struct tdaio_state *prv = (struct tdaio_state *)s->private;
|
||||
int size = nb_sectors * s->sector_size;
|
||||
uint64_t offset = sector * (uint64_t)s->sector_size;
|
||||
long ioidx;
|
||||
|
||||
if (prv->iocb_free_count == 0)
|
||||
return -ENOMEM;
|
||||
io = prv->iocb_free[--prv->iocb_free_count];
|
||||
|
||||
ioidx = IOCB_IDX(prv, io);
|
||||
pio = &prv->pending_aio[ioidx];
|
||||
pio->cb = cb;
|
||||
pio->id = id;
|
||||
pio->private = private;
|
||||
|
||||
io_prep_pwrite(io, prv->fd, buf, size, offset);
|
||||
io->data = (void *)ioidx;
|
||||
|
||||
prv->iocb_queue[prv->iocb_queued++] = io;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tdaio_submit(struct td_state *s)
|
||||
{
|
||||
int ret;
|
||||
struct tdaio_state *prv = (struct tdaio_state *)s->private;
|
||||
|
||||
ret = io_submit(prv->aio_ctx, prv->iocb_queued, prv->iocb_queue);
|
||||
|
||||
/* XXX: TODO: Handle error conditions here. */
|
||||
|
||||
/* Success case: */
|
||||
prv->iocb_queued = 0;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int *tdaio_get_fd(struct td_state *s)
|
||||
{
|
||||
struct tdaio_state *prv = (struct tdaio_state *)s->private;
|
||||
int *fds, i;
|
||||
|
||||
fds = malloc(sizeof(int) * MAX_IOFD);
|
||||
/*initialise the FD array*/
|
||||
for(i=0;i<MAX_IOFD;i++) fds[i] = 0;
|
||||
|
||||
fds[0] = prv->poll_fd;
|
||||
|
||||
return fds;
|
||||
}
|
||||
|
||||
int tdaio_close(struct td_state *s)
|
||||
{
|
||||
struct tdaio_state *prv = (struct tdaio_state *)s->private;
|
||||
|
||||
io_destroy(prv->aio_ctx);
|
||||
close(prv->fd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tdaio_do_callbacks(struct td_state *s, int sid)
|
||||
{
|
||||
int ret, i, rsp = 0;
|
||||
struct io_event *ep;
|
||||
struct tdaio_state *prv = (struct tdaio_state *)s->private;
|
||||
|
||||
/* Non-blocking test for completed io. */
|
||||
ret = io_getevents(prv->aio_ctx, 0, MAX_AIO_REQS, prv->aio_events,
|
||||
NULL);
|
||||
|
||||
for (ep=prv->aio_events,i=ret; i-->0; ep++) {
|
||||
struct iocb *io = ep->obj;
|
||||
struct pending_aio *pio;
|
||||
|
||||
pio = &prv->pending_aio[(long)io->data];
|
||||
|
||||
if (ep->res != io->u.c.nbytes) {
|
||||
/* TODO: handle this case better. */
|
||||
DPRINTF("AIO did less than I asked it to. \n");
|
||||
}
|
||||
rsp += pio->cb(s, ep->res2, pio->id, pio->private);
|
||||
|
||||
prv->iocb_free[prv->iocb_free_count++] = io;
|
||||
}
|
||||
return rsp;
|
||||
}
|
||||
|
||||
struct tap_disk tapdisk_aio = {
|
||||
"tapdisk_aio",
|
||||
sizeof(struct tdaio_state),
|
||||
tdaio_open,
|
||||
tdaio_queue_read,
|
||||
tdaio_queue_write,
|
||||
tdaio_submit,
|
||||
tdaio_get_fd,
|
||||
tdaio_close,
|
||||
tdaio_do_callbacks,
|
||||
};
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,296 @@
|
||||
/* 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 <unistd.h>
|
||||
#include <sys/statvfs.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <linux/fs.h>
|
||||
#include <string.h>
|
||||
#include "tapdisk.h"
|
||||
|
||||
#define MAX_DISK_SIZE 1024000 /*500MB disk limit*/
|
||||
|
||||
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*/
|
||||
s->size = 0;
|
||||
if (ioctl(fd,BLKGETSIZE,&s->size)!=0) {
|
||||
DPRINTF("ERR: BLKGETSIZE failed, couldn't stat image");
|
||||
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 defined(BLKSSZGET)
|
||||
{
|
||||
int arg;
|
||||
s->sector_size = DEFAULT_SECTOR_SIZE;
|
||||
ioctl(fd, BLKSSZGET, &s->sector_size);
|
||||
|
||||
if (s->sector_size != DEFAULT_SECTOR_SIZE)
|
||||
DPRINTF("Note: sector size is %ld (not %d)\n",
|
||||
s->sector_size, DEFAULT_SECTOR_SIZE);
|
||||
}
|
||||
#else
|
||||
s->sector_size = DEFAULT_SECTOR_SIZE;
|
||||
#endif
|
||||
|
||||
} 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[%lu]\n",
|
||||
s->sector_size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Open the disk file and initialize ram state. */
|
||||
int tdram_open (struct td_state *s, const char *name)
|
||||
{
|
||||
int i, fd, ret = 0, count = 0;
|
||||
struct tdram_state *prv = (struct tdram_state *)s->private;
|
||||
uint64_t size;
|
||||
char *p;
|
||||
s->private = prv;
|
||||
|
||||
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[%lu]\n",
|
||||
s->sector_size);
|
||||
|
||||
prv->fd = -1;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Open the file */
|
||||
fd = open(name, O_RDWR | O_DIRECT | O_LARGEFILE);
|
||||
|
||||
if ((fd == -1) && (errno == EINVAL)) {
|
||||
|
||||
/* Maybe O_DIRECT isn't supported. */
|
||||
fd = open(name, O_RDWR | O_LARGEFILE);
|
||||
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;
|
||||
}
|
||||
|
||||
done:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int tdram_queue_read(struct td_state *s, uint64_t sector,
|
||||
int nb_sectors, char *buf, td_callback_t cb,
|
||||
int id, void *private)
|
||||
{
|
||||
struct tdram_state *prv = (struct tdram_state *)s->private;
|
||||
int size = nb_sectors * s->sector_size;
|
||||
uint64_t offset = sector * (uint64_t)s->sector_size;
|
||||
int ret;
|
||||
|
||||
memcpy(buf, img + offset, size);
|
||||
ret = size;
|
||||
|
||||
cb(s, (ret < 0) ? ret: 0, id, private);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int tdram_queue_write(struct td_state *s, uint64_t sector,
|
||||
int nb_sectors, char *buf, td_callback_t cb,
|
||||
int id, void *private)
|
||||
{
|
||||
struct tdram_state *prv = (struct tdram_state *)s->private;
|
||||
int size = nb_sectors * s->sector_size;
|
||||
uint64_t offset = sector * (uint64_t)s->sector_size;
|
||||
int ret;
|
||||
|
||||
/*We assume that write access is controlled at a higher level for multiple disks*/
|
||||
memcpy(img + offset, buf, size);
|
||||
ret = size;
|
||||
|
||||
cb(s, (ret < 0) ? ret : 0, id, private);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int tdram_submit(struct td_state *s)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int *tdram_get_fd(struct td_state *s)
|
||||
{
|
||||
struct tdram_state *prv = (struct tdram_state *)s->private;
|
||||
int *fds, i;
|
||||
|
||||
fds = malloc(sizeof(int) * MAX_IOFD);
|
||||
/*initialise the FD array*/
|
||||
for(i=0;i<MAX_IOFD;i++) fds[i] = 0;
|
||||
|
||||
fds[0] = prv->poll_pipe[0];
|
||||
return fds;
|
||||
}
|
||||
|
||||
int tdram_close(struct td_state *s)
|
||||
{
|
||||
struct tdram_state *prv = (struct tdram_state *)s->private;
|
||||
|
||||
connections--;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tdram_do_callbacks(struct td_state *s, int sid)
|
||||
{
|
||||
/* always ask for a kick */
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct tap_disk tapdisk_ram = {
|
||||
"tapdisk_ram",
|
||||
sizeof(struct tdram_state),
|
||||
tdram_open,
|
||||
tdram_queue_read,
|
||||
tdram_queue_write,
|
||||
tdram_submit,
|
||||
tdram_get_fd,
|
||||
tdram_close,
|
||||
tdram_do_callbacks,
|
||||
};
|
||||
|
||||
@@ -0,0 +1,242 @@
|
||||
/* 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 <linux/fs.h>
|
||||
#include "tapdisk.h"
|
||||
|
||||
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*/
|
||||
s->size = 0;
|
||||
if (ioctl(fd,BLKGETSIZE,&s->size)!=0) {
|
||||
DPRINTF("ERR: BLKGETSIZE failed, couldn't stat image");
|
||||
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 defined(BLKSSZGET)
|
||||
{
|
||||
int arg;
|
||||
s->sector_size = DEFAULT_SECTOR_SIZE;
|
||||
ioctl(fd, BLKSSZGET, &s->sector_size);
|
||||
|
||||
if (s->sector_size != DEFAULT_SECTOR_SIZE)
|
||||
DPRINTF("Note: sector size is %ld (not %d)\n",
|
||||
s->sector_size, DEFAULT_SECTOR_SIZE);
|
||||
}
|
||||
#else
|
||||
s->sector_size = DEFAULT_SECTOR_SIZE;
|
||||
#endif
|
||||
|
||||
} 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;
|
||||
}
|
||||
|
||||
/* Open the disk file and initialize aio state. */
|
||||
int tdsync_open (struct td_state *s, const char *name)
|
||||
{
|
||||
int i, fd, ret = 0;
|
||||
struct tdsync_state *prv = (struct tdsync_state *)s->private;
|
||||
s->private = prv;
|
||||
|
||||
/* 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 */
|
||||
fd = open(name, O_RDWR | O_DIRECT | O_LARGEFILE);
|
||||
|
||||
if ( (fd == -1) && (errno == EINVAL) ) {
|
||||
|
||||
/* Maybe O_DIRECT isn't supported. */
|
||||
fd = open(name, O_RDWR | O_LARGEFILE);
|
||||
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);
|
||||
done:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int tdsync_queue_read(struct td_state *s, uint64_t sector,
|
||||
int nb_sectors, char *buf, td_callback_t cb,
|
||||
int id, void *private)
|
||||
{
|
||||
struct tdsync_state *prv = (struct tdsync_state *)s->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;
|
||||
|
||||
cb(s, (ret < 0) ? ret: 0, id, private);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int tdsync_queue_write(struct td_state *s, uint64_t sector,
|
||||
int nb_sectors, char *buf, td_callback_t cb,
|
||||
int id, void *private)
|
||||
{
|
||||
struct tdsync_state *prv = (struct tdsync_state *)s->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;
|
||||
|
||||
cb(s, (ret < 0) ? ret : 0, id, private);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int tdsync_submit(struct td_state *s)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int *tdsync_get_fd(struct td_state *s)
|
||||
{
|
||||
struct tdsync_state *prv = (struct tdsync_state *)s->private;
|
||||
|
||||
int *fds, i;
|
||||
|
||||
fds = malloc(sizeof(int) * MAX_IOFD);
|
||||
/*initialise the FD array*/
|
||||
for(i=0;i<MAX_IOFD;i++) fds[i] = 0;
|
||||
|
||||
fds[0] = prv->poll_pipe[0];
|
||||
return fds;
|
||||
}
|
||||
|
||||
int tdsync_close(struct td_state *s)
|
||||
{
|
||||
struct tdsync_state *prv = (struct tdsync_state *)s->private;
|
||||
|
||||
close(prv->fd);
|
||||
close(prv->poll_pipe[0]);
|
||||
close(prv->poll_pipe[1]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tdsync_do_callbacks(struct td_state *s, int sid)
|
||||
{
|
||||
/* always ask for a kick */
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct tap_disk tapdisk_sync = {
|
||||
"tapdisk_sync",
|
||||
sizeof(struct tdsync_state),
|
||||
tdsync_open,
|
||||
tdsync_queue_read,
|
||||
tdsync_queue_write,
|
||||
tdsync_submit,
|
||||
tdsync_get_fd,
|
||||
tdsync_close,
|
||||
tdsync_do_callbacks,
|
||||
};
|
||||
|
||||
@@ -0,0 +1,415 @@
|
||||
/* 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 <linux/fs.h>
|
||||
#include <string.h>
|
||||
#include "tapdisk.h"
|
||||
#include "bswap.h"
|
||||
|
||||
#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;
|
||||
};
|
||||
|
||||
|
||||
/* Open the disk file and initialize aio state. */
|
||||
static int tdvmdk_open (struct td_state *s, const char *name)
|
||||
{
|
||||
int ret, fd;
|
||||
int l1_size, i;
|
||||
uint32_t magic;
|
||||
struct tdvmdk_state *prv = (struct tdvmdk_state *)s->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 */
|
||||
fd = open(name, O_RDWR | O_LARGEFILE);
|
||||
|
||||
if ( (fd == -1) && (errno == EINVAL) ) {
|
||||
|
||||
/* Maybe O_DIRECT isn't supported. */
|
||||
fd = open(name, O_RDWR | O_LARGEFILE);
|
||||
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;
|
||||
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 td_state *s,
|
||||
uint64_t offset, int allocate)
|
||||
{
|
||||
struct tdvmdk_state *prv = (struct tdvmdk_state *)s->private;
|
||||
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);
|
||||
ftruncate(prv->fd, cluster_offset +
|
||||
(prv->cluster_sectors << 9));
|
||||
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 td_state *s, uint64_t sector,
|
||||
int nb_sectors, char *buf, td_callback_t cb,
|
||||
int id, void *private)
|
||||
{
|
||||
struct tdvmdk_state *prv = (struct tdvmdk_state *)s->private;
|
||||
int index_in_cluster, n;
|
||||
uint64_t cluster_offset;
|
||||
int ret = 0;
|
||||
while (nb_sectors > 0) {
|
||||
cluster_offset = get_cluster_offset(s, 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:
|
||||
cb(s, ret == -1 ? -1 : 0, id, private);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int tdvmdk_queue_write(struct td_state *s, uint64_t sector,
|
||||
int nb_sectors, char *buf, td_callback_t cb,
|
||||
int id, void *private)
|
||||
{
|
||||
struct tdvmdk_state *prv = (struct tdvmdk_state *)s->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(s, 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:
|
||||
cb(s, ret == -1 ? -1 : 0, id, private);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int tdvmdk_submit(struct td_state *s)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int *tdvmdk_get_fd(struct td_state *s)
|
||||
{
|
||||
struct tdvmdk_state *prv = (struct tdvmdk_state *)s->private;
|
||||
int *fds, i;
|
||||
|
||||
fds = malloc(sizeof(int) * MAX_IOFD);
|
||||
/*initialise the FD array*/
|
||||
for (i=0;i<MAX_IOFD;i++) fds[i] = 0;
|
||||
|
||||
fds[0] = prv->poll_pipe[0];
|
||||
return fds;
|
||||
}
|
||||
|
||||
static int tdvmdk_close(struct td_state *s)
|
||||
{
|
||||
struct tdvmdk_state *prv = (struct tdvmdk_state *)s->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 td_state *s, int sid)
|
||||
{
|
||||
/* always ask for a kick */
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct tap_disk tapdisk_vmdk = {
|
||||
"tapdisk_vmdk",
|
||||
sizeof(struct tdvmdk_state),
|
||||
tdvmdk_open,
|
||||
tdvmdk_queue_read,
|
||||
tdvmdk_queue_write,
|
||||
tdvmdk_submit,
|
||||
tdvmdk_get_fd,
|
||||
tdvmdk_close,
|
||||
tdvmdk_do_callbacks,
|
||||
};
|
||||
|
||||
@@ -0,0 +1,202 @@
|
||||
#ifndef BSWAP_H
|
||||
#define BSWAP_H
|
||||
|
||||
//#include "config-host.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#ifdef HAVE_BYTESWAP_H
|
||||
#include <byteswap.h>
|
||||
#else
|
||||
|
||||
#define bswap_16(x) \
|
||||
({ \
|
||||
uint16_t __x = (x); \
|
||||
((uint16_t)( \
|
||||
(((uint16_t)(__x) & (uint16_t)0x00ffU) << 8) | \
|
||||
(((uint16_t)(__x) & (uint16_t)0xff00U) >> 8) )); \
|
||||
})
|
||||
|
||||
#define bswap_32(x) \
|
||||
({ \
|
||||
uint32_t __x = (x); \
|
||||
((uint32_t)( \
|
||||
(((uint32_t)(__x) & (uint32_t)0x000000ffUL) << 24) | \
|
||||
(((uint32_t)(__x) & (uint32_t)0x0000ff00UL) << 8) | \
|
||||
(((uint32_t)(__x) & (uint32_t)0x00ff0000UL) >> 8) | \
|
||||
(((uint32_t)(__x) & (uint32_t)0xff000000UL) >> 24) )); \
|
||||
})
|
||||
|
||||
#define bswap_64(x) \
|
||||
({ \
|
||||
uint64_t __x = (x); \
|
||||
((uint64_t)( \
|
||||
(uint64_t)(((uint64_t)(__x) & (uint64_t)0x00000000000000ffULL) << 56) | \
|
||||
(uint64_t)(((uint64_t)(__x) & (uint64_t)0x000000000000ff00ULL) << 40) | \
|
||||
(uint64_t)(((uint64_t)(__x) & (uint64_t)0x0000000000ff0000ULL) << 24) | \
|
||||
(uint64_t)(((uint64_t)(__x) & (uint64_t)0x00000000ff000000ULL) << 8) | \
|
||||
(uint64_t)(((uint64_t)(__x) & (uint64_t)0x000000ff00000000ULL) >> 8) | \
|
||||
(uint64_t)(((uint64_t)(__x) & (uint64_t)0x0000ff0000000000ULL) >> 24) | \
|
||||
(uint64_t)(((uint64_t)(__x) & (uint64_t)0x00ff000000000000ULL) >> 40) | \
|
||||
(uint64_t)(((uint64_t)(__x) & (uint64_t)0xff00000000000000ULL) >> 56) )); \
|
||||
})
|
||||
|
||||
#endif /* !HAVE_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);
|
||||
}
|
||||
|
||||
#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 */
|
||||
@@ -0,0 +1,289 @@
|
||||
/* 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 <linux/fs.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 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];
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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*/
|
||||
s->size = 0;
|
||||
if (ioctl(fd,BLKGETSIZE,&s->size)!=0) {
|
||||
DFPRINTF("ERR: BLKGETSIZE failed, "
|
||||
"couldn't stat image");
|
||||
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 defined(BLKSSZGET)
|
||||
{
|
||||
int arg;
|
||||
s->sector_size = DEFAULT_SECTOR_SIZE;
|
||||
ioctl(fd, BLKSSZGET, &s->sector_size);
|
||||
|
||||
if (s->sector_size != DEFAULT_SECTOR_SIZE)
|
||||
DFPRINTF("Note: sector size is %ld (not %d)\n",
|
||||
s->sector_size, DEFAULT_SECTOR_SIZE);
|
||||
}
|
||||
#else
|
||||
s->sector_size = DEFAULT_SECTOR_SIZE;
|
||||
#endif
|
||||
|
||||
} 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 td_state *s, int res, 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 tap_disk *drv;
|
||||
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);
|
||||
|
||||
drv = &tapdisk_qcow;
|
||||
s->private = malloc(drv->private_data_size);
|
||||
|
||||
/*Open qcow file*/
|
||||
if (drv->td_open(s, argv[1])!=0) {
|
||||
DFPRINTF("Unable to open Qcow file [%s]\n",argv[1]);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
io_fd = drv->td_get_fd(s);
|
||||
|
||||
/*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 = drv->td_queue_write(s, 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)
|
||||
drv->td_submit(s);
|
||||
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) drv->td_do_callbacks(s, 0);
|
||||
if (complete && (returned_events == submit_events))
|
||||
running = 0;
|
||||
}
|
||||
memcpy(output+prev+1,"=",1);
|
||||
DFPRINTF("\r%s 100%%\nTRANSFER COMPLETE\n\n", output);
|
||||
drv->td_close(s);
|
||||
free(s->private);
|
||||
free(s);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/* 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 <linux/fs.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
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int ret = -1;
|
||||
uint64_t size;
|
||||
|
||||
if ( (argc < 3) || (argc > 4) ) {
|
||||
fprintf(stderr, "Qcow-utils: v1.0.0\n");
|
||||
fprintf(stderr,
|
||||
"usage: %s <SIZE(MB)> <FILENAME> "
|
||||
"[<BACKING_FILENAME>]\n",
|
||||
argv[0]);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
size = atoi(argv[1]);
|
||||
size = size << 20;
|
||||
DFPRINTF("Creating file size %llu\n",(long long unsigned)size);
|
||||
switch(argc) {
|
||||
case 3:
|
||||
ret = qcow_create(argv[2],size,NULL,0);
|
||||
break;
|
||||
case 4:
|
||||
ret = qcow_create(argv[2],size,argv[3],0);
|
||||
break;
|
||||
}
|
||||
if (ret < 0) DPRINTF("Unable to create QCOW file\n");
|
||||
else DPRINTF("QCOW file successfully created\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,346 @@
|
||||
/* 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 <unistd.h>
|
||||
#include <sys/statvfs.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <linux/fs.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 TAPDISK 1
|
||||
#define BLOCK_PROCESSSZ 4096
|
||||
|
||||
static int maxfds, *qcowio_fd, *aio_fd, running = 1, complete = 0;
|
||||
static int read_complete = 0, write_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 tap_disk *drv1, *drv2;
|
||||
struct td_state *sqcow, *saio;
|
||||
static uint64_t prev = 0, written = 0;
|
||||
static char output[25];
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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 td_state *s, int res, 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;
|
||||
if (complete && (returned_write_events == submit_events))
|
||||
write_complete = 1;
|
||||
|
||||
debug_output(written, s->size << 9);
|
||||
free(private);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int send_read_responses(struct td_state *s, int res, int idx, void *private)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (res < 0) DFPRINTF("AIO FAILURE: res [%d]!\n",res);
|
||||
|
||||
returned_read_events++;
|
||||
read_idx = idx;
|
||||
if (complete && (returned_read_events == submit_events))
|
||||
read_complete = 1;
|
||||
|
||||
ret = drv2->td_queue_write(saio, idx, BLOCK_PROCESSSZ>>9, private,
|
||||
send_write_responses, idx, private);
|
||||
if (ret != 0) {
|
||||
DFPRINTF("ERROR in submitting queue write!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ( (complete && returned_read_events == submit_events) ||
|
||||
(returned_read_events % 10 == 0) ) {
|
||||
drv2->td_submit(saio);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int ret = -1, fd, len,input;
|
||||
long int 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);
|
||||
}
|
||||
|
||||
sqcow = malloc(sizeof(struct td_state));
|
||||
saio = malloc(sizeof(struct td_state));
|
||||
|
||||
/*Open qcow source file*/
|
||||
drv1 = &tapdisk_qcow;
|
||||
sqcow->private = malloc(drv1->private_data_size);
|
||||
|
||||
if (drv1->td_open(sqcow, argv[2])!=0) {
|
||||
DFPRINTF("Unable to open Qcow file [%s]\n",argv[2]);
|
||||
exit(-1);
|
||||
} else DFPRINTF("QCOW file opened, size %llu\n",
|
||||
(long long unsigned)sqcow->size);
|
||||
|
||||
qcowio_fd = drv1->td_get_fd(sqcow);
|
||||
|
||||
/*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)sqcow->size<<9) < 0) {
|
||||
DFPRINTF("Unable to create file "
|
||||
"[%s] of size %llu (errno %d). "
|
||||
"Exiting...\n",
|
||||
argv[1],
|
||||
(long long unsigned)sqcow->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(ioctl(fd,BLKGETSIZE,&size)!=0) {
|
||||
DFPRINTF("ERROR: BLKGETSIZE failed, "
|
||||
"couldn't stat image [%s]\n",
|
||||
argv[1]);
|
||||
close(fd);
|
||||
exit(-1);
|
||||
}
|
||||
if (size < sqcow->size<<9) {
|
||||
DFPRINTF("ERROR: Not enough space on device "
|
||||
"%s (%lu bytes available, %llu bytes required\n",
|
||||
argv[1], size,
|
||||
(long long unsigned)sqcow->size<<9);
|
||||
close(fd);
|
||||
exit(-1);
|
||||
}
|
||||
} else {
|
||||
if (ftruncate(fd, (off_t)sqcow->size<<9) < 0) {
|
||||
DFPRINTF("Unable to create file "
|
||||
"[%s] of size %llu (errno %d). "
|
||||
"Exiting...\n",
|
||||
argv[1],
|
||||
(long long unsigned)sqcow->size<<9,
|
||||
0 - errno);
|
||||
close(fd);
|
||||
exit(-1);
|
||||
} else DFPRINTF("File [%s] truncated to length %llu "
|
||||
"(%llu)\n",
|
||||
argv[1],
|
||||
(long long unsigned)sqcow->size<<9,
|
||||
(long long unsigned)sqcow->size);
|
||||
}
|
||||
close(fd);
|
||||
}
|
||||
|
||||
/*Open aio destination file*/
|
||||
drv2 = &tapdisk_aio;
|
||||
saio->private = malloc(drv2->private_data_size);
|
||||
|
||||
if (drv2->td_open(saio, argv[1])!=0) {
|
||||
DFPRINTF("Unable to open Qcow file [%s]\n", argv[1]);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
aio_fd = drv2->td_get_fd(saio);
|
||||
|
||||
/*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*/
|
||||
ret = drv1->td_queue_read(sqcow, 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;
|
||||
submit_events++;
|
||||
}
|
||||
|
||||
if (i >= sqcow->size<<9) {
|
||||
complete = 1;
|
||||
}
|
||||
|
||||
if ((submit_events % 10 == 0) || complete)
|
||||
drv1->td_submit(sqcow);
|
||||
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))
|
||||
drv1->td_do_callbacks(sqcow, 0);
|
||||
if (FD_ISSET(aio_fd[0], &readfds))
|
||||
drv2->td_do_callbacks(saio, 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;
|
||||
}
|
||||
@@ -0,0 +1,671 @@
|
||||
/* 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 <linux/fs.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;
|
||||
|
||||
void usage(void)
|
||||
{
|
||||
fprintf(stderr, "blktap-utils: v1.0.0\n");
|
||||
fprintf(stderr, "usage: tapdisk <READ fifo> <WRITE fifo>\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
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 unmap_disk(struct td_state *s)
|
||||
{
|
||||
tapdev_info_t *info = s->ring_info;
|
||||
struct tap_disk *drv = s->drv;
|
||||
fd_list_entry_t *ptr, *prev;
|
||||
|
||||
drv->td_close(s);
|
||||
|
||||
if (info != NULL && info->mem > 0)
|
||||
munmap(info->mem, PAGE_SIZE * BLKTAP_MMAP_REGION_SIZE);
|
||||
|
||||
ptr = s->fd_entry;
|
||||
prev = ptr->prev;
|
||||
|
||||
if (prev) {
|
||||
/*There are entries earlier in the list*/
|
||||
prev->next = ptr->next;
|
||||
if (ptr->next) {
|
||||
ptr = ptr->next;
|
||||
ptr->prev = prev;
|
||||
}
|
||||
} else {
|
||||
/*We are the first entry in list*/
|
||||
if (ptr->next) {
|
||||
ptr = ptr->next;
|
||||
fd_start = ptr;
|
||||
ptr->prev = NULL;
|
||||
} else fd_start = NULL;
|
||||
}
|
||||
|
||||
close(info->fd);
|
||||
|
||||
free(s->fd_entry);
|
||||
free(s->blkif);
|
||||
free(s->ring_info);
|
||||
free(s);
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
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;
|
||||
int i;
|
||||
|
||||
ptr = fd_start;
|
||||
while (ptr != NULL) {
|
||||
if (ptr->tap_fd) {
|
||||
FD_SET(ptr->tap_fd, readfds);
|
||||
for (i = 0; i < MAX_IOFD; i++) {
|
||||
if (ptr->io_fd[i])
|
||||
FD_SET(ptr->io_fd[i], readfds);
|
||||
maxfds = (ptr->io_fd[i] > maxfds ?
|
||||
ptr->io_fd[i]: 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, int io_fd[MAX_IOFD], struct td_state *s)
|
||||
{
|
||||
fd_list_entry_t *ptr, *last, *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;
|
||||
for (i = 0; i < MAX_IOFD; i++) entry->io_fd[i] = io_fd[i];
|
||||
entry->s = s;
|
||||
entry->next = NULL;
|
||||
|
||||
ptr = fd_start;
|
||||
if (ptr == NULL) {
|
||||
/*We are the first entry*/
|
||||
fd_start = entry;
|
||||
entry->prev = NULL;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
while (ptr != NULL) {
|
||||
last = ptr;
|
||||
ptr = ptr->next;
|
||||
}
|
||||
last->next = entry;
|
||||
entry->prev = last;
|
||||
|
||||
finish:
|
||||
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 = malloc(sizeof(tapdev_info_t));
|
||||
|
||||
for (i = 0; i < MAX_REQUESTS; i++)
|
||||
blkif->pending_list[i].count = 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;
|
||||
|
||||
asprintf(&devname,"%s/%s%d", BLKTAP_DEV_DIR, BLKTAP_DEV_NAME, minor);
|
||||
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*/
|
||||
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_SHIFT);
|
||||
|
||||
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 int read_msg(char *buf)
|
||||
{
|
||||
int length, len, msglen, tap_fd, *io_fd;
|
||||
char *ptr, *path;
|
||||
image_t *img;
|
||||
struct timeval timeout;
|
||||
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;
|
||||
|
||||
s->drv = drv;
|
||||
s->private = malloc(drv->private_data_size);
|
||||
if (s->private == NULL) {
|
||||
free(s);
|
||||
goto params_done;
|
||||
}
|
||||
|
||||
/*Open file*/
|
||||
ret = drv->td_open(s, path);
|
||||
io_fd = drv->td_get_fd(s);
|
||||
|
||||
entry = add_fd_entry(0, io_fd, 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));
|
||||
wmb();
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
void io_done(struct td_state *s, int sid)
|
||||
{
|
||||
struct tap_disk *drv = s->drv;
|
||||
|
||||
if (!run) return; /*We have received signal to close*/
|
||||
|
||||
if (drv->td_do_callbacks(s, sid) > 0) kick_responses(s);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
int send_responses(struct td_state *s, int res, int idx, void *private)
|
||||
{
|
||||
blkif_request_t *req;
|
||||
int responses_queued = 0;
|
||||
blkif_t *blkif = s->blkif;
|
||||
|
||||
req = &blkif->pending_list[idx].req;
|
||||
|
||||
if ( (idx > MAX_REQUESTS-1) ||
|
||||
(blkif->pending_list[idx].count == 0) )
|
||||
{
|
||||
DPRINTF("invalid index returned(%u)!\n", idx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (res != 0) {
|
||||
DPRINTF("*** request error %d! \n", res);
|
||||
return 0;
|
||||
}
|
||||
|
||||
blkif->pending_list[idx].count--;
|
||||
|
||||
if (blkif->pending_list[idx].count == 0)
|
||||
{
|
||||
blkif_request_t tmp;
|
||||
blkif_response_t *rsp;
|
||||
|
||||
tmp = blkif->pending_list[idx].req;
|
||||
rsp = (blkif_response_t *)req;
|
||||
|
||||
rsp->id = tmp.id;
|
||||
rsp->operation = tmp.operation;
|
||||
rsp->status = blkif->pending_list[idx].status;
|
||||
|
||||
write_rsp_to_ring(s, rsp);
|
||||
responses_queued++;
|
||||
}
|
||||
return responses_queued;
|
||||
}
|
||||
|
||||
static void get_io_request(struct td_state *s)
|
||||
{
|
||||
RING_IDX rp, rc, j, i, ret;
|
||||
blkif_request_t *req;
|
||||
int idx, nsects;
|
||||
uint64_t sector_nr;
|
||||
char *page;
|
||||
int early = 0; /* count early completions */
|
||||
struct tap_disk *drv = s->drv;
|
||||
blkif_t *blkif = s->blkif;
|
||||
tapdev_info_t *info = s->ring_info;
|
||||
|
||||
if (!run) return; /*We have received signal to close*/
|
||||
|
||||
rp = info->fe_ring.sring->req_prod;
|
||||
rmb();
|
||||
for (j = info->fe_ring.req_cons; j != rp; j++)
|
||||
{
|
||||
int done = 0;
|
||||
|
||||
req = NULL;
|
||||
req = RING_GET_REQUEST(&info->fe_ring, j);
|
||||
++info->fe_ring.req_cons;
|
||||
|
||||
if (req == NULL) continue;
|
||||
|
||||
idx = req->id;
|
||||
ASSERT(blkif->pending_list[idx].count == 0);
|
||||
memcpy(&blkif->pending_list[idx].req, req, sizeof(*req));
|
||||
blkif->pending_list[idx].status = BLKIF_RSP_OKAY;
|
||||
blkif->pending_list[idx].count = req->nr_segments;
|
||||
|
||||
sector_nr = req->sector_number;
|
||||
|
||||
for (i = 0; i < req->nr_segments; i++) {
|
||||
nsects = req->seg[i].last_sect -
|
||||
req->seg[i].first_sect + 1;
|
||||
|
||||
if ((req->seg[i].last_sect >= 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;
|
||||
}
|
||||
|
||||
switch (req->operation)
|
||||
{
|
||||
case BLKIF_OP_WRITE:
|
||||
ret = drv->td_queue_write(s, sector_nr,
|
||||
nsects, page, send_responses,
|
||||
idx, NULL);
|
||||
if (ret > 0) early += ret;
|
||||
else if (ret == -EBUSY) {
|
||||
/*
|
||||
* TODO: Sector is locked *
|
||||
* Need to put req back on queue *
|
||||
*/
|
||||
}
|
||||
break;
|
||||
case BLKIF_OP_READ:
|
||||
ret = drv->td_queue_read(s, sector_nr,
|
||||
nsects, page, send_responses,
|
||||
idx, NULL);
|
||||
if (ret > 0) early += ret;
|
||||
else if (ret == -EBUSY) {
|
||||
/*
|
||||
* TODO: Sector is locked *
|
||||
* Need to put req back on queue *
|
||||
*/
|
||||
}
|
||||
break;
|
||||
default:
|
||||
DPRINTF("Unknown block operation\n");
|
||||
break;
|
||||
}
|
||||
sector_nr += nsects;
|
||||
}
|
||||
}
|
||||
|
||||
/*Batch done*/
|
||||
drv->td_submit(s);
|
||||
|
||||
if (early > 0)
|
||||
io_done(s,10);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int len, msglen, ret, i;
|
||||
char *p, *buf;
|
||||
fd_set readfds, writefds;
|
||||
struct timeval timeout;
|
||||
fd_list_entry_t *ptr;
|
||||
struct tap_disk *drv;
|
||||
struct td_state *s;
|
||||
|
||||
if (argc != 3) usage();
|
||||
|
||||
daemonize();
|
||||
|
||||
openlog("TAPDISK", 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);
|
||||
|
||||
timeout.tv_sec = 0;
|
||||
timeout.tv_usec = 1000;
|
||||
|
||||
/*Wait for incoming messages*/
|
||||
ret = select(maxfds + 1, &readfds, (fd_set *) 0,
|
||||
(fd_set *) 0, &timeout);
|
||||
|
||||
if (ret > 0)
|
||||
{
|
||||
ptr = fd_start;
|
||||
while (ptr != NULL) {
|
||||
if (FD_ISSET(ptr->tap_fd, &readfds))
|
||||
get_io_request(ptr->s);
|
||||
for (i = 0; i < MAX_IOFD; i++) {
|
||||
if (ptr->io_fd[i] &&
|
||||
FD_ISSET(ptr->io_fd[i], &readfds))
|
||||
io_done(ptr->s, i);
|
||||
}
|
||||
|
||||
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;
|
||||
drv = s->drv;
|
||||
|
||||
unmap_disk(s);
|
||||
drv->td_close(s);
|
||||
free(s->private);
|
||||
free(s->blkif);
|
||||
free(s->ring_info);
|
||||
free(s);
|
||||
close(ptr->tap_fd);
|
||||
ptr = ptr->next;
|
||||
}
|
||||
closelog();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,211 @@
|
||||
/* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef TAPDISK_H_
|
||||
#define TAPDISK_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <syslog.h>
|
||||
#include "blktaplib.h"
|
||||
|
||||
/*If enabled, log all debug messages to syslog*/
|
||||
#if 1
|
||||
#define DPRINTF(_f, _a...) syslog( LOG_DEBUG, _f , ## _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_REQUESTS 64
|
||||
#define MAX_SEGMENTS_PER_REQ 11
|
||||
#define SECTOR_SHIFT 9
|
||||
#define DEFAULT_SECTOR_SIZE 512
|
||||
|
||||
/* This structure represents the state of an active virtual disk. */
|
||||
struct td_state {
|
||||
void *private;
|
||||
void *drv;
|
||||
void *blkif;
|
||||
void *image;
|
||||
void *ring_info;
|
||||
void *fd_entry;
|
||||
char backing_file[1024]; /*Used by differencing disks, e.g. qcow*/
|
||||
long int sector_size;
|
||||
uint64_t size;
|
||||
long int info;
|
||||
};
|
||||
|
||||
/* Prototype of the callback to activate as requests complete. */
|
||||
typedef int (*td_callback_t)(struct td_state *s, int res, int id, void *prv);
|
||||
|
||||
/* 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 td_state *s, const char *name);
|
||||
int (*td_queue_read) (struct td_state *s, uint64_t sector,
|
||||
int nb_sectors, char *buf, td_callback_t cb,
|
||||
int id, void *prv);
|
||||
int (*td_queue_write) (struct td_state *s, uint64_t sector,
|
||||
int nb_sectors, char *buf, td_callback_t cb,
|
||||
int id, void *prv);
|
||||
int (*td_submit) (struct td_state *s);
|
||||
int *(*td_get_fd) (struct td_state *s);
|
||||
int (*td_close) (struct td_state *s);
|
||||
int (*td_do_callbacks)(struct td_state *s, int sid);
|
||||
};
|
||||
|
||||
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? */
|
||||
#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;
|
||||
|
||||
#define MAX_DISK_TYPES 20
|
||||
#define MAX_IOFD 2
|
||||
|
||||
#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 Individual Disk Parameters here */
|
||||
static disk_info_t aio_disk = {
|
||||
DISK_TYPE_AIO,
|
||||
"raw image (aio)",
|
||||
"aio",
|
||||
0,
|
||||
#ifdef TAPDISK
|
||||
&tapdisk_aio,
|
||||
#endif
|
||||
};
|
||||
|
||||
static disk_info_t sync_disk = {
|
||||
DISK_TYPE_SYNC,
|
||||
"raw image (sync)",
|
||||
"sync",
|
||||
0,
|
||||
#ifdef TAPDISK
|
||||
&tapdisk_sync,
|
||||
#endif
|
||||
};
|
||||
|
||||
static disk_info_t vmdk_disk = {
|
||||
DISK_TYPE_VMDK,
|
||||
"vmware image (vmdk)",
|
||||
"vmdk",
|
||||
1,
|
||||
#ifdef TAPDISK
|
||||
&tapdisk_vmdk,
|
||||
#endif
|
||||
};
|
||||
|
||||
static disk_info_t ram_disk = {
|
||||
DISK_TYPE_RAM,
|
||||
"ramdisk image (ram)",
|
||||
"ram",
|
||||
1,
|
||||
#ifdef TAPDISK
|
||||
&tapdisk_ram,
|
||||
#endif
|
||||
};
|
||||
|
||||
static disk_info_t qcow_disk = {
|
||||
DISK_TYPE_QCOW,
|
||||
"qcow disk (qcow)",
|
||||
"qcow",
|
||||
0,
|
||||
#ifdef TAPDISK
|
||||
&tapdisk_qcow,
|
||||
#endif
|
||||
};
|
||||
|
||||
/*Main disk info array */
|
||||
static disk_info_t *dtypes[] = {
|
||||
&aio_disk,
|
||||
&sync_disk,
|
||||
&vmdk_disk,
|
||||
&ram_disk,
|
||||
&qcow_disk,
|
||||
};
|
||||
|
||||
typedef struct driver_list_entry {
|
||||
void *blkif;
|
||||
void *prev;
|
||||
void *next;
|
||||
} driver_list_entry_t;
|
||||
|
||||
typedef struct fd_list_entry {
|
||||
int cookie;
|
||||
int tap_fd;
|
||||
int io_fd[MAX_IOFD];
|
||||
struct td_state *s;
|
||||
void *prev;
|
||||
void *next;
|
||||
} fd_list_entry_t;
|
||||
|
||||
int qcow_create(const char *filename, uint64_t total_size,
|
||||
const char *backing_file, int flags);
|
||||
|
||||
#endif /*TAPDISK_H_*/
|
||||
@@ -0,0 +1,66 @@
|
||||
XEN_ROOT = ../../..
|
||||
include $(XEN_ROOT)/tools/Rules.mk
|
||||
|
||||
MAJOR = 3.0
|
||||
MINOR = 0
|
||||
SONAME = libblktap.so.$(MAJOR)
|
||||
|
||||
BLKTAP_INSTALL_DIR = /usr/sbin
|
||||
|
||||
INSTALL = install
|
||||
INSTALL_PROG = $(INSTALL) -m0755
|
||||
INSTALL_DIR = $(INSTALL) -d -m0755
|
||||
|
||||
INCLUDES += -I. -I.. -I $(XEN_LIBXC) -I $(XEN_XENSTORE)
|
||||
|
||||
LIBS := -lz
|
||||
|
||||
SRCS :=
|
||||
SRCS += xenbus.c blkif.c xs_api.c
|
||||
|
||||
CFLAGS += -Werror
|
||||
CFLAGS += -Wno-unused
|
||||
CFLAGS += -fno-strict-aliasing -fPIC
|
||||
CFLAGS += -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE
|
||||
# get asprintf():
|
||||
CFLAGS += -D _GNU_SOURCE
|
||||
|
||||
# Get gcc to generate the dependencies for us.
|
||||
CFLAGS += -Wp,-MD,.$(@F).d
|
||||
CFLAGS += $(INCLUDES)
|
||||
DEPS = .*.d
|
||||
|
||||
OBJS = $(patsubst %.c,%.o,$(SRCS))
|
||||
IBINS :=
|
||||
|
||||
LIB = libblktap.a libblktap.so libblktap.so.$(MAJOR) libblktap.so.$(MAJOR).$(MINOR)
|
||||
|
||||
all: build
|
||||
|
||||
build:
|
||||
$(MAKE) libblktap
|
||||
|
||||
install: all
|
||||
$(INSTALL_DIR) -p $(DESTDIR)/usr/$(LIBDIR)
|
||||
$(INSTALL_DIR) -p $(DESTDIR)/usr/include
|
||||
$(INSTALL_PROG) $(LIB) $(DESTDIR)/usr/$(LIBDIR)
|
||||
$(INSTALL_PROG) blktaplib.h $(DESTDIR)/usr/include
|
||||
|
||||
clean:
|
||||
rm -rf *.a *.so *.o *.rpm $(LIB) *~ $(DEPS) xen TAGS
|
||||
|
||||
libblktap: $(OBJS)
|
||||
$(CC) $(CFLAGS) -Wl,-soname -Wl,$(SONAME) -shared \
|
||||
-L$(XEN_XENSTORE) -l xenstore \
|
||||
-o libblktap.so.$(MAJOR).$(MINOR) $^ $(LIBS)
|
||||
ln -sf libblktap.so.$(MAJOR).$(MINOR) libblktap.so.$(MAJOR)
|
||||
ln -sf libblktap.so.$(MAJOR) $@.so
|
||||
ar rc libblktap.a $@.so
|
||||
|
||||
.PHONY: TAGS all build clean install libblktap
|
||||
|
||||
TAGS:
|
||||
etags -t $(SRCS) *.h
|
||||
|
||||
-include $(DEPS)
|
||||
|
||||
@@ -0,0 +1,185 @@
|
||||
/*
|
||||
* 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));
|
||||
}
|
||||
@@ -0,0 +1,223 @@
|
||||
/* 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/user.h>
|
||||
#include <xen/xen.h>
|
||||
#include <xen/io/blkif.h>
|
||||
#include <xen/io/ring.h>
|
||||
#include <xs.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define BLK_RING_SIZE __RING_SIZE((blkif_sring_t *)0, PAGE_SIZE)
|
||||
|
||||
/* size of the extra VMA area to map in attached pages. */
|
||||
#define BLKTAP_VMA_PAGES BLK_RING_SIZE
|
||||
|
||||
/* blktap IOCTLs: These must correspond with the blktap driver ioctls*/
|
||||
#define BLKTAP_IOCTL_KICK_FE 1
|
||||
#define BLKTAP_IOCTL_KICK_BE 2
|
||||
#define BLKTAP_IOCTL_SETMODE 3
|
||||
#define BLKTAP_IOCTL_SENDPID 4
|
||||
#define BLKTAP_IOCTL_NEWINTF 5
|
||||
#define BLKTAP_IOCTL_MINOR 6
|
||||
#define BLKTAP_IOCTL_MAJOR 7
|
||||
#define BLKTAP_QUERY_ALLOC_REQS 8
|
||||
#define BLKTAP_IOCTL_FREEINTF 9
|
||||
#define BLKTAP_IOCTL_PRINT_IDXS 100
|
||||
|
||||
/* 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 64
|
||||
|
||||
#define BLKTAP_IOCTL_KICK 1
|
||||
#define MAX_PENDING_REQS 64
|
||||
#define BLKTAP_DEV_DIR "/dev/xen"
|
||||
#define BLKTAP_DEV_NAME "blktap"
|
||||
#define BLKTAP_DEV_MAJOR 254
|
||||
#define BLKTAP_DEV_MINOR 0
|
||||
|
||||
#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 count;
|
||||
int16_t status;
|
||||
} pending_req_t;
|
||||
|
||||
struct blkif_ops {
|
||||
long int (*get_size)(struct blkif *blkif);
|
||||
long int (*get_secsize)(struct blkif *blkif);
|
||||
unsigned (*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 tapdev_info {
|
||||
int fd;
|
||||
char *mem;
|
||||
blkif_sring_t *sring;
|
||||
blkif_back_ring_t fe_ring;
|
||||
unsigned long vstart;
|
||||
blkif_t *blkif;
|
||||
} tapdev_info_t;
|
||||
|
||||
typedef struct domid_translate {
|
||||
unsigned short domid;
|
||||
unsigned short busid;
|
||||
} domid_translate_t ;
|
||||
|
||||
typedef struct image {
|
||||
long int size;
|
||||
long int secsize;
|
||||
long int info;
|
||||
} image_t;
|
||||
|
||||
typedef struct msg_hdr {
|
||||
uint16_t type;
|
||||
uint16_t len;
|
||||
uint16_t drivertype;
|
||||
uint16_t cookie;
|
||||
} msg_hdr_t;
|
||||
|
||||
typedef struct msg_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
|
||||
|
||||
/* xenstore/xenbus: */
|
||||
extern int add_blockdevice_probe_watch(struct xs_handle *h,
|
||||
const char *domname);
|
||||
int xs_fire_next_watch(struct xs_handle *h);
|
||||
|
||||
|
||||
/* Abitrary values, must match the underlying driver... */
|
||||
#define MAX_PENDING_REQS 64
|
||||
#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 * PAGE_SIZE) + \
|
||||
((_seg) * PAGE_SIZE))
|
||||
|
||||
/* Defines that are only used by library clients */
|
||||
|
||||
#ifndef __COMPILING_BLKTAP_LIB
|
||||
|
||||
static char *blkif_op_name[] = {
|
||||
[BLKIF_OP_READ] = "READ",
|
||||
[BLKIF_OP_WRITE] = "WRITE",
|
||||
};
|
||||
|
||||
#endif /* __COMPILING_BLKTAP_LIB */
|
||||
|
||||
#endif /* __BLKTAPLIB_H__ */
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* list.h
|
||||
*
|
||||
* This is a subset of linux's list.h intended to be used in user-space.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __LIST_H__
|
||||
#define __LIST_H__
|
||||
|
||||
#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__ */
|
||||
@@ -0,0 +1,387 @@
|
||||
/*
|
||||
* 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 <printf.h>
|
||||
#include <string.h>
|
||||
#include <err.h>
|
||||
#include <stdarg.h>
|
||||
#include <errno.h>
|
||||
#include <xs.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <poll.h>
|
||||
#include <time.h>
|
||||
#include <sys/time.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 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 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;
|
||||
|
||||
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*/
|
||||
asprintf(&path, "%s/%s", bepath, "read-only");
|
||||
if (xs_exists(h, path))
|
||||
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;
|
||||
}
|
||||
|
||||
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", "%lu",
|
||||
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;
|
||||
DPRINTF("[SETUP] Complete\n\n");
|
||||
goto close;
|
||||
|
||||
fail:
|
||||
if ( (be != NULL) && (be->blkif != NULL) )
|
||||
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;
|
||||
bepath[len] = '\0';
|
||||
|
||||
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);
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
*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.
|
||||
*/
|
||||
|
||||
int add_blockdevice_probe_watch(struct xs_handle *h, const char *domname)
|
||||
{
|
||||
char *domid, *path;
|
||||
struct xenbus_watch *vbd_watch;
|
||||
int er;
|
||||
|
||||
domid = get_dom_domid(h, domname);
|
||||
|
||||
DPRINTF("%s: %s\n",
|
||||
domname, (domid != NULL) ? domid : "[ not found! ]");
|
||||
|
||||
asprintf(&path, "/local/domain/%s/backend/tap", domid);
|
||||
if (path == NULL)
|
||||
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;
|
||||
er = register_xenbus_watch(h, vbd_watch);
|
||||
if (er == 0) {
|
||||
DPRINTF("ERROR: adding vbd probe watch %s\n", path);
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,364 @@
|
||||
/*
|
||||
* 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 <printf.h>
|
||||
#include <string.h>
|
||||
#include <err.h>
|
||||
#include <stdarg.h>
|
||||
#include <errno.h>
|
||||
#include <xs.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);
|
||||
|
||||
asprintf(&path, "%s/%s", dir, node);
|
||||
|
||||
if ( (path == NULL) || (buf == NULL) )
|
||||
return 0;
|
||||
|
||||
ret = xs_write(h, XBT_NULL, path, buf, strlen(buf)+1);
|
||||
|
||||
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, const char *name)
|
||||
{
|
||||
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);
|
||||
|
||||
i = 0;
|
||||
while (i < num) {
|
||||
asprintf(&path, "/local/domain/%s/name", e[i]);
|
||||
val = xs_read(h, xth, path, &len);
|
||||
free(path);
|
||||
if (val == NULL)
|
||||
continue;
|
||||
|
||||
if (strcmp(val, name) == 0) {
|
||||
/* match! */
|
||||
asprintf(&path, "/local/domain/%s/domid", e[i]);
|
||||
domid = xs_read(h, xth, path, &len);
|
||||
free(val);
|
||||
free(path);
|
||||
break;
|
||||
}
|
||||
free(val);
|
||||
i++;
|
||||
}
|
||||
xs_transaction_end(h, xth, 0);
|
||||
|
||||
free(e);
|
||||
return domid;
|
||||
}
|
||||
|
||||
int convert_dev_name_to_num(char *name) {
|
||||
char *p_sd, *p_hd, *p_xvd, *p_plx, *p, *alpha,*ptr;
|
||||
int majors[10] = {3,22,33,34,56,57,88,89,90,91};
|
||||
int maj,i;
|
||||
|
||||
asprintf(&p_sd,"/dev/sd");
|
||||
asprintf(&p_hd,"/dev/hd");
|
||||
asprintf(&p_xvd,"/dev/xvd");
|
||||
asprintf(&p_plx,"plx");
|
||||
asprintf(&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++;
|
||||
return 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++;
|
||||
return (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++;
|
||||
return (202*256) + (16*i) + atoi(p);
|
||||
|
||||
} else if (strstr(name, p_plx) != NULL) {
|
||||
p = name + strlen(p_plx);
|
||||
return atoi(p);
|
||||
|
||||
} else {
|
||||
DPRINTF("Unknown device type, setting to default.\n");
|
||||
return BASE_DEV_VAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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];
|
||||
int er;
|
||||
|
||||
sprintf(token, "%lX", (long)watch);
|
||||
if (find_watch(token))
|
||||
{
|
||||
DPRINTF("watch collision!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
er = xs_watch(h, watch->node, token);
|
||||
if (er != 0) {
|
||||
list_add(&watch->list, &watches);
|
||||
}
|
||||
|
||||
return er;
|
||||
}
|
||||
|
||||
int unregister_xenbus_watch(struct xs_handle *h, struct xenbus_watch *watch)
|
||||
{
|
||||
char token[sizeof(watch) * 2 + 1];
|
||||
int er;
|
||||
|
||||
sprintf(token, "%lX", (long)watch);
|
||||
if (!find_watch(token))
|
||||
{
|
||||
DPRINTF("no such watch!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
||||
er = xs_unwatch(h, watch->node, token);
|
||||
list_del(&watch->list);
|
||||
|
||||
if (er == 0)
|
||||
DPRINTF("XENBUS Failed to release watch %s: %i\n",
|
||||
watch->node, er);
|
||||
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) {
|
||||
sprintf(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)
|
||||
{
|
||||
DPRINTF("unregistered watch fired\n");
|
||||
goto done;
|
||||
}
|
||||
w->callback(h, w, node);
|
||||
|
||||
done:
|
||||
free(res);
|
||||
return 1;
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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, const char *name);
|
||||
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);
|
||||
@@ -26,6 +26,7 @@ XEN_SCRIPTS += network-route vif-route
|
||||
XEN_SCRIPTS += network-nat vif-nat
|
||||
XEN_SCRIPTS += block
|
||||
XEN_SCRIPTS += block-enbd block-nbd
|
||||
XEN_SCRIPTS += blktap
|
||||
XEN_SCRIPTS += vtpm vtpm-delete
|
||||
XEN_SCRIPTS += xen-hotplug-cleanup
|
||||
XEN_SCRIPTS += external-device-migrate
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Copyright (c) 2005, XenSource Ltd.
|
||||
|
||||
dir=$(dirname "$0")
|
||||
. "$dir/xen-hotplug-common.sh"
|
||||
|
||||
findCommand "$@"
|
||||
|
||||
if [ "$command" == 'add' ]
|
||||
then
|
||||
success
|
||||
fi
|
||||
|
||||
exit 0
|
||||
@@ -7,6 +7,9 @@ PATH=/etc/xen/scripts:$PATH
|
||||
claim_lock xenbus_hotplug_global
|
||||
|
||||
case "$XENBUS_TYPE" in
|
||||
tap)
|
||||
/etc/xen/scripts/blktap "$ACTION"
|
||||
;;
|
||||
vbd)
|
||||
/etc/xen/scripts/block "$ACTION"
|
||||
;;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
SUBSYSTEM=="xen-backend", KERNEL=="tap*", RUN+="/etc/xen/scripts/blktap $env{ACTION}"
|
||||
SUBSYSTEM=="xen-backend", KERNEL=="vbd*", RUN+="/etc/xen/scripts/block $env{ACTION}"
|
||||
SUBSYSTEM=="xen-backend", KERNEL=="vtpm*", RUN+="/etc/xen/scripts/vtpm $env{ACTION}"
|
||||
SUBSYSTEM=="xen-backend", KERNEL=="vif*", ACTION=="online", RUN+="$env{script} online"
|
||||
|
||||
@@ -0,0 +1,515 @@
|
||||
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 2.1, February 1999
|
||||
|
||||
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
|
||||
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
[This is the first released version of the Lesser GPL. It also counts
|
||||
as the successor of the GNU Library Public License, version 2, hence
|
||||
the version number 2.1.]
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
Licenses are intended to guarantee your freedom to share and change
|
||||
free software--to make sure the software is free for all its users.
|
||||
|
||||
This license, the Lesser General Public License, applies to some
|
||||
specially designated software packages--typically libraries--of the
|
||||
Free Software Foundation and other authors who decide to use it. You
|
||||
can use it too, but we suggest you first think carefully about whether
|
||||
this license or the ordinary General Public License is the better
|
||||
strategy to use in any particular case, based on the explanations
|
||||
below.
|
||||
|
||||
When we speak of free software, we are referring to freedom of use,
|
||||
not price. Our General Public Licenses are designed to make sure that
|
||||
you have the freedom to distribute copies of free software (and charge
|
||||
for this service if you wish); that you receive source code or can get
|
||||
it if you want it; that you can change the software and use pieces of
|
||||
it in new free programs; and that you are informed that you can do
|
||||
these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
distributors to deny you these rights or to ask you to surrender these
|
||||
rights. These restrictions translate to certain responsibilities for
|
||||
you if you distribute copies of the library or if you modify it.
|
||||
|
||||
For example, if you distribute copies of the library, whether gratis
|
||||
or for a fee, you must give the recipients all the rights that we gave
|
||||
you. You must make sure that they, too, receive or can get the source
|
||||
code. If you link other code with the library, you must provide
|
||||
complete object files to the recipients, so that they can relink them
|
||||
with the library after making changes to the library and recompiling
|
||||
it. And you must show them these terms so they know their rights.
|
||||
|
||||
We protect your rights with a two-step method: (1) we copyright the
|
||||
library, and (2) we offer you this license, which gives you legal
|
||||
permission to copy, distribute and/or modify the library.
|
||||
|
||||
To protect each distributor, we want to make it very clear that
|
||||
there is no warranty for the free library. Also, if the library is
|
||||
modified by someone else and passed on, the recipients should know
|
||||
that what they have is not the original version, so that the original
|
||||
author's reputation will not be affected by problems that might be
|
||||
introduced by others.
|
||||
^L
|
||||
Finally, software patents pose a constant threat to the existence of
|
||||
any free program. We wish to make sure that a company cannot
|
||||
effectively restrict the users of a free program by obtaining a
|
||||
restrictive license from a patent holder. Therefore, we insist that
|
||||
any patent license obtained for a version of the library must be
|
||||
consistent with the full freedom of use specified in this license.
|
||||
|
||||
Most GNU software, including some libraries, is covered by the
|
||||
ordinary GNU General Public License. This license, the GNU Lesser
|
||||
General Public License, applies to certain designated libraries, and
|
||||
is quite different from the ordinary General Public License. We use
|
||||
this license for certain libraries in order to permit linking those
|
||||
libraries into non-free programs.
|
||||
|
||||
When a program is linked with a library, whether statically or using
|
||||
a shared library, the combination of the two is legally speaking a
|
||||
combined work, a derivative of the original library. The ordinary
|
||||
General Public License therefore permits such linking only if the
|
||||
entire combination fits its criteria of freedom. The Lesser General
|
||||
Public License permits more lax criteria for linking other code with
|
||||
the library.
|
||||
|
||||
We call this license the "Lesser" General Public License because it
|
||||
does Less to protect the user's freedom than the ordinary General
|
||||
Public License. It also provides other free software developers Less
|
||||
of an advantage over competing non-free programs. These disadvantages
|
||||
are the reason we use the ordinary General Public License for many
|
||||
libraries. However, the Lesser license provides advantages in certain
|
||||
special circumstances.
|
||||
|
||||
For example, on rare occasions, there may be a special need to
|
||||
encourage the widest possible use of a certain library, so that it
|
||||
becomes
|
||||
a de-facto standard. To achieve this, non-free programs must be
|
||||
allowed to use the library. A more frequent case is that a free
|
||||
library does the same job as widely used non-free libraries. In this
|
||||
case, there is little to gain by limiting the free library to free
|
||||
software only, so we use the Lesser General Public License.
|
||||
|
||||
In other cases, permission to use a particular library in non-free
|
||||
programs enables a greater number of people to use a large body of
|
||||
free software. For example, permission to use the GNU C Library in
|
||||
non-free programs enables many more people to use the whole GNU
|
||||
operating system, as well as its variant, the GNU/Linux operating
|
||||
system.
|
||||
|
||||
Although the Lesser General Public License is Less protective of the
|
||||
users' freedom, it does ensure that the user of a program that is
|
||||
linked with the Library has the freedom and the wherewithal to run
|
||||
that program using a modified version of the Library.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow. Pay close attention to the difference between a
|
||||
"work based on the library" and a "work that uses the library". The
|
||||
former contains code derived from the library, whereas the latter must
|
||||
be combined with the library in order to run.
|
||||
^L
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License Agreement applies to any software library or other
|
||||
program which contains a notice placed by the copyright holder or
|
||||
other authorized party saying it may be distributed under the terms of
|
||||
this Lesser General Public License (also called "this License").
|
||||
Each licensee is addressed as "you".
|
||||
|
||||
A "library" means a collection of software functions and/or data
|
||||
prepared so as to be conveniently linked with application programs
|
||||
(which use some of those functions and data) to form executables.
|
||||
|
||||
The "Library", below, refers to any such software library or work
|
||||
which has been distributed under these terms. A "work based on the
|
||||
Library" means either the Library or any derivative work under
|
||||
copyright law: that is to say, a work containing the Library or a
|
||||
portion of it, either verbatim or with modifications and/or translated
|
||||
straightforwardly into another language. (Hereinafter, translation is
|
||||
included without limitation in the term "modification".)
|
||||
|
||||
"Source code" for a work means the preferred form of the work for
|
||||
making modifications to it. For a library, complete source code means
|
||||
all the source code for all modules it contains, plus any associated
|
||||
interface definition files, plus the scripts used to control
|
||||
compilation
|
||||
and installation of the library.
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running a program using the Library is not restricted, and output from
|
||||
such a program is covered only if its contents constitute a work based
|
||||
on the Library (independent of the use of the Library in a tool for
|
||||
writing it). Whether that is true depends on what the Library does
|
||||
and what the program that uses the Library does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Library's
|
||||
complete source code as you receive it, in any medium, provided that
|
||||
you conspicuously and appropriately publish on each copy an
|
||||
appropriate copyright notice and disclaimer of warranty; keep intact
|
||||
all the notices that refer to this License and to the absence of any
|
||||
warranty; and distribute a copy of this License along with the
|
||||
Library.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy,
|
||||
and you may at your option offer warranty protection in exchange for a
|
||||
fee.
|
||||
|
||||
2. You may modify your copy or copies of the Library or any portion
|
||||
of it, thus forming a work based on the Library, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) The modified work must itself be a software library.
|
||||
|
||||
b) You must cause the files modified to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
c) You must cause the whole of the work to be licensed at no
|
||||
charge to all third parties under the terms of this License.
|
||||
|
||||
d) If a facility in the modified Library refers to a function or a
|
||||
table of data to be supplied by an application program that uses
|
||||
the facility, other than as an argument passed when the facility
|
||||
is invoked, then you must make a good faith effort to ensure that,
|
||||
in the event an application does not supply such function or
|
||||
table, the facility still operates, and performs whatever part of
|
||||
its purpose remains meaningful.
|
||||
|
||||
(For example, a function in a library to compute square roots has
|
||||
a purpose that is entirely well-defined independent of the
|
||||
application. Therefore, Subsection 2d requires that any
|
||||
application-supplied function or table used by this function must
|
||||
be optional: if the application does not supply it, the square
|
||||
root function must still compute square roots.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Library,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Library, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote
|
||||
it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Library.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Library
|
||||
with the Library (or with a work based on the Library) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may opt to apply the terms of the ordinary GNU General Public
|
||||
License instead of this License to a given copy of the Library. To do
|
||||
this, you must alter all the notices that refer to this License, so
|
||||
that they refer to the ordinary GNU General Public License, version 2,
|
||||
instead of to this License. (If a newer version than version 2 of the
|
||||
ordinary GNU General Public License has appeared, then you can specify
|
||||
that version instead if you wish.) Do not make any other change in
|
||||
these notices.
|
||||
^L
|
||||
Once this change is made in a given copy, it is irreversible for
|
||||
that copy, so the ordinary GNU General Public License applies to all
|
||||
subsequent copies and derivative works made from that copy.
|
||||
|
||||
This option is useful when you wish to copy part of the code of
|
||||
the Library into a program that is not a library.
|
||||
|
||||
4. You may copy and distribute the Library (or a portion or
|
||||
derivative of it, under Section 2) in object code or executable form
|
||||
under the terms of Sections 1 and 2 above provided that you accompany
|
||||
it with the complete corresponding machine-readable source code, which
|
||||
must be distributed under the terms of Sections 1 and 2 above on a
|
||||
medium customarily used for software interchange.
|
||||
|
||||
If distribution of object code is made by offering access to copy
|
||||
from a designated place, then offering equivalent access to copy the
|
||||
source code from the same place satisfies the requirement to
|
||||
distribute the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
5. A program that contains no derivative of any portion of the
|
||||
Library, but is designed to work with the Library by being compiled or
|
||||
linked with it, is called a "work that uses the Library". Such a
|
||||
work, in isolation, is not a derivative work of the Library, and
|
||||
therefore falls outside the scope of this License.
|
||||
|
||||
However, linking a "work that uses the Library" with the Library
|
||||
creates an executable that is a derivative of the Library (because it
|
||||
contains portions of the Library), rather than a "work that uses the
|
||||
library". The executable is therefore covered by this License.
|
||||
Section 6 states terms for distribution of such executables.
|
||||
|
||||
When a "work that uses the Library" uses material from a header file
|
||||
that is part of the Library, the object code for the work may be a
|
||||
derivative work of the Library even though the source code is not.
|
||||
Whether this is true is especially significant if the work can be
|
||||
linked without the Library, or if the work is itself a library. The
|
||||
threshold for this to be true is not precisely defined by law.
|
||||
|
||||
If such an object file uses only numerical parameters, data
|
||||
structure layouts and accessors, and small macros and small inline
|
||||
functions (ten lines or less in length), then the use of the object
|
||||
file is unrestricted, regardless of whether it is legally a derivative
|
||||
work. (Executables containing this object code plus portions of the
|
||||
Library will still fall under Section 6.)
|
||||
|
||||
Otherwise, if the work is a derivative of the Library, you may
|
||||
distribute the object code for the work under the terms of Section 6.
|
||||
Any executables containing that work also fall under Section 6,
|
||||
whether or not they are linked directly with the Library itself.
|
||||
^L
|
||||
6. As an exception to the Sections above, you may also combine or
|
||||
link a "work that uses the Library" with the Library to produce a
|
||||
work containing portions of the Library, and distribute that work
|
||||
under terms of your choice, provided that the terms permit
|
||||
modification of the work for the customer's own use and reverse
|
||||
engineering for debugging such modifications.
|
||||
|
||||
You must give prominent notice with each copy of the work that the
|
||||
Library is used in it and that the Library and its use are covered by
|
||||
this License. You must supply a copy of this License. If the work
|
||||
during execution displays copyright notices, you must include the
|
||||
copyright notice for the Library among them, as well as a reference
|
||||
directing the user to the copy of this License. Also, you must do one
|
||||
of these things:
|
||||
|
||||
a) Accompany the work with the complete corresponding
|
||||
machine-readable source code for the Library including whatever
|
||||
changes were used in the work (which must be distributed under
|
||||
Sections 1 and 2 above); and, if the work is an executable linked
|
||||
with the Library, with the complete machine-readable "work that
|
||||
uses the Library", as object code and/or source code, so that the
|
||||
user can modify the Library and then relink to produce a modified
|
||||
executable containing the modified Library. (It is understood
|
||||
that the user who changes the contents of definitions files in the
|
||||
Library will not necessarily be able to recompile the application
|
||||
to use the modified definitions.)
|
||||
|
||||
b) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (1) uses at run time a
|
||||
copy of the library already present on the user's computer system,
|
||||
rather than copying library functions into the executable, and (2)
|
||||
will operate properly with a modified version of the library, if
|
||||
the user installs one, as long as the modified version is
|
||||
interface-compatible with the version that the work was made with.
|
||||
|
||||
c) Accompany the work with a written offer, valid for at
|
||||
least three years, to give the same user the materials
|
||||
specified in Subsection 6a, above, for a charge no more
|
||||
than the cost of performing this distribution.
|
||||
|
||||
d) If distribution of the work is made by offering access to copy
|
||||
from a designated place, offer equivalent access to copy the above
|
||||
specified materials from the same place.
|
||||
|
||||
e) Verify that the user has already received a copy of these
|
||||
materials or that you have already sent this user a copy.
|
||||
|
||||
For an executable, the required form of the "work that uses the
|
||||
Library" must include any data and utility programs needed for
|
||||
reproducing the executable from it. However, as a special exception,
|
||||
the materials to be distributed need not include anything that is
|
||||
normally distributed (in either source or binary form) with the major
|
||||
components (compiler, kernel, and so on) of the operating system on
|
||||
which the executable runs, unless that component itself accompanies
|
||||
the executable.
|
||||
|
||||
It may happen that this requirement contradicts the license
|
||||
restrictions of other proprietary libraries that do not normally
|
||||
accompany the operating system. Such a contradiction means you cannot
|
||||
use both them and the Library together in an executable that you
|
||||
distribute.
|
||||
^L
|
||||
7. You may place library facilities that are a work based on the
|
||||
Library side-by-side in a single library together with other library
|
||||
facilities not covered by this License, and distribute such a combined
|
||||
library, provided that the separate distribution of the work based on
|
||||
the Library and of the other library facilities is otherwise
|
||||
permitted, and provided that you do these two things:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work
|
||||
based on the Library, uncombined with any other library
|
||||
facilities. This must be distributed under the terms of the
|
||||
Sections above.
|
||||
|
||||
b) Give prominent notice with the combined library of the fact
|
||||
that part of it is a work based on the Library, and explaining
|
||||
where to find the accompanying uncombined form of the same work.
|
||||
|
||||
8. You may not copy, modify, sublicense, link with, or distribute
|
||||
the Library except as expressly provided under this License. Any
|
||||
attempt otherwise to copy, modify, sublicense, link with, or
|
||||
distribute the Library is void, and will automatically terminate your
|
||||
rights under this License. However, parties who have received copies,
|
||||
or rights, from you under this License will not have their licenses
|
||||
terminated so long as such parties remain in full compliance.
|
||||
|
||||
9. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Library or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Library (or any work based on the
|
||||
Library), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Library or works based on it.
|
||||
|
||||
10. Each time you redistribute the Library (or any work based on the
|
||||
Library), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute, link with or modify the Library
|
||||
subject to these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties with
|
||||
this License.
|
||||
^L
|
||||
11. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Library at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Library by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Library.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply, and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
12. If the distribution and/or use of the Library is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Library under this License
|
||||
may add an explicit geographical distribution limitation excluding those
|
||||
countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
13. The Free Software Foundation may publish revised and/or new
|
||||
versions of the Lesser General Public License from time to time.
|
||||
Such new versions will be similar in spirit to the present version,
|
||||
but may differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Library
|
||||
specifies a version number of this License which applies to it and
|
||||
"any later version", you have the option of following the terms and
|
||||
conditions either of that version or of any later version published by
|
||||
the Free Software Foundation. If the Library does not specify a
|
||||
license version number, you may choose any version ever published by
|
||||
the Free Software Foundation.
|
||||
^L
|
||||
14. If you wish to incorporate parts of the Library into other free
|
||||
programs whose distribution conditions are incompatible with these,
|
||||
write to the author to ask for permission. For software which is
|
||||
copyrighted by the Free Software Foundation, write to the Free
|
||||
Software Foundation; we sometimes make exceptions for this. Our
|
||||
decision will be guided by the two goals of preserving the free status
|
||||
of all derivatives of our free software and of promoting the sharing
|
||||
and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
|
||||
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
|
||||
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
|
||||
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
|
||||
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
|
||||
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
|
||||
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
|
||||
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
|
||||
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
|
||||
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
|
||||
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
|
||||
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
|
||||
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
|
||||
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
|
||||
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||
DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
^L
|
||||
How to Apply These Terms to Your New Libraries
|
||||
|
||||
If you develop a new library, and you want it to be of the greatest
|
||||
possible use to the public, we recommend making it free software that
|
||||
everyone can redistribute and change. You can do so by permitting
|
||||
redistribution under these terms (or, alternatively, under the terms
|
||||
of the ordinary General Public License).
|
||||
|
||||
To apply these terms, attach the following notices to the library.
|
||||
It is safest to attach them to the start of each source file to most
|
||||
effectively convey the exclusion of warranty; and each file should
|
||||
have at least the "copyright" line and a pointer to where the full
|
||||
notice is found.
|
||||
|
||||
|
||||
<one line to give the library's name and a brief idea of what it
|
||||
does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
Also add information on how to contact you by electronic and paper
|
||||
mail.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or
|
||||
your
|
||||
school, if any, to sign a "copyright disclaimer" for the library, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the
|
||||
library `Frob' (a library for tweaking knobs) written by James
|
||||
Random Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1990
|
||||
Ty Coon, President of Vice
|
||||
|
||||
That's all there is to it!
|
||||
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
0.4.0
|
||||
- remove libredhat-kernel
|
||||
- add rough outline for man pages
|
||||
- make the compiled io_getevents() add the extra parameter and
|
||||
pass the timeout for updating as per 2.5
|
||||
- fixes for ia64, now works
|
||||
- fixes for x86-64
|
||||
- powerpc support from Gianni Tedesco <gianni@ecsc.co.uk>
|
||||
- disable the NULL check in harness/cases/4.t on ia64: ia64
|
||||
maps the 0 page and causes this check to fail.
|
||||
|
||||
0.3.15
|
||||
- use real syscall interface, but don't break source compatibility
|
||||
yet (that will happen with 0.4.0)
|
||||
|
||||
0.3.13
|
||||
- add test cases
|
||||
|
||||
0.3.11
|
||||
- use library versioning of libredhat-kernel to always provide a
|
||||
fallback
|
||||
|
||||
0.3.9
|
||||
- add io_queue_release function
|
||||
|
||||
0.3.8
|
||||
- make clean deletes libredhat-kernel.so.1
|
||||
- const struct timespec *
|
||||
- add make srpm target
|
||||
|
||||
0.3.7
|
||||
- fix assembly function .types
|
||||
- export io_getevents
|
||||
- fix io_submit function prototype to match the kernel
|
||||
- provide /usr/lib/libredhat-kernel.so link for compilation
|
||||
(do NOT link against libredhat-kernel.so directly)
|
||||
- fix soname to libaio.so.1
|
||||
- fix dummy libredhat-kernel's soname
|
||||
- work around nfs bug
|
||||
- provide and install libredhat-kernel.so.1 stub
|
||||
- Makefile improvements
|
||||
- make sure dummy libredhat-kernel.so only returns -ENOSYS
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
To install the library, execute the command:
|
||||
|
||||
make prefix=`pwd`/usr install
|
||||
|
||||
which will install the binaries and header files into the directory
|
||||
usr. Set prefix=/usr to get them installed into the main system.
|
||||
|
||||
Please note: Do not attempt to install on the system the
|
||||
"libredhat-kernel.so" file. It is a dummy shared library
|
||||
provided only for the purpose of being able to bootstrap
|
||||
this facility while running on systems without the correct
|
||||
libredhat-kernel.so built. The contents of the included
|
||||
libredhat-kernel.so are only stubs; this library is NOT
|
||||
functional for anything except the internal purpose of
|
||||
linking libaio.so against the provided stubs. At runtime,
|
||||
libaio.so requires a real libredhat-kernel.so library; this
|
||||
is provided by the Red Hat kernel RPM packages with async
|
||||
I/O functionality.
|
||||
@@ -0,0 +1,40 @@
|
||||
NAME=libaio
|
||||
SPECFILE=$(NAME).spec
|
||||
VERSION=$(shell awk '/Version:/ { print $$2 }' $(SPECFILE))
|
||||
RELEASE=$(shell awk '/Release:/ { print $$2 }' $(SPECFILE))
|
||||
CVSTAG = $(NAME)_$(subst .,-,$(VERSION))_$(subst .,-,$(RELEASE))
|
||||
RPMBUILD=$(shell `which rpmbuild >&/dev/null` && echo "rpmbuild" || echo "rpm")
|
||||
|
||||
prefix=/usr
|
||||
includedir=$(prefix)/include
|
||||
libdir=$(prefix)/lib
|
||||
|
||||
default: all
|
||||
|
||||
all:
|
||||
@$(MAKE) -C src
|
||||
|
||||
install: all
|
||||
|
||||
clean:
|
||||
@$(MAKE) -C src clean
|
||||
@$(MAKE) -C harness clean
|
||||
|
||||
tag-archive:
|
||||
@cvs -Q tag -F $(CVSTAG)
|
||||
|
||||
create-archive: tag-archive
|
||||
@rm -rf /tmp/$(NAME)
|
||||
@cd /tmp; cvs -Q -d $(CVSROOT) export -r$(CVSTAG) $(NAME) || echo GRRRrrrrr -- ignore [export aborted]
|
||||
@mv /tmp/$(NAME) /tmp/$(NAME)-$(VERSION)
|
||||
@cd /tmp; tar czSpf $(NAME)-$(VERSION).tar.gz $(NAME)-$(VERSION)
|
||||
@rm -rf /tmp/$(NAME)-$(VERSION)
|
||||
@cp /tmp/$(NAME)-$(VERSION).tar.gz .
|
||||
@rm -f /tmp/$(NAME)-$(VERSION).tar.gz
|
||||
@echo " "
|
||||
@echo "The final archive is ./$(NAME)-$(VERSION).tar.gz."
|
||||
|
||||
archive: clean tag-archive create-archive
|
||||
|
||||
srpm: create-archive
|
||||
$(RPMBUILD) --define "_sourcedir `pwd`" --define "_srcrpmdir `pwd`" --nodeps -bs $(SPECFILE)
|
||||
@@ -0,0 +1,4 @@
|
||||
- Write man pages.
|
||||
- Make -static links against libaio work.
|
||||
- Fallback on userspace if the kernel calls return -ENOSYS.
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
# foo.
|
||||
TEST_SRCS:=$(shell find cases/ -name \*.t | sort -n -t/ -k2)
|
||||
PROGS:=$(patsubst %.t,%.p,$(TEST_SRCS))
|
||||
HARNESS_SRCS:=main.c
|
||||
# io_queue.c
|
||||
|
||||
CFLAGS=-Wall -Werror -g -O -laio
|
||||
#-lpthread -lrt
|
||||
|
||||
all: $(PROGS)
|
||||
|
||||
$(PROGS): %.p: %.t $(HARNESS_SRCS)
|
||||
$(CC) $(CFLAGS) -DTEST_NAME=\"$<\" -o $@ main.c
|
||||
|
||||
clean:
|
||||
rm -f $(PROGS) *.o runtests.out rofile wofile rwfile
|
||||
|
||||
.PHONY:
|
||||
|
||||
testdir/rofile: .PHONY
|
||||
rm -f $@
|
||||
echo "test" >$@
|
||||
chmod 400 $@
|
||||
|
||||
testdir/wofile: .PHONY
|
||||
rm -f $@
|
||||
echo "test" >$@
|
||||
chmod 200 $@
|
||||
|
||||
testdir/rwfile: .PHONY
|
||||
rm -f $@
|
||||
echo "test" >$@
|
||||
chmod 600 $@
|
||||
|
||||
check: $(PROGS) testdir/rofile testdir/rwfile testdir/wofile
|
||||
./runtests.sh $(PROGS)
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
Notes on running this test suite:
|
||||
|
||||
To run the test suite, run "make check". All test cases should pass
|
||||
and there should be 0 fails.
|
||||
|
||||
Several of the test cases require a directory on the filesystem under
|
||||
test for the creation of test files, as well as the generation of
|
||||
error conditions. The test cases assume the directories (or symlinks
|
||||
to directories) are as follows:
|
||||
|
||||
testdir/
|
||||
- used for general read/write test cases. Must have at
|
||||
least as much free space as the machine has RAM (up
|
||||
to 768MB).
|
||||
testdir.enospc/
|
||||
- a filesystem that has space for writing 8KB out, but
|
||||
fails with -ENOSPC beyond 8KB.
|
||||
testdir.ext2/
|
||||
- must be an ext2 filesystem.
|
||||
@@ -0,0 +1,9 @@
|
||||
/* 0.t
|
||||
Test harness check: okay.
|
||||
*/
|
||||
int test_main(void)
|
||||
{
|
||||
printf("test_main: okay\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
/* 1.t
|
||||
Test harness check: fail.
|
||||
*/
|
||||
int test_main(void)
|
||||
{
|
||||
printf("test_main: fail\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
/* 10.t - uses testdir.enospc/rwfile
|
||||
- Check results on out-of-space and out-of-quota. (10.t)
|
||||
- write that fills filesystem but does not go over should succeed
|
||||
- write that fills filesystem and goes over should be partial
|
||||
- write to full filesystem should return -ENOSPC
|
||||
- read beyond end of file after ENOSPC should return 0
|
||||
*/
|
||||
#include "aio_setup.h"
|
||||
|
||||
#include <sys/time.h>
|
||||
#include <sys/resource.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int test_main(void)
|
||||
{
|
||||
/* Note: changing either of these requires updating the ext2-enospc.img
|
||||
* filesystem image. Also, if SIZE is less than PAGE_SIZE, problems
|
||||
* crop up due to ext2's preallocation.
|
||||
*/
|
||||
#define LIMIT 65536
|
||||
#define SIZE 65536
|
||||
char *buf;
|
||||
int rwfd;
|
||||
int status = 0, res;
|
||||
|
||||
rwfd = open("testdir.enospc/rwfile", O_RDWR|O_CREAT|O_TRUNC, 0600);
|
||||
assert(rwfd != -1);
|
||||
res = ftruncate(rwfd, 0); assert(res == 0);
|
||||
buf = malloc(SIZE); assert(buf != NULL);
|
||||
memset(buf, 0, SIZE);
|
||||
|
||||
|
||||
status |= attempt_rw(rwfd, buf, SIZE, LIMIT-SIZE, WRITE, SIZE);
|
||||
status |= attempt_rw(rwfd, buf, SIZE, LIMIT-SIZE, READ, SIZE);
|
||||
|
||||
status |= attempt_rw(rwfd, buf, SIZE, LIMIT, WRITE, -ENOSPC);
|
||||
status |= attempt_rw(rwfd, buf, SIZE, LIMIT, READ, 0);
|
||||
|
||||
res = ftruncate(rwfd, 0); assert(res == 0);
|
||||
|
||||
status |= attempt_rw(rwfd, buf, SIZE, 1+LIMIT-SIZE, WRITE, SIZE-1);
|
||||
status |= attempt_rw(rwfd, buf, SIZE, 1+LIMIT-SIZE, READ, SIZE-1);
|
||||
status |= attempt_rw(rwfd, buf, SIZE, LIMIT, READ, 0);
|
||||
|
||||
status |= attempt_rw(rwfd, buf, SIZE, LIMIT, WRITE, -ENOSPC);
|
||||
status |= attempt_rw(rwfd, buf, SIZE, LIMIT, READ, 0);
|
||||
status |= attempt_rw(rwfd, buf, 0, LIMIT, WRITE, 0);
|
||||
|
||||
res = close(rwfd); assert(res == 0);
|
||||
res = unlink("testdir.enospc/rwfile"); assert(res == 0);
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
/* 11.t - uses testdir/rwfile
|
||||
- repeated read / write of same page (to check accounting) (11.t)
|
||||
*/
|
||||
#include "aio_setup.h"
|
||||
|
||||
#include <sys/time.h>
|
||||
#include <sys/resource.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int test_main(void)
|
||||
{
|
||||
#define COUNT 1000000
|
||||
#define SIZE 256
|
||||
char *buf;
|
||||
int rwfd;
|
||||
int status = 0;
|
||||
int i;
|
||||
|
||||
rwfd = open("testdir/rwfile", O_RDWR|O_CREAT|O_TRUNC, 0600);
|
||||
assert(rwfd != -1);
|
||||
buf = malloc(SIZE); assert(buf != NULL);
|
||||
memset(buf, 0, SIZE);
|
||||
|
||||
for (i=0; i<COUNT; i++) {
|
||||
status |= attempt_rw(rwfd, buf, SIZE, 0, WRITE_SILENT, SIZE);
|
||||
if (status)
|
||||
break;
|
||||
}
|
||||
printf("completed %d out of %d writes\n", i, COUNT);
|
||||
for (i=0; i<COUNT; i++) {
|
||||
status |= attempt_rw(rwfd, buf, SIZE, 0, READ_SILENT, SIZE);
|
||||
if (status)
|
||||
break;
|
||||
}
|
||||
printf("completed %d out of %d reads\n", i, COUNT);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
/* 12.t
|
||||
- ioctx access across fork() (12.t)
|
||||
*/
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include "aio_setup.h"
|
||||
|
||||
void test_child(void)
|
||||
{
|
||||
int res;
|
||||
res = attempt_io_submit(io_ctx, 0, NULL, -EINVAL);
|
||||
fflush(stdout);
|
||||
_exit(res);
|
||||
}
|
||||
|
||||
int test_main(void)
|
||||
{
|
||||
int res, status;
|
||||
pid_t pid;
|
||||
|
||||
if (attempt_io_submit(io_ctx, 0, NULL, 0))
|
||||
return 1;
|
||||
|
||||
sigblock(sigmask(SIGCHLD) | siggetmask());
|
||||
fflush(NULL);
|
||||
pid = fork(); assert(pid != -1);
|
||||
|
||||
if (pid == 0)
|
||||
test_child();
|
||||
|
||||
res = waitpid(pid, &status, 0);
|
||||
|
||||
if (WIFEXITED(status)) {
|
||||
int failed = (WEXITSTATUS(status) != 0);
|
||||
printf("child exited with status %d%s\n", WEXITSTATUS(status),
|
||||
failed ? " -- FAILED" : "");
|
||||
return failed;
|
||||
}
|
||||
|
||||
/* anything else: failed */
|
||||
if (WIFSIGNALED(status))
|
||||
printf("child killed by signal %d -- FAILED.\n",
|
||||
WTERMSIG(status));
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/* 13.t - uses testdir/rwfile
|
||||
- Submit multiple writes larger than aio-max-size (deadlocks on older
|
||||
aio code)
|
||||
*/
|
||||
#include "aio_setup.h"
|
||||
|
||||
#include <sys/time.h>
|
||||
#include <sys/resource.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int test_main(void)
|
||||
{
|
||||
#define SIZE (1024 * 1024)
|
||||
#define IOS 8
|
||||
struct iocb iocbs[IOS];
|
||||
struct iocb *iocb_list[IOS];
|
||||
char *bufs[IOS];
|
||||
int rwfd;
|
||||
int status = 0, res;
|
||||
int i;
|
||||
|
||||
rwfd = open("testdir/rwfile", O_RDWR|O_CREAT|O_TRUNC, 0600);
|
||||
assert(rwfd != -1);
|
||||
res = ftruncate(rwfd, 0); assert(res == 0);
|
||||
|
||||
for (i=0; i<IOS; i++) {
|
||||
bufs[i] = malloc(SIZE);
|
||||
assert(bufs[i] != NULL);
|
||||
memset(bufs[i], 0, SIZE);
|
||||
|
||||
io_prep_pwrite(&iocbs[i], rwfd, bufs[i], SIZE, i * SIZE);
|
||||
iocb_list[i] = &iocbs[i];
|
||||
}
|
||||
|
||||
status |= attempt_io_submit(io_ctx, IOS, iocb_list, IOS);
|
||||
|
||||
for (i=0; i<IOS; i++) {
|
||||
struct timespec ts = { tv_sec: 30, tv_nsec: 0 };
|
||||
struct io_event event;
|
||||
struct iocb *iocb;
|
||||
|
||||
res = io_getevents(io_ctx, 0, 1, &event, &ts);
|
||||
if (res != 1) {
|
||||
status |= 1;
|
||||
printf("io_getevents failed [%d] with res=%d [%s]\n",
|
||||
i, res, (res < 0) ? strerror(-res) : "okay");
|
||||
break;
|
||||
}
|
||||
|
||||
if (event.res != SIZE)
|
||||
status |= 1;
|
||||
|
||||
iocb = (void *)event.obj;
|
||||
printf("event[%d]: write[%d] %s, returned: %ld [%s]\n",
|
||||
i, (int)(iocb - &iocbs[0]),
|
||||
(event.res != SIZE) ? "failed" : "okay",
|
||||
(long)event.res,
|
||||
(event.res < 0) ? strerror(-event.res) : "okay"
|
||||
);
|
||||
}
|
||||
|
||||
res = ftruncate(rwfd, 0); assert(res == 0);
|
||||
res = close(rwfd); assert(res == 0);
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include "aio_setup.h"
|
||||
#include <sys/mman.h>
|
||||
|
||||
#define SIZE 768*1024*1024
|
||||
|
||||
//just submit an I/O
|
||||
|
||||
int test_child(void)
|
||||
{
|
||||
char *buf;
|
||||
int rwfd;
|
||||
int res;
|
||||
long size;
|
||||
struct iocb iocb;
|
||||
struct iocb *iocbs[] = { &iocb };
|
||||
int loop = 10;
|
||||
int i;
|
||||
|
||||
aio_setup(1024);
|
||||
|
||||
size = SIZE;
|
||||
|
||||
printf("size = %ld\n", size);
|
||||
|
||||
rwfd = open("testdir/rwfile", O_RDWR); assert(rwfd !=
|
||||
-1);
|
||||
res = ftruncate(rwfd, 0); assert(res == 0);
|
||||
buf = malloc(size); assert(buf !=
|
||||
NULL);
|
||||
|
||||
for(i=0;i<loop;i++) {
|
||||
|
||||
switch(i%2) {
|
||||
case 0:
|
||||
io_prep_pwrite(&iocb, rwfd, buf, size, 0);
|
||||
break;
|
||||
case 1:
|
||||
io_prep_pread(&iocb, rwfd, buf, size, 0);
|
||||
}
|
||||
|
||||
res = io_submit(io_ctx, 1, iocbs);
|
||||
if (res != 1) {
|
||||
printf("child: submit: io_submit res=%d [%s]\n", res,
|
||||
strerror(-res));
|
||||
_exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
res = ftruncate(rwfd, 0); assert(res == 0);
|
||||
|
||||
_exit(0);
|
||||
}
|
||||
|
||||
/* from 12.t */
|
||||
int test_main(void)
|
||||
{
|
||||
int res, status;
|
||||
pid_t pid;
|
||||
|
||||
if (attempt_io_submit(io_ctx, 0, NULL, 0))
|
||||
return 1;
|
||||
|
||||
sigblock(sigmask(SIGCHLD) | siggetmask());
|
||||
fflush(NULL);
|
||||
pid = fork(); assert(pid != -1);
|
||||
|
||||
if (pid == 0)
|
||||
test_child();
|
||||
|
||||
res = waitpid(pid, &status, 0);
|
||||
|
||||
if (WIFEXITED(status)) {
|
||||
int failed = (WEXITSTATUS(status) != 0);
|
||||
printf("child exited with status %d%s\n", WEXITSTATUS(status),
|
||||
failed ? " -- FAILED" : "");
|
||||
return failed;
|
||||
}
|
||||
|
||||
/* anything else: failed */
|
||||
if (WIFSIGNALED(status))
|
||||
printf("child killed by signal %d -- FAILED.\n",
|
||||
WTERMSIG(status));
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/* 2.t
|
||||
- io_setup (#2)
|
||||
- with invalid context pointer
|
||||
- with maxevents <= 0
|
||||
- with an already initialized ctxp
|
||||
*/
|
||||
|
||||
int attempt(int n, io_context_t *ctxp, int expect)
|
||||
{
|
||||
int res;
|
||||
|
||||
printf("expect %3d: io_setup(%5d, %p) = ", expect, n, ctxp);
|
||||
fflush(stdout);
|
||||
res = io_setup(n, ctxp);
|
||||
printf("%3d [%s]%s\n", res, strerror(-res),
|
||||
(res != expect) ? " -- FAILED" : "");
|
||||
if (res != expect)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int test_main(void)
|
||||
{
|
||||
io_context_t ctx;
|
||||
int status = 0;
|
||||
|
||||
ctx = NULL;
|
||||
status |= attempt(-1000, KERNEL_RW_POINTER, -EFAULT);
|
||||
status |= attempt( 1000, KERNEL_RW_POINTER, -EFAULT);
|
||||
status |= attempt( 0, KERNEL_RW_POINTER, -EFAULT);
|
||||
status |= attempt(-1000, &ctx, -EINVAL);
|
||||
status |= attempt( -1, &ctx, -EINVAL);
|
||||
status |= attempt( 0, &ctx, -EINVAL);
|
||||
assert(ctx == NULL);
|
||||
status |= attempt( 1, &ctx, 0);
|
||||
status |= attempt( 1, &ctx, -EINVAL);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
/* 3.t
|
||||
- io_submit/io_getevents with invalid addresses (3.t)
|
||||
|
||||
*/
|
||||
#include "aio_setup.h"
|
||||
|
||||
int test_main(void)
|
||||
{
|
||||
struct iocb a, b;
|
||||
struct iocb *good_ios[] = { &a, &b };
|
||||
struct iocb *bad1_ios[] = { NULL, &b };
|
||||
struct iocb *bad2_ios[] = { KERNEL_RW_POINTER, &a };
|
||||
int status = 0;
|
||||
|
||||
status |= attempt_io_submit(BAD_CTX, 1, good_ios, -EINVAL);
|
||||
status |= attempt_io_submit( io_ctx, 0, good_ios, 0);
|
||||
status |= attempt_io_submit( io_ctx, 1, NULL, -EFAULT);
|
||||
status |= attempt_io_submit( io_ctx, 1, (void *)-1, -EFAULT);
|
||||
status |= attempt_io_submit( io_ctx, 2, bad1_ios, -EFAULT);
|
||||
status |= attempt_io_submit( io_ctx, 2, bad2_ios, -EFAULT);
|
||||
status |= attempt_io_submit( io_ctx, -1, good_ios, -EINVAL);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
/* 4.t
|
||||
- read of descriptor without read permission (4.t)
|
||||
- write to descriptor without write permission (4.t)
|
||||
- check that O_APPEND writes actually append
|
||||
|
||||
*/
|
||||
#include "aio_setup.h"
|
||||
|
||||
#define SIZE 512
|
||||
#define READ 'r'
|
||||
#define WRITE 'w'
|
||||
int attempt(int fd, void *buf, int count, long long pos, int rw, int expect)
|
||||
{
|
||||
struct iocb iocb;
|
||||
int res;
|
||||
|
||||
switch(rw) {
|
||||
case READ: io_prep_pread (&iocb, fd, buf, count, pos); break;
|
||||
case WRITE: io_prep_pwrite(&iocb, fd, buf, count, pos); break;
|
||||
}
|
||||
|
||||
printf("expect %3d: (%c), res = ", expect, rw);
|
||||
fflush(stdout);
|
||||
res = sync_submit(&iocb);
|
||||
printf("%3d [%s]%s\n", res, (res <= 0) ? strerror(-res) : "Success",
|
||||
(res != expect) ? " -- FAILED" : "");
|
||||
if (res != expect)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int test_main(void)
|
||||
{
|
||||
char buf[SIZE];
|
||||
int rofd, wofd, rwfd;
|
||||
int status = 0, res;
|
||||
|
||||
memset(buf, 0, SIZE);
|
||||
|
||||
rofd = open("testdir/rofile", O_RDONLY); assert(rofd != -1);
|
||||
wofd = open("testdir/wofile", O_WRONLY); assert(wofd != -1);
|
||||
rwfd = open("testdir/rwfile", O_RDWR); assert(rwfd != -1);
|
||||
|
||||
status |= attempt(rofd, buf, SIZE, 0, WRITE, -EBADF);
|
||||
status |= attempt(wofd, buf, SIZE, 0, READ, -EBADF);
|
||||
status |= attempt(rwfd, buf, SIZE, 0, WRITE, SIZE);
|
||||
status |= attempt(rwfd, buf, SIZE, 0, READ, SIZE);
|
||||
status |= attempt(rwfd, buf, SIZE, -1, READ, -EINVAL);
|
||||
status |= attempt(rwfd, buf, SIZE, -1, WRITE, -EINVAL);
|
||||
|
||||
rwfd = open("testdir/rwfile", O_RDWR|O_APPEND); assert(rwfd != -1);
|
||||
res = ftruncate(rwfd, 0); assert(res == 0);
|
||||
status |= attempt(rwfd, buf, SIZE, 0, READ, 0);
|
||||
status |= attempt(rwfd, "1234", 4, 0, WRITE, 4);
|
||||
status |= attempt(rwfd, "5678", 4, 0, WRITE, 4);
|
||||
memset(buf, 0, SIZE);
|
||||
status |= attempt(rwfd, buf, SIZE, 0, READ, 8);
|
||||
printf("read after append: [%s]\n", buf);
|
||||
assert(memcmp(buf, "12345678", 8) == 0);
|
||||
|
||||
status |= attempt(rwfd, KERNEL_RW_POINTER, SIZE, 0, READ, -EFAULT);
|
||||
status |= attempt(rwfd, KERNEL_RW_POINTER, SIZE, 0, WRITE, -EFAULT);
|
||||
|
||||
/* Some architectures map the 0 page. Ugh. */
|
||||
#if !defined(__ia64__)
|
||||
status |= attempt(rwfd, NULL, SIZE, 0, WRITE, -EFAULT);
|
||||
#endif
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
/* 5.t
|
||||
- Write from a mmap() of the same file. (5.t)
|
||||
*/
|
||||
#include "aio_setup.h"
|
||||
#include <sys/mman.h>
|
||||
|
||||
int test_main(void)
|
||||
{
|
||||
int page_size = getpagesize();
|
||||
#define SIZE 512
|
||||
char *buf;
|
||||
int rwfd;
|
||||
int status = 0, res;
|
||||
|
||||
rwfd = open("testdir/rwfile", O_RDWR); assert(rwfd != -1);
|
||||
res = ftruncate(rwfd, 512); assert(res == 0);
|
||||
|
||||
buf = mmap(0, page_size, PROT_READ|PROT_WRITE, MAP_SHARED, rwfd, 0);
|
||||
assert(buf != (char *)-1);
|
||||
|
||||
status |= attempt_rw(rwfd, buf, SIZE, 0, WRITE, SIZE);
|
||||
status |= attempt_rw(rwfd, buf, SIZE, 0, READ, SIZE);
|
||||
|
||||
res = munmap(buf, page_size); assert(res == 0);
|
||||
buf = mmap(0, page_size, PROT_READ|PROT_WRITE, MAP_SHARED, rwfd, 0);
|
||||
assert(buf != (char *)-1);
|
||||
|
||||
status |= attempt_rw(rwfd, buf, SIZE, 0, READ, SIZE);
|
||||
status |= attempt_rw(rwfd, buf, SIZE, 0, WRITE, SIZE);
|
||||
|
||||
res = munmap(buf, page_size); assert(res == 0);
|
||||
buf = mmap(0, page_size, PROT_READ, MAP_SHARED, rwfd, 0);
|
||||
assert(buf != (char *)-1);
|
||||
|
||||
status |= attempt_rw(rwfd, buf, SIZE, 0, WRITE, SIZE);
|
||||
status |= attempt_rw(rwfd, buf, SIZE, 0, READ, -EFAULT);
|
||||
|
||||
res = munmap(buf, page_size); assert(res == 0);
|
||||
buf = mmap(0, page_size, PROT_WRITE, MAP_SHARED, rwfd, 0);
|
||||
assert(buf != (char *)-1);
|
||||
|
||||
status |= attempt_rw(rwfd, buf, SIZE, 0, READ, SIZE);
|
||||
status |= attempt_rw(rwfd, buf, SIZE, 0, WRITE, -EFAULT);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
/* 6.t
|
||||
- huge reads (pinned pages) (6.t)
|
||||
- huge writes (6.t)
|
||||
*/
|
||||
#include "aio_setup.h"
|
||||
#include <sys/mman.h>
|
||||
|
||||
long getmemsize(void)
|
||||
{
|
||||
FILE *f = fopen("/proc/meminfo", "r");
|
||||
long size;
|
||||
int gotit = 0;
|
||||
char str[256];
|
||||
|
||||
assert(f != NULL);
|
||||
while (NULL != fgets(str, 255, f)) {
|
||||
str[255] = 0;
|
||||
if (0 == memcmp(str, "MemTotal:", 9)) {
|
||||
if (1 == sscanf(str + 9, "%ld", &size)) {
|
||||
gotit = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
fclose(f);
|
||||
|
||||
assert(gotit != 0);
|
||||
return size;
|
||||
}
|
||||
|
||||
int test_main(void)
|
||||
{
|
||||
char *buf;
|
||||
int rwfd;
|
||||
int status = 0, res;
|
||||
long size;
|
||||
|
||||
size = getmemsize();
|
||||
printf("size = %ld\n", size);
|
||||
assert(size >= (16 * 1024));
|
||||
if (size > (768 * 1024))
|
||||
size = 768 * 1024;
|
||||
size *= 1024;
|
||||
|
||||
rwfd = open("testdir/rwfile", O_RDWR); assert(rwfd != -1);
|
||||
res = ftruncate(rwfd, 0); assert(res == 0);
|
||||
buf = malloc(size); assert(buf != NULL);
|
||||
|
||||
//memset(buf, 0, size);
|
||||
status |= attempt_rw(rwfd, buf, size, 0, WRITE, size);
|
||||
status |= attempt_rw(rwfd, buf, size, 0, READ, size);
|
||||
|
||||
//res = ftruncate(rwfd, 0); assert(res == 0);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
/* 7.t
|
||||
- Write overlapping the file size rlimit boundary: should be a short
|
||||
write. (7.t)
|
||||
- Write at the file size rlimit boundary: should give EFBIG. (I think
|
||||
the spec requires that you do NOT deliver SIGXFSZ in this case, where
|
||||
you would do so for sync IO.) (7.t)
|
||||
- Special case: a write of zero bytes at or beyond the file size rlimit
|
||||
boundary must return success. (7.t)
|
||||
*/
|
||||
|
||||
#include <sys/resource.h>
|
||||
|
||||
void SET_RLIMIT(long long limit)
|
||||
{
|
||||
struct rlimit rlim;
|
||||
int res;
|
||||
|
||||
rlim.rlim_cur = limit; assert(rlim.rlim_cur == limit);
|
||||
rlim.rlim_max = limit; assert(rlim.rlim_max == limit);
|
||||
|
||||
res = setrlimit(RLIMIT_FSIZE, &rlim); assert(res == 0);
|
||||
}
|
||||
|
||||
#define LIMIT 8192
|
||||
#define FILENAME "testdir/rwfile"
|
||||
|
||||
#include "common-7-8.h"
|
||||
@@ -0,0 +1,49 @@
|
||||
/* 8.t
|
||||
- Ditto for the above three tests at the offset maximum (largest
|
||||
possible ext2/3 file size.) (8.t)
|
||||
*/
|
||||
#include <sys/vfs.h>
|
||||
|
||||
#define EXT2_OLD_SUPER_MAGIC 0xEF51
|
||||
#define EXT2_SUPER_MAGIC 0xEF53
|
||||
|
||||
long long get_fs_limit(int fd)
|
||||
{
|
||||
struct statfs s;
|
||||
int res;
|
||||
long long lim = 0;
|
||||
|
||||
res = fstatfs(fd, &s); assert(res == 0);
|
||||
|
||||
switch(s.f_type) {
|
||||
case EXT2_OLD_SUPER_MAGIC:
|
||||
case EXT2_SUPER_MAGIC:
|
||||
#if 0
|
||||
{
|
||||
long long tmp;
|
||||
tmp = s.f_bsize / 4;
|
||||
/* 12 direct + indirect block + dind + tind */
|
||||
lim = 12 + tmp + tmp * tmp + tmp * tmp * tmp;
|
||||
lim *= s.f_bsize;
|
||||
printf("limit(%ld) = %Ld\n", (long)s.f_bsize, lim);
|
||||
}
|
||||
#endif
|
||||
switch(s.f_bsize) {
|
||||
case 4096: lim = 2199023251456; break;
|
||||
default:
|
||||
printf("unknown ext2 blocksize %ld\n", (long)s.f_bsize);
|
||||
exit(3);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
printf("unknown filesystem 0x%08lx\n", (long)s.f_type);
|
||||
exit(3);
|
||||
}
|
||||
return lim;
|
||||
}
|
||||
|
||||
#define SET_RLIMIT(x) do ; while (0)
|
||||
#define LIMIT get_fs_limit(rwfd)
|
||||
#define FILENAME "testdir.ext2/rwfile"
|
||||
|
||||
#include "common-7-8.h"
|
||||
@@ -0,0 +1,98 @@
|
||||
io_context_t io_ctx;
|
||||
#define BAD_CTX ((io_context_t)-1)
|
||||
|
||||
void aio_setup(int n)
|
||||
{
|
||||
int res = io_queue_init(n, &io_ctx);
|
||||
if (res != 0) {
|
||||
printf("io_queue_setup(%d) returned %d (%s)\n",
|
||||
n, res, strerror(-res));
|
||||
exit(3);
|
||||
}
|
||||
}
|
||||
|
||||
int attempt_io_submit(io_context_t ctx, long nr, struct iocb *ios[], int expect)
|
||||
{
|
||||
int res;
|
||||
|
||||
printf("expect %3d: io_submit(%10p, %3ld, %10p) = ", expect, ctx, nr, ios);
|
||||
fflush(stdout);
|
||||
res = io_submit(ctx, nr, ios);
|
||||
printf("%3d [%s]%s\n", res, (res <= 0) ? strerror(-res) : "",
|
||||
(res != expect) ? " -- FAILED" : "");
|
||||
if (res != expect)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sync_submit(struct iocb *iocb)
|
||||
{
|
||||
struct io_event event;
|
||||
struct iocb *iocbs[] = { iocb };
|
||||
int res;
|
||||
|
||||
/* 30 second timeout should be enough */
|
||||
struct timespec ts;
|
||||
ts.tv_sec = 30;
|
||||
ts.tv_nsec = 0;
|
||||
|
||||
res = io_submit(io_ctx, 1, iocbs);
|
||||
if (res != 1) {
|
||||
printf("sync_submit: io_submit res=%d [%s]\n", res, strerror(-res));
|
||||
return res;
|
||||
}
|
||||
|
||||
res = io_getevents(io_ctx, 0, 1, &event, &ts);
|
||||
if (res != 1) {
|
||||
printf("sync_submit: io_getevents res=%d [%s]\n", res, strerror(-res));
|
||||
return res;
|
||||
}
|
||||
return event.res;
|
||||
}
|
||||
|
||||
#define SETUP aio_setup(1024)
|
||||
|
||||
|
||||
#define READ 'r'
|
||||
#define WRITE 'w'
|
||||
#define READ_SILENT 'R'
|
||||
#define WRITE_SILENT 'W'
|
||||
int attempt_rw(int fd, void *buf, int count, long long pos, int rw, int expect)
|
||||
{
|
||||
struct iocb iocb;
|
||||
int res;
|
||||
int silent = 0;
|
||||
|
||||
switch(rw) {
|
||||
case READ_SILENT:
|
||||
silent = 1;
|
||||
case READ:
|
||||
io_prep_pread (&iocb, fd, buf, count, pos);
|
||||
break;
|
||||
case WRITE_SILENT:
|
||||
silent = 1;
|
||||
case WRITE:
|
||||
io_prep_pwrite(&iocb, fd, buf, count, pos);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!silent) {
|
||||
printf("expect %5d: (%c), res = ", expect, rw);
|
||||
fflush(stdout);
|
||||
}
|
||||
res = sync_submit(&iocb);
|
||||
if (!silent || res != expect) {
|
||||
if (silent)
|
||||
printf("expect %5d: (%c), res = ", expect, rw);
|
||||
printf("%5d [%s]%s\n", res,
|
||||
(res <= 0) ? strerror(-res) : "Success",
|
||||
(res != expect) ? " -- FAILED" : "");
|
||||
}
|
||||
|
||||
if (res != expect)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
/* common-7-8.h
|
||||
*/
|
||||
#include "aio_setup.h"
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#define SIZE 512
|
||||
|
||||
int test_main(void)
|
||||
{
|
||||
char *buf;
|
||||
int rwfd;
|
||||
int status = 0, res;
|
||||
long long limit;
|
||||
|
||||
rwfd = open(FILENAME, O_RDWR); assert(rwfd != -1);
|
||||
res = ftruncate(rwfd, 0); assert(res == 0);
|
||||
buf = malloc(SIZE); assert(buf != NULL);
|
||||
memset(buf, 0, SIZE);
|
||||
|
||||
limit = LIMIT;
|
||||
|
||||
SET_RLIMIT(limit);
|
||||
|
||||
status |= attempt_rw(rwfd, buf, SIZE, limit-SIZE, WRITE, SIZE);
|
||||
status |= attempt_rw(rwfd, buf, SIZE, limit-SIZE, READ, SIZE);
|
||||
|
||||
status |= attempt_rw(rwfd, buf, SIZE, 1+limit-SIZE, WRITE, SIZE-1);
|
||||
status |= attempt_rw(rwfd, buf, SIZE, 1+limit-SIZE, READ, SIZE-1);
|
||||
|
||||
status |= attempt_rw(rwfd, buf, SIZE, limit, WRITE, -EFBIG);
|
||||
status |= attempt_rw(rwfd, buf, SIZE, limit, READ, 0);
|
||||
status |= attempt_rw(rwfd, buf, 0, limit, WRITE, 0);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <libaio.h>
|
||||
|
||||
#if defined(__i386__)
|
||||
#define KERNEL_RW_POINTER ((void *)0xc0010000)
|
||||
#else
|
||||
//#warning Not really sure where kernel memory is. Guessing.
|
||||
#define KERNEL_RW_POINTER ((void *)0xffffffffc0010000)
|
||||
#endif
|
||||
|
||||
|
||||
char test_name[] = TEST_NAME;
|
||||
|
||||
#include TEST_NAME
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
#if defined(SETUP)
|
||||
SETUP;
|
||||
#endif
|
||||
|
||||
res = test_main();
|
||||
printf("test %s completed %s.\n", test_name,
|
||||
res ? "FAILED" : "PASSED"
|
||||
);
|
||||
fflush(stdout);
|
||||
return res ? 1 : 0;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
#!/bin/sh
|
||||
|
||||
passes=0
|
||||
fails=0
|
||||
|
||||
echo "Test run starting at" `date`
|
||||
|
||||
while [ $# -ge 1 ] ; do
|
||||
this_test=$1
|
||||
shift
|
||||
echo "Starting $this_test"
|
||||
$this_test 2>&1
|
||||
res=$?
|
||||
if [ $res -eq 0 ] ; then str="" ; passes=$[passes + 1] ; else str=" -- FAILED" ; fails=$[fails + 1] ; fi
|
||||
echo "Completed $this_test with $res$str".
|
||||
done
|
||||
|
||||
echo "Pass: $passes Fail: $fails"
|
||||
echo "Test run complete at" `date`
|
||||
@@ -0,0 +1,177 @@
|
||||
Name: libaio
|
||||
Version: 0.3.104
|
||||
Release: 1
|
||||
Summary: Linux-native asynchronous I/O access library
|
||||
Copyright: LGPL
|
||||
Group: System Environment/Libraries
|
||||
Source: %{name}-%{version}.tar.gz
|
||||
BuildRoot: %{_tmppath}/%{name}-root
|
||||
# Fix ExclusiveArch as we implement this functionality on more architectures
|
||||
ExclusiveArch: i386 x86_64 ia64 s390 s390x ppc ppc64 ppc64pseries ppc64iseries alpha alphaev6
|
||||
|
||||
%description
|
||||
The Linux-native asynchronous I/O facility ("async I/O", or "aio") has a
|
||||
richer API and capability set than the simple POSIX async I/O facility.
|
||||
This library, libaio, provides the Linux-native API for async I/O.
|
||||
The POSIX async I/O facility requires this library in order to provide
|
||||
kernel-accelerated async I/O capabilities, as do applications which
|
||||
require the Linux-native async I/O API.
|
||||
|
||||
%package devel
|
||||
Summary: Development files for Linux-native asynchronous I/O access
|
||||
Group: Development/System
|
||||
Requires: libaio
|
||||
Provides: libaio.so.1
|
||||
|
||||
%description devel
|
||||
This package provides header files to include and libraries to link with
|
||||
for the Linux-native asynchronous I/O facility ("async I/O", or "aio").
|
||||
|
||||
%prep
|
||||
%setup
|
||||
|
||||
%build
|
||||
make
|
||||
|
||||
%install
|
||||
[ "$RPM_BUILD_ROOT" != "/" ] && rm -rf $RPM_BUILD_ROOT
|
||||
|
||||
make install prefix=$RPM_BUILD_ROOT/usr \
|
||||
libdir=$RPM_BUILD_ROOT/%{_libdir} \
|
||||
root=$RPM_BUILD_ROOT
|
||||
|
||||
%clean
|
||||
[ "$RPM_BUILD_ROOT" != "/" ] && rm -rf $RPM_BUILD_ROOT
|
||||
|
||||
%post -p /sbin/ldconfig
|
||||
|
||||
%postun -p /sbin/ldconfig
|
||||
|
||||
%files
|
||||
%defattr(-,root,root)
|
||||
%attr(0755,root,root) %{_libdir}/libaio.so.*
|
||||
%doc COPYING TODO
|
||||
|
||||
%files devel
|
||||
%defattr(-,root,root)
|
||||
%attr(0644,root,root) %{_includedir}/*
|
||||
%attr(0755,root,root) %{_libdir}/libaio.so
|
||||
%attr(0644,root,root) %{_libdir}/libaio.a
|
||||
|
||||
%changelog
|
||||
* Fri Apr 1 2005 Jeff Moyer <jmoyer@redhat.com> - 0.3.104-1
|
||||
- Add Alpha architecture support. (Sergey Tikhonov <tsv@solvo.ru>)
|
||||
|
||||
* Tue Jan 25 2005 Jeff Moyer <jmoyer@redhat.com> - 0.3.103-1
|
||||
- Fix SONAME breakage. In changing file names around, I also changed the
|
||||
SONAME, which is a no no.
|
||||
|
||||
* Thu Oct 14 2004 Jeff Moyer <jmoyer@redhat.com> - 0.3.102-1
|
||||
- S390 asm had a bug; I forgot to update the clobber list. Lucky for me,
|
||||
newer compilers complain about such things.
|
||||
- Also update the s390 asm to look more like the new kernel variants.
|
||||
|
||||
* Wed Oct 13 2004 Jeff Moyer <jmoyer@redhat.com> - 0.3.101-1
|
||||
- Revert syscall return values to be -ERRNO. This was an inadvertant bug
|
||||
introduced when clobber lists changed.
|
||||
- add ppc64pseries and ppc64iseries to exclusivearch
|
||||
|
||||
* Tue Sep 14 2004 Jeff Moyer <jmoyer@redhat.com> - 0.3.100-1
|
||||
- Switch around the tests for _PPC_ and _powerpc64_ so that the ppc64
|
||||
platforms get the right padding.
|
||||
|
||||
* Wed Jul 14 2004 Jeff Moyer <jmoyer@redhat.com> - 0.3.99-4
|
||||
- Ok, there was a race in moving the cvs module. Someone rebuild from
|
||||
the old cvs into fc3. *sigh* bumping rev.
|
||||
|
||||
* Wed Jul 14 2004 Jeff Moyer <jmoyer@redhat.com> - 0.3.99-3
|
||||
- Actually provide libaio.so.1.
|
||||
|
||||
* Tue Mar 30 2004 Jeff Moyer <jmoyer@redhat.com> - 0.3.99-2
|
||||
- Apparently the 0.3.93 patch was not meant for 0.3.96. Backed it out.
|
||||
|
||||
* Tue Mar 30 2004 Jeff Moyer <jmoyer@redhat.com> - 0.3.99-1
|
||||
- Fix compat calls.
|
||||
- make library .so.1.0.0 and make symlinks properly.
|
||||
- Fix header file for inclusion in c++ code.
|
||||
|
||||
* Thu Feb 26 2004 Jeff Moyer <jmoyer@redhat.com> 0.3.98-2
|
||||
- bah. fix version nr in changelog.
|
||||
|
||||
* Thu Feb 26 2004 Jeff Moyer <jmoyer@redhat.com> 0.3.98-1
|
||||
- fix compiler warnings.
|
||||
|
||||
* Thu Feb 26 2004 Jeff Moyer <jmoyer@redhat.com> 0.3.97-2
|
||||
- make srpm was using rpm to do a build. changed that to use rpmbuild if
|
||||
it exists, and fallback to rpm if it doesn't.
|
||||
|
||||
* Tue Feb 24 2004 Jeff Moyer <jmoyer@redhat.com> 0.3.97-1
|
||||
- Use libc syscall(2) instead of rolling our own calling mechanism. This
|
||||
change is inspired due to a failure to build with newer gcc, since clobber
|
||||
lists were wrong.
|
||||
- Add -fpic to the CFLAGS for all architectures. Should address bz #109457.
|
||||
- change a #include from <linux/types.h> to <sys/types.h>. Fixes a build
|
||||
issue on s390.
|
||||
|
||||
* Wed Jul 7 2003 Bill Nottingham <notting@redhat.com> 0.3.96-3
|
||||
- fix paths on lib64 arches
|
||||
|
||||
* Wed Jun 18 2003 Michael K. Johnson <johnsonm@redhat.com> 0.3.96-2
|
||||
- optimization in io_getevents from Arjan van de Ven in 0.3.96-1
|
||||
- deal with ia64 in 0.3.96-2
|
||||
|
||||
* Wed May 28 2003 Michael K. Johnson <johnsonm@redhat.com> 0.3.95-1
|
||||
- ppc bugfix from Julie DeWandel
|
||||
|
||||
* Tue May 20 2003 Michael K. Johnson <johnsonm@redhat.com> 0.3.94-1
|
||||
- symbol versioning fix from Ulrich Drepper
|
||||
|
||||
* Mon Jan 27 2003 Benjamin LaHaise <bcrl@redhat.com>
|
||||
- bump to 0.3.93-3 for rebuild.
|
||||
|
||||
* Mon Dec 16 2002 Benjamin LaHaise <bcrl@redhat.com>
|
||||
- libaio 0.3.93 test release
|
||||
- add powerpc support from Gianni Tedesco <gianni@ecsc.co.uk>
|
||||
- add s/390 support from Arnd Bergmann <arnd@bergmann-dalldorf.de>
|
||||
|
||||
* Fri Sep 12 2002 Benjamin LaHaise <bcrl@redhat.com>
|
||||
- libaio 0.3.92 test release
|
||||
- build on x86-64
|
||||
|
||||
* Thu Sep 12 2002 Benjamin LaHaise <bcrl@redhat.com>
|
||||
- libaio 0.3.91 test release
|
||||
- build on ia64
|
||||
- remove libredhat-kernel from the .spec file
|
||||
|
||||
* Thu Sep 5 2002 Benjamin LaHaise <bcrl@redhat.com>
|
||||
- libaio 0.3.90 test release
|
||||
|
||||
* Mon Apr 29 2002 Benjamin LaHaise <bcrl@redhat.com>
|
||||
- add requires initscripts >= 6.47-1 to get boot time libredhat-kernel
|
||||
linkage correct.
|
||||
- typo fix
|
||||
|
||||
* Thu Apr 25 2002 Benjamin LaHaise <bcrl@redhat.com>
|
||||
- make /usr/lib/libredhat-kernel.so point to /lib/libredhat-kernel.so.1.0.0
|
||||
|
||||
* Mon Apr 15 2002 Tim Powers <timp@redhat.com>
|
||||
- make the post scriptlet not use /bin/sh
|
||||
|
||||
* Sat Apr 12 2002 Benjamin LaHaise <bcrl@redhat.com>
|
||||
- add /lib/libredhat-kernel* to %files.
|
||||
|
||||
* Fri Apr 12 2002 Benjamin LaHaise <bcrl@redhat.com>
|
||||
- make the dummy install as /lib/libredhat-kernel.so.1.0.0 so
|
||||
that ldconfig will link against it if no other is installed.
|
||||
|
||||
* Tue Jan 22 2002 Benjamin LaHaise <bcrl@redhat.com>
|
||||
- add io_getevents
|
||||
|
||||
* Tue Jan 22 2002 Michael K. Johnson <johnsonm@redhat.com>
|
||||
- Make linker happy with /usr/lib symlink for libredhat-kernel.so
|
||||
|
||||
* Mon Jan 21 2002 Michael K. Johnson <johnsonm@redhat.com>
|
||||
- Added stub library
|
||||
|
||||
* Sun Jan 20 2002 Michael K. Johnson <johnsonm@redhat.com>
|
||||
- Initial packaging
|
||||
@@ -0,0 +1,315 @@
|
||||
.TH aio 3 2002-09-12 "Linux 2.4" Linux AIO"
|
||||
.SH NAME
|
||||
aio \- Asynchronous IO
|
||||
.SH SYNOPSIS
|
||||
.nf
|
||||
.B #include <errno.h>
|
||||
.sp
|
||||
.br
|
||||
.B #include <aio.h>
|
||||
.sp
|
||||
.fi
|
||||
.SH DESCRIPTION
|
||||
The POSIX.1b standard defines a new set of I/O operations which can
|
||||
significantly reduce the time an application spends waiting at I/O. The
|
||||
new functions allow a program to initiate one or more I/O operations and
|
||||
then immediately resume normal work while the I/O operations are
|
||||
executed in parallel. This functionality is available if the
|
||||
.IR "unistd.h"
|
||||
file defines the symbol
|
||||
.B "_POSIX_ASYNCHRONOUS_IO"
|
||||
.
|
||||
|
||||
These functions are part of the library with realtime functions named
|
||||
.IR "librt"
|
||||
. They are not actually part of the
|
||||
.IR "libc"
|
||||
binary.
|
||||
The implementation of these functions can be done using support in the
|
||||
kernel (if available) or using an implementation based on threads at
|
||||
userlevel. In the latter case it might be necessary to link applications
|
||||
with the thread library
|
||||
.IR "libpthread"
|
||||
in addition to
|
||||
.IR "librt"
|
||||
and
|
||||
.IR "libaio"
|
||||
.
|
||||
|
||||
All AIO operations operate on files which were opened previously. There
|
||||
might be arbitrarily many operations running for one file. The
|
||||
asynchronous I/O operations are controlled using a data structure named
|
||||
.IR "struct aiocb"
|
||||
It is defined in
|
||||
.IR "aio.h"
|
||||
as follows.
|
||||
|
||||
.nf
|
||||
struct aiocb
|
||||
{
|
||||
int aio_fildes; /* File desriptor. */
|
||||
int aio_lio_opcode; /* Operation to be performed. */
|
||||
int aio_reqprio; /* Request priority offset. */
|
||||
volatile void *aio_buf; /* Location of buffer. */
|
||||
size_t aio_nbytes; /* Length of transfer. */
|
||||
struct sigevent aio_sigevent; /* Signal number and value. */
|
||||
|
||||
/* Internal members. */
|
||||
struct aiocb *__next_prio;
|
||||
int __abs_prio;
|
||||
int __policy;
|
||||
int __error_code;
|
||||
__ssize_t __return_value;
|
||||
|
||||
#ifndef __USE_FILE_OFFSET64
|
||||
__off_t aio_offset; /* File offset. */
|
||||
char __pad[sizeof (__off64_t) - sizeof (__off_t)];
|
||||
#else
|
||||
__off64_t aio_offset; /* File offset. */
|
||||
#endif
|
||||
char __unused[32];
|
||||
};
|
||||
|
||||
.fi
|
||||
The POSIX.1b standard mandates that the
|
||||
.IR "struct aiocb"
|
||||
structure
|
||||
contains at least the members described in the following table. There
|
||||
might be more elements which are used by the implementation, but
|
||||
depending upon these elements is not portable and is highly deprecated.
|
||||
|
||||
.TP
|
||||
.IR "int aio_fildes"
|
||||
This element specifies the file descriptor to be used for the
|
||||
operation. It must be a legal descriptor, otherwise the operation will
|
||||
fail.
|
||||
|
||||
The device on which the file is opened must allow the seek operation.
|
||||
I.e., it is not possible to use any of the AIO operations on devices
|
||||
like terminals where an
|
||||
.IR "lseek"
|
||||
call would lead to an error.
|
||||
.TP
|
||||
.IR "off_t aio_offset"
|
||||
This element specifies the offset in the file at which the operation (input
|
||||
or output) is performed. Since the operations are carried out in arbitrary
|
||||
order and more than one operation for one file descriptor can be
|
||||
started, one cannot expect a current read/write position of the file
|
||||
descriptor.
|
||||
.TP
|
||||
.IR "volatile void *aio_buf"
|
||||
This is a pointer to the buffer with the data to be written or the place
|
||||
where the read data is stored.
|
||||
.TP
|
||||
.IR "size_t aio_nbytes"
|
||||
This element specifies the length of the buffer pointed to by
|
||||
.IR "aio_buf"
|
||||
.
|
||||
.TP
|
||||
.IR "int aio_reqprio"
|
||||
If the platform has defined
|
||||
.B "_POSIX_PRIORITIZED_IO"
|
||||
and
|
||||
.B "_POSIX_PRIORITY_SCHEDULING"
|
||||
, the AIO requests are
|
||||
processed based on the current scheduling priority. The
|
||||
.IR "aio_reqprio"
|
||||
element can then be used to lower the priority of the
|
||||
AIO operation.
|
||||
.TP
|
||||
.IR "struct sigevent aio_sigevent"
|
||||
This element specifies how the calling process is notified once the
|
||||
operation terminates. If the
|
||||
.IR "sigev_notify"
|
||||
element is
|
||||
.B "SIGEV_NONE"
|
||||
, no notification is sent. If it is
|
||||
.B "SIGEV_SIGNAL"
|
||||
,
|
||||
the signal determined by
|
||||
.IR "sigev_signo"
|
||||
is sent. Otherwise,
|
||||
.IR "sigev_notify"
|
||||
must be
|
||||
.B "SIGEV_THREAD"
|
||||
. In this case, a thread
|
||||
is created which starts executing the function pointed to by
|
||||
.IR "sigev_notify_function"
|
||||
.
|
||||
.TP
|
||||
.IR "int aio_lio_opcode"
|
||||
This element is only used by the
|
||||
.IR "lio_listio"
|
||||
and
|
||||
.IR "lio_listio64"
|
||||
functions. Since these functions allow an
|
||||
arbitrary number of operations to start at once, and each operation can be
|
||||
input or output (or nothing), the information must be stored in the
|
||||
control block. The possible values are:
|
||||
.TP
|
||||
.B "LIO_READ"
|
||||
Start a read operation. Read from the file at position
|
||||
.IR "aio_offset"
|
||||
and store the next
|
||||
.IR "aio_nbytes"
|
||||
bytes in the
|
||||
buffer pointed to by
|
||||
.IR "aio_buf"
|
||||
.
|
||||
.TP
|
||||
.B "LIO_WRITE"
|
||||
Start a write operation. Write
|
||||
.IR "aio_nbytes"
|
||||
bytes starting at
|
||||
.IR "aio_buf"
|
||||
into the file starting at position
|
||||
.IR "aio_offset"
|
||||
.
|
||||
.TP
|
||||
.B "LIO_NOP"
|
||||
Do nothing for this control block. This value is useful sometimes when
|
||||
an array of
|
||||
.IR "struct aiocb"
|
||||
values contains holes, i.e., some of the
|
||||
values must not be handled although the whole array is presented to the
|
||||
.IR "lio_listio"
|
||||
function.
|
||||
|
||||
When the sources are compiled using
|
||||
.B "_FILE_OFFSET_BITS == 64"
|
||||
on a
|
||||
32 bit machine, this type is in fact
|
||||
.IR "struct aiocb64"
|
||||
, since the LFS
|
||||
interface transparently replaces the
|
||||
.IR "struct aiocb"
|
||||
definition.
|
||||
.PP
|
||||
For use with the AIO functions defined in the LFS, there is a similar type
|
||||
defined which replaces the types of the appropriate members with larger
|
||||
types but otherwise is equivalent to
|
||||
.IR "struct aiocb"
|
||||
. Particularly,
|
||||
all member names are the same.
|
||||
|
||||
.nf
|
||||
/* The same for the 64bit offsets. Please note that the members aio_fildes
|
||||
to __return_value have to be the same in aiocb and aiocb64. */
|
||||
#ifdef __USE_LARGEFILE64
|
||||
struct aiocb64
|
||||
{
|
||||
int aio_fildes; /* File desriptor. */
|
||||
int aio_lio_opcode; /* Operation to be performed. */
|
||||
int aio_reqprio; /* Request priority offset. */
|
||||
volatile void *aio_buf; /* Location of buffer. */
|
||||
size_t aio_nbytes; /* Length of transfer. */
|
||||
struct sigevent aio_sigevent; /* Signal number and value. */
|
||||
|
||||
/* Internal members. */
|
||||
struct aiocb *__next_prio;
|
||||
int __abs_prio;
|
||||
int __policy;
|
||||
int __error_code;
|
||||
__ssize_t __return_value;
|
||||
|
||||
__off64_t aio_offset; /* File offset. */
|
||||
char __unused[32];
|
||||
};
|
||||
|
||||
.fi
|
||||
.TP
|
||||
.IR "int aio_fildes"
|
||||
This element specifies the file descriptor which is used for the
|
||||
operation. It must be a legal descriptor since otherwise the operation
|
||||
fails for obvious reasons.
|
||||
The device on which the file is opened must allow the seek operation.
|
||||
I.e., it is not possible to use any of the AIO operations on devices
|
||||
like terminals where an
|
||||
.IR "lseek"
|
||||
call would lead to an error.
|
||||
.TP
|
||||
.IR "off64_t aio_offset"
|
||||
This element specifies at which offset in the file the operation (input
|
||||
or output) is performed. Since the operation are carried in arbitrary
|
||||
order and more than one operation for one file descriptor can be
|
||||
started, one cannot expect a current read/write position of the file
|
||||
descriptor.
|
||||
.TP
|
||||
.IR "volatile void *aio_buf"
|
||||
This is a pointer to the buffer with the data to be written or the place
|
||||
where the read data is stored.
|
||||
.TP
|
||||
.IR "size_t aio_nbytes"
|
||||
This element specifies the length of the buffer pointed to by
|
||||
.IR "aio_buf"
|
||||
.
|
||||
.TP
|
||||
.IR "int aio_reqprio"
|
||||
If for the platform
|
||||
.B "_POSIX_PRIORITIZED_IO"
|
||||
and
|
||||
.B "_POSIX_PRIORITY_SCHEDULING"
|
||||
are defined the AIO requests are
|
||||
processed based on the current scheduling priority. The
|
||||
.IR "aio_reqprio"
|
||||
element can then be used to lower the priority of the
|
||||
AIO operation.
|
||||
.TP
|
||||
.IR "struct sigevent aio_sigevent"
|
||||
This element specifies how the calling process is notified once the
|
||||
operation terminates. If the
|
||||
.IR "sigev_notify"
|
||||
, element is
|
||||
.B "SIGEV_NONE"
|
||||
no notification is sent. If it is
|
||||
.B "SIGEV_SIGNAL"
|
||||
,
|
||||
the signal determined by
|
||||
.IR "sigev_signo"
|
||||
is sent. Otherwise,
|
||||
.IR "sigev_notify"
|
||||
must be
|
||||
.B "SIGEV_THREAD"
|
||||
in which case a thread
|
||||
which starts executing the function pointed to by
|
||||
.IR "sigev_notify_function"
|
||||
.
|
||||
.TP
|
||||
.IR "int aio_lio_opcode"
|
||||
This element is only used by the
|
||||
.IR "lio_listio"
|
||||
and
|
||||
.IR "lio_listio64"
|
||||
functions. Since these functions allow an
|
||||
arbitrary number of operations to start at once, and since each operation can be
|
||||
input or output (or nothing), the information must be stored in the
|
||||
control block. See the description of
|
||||
.IR "struct aiocb"
|
||||
for a description
|
||||
of the possible values.
|
||||
.PP
|
||||
When the sources are compiled using
|
||||
.B "_FILE_OFFSET_BITS == 64"
|
||||
on a
|
||||
32 bit machine, this type is available under the name
|
||||
.IR "struct aiocb64"
|
||||
, since the LFS transparently replaces the old interface.
|
||||
.SH "RETURN VALUES"
|
||||
.SH ERRORS
|
||||
.SH "SEE ALSO"
|
||||
.BR aio_cancel(3),
|
||||
.BR aio_cancel64(3),
|
||||
.BR aio_error(3),
|
||||
.BR aio_error64(3),
|
||||
.BR aio_fsync(3),
|
||||
.BR aio_fsync64(3),
|
||||
.BR aio_init(3),
|
||||
.BR aio_read(3),
|
||||
.BR aio_read64(3),
|
||||
.BR aio_return(3),
|
||||
.BR aio_return64(3),
|
||||
.BR aio_suspend(3),
|
||||
.BR aio_suspend64(3),
|
||||
.BR aio_write(3),
|
||||
.BR aio_write64(3),
|
||||
.BR errno(3),
|
||||
@@ -0,0 +1,137 @@
|
||||
.TH aio_cancel 3 2002-09-12 "Linux 2.4" Linux AIO"
|
||||
.SH NAME
|
||||
aio_cancel - Cancel asynchronous I/O requests
|
||||
.SH SYNOPSYS
|
||||
.nf
|
||||
.B #include <errno.h>
|
||||
.sp
|
||||
.br
|
||||
.B #include <aio.h>
|
||||
.sp
|
||||
.br
|
||||
.BI "int aio_cancel (int fildes " , struct aiocb *aiocbp " )"
|
||||
.fi
|
||||
.SH DESCRIPTION
|
||||
When one or more requests are asynchronously processed, it might be
|
||||
useful in some situations to cancel a selected operation, e.g., if it
|
||||
becomes obvious that the written data is no longer accurate and would
|
||||
have to be overwritten soon. As an example, assume an application, which
|
||||
writes data in files in a situation where new incoming data would have
|
||||
to be written in a file which will be updated by an enqueued request.
|
||||
The POSIX AIO implementation provides such a function, but this function
|
||||
is not capable of forcing the cancellation of the request. It is up to the
|
||||
implementation to decide whether it is possible to cancel the operation
|
||||
or not. Therefore using this function is merely a hint.
|
||||
.B "The libaio implementation does not implement the cancel operation in the"
|
||||
.B "POSIX libraries".
|
||||
.PP
|
||||
The
|
||||
.IR aio_cancel
|
||||
function can be used to cancel one or more
|
||||
outstanding requests. If the
|
||||
.IR aiocbp
|
||||
parameter is
|
||||
.IR NULL
|
||||
, the
|
||||
function tries to cancel all of the outstanding requests which would process
|
||||
the file descriptor
|
||||
.IR fildes
|
||||
(i.e., whose
|
||||
.IR aio_fildes
|
||||
member
|
||||
is
|
||||
.IR fildes
|
||||
). If
|
||||
.IR aiocbp is not
|
||||
.IR NULL
|
||||
,
|
||||
.IR aio_cancel
|
||||
attempts to cancel the specific request pointed to by
|
||||
.IR aiocbp.
|
||||
|
||||
For requests which were successfully canceled, the normal notification
|
||||
about the termination of the request should take place. I.e., depending
|
||||
on the
|
||||
.IR "struct sigevent"
|
||||
object which controls this, nothing
|
||||
happens, a signal is sent or a thread is started. If the request cannot
|
||||
be canceled, it terminates the usual way after performing the operation.
|
||||
After a request is successfully canceled, a call to
|
||||
.IR aio_error
|
||||
with
|
||||
a reference to this request as the parameter will return
|
||||
.B ECANCELED
|
||||
and a call to
|
||||
.IR aio_return
|
||||
will return
|
||||
.IR -1.
|
||||
If the request wasn't canceled and is still running the error status is
|
||||
still
|
||||
.B EINPROGRESS.
|
||||
When the sources are compiled with
|
||||
.IR "_FILE_OFFSET_BITS == 64"
|
||||
, this
|
||||
function is in fact
|
||||
.IR aio_cancel64
|
||||
since the LFS interface
|
||||
transparently replaces the normal implementation.
|
||||
|
||||
.SH "RETURN VALUES"
|
||||
.TP
|
||||
.B AIO_CANCELED
|
||||
If there were
|
||||
requests which haven't terminated and which were successfully canceled.
|
||||
.TP
|
||||
.B AIO_NOTCANCELED
|
||||
If there is one or more requests left which couldn't be canceled,
|
||||
. In this case
|
||||
.IR aio_error
|
||||
must be used to find out which of the, perhaps multiple, requests (in
|
||||
.IR aiocbp
|
||||
is
|
||||
.IR NULL
|
||||
) weren't successfully canceled.
|
||||
.TP
|
||||
.B AIO_ALLDONE
|
||||
If all
|
||||
requests already terminated at the time
|
||||
.IR aio_cancel
|
||||
is called the
|
||||
return value is
|
||||
.
|
||||
.SH ERRORS
|
||||
If an error occurred during the execution of
|
||||
.IR aio_cancel
|
||||
the
|
||||
function returns
|
||||
.IR -1
|
||||
and sets
|
||||
.IR errno
|
||||
to one of the following
|
||||
values.
|
||||
.TP
|
||||
.B EBADF
|
||||
The file descriptor
|
||||
.IR fildes
|
||||
is not valid.
|
||||
.TP
|
||||
.B ENOSYS
|
||||
.IR aio_cancel
|
||||
is not implemented.
|
||||
.SH "SEE ALSO"
|
||||
.BR aio(3),
|
||||
.BR aio_cancel64(3),
|
||||
.BR aio_error(3),
|
||||
.BR aio_error64(3),
|
||||
.BR aio_fsync(3),
|
||||
.BR aio_fsync64(3),
|
||||
.BR aio_init(3),
|
||||
.BR aio_read(3),
|
||||
.BR aio_read64(3),
|
||||
.BR aio_return(3),
|
||||
.BR aio_return64(3),
|
||||
.BR aio_suspend(3),
|
||||
.BR aio_suspend64(3),
|
||||
.BR aio_write(3),
|
||||
.BR aio_write64(3),
|
||||
.BR errno(3),
|
||||
@@ -0,0 +1,50 @@
|
||||
.TH aio_cancel64 3 2002-09-12 "Linux 2.4" Linux AIO"
|
||||
.SH NAME
|
||||
aio_cancel64 \- Cancel asynchronous I/O requests
|
||||
.SH SYNOPSYS
|
||||
.nf
|
||||
.B #include <errno.h>
|
||||
.sp
|
||||
.br
|
||||
.B #include <aio.h>
|
||||
.sp
|
||||
.br
|
||||
.BI "int aio_cancel64 (int fildes, struct aiocb64 *aiocbp)"
|
||||
.fi
|
||||
.SH DESCRIPTION
|
||||
This function is similar to
|
||||
.IR aio_cancel
|
||||
with the only difference
|
||||
that the argument is a reference to a variable of type
|
||||
.IR struct aiocb64
|
||||
.
|
||||
|
||||
When the sources are compiled with
|
||||
.IR _FILE_OFFSET_BITS == 64
|
||||
, this
|
||||
function is available under the name
|
||||
.IR aio_cancel
|
||||
and so
|
||||
transparently replaces the interface for small files on 32 bit
|
||||
machines.
|
||||
.SH "RETURN VALUES"
|
||||
See aio_cancel(3).
|
||||
.SH ERRORS
|
||||
See aio_cancel(3).
|
||||
.SH "SEE ALSO"
|
||||
.BR aio(3),
|
||||
.BR aio_cancel(3),
|
||||
.BR aio_error(3),
|
||||
.BR aio_error64(3),
|
||||
.BR aio_fsync(3),
|
||||
.BR aio_fsync64(3),
|
||||
.BR aio_init(3),
|
||||
.BR aio_read(3),
|
||||
.BR aio_read64(3),
|
||||
.BR aio_return(3),
|
||||
.BR aio_return64(3),
|
||||
.BR aio_suspend(3),
|
||||
.BR aio_suspend64(3),
|
||||
.BR aio_write(3),
|
||||
.BR aio_write64(3),
|
||||
.BR errno(3),
|
||||
@@ -0,0 +1,81 @@
|
||||
.TH aio_error 3 2002-09-12 "Linux 2.4" Linux AIO"
|
||||
.SH NAME
|
||||
aio_error \- Getting the Status of AIO Operations
|
||||
.SH SYNOPSYS
|
||||
.nf
|
||||
.B #include <errno.h>
|
||||
.sp
|
||||
.br
|
||||
.B #include <aio.h>
|
||||
.sp
|
||||
.br
|
||||
.BI "int aio_error (const struct aiocb *aiocbp)"
|
||||
.fi
|
||||
.SH DESCRIPTION
|
||||
The function
|
||||
.IR aio_error
|
||||
determines the error state of the request described by the
|
||||
.IR "struct aiocb"
|
||||
variable pointed to by
|
||||
.I aiocbp
|
||||
.
|
||||
|
||||
When the operation is performed truly asynchronously (as with
|
||||
.IR "aio_read"
|
||||
and
|
||||
.IR "aio_write"
|
||||
and with
|
||||
.IR "lio_listio"
|
||||
when the mode is
|
||||
.IR "LIO_NOWAIT"
|
||||
), one sometimes needs to know whether a
|
||||
specific request already terminated and if so, what the result was.
|
||||
When the sources are compiled with
|
||||
.IR "_FILE_OFFSET_BITS == 64"
|
||||
this function is in fact
|
||||
.IR "aio_error64"
|
||||
since the LFS interface transparently replaces the normal implementation.
|
||||
.SH "RETURN VALUES"
|
||||
If the request has not yet terminated the value returned is always
|
||||
.IR "EINPROGRESS"
|
||||
. Once the request has terminated the value
|
||||
.IR "aio_error"
|
||||
returns is either
|
||||
.I 0
|
||||
if the request completed successfully or it returns the value which would be stored in the
|
||||
.IR "errno"
|
||||
variable if the request would have been done using
|
||||
.IR "read"
|
||||
,
|
||||
.IR "write"
|
||||
, or
|
||||
.IR "fsync"
|
||||
.
|
||||
.SH ERRORS
|
||||
.TP
|
||||
.IR "ENOSYS"
|
||||
if it is not implemented. It
|
||||
could also return
|
||||
.TP
|
||||
.IR "EINVAL"
|
||||
if the
|
||||
.I aiocbp
|
||||
parameter does not
|
||||
refer to an asynchronous operation whose return status is not yet known.
|
||||
.SH "SEE ALSO"
|
||||
.BR aio(3),
|
||||
.BR aio_cancel(3),
|
||||
.BR aio_cancel64(3),
|
||||
.BR aio_error64(3),
|
||||
.BR aio_fsync(3),
|
||||
.BR aio_fsync64(3),
|
||||
.BR aio_init(3),
|
||||
.BR aio_read(3),
|
||||
.BR aio_read64(3),
|
||||
.BR aio_return(3),
|
||||
.BR aio_return64(3),
|
||||
.BR aio_suspend(3),
|
||||
.BR aio_suspend64(3),
|
||||
.BR aio_write(3),
|
||||
.BR aio_write64(3),
|
||||
.BR errno(3),
|
||||
@@ -0,0 +1,64 @@
|
||||
.TH aio_error64 3 2002-09-12 "Linux 2.4" Linux AIO"
|
||||
.SH NAME
|
||||
aio_error64 \- Return errors
|
||||
.SH SYNOPSYS
|
||||
.nf
|
||||
.B #include <errno.h>
|
||||
.sp
|
||||
.br
|
||||
.B #include <aio.h>
|
||||
.sp
|
||||
.br
|
||||
.BI "int aio_error64 (const struct aiocb64 *aiocbp)"
|
||||
.fi
|
||||
.SH DESCRIPTION
|
||||
This function is similar to
|
||||
.IR aio_error
|
||||
with the only difference
|
||||
that the argument is a reference to a variable of type
|
||||
.IR "struct aiocb64".
|
||||
.PP
|
||||
When the sources are compiled with
|
||||
.IR "_FILE_OFFSET_BITS == 64"
|
||||
this
|
||||
function is available under the name
|
||||
.IR aio_error
|
||||
and so
|
||||
transparently replaces the interface for small files on 32 bit
|
||||
machines.
|
||||
.SH "RETURN VALUES"
|
||||
If the request has not yet terminated the value returned is always
|
||||
.IR "EINPROGRESS"
|
||||
. Once the request has terminated the value
|
||||
.IR "aio_error"
|
||||
returns is either
|
||||
.I 0
|
||||
if the request completed successfully or it returns the value which would be stored in the
|
||||
.IR "errno"
|
||||
variable if the request would have been done using
|
||||
.IR "read"
|
||||
,
|
||||
.IR "write"
|
||||
, or
|
||||
.IR "fsync"
|
||||
.
|
||||
.SH ERRORS
|
||||
See
|
||||
.IR aio_error(3).
|
||||
.SH "SEE ALSO"
|
||||
.BR aio(3),
|
||||
.BR aio_cancel(3),
|
||||
.BR aio_cancel64(3),
|
||||
.BR aio_error(3),
|
||||
.BR aio_fsync(3),
|
||||
.BR aio_fsync64(3),
|
||||
.BR aio_init(3),
|
||||
.BR aio_read(3),
|
||||
.BR aio_read64(3),
|
||||
.BR aio_return(3),
|
||||
.BR aio_return64(3),
|
||||
.BR aio_suspend(3),
|
||||
.BR aio_suspend64(3),
|
||||
.BR aio_write(3),
|
||||
.BR aio_write64(3),
|
||||
.BR errno(3),
|
||||
@@ -0,0 +1,139 @@
|
||||
.TH aio_fsync 3 2002-09-12 "Linux 2.4" Linux AIO"
|
||||
.SH NAME
|
||||
aio_fsync \- Synchronize a file's complete in-core state with that on disk
|
||||
.SH SYNOPSYS
|
||||
.nf
|
||||
.B #include <errno.h>
|
||||
.sp
|
||||
.br
|
||||
.B #include <aio.h>
|
||||
.sp
|
||||
.br
|
||||
.BI "int aio_fsync (int op, struct aiocb aiocbp)"
|
||||
.fi
|
||||
.SH DESCRIPTION
|
||||
.PP
|
||||
When dealing with asynchronous operations it is sometimes necessary to
|
||||
get into a consistent state. This would mean for AIO that one wants to
|
||||
know whether a certain request or a group of request were processed.
|
||||
This could be done by waiting for the notification sent by the system
|
||||
after the operation terminated, but this sometimes would mean wasting
|
||||
resources (mainly computation time). Instead POSIX.1b defines two
|
||||
functions which will help with most kinds of consistency.
|
||||
.PP
|
||||
The
|
||||
.IR aio_fsync
|
||||
and
|
||||
.IR "aio_fsync64"
|
||||
functions are only available
|
||||
if the symbol
|
||||
.IR "_POSIX_SYNCHRONIZED_IO"
|
||||
is defined in
|
||||
.I unistd.h
|
||||
.
|
||||
|
||||
Calling this function forces all I/O operations operating queued at the
|
||||
time of the function call operating on the file descriptor
|
||||
.IR "aiocbp->aio_fildes"
|
||||
into the synchronized I/O completion state . The
|
||||
.IR "aio_fsync"
|
||||
function returns
|
||||
immediately but the notification through the method described in
|
||||
.IR "aiocbp->aio_sigevent"
|
||||
will happen only after all requests for this
|
||||
file descriptor have terminated and the file is synchronized. This also
|
||||
means that requests for this very same file descriptor which are queued
|
||||
after the synchronization request are not affected.
|
||||
|
||||
If
|
||||
.IR "op"
|
||||
is
|
||||
.IR "O_DSYNC"
|
||||
the synchronization happens as with a call
|
||||
to
|
||||
.IR "fdatasync"
|
||||
. Otherwise
|
||||
.IR "op"
|
||||
should be
|
||||
.IR "O_SYNC"
|
||||
and
|
||||
the synchronization happens as with
|
||||
.IR "fsync"
|
||||
.
|
||||
|
||||
As long as the synchronization has not happened, a call to
|
||||
.IR "aio_error"
|
||||
with the reference to the object pointed to by
|
||||
.IR "aiocbp"
|
||||
returns
|
||||
.IR "EINPROGRESS"
|
||||
. Once the synchronization is
|
||||
done
|
||||
.IR "aio_error"
|
||||
return
|
||||
.IR 0
|
||||
if the synchronization was not
|
||||
successful. Otherwise the value returned is the value to which the
|
||||
.IR "fsync"
|
||||
or
|
||||
.IR "fdatasync"
|
||||
function would have set the
|
||||
.IR "errno"
|
||||
variable. In this case nothing can be assumed about the
|
||||
consistency for the data written to this file descriptor.
|
||||
|
||||
.SH "RETURN VALUES"
|
||||
The return value of this function is
|
||||
.IR 0
|
||||
if the request was
|
||||
successfully enqueued. Otherwise the return value is
|
||||
.IR -1
|
||||
and
|
||||
.IR "errno".
|
||||
.SH ERRORS
|
||||
.TP
|
||||
.B EAGAIN
|
||||
The request could not be enqueued due to temporary lack of resources.
|
||||
.TP
|
||||
.B EBADF
|
||||
The file descriptor
|
||||
.IR "aiocbp->aio_fildes"
|
||||
is not valid or not open
|
||||
for writing.
|
||||
.TP
|
||||
.B EINVAL
|
||||
The implementation does not support I/O synchronization or the
|
||||
.IR "op"
|
||||
parameter is other than
|
||||
.IR "O_DSYNC"
|
||||
and
|
||||
.IR "O_SYNC"
|
||||
.
|
||||
.TP
|
||||
.B ENOSYS
|
||||
This function is not implemented.
|
||||
.PP
|
||||
When the sources are compiled with
|
||||
.IR "_FILE_OFFSET_BITS == 64"
|
||||
this
|
||||
function is in fact
|
||||
.IR "aio_return64"
|
||||
since the LFS interface
|
||||
transparently replaces the normal implementation.
|
||||
.SH "SEE ALSO"
|
||||
.BR aio(3),
|
||||
.BR aio_cancel(3),
|
||||
.BR aio_cancel64(3),
|
||||
.BR aio_error(3),
|
||||
.BR aio_error64(3),
|
||||
.BR aio_fsync64(3),
|
||||
.BR aio_init(3),
|
||||
.BR aio_read(3),
|
||||
.BR aio_read64(3),
|
||||
.BR aio_return(3),
|
||||
.BR aio_return64(3),
|
||||
.BR aio_suspend(3),
|
||||
.BR aio_suspend64(3),
|
||||
.BR aio_write(3),
|
||||
.BR aio_write64(3),
|
||||
.BR errno(3),
|
||||
@@ -0,0 +1,51 @@
|
||||
.TH aio_fsync64 3 2002-09-12 "Linux 2.4" Linux AIO"
|
||||
.SH NAME
|
||||
aio_fsync64 \- Synchronize a file's complete in-core state with that on disk
|
||||
.SH SYNOPSYS
|
||||
.nf
|
||||
.B #include <errno.h>
|
||||
.sp
|
||||
.br
|
||||
.B #include <aio.h>
|
||||
.sp
|
||||
.br
|
||||
.BI "int aio_fsync64 (int op, struct aiocb64 *aiocbp)"
|
||||
.fi
|
||||
.SH DESCRIPTION
|
||||
This function is similar to
|
||||
.IR aio_fsync
|
||||
with the only difference
|
||||
that the argument is a reference to a variable of type
|
||||
.IR "struct aiocb64".
|
||||
|
||||
When the sources are compiled with
|
||||
.IR "_FILE_OFFSET_BITS == 64"
|
||||
this
|
||||
function is available under the name
|
||||
.IR aio_fsync
|
||||
and so
|
||||
transparently replaces the interface for small files on 32 bit
|
||||
machines.
|
||||
.SH "RETURN VALUES"
|
||||
See
|
||||
.IR aio_fsync.
|
||||
.SH ERRORS
|
||||
See
|
||||
.IR aio_fsync.
|
||||
.SH "SEE ALSO"
|
||||
.BR aio(3),
|
||||
.BR aio_cancel(3),
|
||||
.BR aio_cancel64(3),
|
||||
.BR aio_error(3),
|
||||
.BR aio_error64(3),
|
||||
.BR aio_fsync(3),
|
||||
.BR aio_init(3),
|
||||
.BR aio_read(3),
|
||||
.BR aio_read64(3),
|
||||
.BR aio_return(3),
|
||||
.BR aio_return64(3),
|
||||
.BR aio_suspend(3),
|
||||
.BR aio_suspend64(3),
|
||||
.BR aio_write(3),
|
||||
.BR aio_write64(3),
|
||||
.BR errno(3),
|
||||
@@ -0,0 +1,96 @@
|
||||
.TH aio_init 3 2002-09-12 "Linux 2.4" Linux AIO"
|
||||
.SH NAME
|
||||
aio_init \- How to optimize the AIO implementation
|
||||
.SH SYNOPSYS
|
||||
.nf
|
||||
.B #include <errno.h>
|
||||
.sp
|
||||
.br
|
||||
.B #include <aio.h>
|
||||
.sp
|
||||
.br
|
||||
.BI "void aio_init (const struct aioinit *init)"
|
||||
.fi
|
||||
.SH DESCRIPTION
|
||||
|
||||
The POSIX standard does not specify how the AIO functions are
|
||||
implemented. They could be system calls, but it is also possible to
|
||||
emulate them at userlevel.
|
||||
|
||||
At the point of this writing, the available implementation is a userlevel
|
||||
implementation which uses threads for handling the enqueued requests.
|
||||
While this implementation requires making some decisions about
|
||||
limitations, hard limitations are something which is best avoided
|
||||
in the GNU C library. Therefore, the GNU C library provides a means
|
||||
for tuning the AIO implementation according to the individual use.
|
||||
|
||||
.BI "struct aioinit"
|
||||
.PP
|
||||
This data type is used to pass the configuration or tunable parameters
|
||||
to the implementation. The program has to initialize the members of
|
||||
this struct and pass it to the implementation using the
|
||||
.IR aio_init
|
||||
function.
|
||||
.TP
|
||||
.B "int aio_threads"
|
||||
This member specifies the maximal number of threads which may be used
|
||||
at any one time.
|
||||
.TP
|
||||
.B "int aio_num"
|
||||
This number provides an estimate on the maximal number of simultaneously
|
||||
enqueued requests.
|
||||
.TP
|
||||
.B "int aio_locks"
|
||||
Unused.
|
||||
.TP
|
||||
.B "int aio_usedba"
|
||||
Unused.
|
||||
.TP
|
||||
.B "int aio_debug"
|
||||
Unused.
|
||||
.TP
|
||||
.B "int aio_numusers"
|
||||
Unused.
|
||||
.TP
|
||||
.B "int aio_reserved[2]"
|
||||
Unused.
|
||||
.PP
|
||||
This function must be called before any other AIO function. Calling it
|
||||
is completely voluntary, as it is only meant to help the AIO
|
||||
implementation perform better.
|
||||
|
||||
Before calling the
|
||||
.IR aio_init
|
||||
, function the members of a variable of
|
||||
type
|
||||
.IR "struct aioinit"
|
||||
must be initialized. Then a reference to
|
||||
this variable is passed as the parameter to
|
||||
.IR aio_init
|
||||
which itself
|
||||
may or may not pay attention to the hints.
|
||||
|
||||
It is a extension which follows a proposal from the SGI implementation in
|
||||
.IR Irix 6
|
||||
. It is not covered by POSIX.1b or Unix98.
|
||||
.SH "RETURN VALUES"
|
||||
The function has no return value.
|
||||
.SH ERRORS
|
||||
The function has no error cases defined.
|
||||
.SH "SEE ALSO"
|
||||
.BR aio(3),
|
||||
.BR aio_cancel(3),
|
||||
.BR aio_cancel64(3),
|
||||
.BR aio_error(3),
|
||||
.BR aio_error64(3),
|
||||
.BR aio_fsync(3),
|
||||
.BR aio_fsync64(3),
|
||||
.BR aio_read(3),
|
||||
.BR aio_read64(3),
|
||||
.BR aio_return(3),
|
||||
.BR aio_return64(3),
|
||||
.BR aio_suspend(3),
|
||||
.BR aio_suspend64(3),
|
||||
.BR aio_write(3),
|
||||
.BR aio_write64(3),
|
||||
.BR errno(3),
|
||||
@@ -0,0 +1,146 @@
|
||||
.TH aio_read 3 2002-09-12 "Linux 2.4" Linux AIO"
|
||||
.SH NAME
|
||||
aio_read \- Initiate an asynchronous read operation
|
||||
.SH SYNOPSYS
|
||||
.nf
|
||||
.B #include <errno.h>
|
||||
.sp
|
||||
.br
|
||||
.B #include <aio.h>
|
||||
.sp
|
||||
.br
|
||||
.BI "int aio_read (struct aiocb *aiocbp)"
|
||||
.fi
|
||||
.SH DESCRIPTION
|
||||
This function initiates an asynchronous read operation. It
|
||||
immediately returns after the operation was enqueued or when an
|
||||
error was encountered.
|
||||
|
||||
The first
|
||||
.IR "aiocbp->aio_nbytes"
|
||||
bytes of the file for which
|
||||
.IR "aiocbp->aio_fildes"
|
||||
is a descriptor are written to the buffer
|
||||
starting at
|
||||
.IR "aiocbp->aio_buf"
|
||||
. Reading starts at the absolute
|
||||
position
|
||||
.IR "aiocbp->aio_offset"
|
||||
in the file.
|
||||
|
||||
If prioritized I/O is supported by the platform the
|
||||
.IR "aiocbp->aio_reqprio"
|
||||
value is used to adjust the priority before
|
||||
the request is actually enqueued.
|
||||
|
||||
The calling process is notified about the termination of the read
|
||||
request according to the
|
||||
.IR "aiocbp->aio_sigevent"
|
||||
value.
|
||||
|
||||
.SH "RETURN VALUES"
|
||||
When
|
||||
.IR "aio_read"
|
||||
returns, the return value is zero if no error
|
||||
occurred that can be found before the process is enqueued. If such an
|
||||
early error is found, the function returns
|
||||
.IR -1
|
||||
and sets
|
||||
.IR "errno".
|
||||
|
||||
.PP
|
||||
If
|
||||
.IR "aio_read"
|
||||
returns zero, the current status of the request
|
||||
can be queried using
|
||||
.IR "aio_error"
|
||||
and
|
||||
.IR "aio_return"
|
||||
functions.
|
||||
As long as the value returned by
|
||||
.IR "aio_error"
|
||||
is
|
||||
.IR "EINPROGRESS"
|
||||
the operation has not yet completed. If
|
||||
.IR "aio_error"
|
||||
returns zero,
|
||||
the operation successfully terminated, otherwise the value is to be
|
||||
interpreted as an error code. If the function terminated, the result of
|
||||
the operation can be obtained using a call to
|
||||
.IR "aio_return"
|
||||
. The
|
||||
returned value is the same as an equivalent call to
|
||||
.IR "read"
|
||||
would
|
||||
have returned.
|
||||
When the sources are compiled with
|
||||
.IR "_FILE_OFFSET_BITS == 64"
|
||||
this
|
||||
function is in fact
|
||||
.IR "aio_read64"
|
||||
since the LFS interface transparently
|
||||
replaces the normal implementation.
|
||||
|
||||
.SH ERRORS
|
||||
In the case of an early error:
|
||||
.TP
|
||||
.B EAGAIN
|
||||
The request was not enqueued due to (temporarily) exceeded resource
|
||||
limitations.
|
||||
.TP
|
||||
.B ENOSYS
|
||||
The
|
||||
.IR "aio_read"
|
||||
function is not implemented.
|
||||
.TP
|
||||
.B EBADF
|
||||
The
|
||||
.IR "aiocbp->aio_fildes"
|
||||
descriptor is not valid. This condition
|
||||
need not be recognized before enqueueing the request and so this error
|
||||
might also be signaled asynchronously.
|
||||
.TP
|
||||
.B EINVAL
|
||||
The
|
||||
.IR "aiocbp->aio_offset"
|
||||
or
|
||||
.IR "aiocbp->aio_reqpiro"
|
||||
value is
|
||||
invalid. This condition need not be recognized before enqueueing the
|
||||
request and so this error might also be signaled asynchronously.
|
||||
|
||||
.PP
|
||||
In the case of a normal return, possible error codes returned by
|
||||
.IR "aio_error"
|
||||
are:
|
||||
.TP
|
||||
.B EBADF
|
||||
The
|
||||
.IR "aiocbp->aio_fildes"
|
||||
descriptor is not valid.
|
||||
.TP
|
||||
.B ECANCELED
|
||||
The operation was canceled before the operation was finished
|
||||
.TP
|
||||
.B EINVAL
|
||||
The
|
||||
.IR "aiocbp->aio_offset"
|
||||
value is invalid.
|
||||
.PP
|
||||
.SH "SEE ALSO"
|
||||
.BR aio(3),
|
||||
.BR aio_cancel(3),
|
||||
.BR aio_cancel64(3),
|
||||
.BR aio_error(3),
|
||||
.BR aio_error64(3),
|
||||
.BR aio_fsync(3),
|
||||
.BR aio_fsync64(3),
|
||||
.BR aio_init(3),
|
||||
.BR aio_read64(3),
|
||||
.BR aio_return(3),
|
||||
.BR aio_return64(3),
|
||||
.BR aio_suspend(3),
|
||||
.BR aio_suspend64(3),
|
||||
.BR aio_write(3),
|
||||
.BR aio_write64(3),
|
||||
.BR errno(3),
|
||||
@@ -0,0 +1,60 @@
|
||||
.TH aio_read64 3 2002-09-12 "Linux 2.4" Linux AIO"
|
||||
.SH NAME
|
||||
aio_read64 \- Initiate an asynchronous read operation
|
||||
.SH SYNOPSYS
|
||||
.nf
|
||||
.B #include <errno.h>
|
||||
.br
|
||||
.B #include <aio.h>
|
||||
.sp
|
||||
.br
|
||||
.BI "int aio_read64 (struct aiocb *aiocbp)"
|
||||
.fi
|
||||
.SH DESCRIPTION
|
||||
This function is similar to the
|
||||
.IR "aio_read"
|
||||
function. The only
|
||||
difference is that on
|
||||
.IR "32 bit"
|
||||
machines, the file descriptor should
|
||||
be opened in the large file mode. Internally,
|
||||
.IR "aio_read64"
|
||||
uses
|
||||
functionality equivalent to
|
||||
.IR "lseek64"
|
||||
to position the file descriptor correctly for the reading,
|
||||
as opposed to
|
||||
.IR "lseek"
|
||||
functionality used in
|
||||
.IR "aio_read".
|
||||
|
||||
When the sources are compiled with
|
||||
.IR "_FILE_OFFSET_BITS == 64"
|
||||
, this
|
||||
function is available under the name
|
||||
.IR "aio_read"
|
||||
and so transparently
|
||||
replaces the interface for small files on 32 bit machines.
|
||||
.SH "RETURN VALUES"
|
||||
See
|
||||
.IR aio_read.
|
||||
.SH ERRORS
|
||||
See
|
||||
.IR aio_read.
|
||||
.SH "SEE ALSO"
|
||||
.BR aio(3),
|
||||
.BR aio_cancel(3),
|
||||
.BR aio_cancel64(3),
|
||||
.BR aio_error(3),
|
||||
.BR aio_error64(3),
|
||||
.BR aio_fsync(3),
|
||||
.BR aio_fsync64(3),
|
||||
.BR aio_init(3),
|
||||
.BR aio_read(3),
|
||||
.BR aio_return(3),
|
||||
.BR aio_return64(3),
|
||||
.BR aio_suspend(3),
|
||||
.BR aio_suspend64(3),
|
||||
.BR aio_write(3),
|
||||
.BR aio_write64(3),
|
||||
.BR errno(3),
|
||||
@@ -0,0 +1,71 @@
|
||||
.TH aio_return 3 2002-09-12 "Linux 2.4" Linux AIO"
|
||||
.SH NAME
|
||||
aio_return \- Retrieve status of asynchronous I/O operation
|
||||
.SH SYNOPSYS
|
||||
.nf
|
||||
.B #include <errno.h>
|
||||
.sp
|
||||
.br
|
||||
.B #include <aio.h>
|
||||
.sp
|
||||
.br
|
||||
.BI "ssize_t aio_return (const struct aiocb *aiocbp)"
|
||||
.fi
|
||||
.SH DESCRIPTION
|
||||
This function can be used to retrieve the return status of the operation
|
||||
carried out by the request described in the variable pointed to by
|
||||
.IR aiocbp
|
||||
. As long as the error status of this request as returned
|
||||
by
|
||||
.IR aio_error
|
||||
is
|
||||
.IR EINPROGRESS
|
||||
the return of this function is
|
||||
undefined.
|
||||
|
||||
Once the request is finished this function can be used exactly once to
|
||||
retrieve the return value. Following calls might lead to undefined
|
||||
behavior.
|
||||
When the sources are compiled with
|
||||
.B "_FILE_OFFSET_BITS == 64"
|
||||
this function is in fact
|
||||
.IR aio_return64
|
||||
since the LFS interface
|
||||
transparently replaces the normal implementation.
|
||||
.SH "RETURN VALUES"
|
||||
The return value itself is the value which would have been
|
||||
returned by the
|
||||
.IR read
|
||||
,
|
||||
.IR write
|
||||
, or
|
||||
.IR fsync
|
||||
call.
|
||||
.SH ERRORS
|
||||
The function can return
|
||||
.TP
|
||||
.B ENOSYS
|
||||
if it is not implemented.
|
||||
.TP
|
||||
.B EINVAL
|
||||
if the
|
||||
.IR aiocbp
|
||||
parameter does not
|
||||
refer to an asynchronous operation whose return status is not yet known.
|
||||
.SH "SEE ALSO"
|
||||
.BR aio(3),
|
||||
.BR aio_cancel(3),
|
||||
.BR aio_cancel64(3),
|
||||
.BR aio_error(3),
|
||||
.BR aio_error64(3),
|
||||
.BR aio_fsync(3),
|
||||
.BR aio_fsync64(3),
|
||||
.BR aio_init(3),
|
||||
.BR aio_read(3),
|
||||
.BR aio_read64(3),
|
||||
.BR aio_return64(3),
|
||||
.BR aio_suspend(3),
|
||||
.BR aio_suspend64(3),
|
||||
.BR aio_write(3),
|
||||
.BR aio_write64(3),
|
||||
.BR errno(3),
|
||||
@@ -0,0 +1,51 @@
|
||||
.TH aio_read64 3 2002-09-12 "Linux 2.4" Linux AIO"
|
||||
.SH NAME
|
||||
aio_read64 \- Retrieve status of asynchronous I/O operation
|
||||
.SH SYNOPSYS
|
||||
.nf
|
||||
.B #include <errno.h>
|
||||
.sp
|
||||
.br
|
||||
.B #include <aio.h>
|
||||
.sp
|
||||
.br
|
||||
.BI "int aio_return64 (const struct aiocb64 *aiocbp)"
|
||||
.fi
|
||||
.SH DESCRIPTION
|
||||
This function is similar to
|
||||
.IR "aio_return"
|
||||
with the only difference
|
||||
that the argument is a reference to a variable of type
|
||||
.IR "struct aiocb64".
|
||||
|
||||
When the sources are compiled with
|
||||
.IR "_FILE_OFFSET_BITS == 64"
|
||||
this
|
||||
function is available under the name
|
||||
.IR "aio_return"
|
||||
and so
|
||||
transparently replaces the interface for small files on 32 bit
|
||||
machines.
|
||||
.SH "RETURN VALUES"
|
||||
See
|
||||
.IR aio_return.
|
||||
.SH ERRORS
|
||||
See
|
||||
.IR aio_return.
|
||||
.SH "SEE ALSO"
|
||||
.BR aio(3),
|
||||
.BR aio_cancel(3),
|
||||
.BR aio_cancel64(3),
|
||||
.BR aio_error(3),
|
||||
.BR aio_error64(3),
|
||||
.BR aio_fsync(3),
|
||||
.BR aio_fsync64(3),
|
||||
.BR aio_init(3),
|
||||
.BR aio_read(3),
|
||||
.BR aio_read64(3),
|
||||
.BR aio_return(3),
|
||||
.BR aio_suspend(3),
|
||||
.BR aio_suspend64(3),
|
||||
.BR aio_write(3),
|
||||
.BR aio_write64(3),
|
||||
.BR errno(3),
|
||||
@@ -0,0 +1,123 @@
|
||||
.TH aio_suspend 3 2002-09-12 "Linux 2.4" Linux AIO"
|
||||
.SH NAME
|
||||
aio_suspend \- Wait until one or more requests of a specific set terminates.
|
||||
.SH SYNOPSYS
|
||||
.nf
|
||||
.B "#include <errno.h>"
|
||||
.sp
|
||||
.br
|
||||
.B "#include <aio.h>"
|
||||
.sp
|
||||
.br
|
||||
.BI "int aio_suspend (const struct aiocb *const list[], int nent, const struct timespec *timeout)"
|
||||
.fi
|
||||
.SH DESCRIPTION
|
||||
Another method of synchronization is to wait until one or more requests of a
|
||||
specific set terminated. This could be achieved by the
|
||||
.IR "aio_*"
|
||||
functions to notify the initiating process about the termination but in
|
||||
some situations this is not the ideal solution. In a program which
|
||||
constantly updates clients somehow connected to the server it is not
|
||||
always the best solution to go round robin since some connections might
|
||||
be slow. On the other hand letting the
|
||||
.IR "aio_*"
|
||||
function notify the
|
||||
caller might also be not the best solution since whenever the process
|
||||
works on preparing data for on client it makes no sense to be
|
||||
interrupted by a notification since the new client will not be handled
|
||||
before the current client is served. For situations like this
|
||||
.IR "aio_suspend"
|
||||
should be used.
|
||||
.PP
|
||||
When calling this function, the calling thread is suspended until at
|
||||
least one of the requests pointed to by the
|
||||
.IR "nent"
|
||||
elements of the
|
||||
array
|
||||
.IR "list"
|
||||
has completed. If any of the requests has already
|
||||
completed at the time
|
||||
.IR "aio_suspend"
|
||||
is called, the function returns
|
||||
immediately. Whether a request has terminated or not is determined by
|
||||
comparing the error status of the request with
|
||||
.IR "EINPROGRESS"
|
||||
. If
|
||||
an element of
|
||||
.IR "list"
|
||||
is
|
||||
.IR "NULL"
|
||||
, the entry is simply ignored.
|
||||
|
||||
If no request has finished, the calling process is suspended. If
|
||||
.IR "timeout"
|
||||
is
|
||||
.IR "NULL"
|
||||
, the process is not woken until a request
|
||||
has finished. If
|
||||
.IR "timeout"
|
||||
is not
|
||||
.IR "NULL"
|
||||
, the process remains
|
||||
suspended at least as long as specified in
|
||||
.IR "timeout"
|
||||
. In this case,
|
||||
.IR "aio_suspend"
|
||||
returns with an error.
|
||||
.PP
|
||||
When the sources are compiled with
|
||||
.IR "_FILE_OFFSET_BITS == 64"
|
||||
this
|
||||
function is in fact
|
||||
.IR "aio_suspend64"
|
||||
since the LFS interface
|
||||
transparently replaces the normal implementation.
|
||||
.SH "RETURN VALUES"
|
||||
The return value of the function is
|
||||
.IR 0
|
||||
if one or more requests
|
||||
from the
|
||||
.IR "list"
|
||||
have terminated. Otherwise the function returns
|
||||
.IR -1
|
||||
and
|
||||
.IR "errno"
|
||||
is set.
|
||||
.SH ERRORS
|
||||
.TP
|
||||
.B EAGAIN
|
||||
None of the requests from the
|
||||
.IR "list"
|
||||
completed in the time specified
|
||||
by
|
||||
.IR "timeout"
|
||||
.
|
||||
.TP
|
||||
.B EINTR
|
||||
A signal interrupted the
|
||||
.IR "aio_suspend"
|
||||
function. This signal might
|
||||
also be sent by the AIO implementation while signalling the termination
|
||||
of one of the requests.
|
||||
.TP
|
||||
.B ENOSYS
|
||||
The
|
||||
.IR "aio_suspend"
|
||||
function is not implemented.
|
||||
.SH "SEE ALSO"
|
||||
.BR aio(3),
|
||||
.BR aio_cancel(3),
|
||||
.BR aio_cancel64(3),
|
||||
.BR aio_error(3),
|
||||
.BR aio_error64(3),
|
||||
.BR aio_fsync(3),
|
||||
.BR aio_fsync64(3),
|
||||
.BR aio_init(3),
|
||||
.BR aio_read(3),
|
||||
.BR aio_read64(3),
|
||||
.BR aio_return(3),
|
||||
.BR aio_return64(3),
|
||||
.BR aio_suspend64(3),
|
||||
.BR aio_write(3),
|
||||
.BR aio_write64(3),
|
||||
.BR errno(3),
|
||||
@@ -0,0 +1,51 @@
|
||||
.TH aio_suspend64 3 2002-09-12 "Linux 2.4" Linux AIO"
|
||||
.SH NAME
|
||||
aio_suspend64 \- Wait until one or more requests of a specific set terminates
|
||||
.SH SYNOPSYS
|
||||
.nf
|
||||
.B #include <errno.h>
|
||||
.sp
|
||||
.br
|
||||
.B #include <aio.h>
|
||||
.sp
|
||||
.br
|
||||
.BI "int aio_suspend64 (const struct aiocb64 *const list[], int nent, const struct timespec *timeout)"
|
||||
.fi
|
||||
.SH DESCRIPTION
|
||||
This function is similar to
|
||||
.IR "aio_suspend"
|
||||
with the only difference
|
||||
that the argument is a reference to a variable of type
|
||||
.IR "struct aiocb64".
|
||||
|
||||
When the sources are compiled with
|
||||
.IR "_FILE_OFFSET_BITS == 64"
|
||||
this
|
||||
function is available under the name
|
||||
.IR "aio_suspend"
|
||||
and so
|
||||
transparently replaces the interface for small files on 32 bit
|
||||
machines.
|
||||
.SH "RETURN VALUES"
|
||||
See
|
||||
.IR aio_suspend.
|
||||
.SH ERRORS
|
||||
See
|
||||
.IR aio_suspend.
|
||||
.SH "SEE ALSO"
|
||||
.BR aio(3),
|
||||
.BR aio_cancel(3),
|
||||
.BR aio_cancel64(3),
|
||||
.BR aio_error(3),
|
||||
.BR aio_error64(3),
|
||||
.BR aio_fsync(3),
|
||||
.BR aio_fsync64(3),
|
||||
.BR aio_init(3),
|
||||
.BR aio_read(3),
|
||||
.BR aio_read64(3),
|
||||
.BR aio_return(3),
|
||||
.BR aio_return64(3),
|
||||
.BR aio_suspend(3),
|
||||
.BR aio_write(3),
|
||||
.BR aio_write64(3),
|
||||
.BR errno(3),
|
||||
@@ -0,0 +1,176 @@
|
||||
.TH aio_write 3 2002-09-12 "Linux 2.4" Linux AIO"
|
||||
.SH NAME
|
||||
aio_write \- Initiate an asynchronous write operation
|
||||
.SH SYNOPSYS
|
||||
.nf
|
||||
.B #include <errno.h>
|
||||
.sp
|
||||
.br
|
||||
.B #include <aio.h>
|
||||
.sp
|
||||
.br
|
||||
.BI "int aio_write (struct aiocb * aiocbp);"
|
||||
.fi
|
||||
.SH DESCRIPTION
|
||||
This function initiates an asynchronous write operation. The function
|
||||
call immediately returns after the operation was enqueued or if before
|
||||
this happens an error was encountered.
|
||||
|
||||
The first
|
||||
.IR "aiocbp->aio_nbytes"
|
||||
bytes from the buffer starting at
|
||||
.IR "aiocbp->aio_buf"
|
||||
are written to the file for which
|
||||
.IR "aiocbp->aio_fildes"
|
||||
is an descriptor, starting at the absolute
|
||||
position
|
||||
.IR "aiocbp->aio_offset"
|
||||
in the file.
|
||||
|
||||
If prioritized I/O is supported by the platform, the
|
||||
.IR "aiocbp->aio_reqprio "
|
||||
value is used to adjust the priority before
|
||||
the request is actually enqueued.
|
||||
|
||||
The calling process is notified about the termination of the read
|
||||
request according to the
|
||||
.IR "aiocbp->aio_sigevent"
|
||||
value.
|
||||
|
||||
When
|
||||
.IR "aio_write"
|
||||
returns, the return value is zero if no error
|
||||
occurred that can be found before the process is enqueued. If such an
|
||||
early error is found the function returns
|
||||
.IR -1
|
||||
and sets
|
||||
.IR "errno"
|
||||
to one of the following values.
|
||||
|
||||
.TP
|
||||
.B EAGAIN
|
||||
The request was not enqueued due to (temporarily) exceeded resource
|
||||
limitations.
|
||||
.TP
|
||||
.B ENOSYS
|
||||
The
|
||||
.IR "aio_write"
|
||||
function is not implemented.
|
||||
.TP
|
||||
.B EBADF
|
||||
The
|
||||
.IR "aiocbp->aio_fildes"
|
||||
descriptor is not valid. This condition
|
||||
may not be recognized before enqueueing the request, and so this error
|
||||
might also be signaled asynchronously.
|
||||
.TP
|
||||
.B EINVAL
|
||||
The
|
||||
.IR "aiocbp->aio_offset"
|
||||
or
|
||||
.IR "aiocbp->aio_reqprio"
|
||||
value is
|
||||
invalid. This condition may not be recognized before enqueueing the
|
||||
request and so this error might also be signaled asynchronously.
|
||||
.PP
|
||||
|
||||
In the case
|
||||
.IR "aio_write"
|
||||
returns zero, the current status of the
|
||||
request can be queried using
|
||||
.IR "aio_error"
|
||||
and
|
||||
.IR "aio_return"
|
||||
functions. As long as the value returned by
|
||||
.IR "aio_error"
|
||||
is
|
||||
.IR "EINPROGRESS"
|
||||
the operation has not yet completed. If
|
||||
.IR "aio_error"
|
||||
returns zero, the operation successfully terminated,
|
||||
otherwise the value is to be interpreted as an error code. If the
|
||||
function terminated, the result of the operation can be get using a call
|
||||
to
|
||||
.IR "aio_return"
|
||||
. The returned value is the same as an equivalent
|
||||
call to
|
||||
.IR "read"
|
||||
would have returned. Possible error codes returned
|
||||
by
|
||||
.IR "aio_error"
|
||||
are:
|
||||
|
||||
.TP
|
||||
.B EBADF
|
||||
The
|
||||
.IR "aiocbp->aio_fildes"
|
||||
descriptor is not valid.
|
||||
.TP
|
||||
.B ECANCELED
|
||||
The operation was canceled before the operation was finished.
|
||||
.TP
|
||||
.B EINVAL
|
||||
The
|
||||
.IR "aiocbp->aio_offset"
|
||||
value is invalid.
|
||||
.PP
|
||||
When the sources are compiled with
|
||||
.IR "_FILE_OFFSET_BITS == 64"
|
||||
, this
|
||||
function is in fact
|
||||
.IR "aio_write64"
|
||||
since the LFS interface transparently
|
||||
replaces the normal implementation.
|
||||
.SH "RETURN VALUES"
|
||||
When
|
||||
.IR "aio_write"
|
||||
returns, the return value is zero if no error
|
||||
occurred that can be found before the process is enqueued. If such an
|
||||
early error is found the function returns
|
||||
.IR -1
|
||||
and sets
|
||||
.IR "errno"
|
||||
to one of the following values.
|
||||
.SH ERRORS
|
||||
.TP
|
||||
.B EAGAIN
|
||||
The request was not enqueued due to (temporarily) exceeded resource
|
||||
limitations.
|
||||
.TP
|
||||
.B ENOSYS
|
||||
The
|
||||
.IR "aio_write"
|
||||
function is not implemented.
|
||||
.TP
|
||||
.B EBADF
|
||||
The
|
||||
.IR "aiocbp->aio_fildes"
|
||||
descriptor is not valid. This condition
|
||||
may not be recognized before enqueueing the request, and so this error
|
||||
might also be signaled asynchronously.
|
||||
.TP
|
||||
.B EINVAL
|
||||
The
|
||||
.IR "aiocbp->aio_offset"
|
||||
or
|
||||
.IR "aiocbp->aio_reqprio"
|
||||
value is
|
||||
invalid. This condition may not be recognized before enqueueing the
|
||||
request and so this error might also be signaled asynchronously.
|
||||
.SH "SEE ALSO"
|
||||
.BR aio(3),
|
||||
.BR aio_cancel(3),
|
||||
.BR aio_cancel64(3),
|
||||
.BR aio_error(3),
|
||||
.BR aio_error64(3),
|
||||
.BR aio_fsync(3),
|
||||
.BR aio_fsync64(3),
|
||||
.BR aio_init(3),
|
||||
.BR aio_read(3),
|
||||
.BR aio_read64(3),
|
||||
.BR aio_return(3),
|
||||
.BR aio_return64(3),
|
||||
.BR aio_suspend(3),
|
||||
.BR aio_suspend64(3),
|
||||
.BR aio_write64(3),
|
||||
.BR errno(3),
|
||||
@@ -0,0 +1,61 @@
|
||||
.TH aio_write64 3 2002-09-12 "Linux 2.4" Linux AIO"
|
||||
.SH NAME
|
||||
aio_write64 \- Initiate an asynchronous write operation
|
||||
.SH SYNOPSYS
|
||||
.nf
|
||||
.B #include <errno.h>
|
||||
.sp
|
||||
.br
|
||||
.B #include <aio.h>
|
||||
.sp
|
||||
.br
|
||||
.BI "int aio_write64 (struct aiocb *aiocbp)"
|
||||
.fi
|
||||
.SH DESCRIPTION
|
||||
This function is similar to the
|
||||
.IR "aio_write"
|
||||
function. The only
|
||||
difference is that on
|
||||
.IR "32 bit"
|
||||
machines the file descriptor should
|
||||
be opened in the large file mode. Internally
|
||||
.IR "aio_write64"
|
||||
uses
|
||||
functionality equivalent to
|
||||
.IR "lseek64"
|
||||
to position the file descriptor correctly for the writing,
|
||||
as opposed to
|
||||
.IR "lseek"
|
||||
functionality used in
|
||||
.IR "aio_write".
|
||||
|
||||
When the sources are compiled with
|
||||
.IR "_FILE_OFFSET_BITS == 64"
|
||||
, this
|
||||
function is available under the name
|
||||
.IR "aio_write"
|
||||
and so transparently
|
||||
replaces the interface for small files on 32 bit machines.
|
||||
.SH "RETURN VALUES"
|
||||
See
|
||||
.IR aio_write.
|
||||
.SH ERRORS
|
||||
See
|
||||
.IR aio_write.
|
||||
.SH "SEE ALSO"
|
||||
.BR aio(3),
|
||||
.BR aio_cancel(3),
|
||||
.BR aio_cancel64(3),
|
||||
.BR aio_error(3),
|
||||
.BR aio_error64(3),
|
||||
.BR aio_fsync(3),
|
||||
.BR aio_fsync64(3),
|
||||
.BR aio_init(3),
|
||||
.BR aio_read(3),
|
||||
.BR aio_read64(3),
|
||||
.BR aio_return(3),
|
||||
.BR aio_return64(3),
|
||||
.BR aio_suspend(3),
|
||||
.BR aio_suspend64(3),
|
||||
.BR aio_write(3),
|
||||
.BR errno(3),
|
||||
@@ -0,0 +1,351 @@
|
||||
.TH io 3 2002-09-12 "Linux 2.4" Linux IO"
|
||||
.SH NAME
|
||||
io \- Asynchronous IO
|
||||
.SH SYNOPSYS
|
||||
.nf
|
||||
.B #include <errno.h>
|
||||
.sp
|
||||
.br
|
||||
.B #include <libio.h>
|
||||
.sp
|
||||
.fi
|
||||
.SH DESCRIPTION
|
||||
The libaio library defines a new set of I/O operations which can
|
||||
significantly reduce the time an application spends waiting at I/O. The
|
||||
new functions allow a program to initiate one or more I/O operations and
|
||||
then immediately resume normal work while the I/O operations are
|
||||
executed in parallel.
|
||||
|
||||
These functions are part of the library with realtime functions named
|
||||
.IR "libaio"
|
||||
. They are not actually part of the
|
||||
.IR "libc"
|
||||
binary.
|
||||
The implementation of these functions can be done using support in the
|
||||
kernel.
|
||||
|
||||
All IO operations operate on files which were opened previously. There
|
||||
might be arbitrarily many operations running for one file. The
|
||||
asynchronous I/O operations are controlled using a data structure named
|
||||
.IR "struct iocb"
|
||||
It is defined in
|
||||
.IR "libio.h"
|
||||
as follows.
|
||||
|
||||
.nf
|
||||
|
||||
typedef struct io_context *io_context_t;
|
||||
|
||||
typedef enum io_iocb_cmd {
|
||||
IO_CMD_PREAD = 0,
|
||||
IO_CMD_PWRITE = 1,
|
||||
|
||||
IO_CMD_FSYNC = 2,
|
||||
IO_CMD_FDSYNC = 3,
|
||||
|
||||
IO_CMD_POLL = 5,
|
||||
IO_CMD_NOOP = 6,
|
||||
} io_iocb_cmd_t;
|
||||
|
||||
struct io_iocb_common {
|
||||
void *buf;
|
||||
unsigned __pad1;
|
||||
long nbytes;
|
||||
unsigned __pad2;
|
||||
long long offset;
|
||||
long long __pad3, __pad4;
|
||||
}; /* result code is the amount read or -'ve errno */
|
||||
|
||||
|
||||
struct iocb {
|
||||
void *data;
|
||||
unsigned key;
|
||||
short aio_lio_opcode;
|
||||
short aio_reqprio;
|
||||
int aio_fildes;
|
||||
union {
|
||||
struct io_iocb_common c;
|
||||
struct io_iocb_vector v;
|
||||
struct io_iocb_poll poll;
|
||||
struct io_iocb_sockaddr saddr;
|
||||
} u;
|
||||
};
|
||||
|
||||
|
||||
.fi
|
||||
.TP
|
||||
.IR "int aio_fildes"
|
||||
This element specifies the file descriptor to be used for the
|
||||
operation. It must be a legal descriptor, otherwise the operation will
|
||||
fail.
|
||||
|
||||
The device on which the file is opened must allow the seek operation.
|
||||
I.e., it is not possible to use any of the IO operations on devices
|
||||
like terminals where an
|
||||
.IR "lseek"
|
||||
call would lead to an error.
|
||||
.TP
|
||||
.IR "long u.c.offset"
|
||||
This element specifies the offset in the file at which the operation (input
|
||||
or output) is performed. Since the operations are carried out in arbitrary
|
||||
order and more than one operation for one file descriptor can be
|
||||
started, one cannot expect a current read/write position of the file
|
||||
descriptor.
|
||||
.TP
|
||||
.IR "void *buf"
|
||||
This is a pointer to the buffer with the data to be written or the place
|
||||
where the read data is stored.
|
||||
.TP
|
||||
.IR "long u.c.nbytes"
|
||||
This element specifies the length of the buffer pointed to by
|
||||
.IR "io_buf"
|
||||
.
|
||||
.TP
|
||||
.IR "int aio_reqprio"
|
||||
Is not currently used.
|
||||
.TP
|
||||
.B "IO_CMD_PREAD"
|
||||
Start a read operation. Read from the file at position
|
||||
.IR "u.c.offset"
|
||||
and store the next
|
||||
.IR "u.c.nbytes"
|
||||
bytes in the
|
||||
buffer pointed to by
|
||||
.IR "buf"
|
||||
.
|
||||
.TP
|
||||
.B "IO_CMD_PWRITE"
|
||||
Start a write operation. Write
|
||||
.IR "u.c.nbytes"
|
||||
bytes starting at
|
||||
.IR "buf"
|
||||
into the file starting at position
|
||||
.IR "u.c.offset"
|
||||
.
|
||||
.TP
|
||||
.B "IO_CMD_NOP"
|
||||
Do nothing for this control block. This value is useful sometimes when
|
||||
an array of
|
||||
.IR "struct iocb"
|
||||
values contains holes, i.e., some of the
|
||||
values must not be handled although the whole array is presented to the
|
||||
.IR "io_submit"
|
||||
function.
|
||||
.TP
|
||||
.B "IO_CMD_FSYNC"
|
||||
.TP
|
||||
.B "IO_CMD_POLL"
|
||||
This is experimental.
|
||||
.SH EXAMPLE
|
||||
.nf
|
||||
/*
|
||||
* Simplistic version of copy command using async i/o
|
||||
*
|
||||
* From: Stephen Hemminger <shemminger@osdl.org>
|
||||
* Copy file by using a async I/O state machine.
|
||||
* 1. Start read request
|
||||
* 2. When read completes turn it into a write request
|
||||
* 3. When write completes decrement counter and free resources
|
||||
*
|
||||
*
|
||||
* Usage: aiocp file(s) desination
|
||||
*/
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/param.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <libaio.h>
|
||||
|
||||
#define AIO_BLKSIZE (64*1024)
|
||||
#define AIO_MAXIO 32
|
||||
|
||||
static int busy = 0; // # of I/O's in flight
|
||||
static int tocopy = 0; // # of blocks left to copy
|
||||
static int dstfd = -1; // destination file descriptor
|
||||
static const char *dstname = NULL;
|
||||
static const char *srcname = NULL;
|
||||
|
||||
|
||||
/* Fatal error handler */
|
||||
static void io_error(const char *func, int rc)
|
||||
{
|
||||
if (rc == -ENOSYS)
|
||||
fprintf(stderr, "AIO not in this kernel\n");
|
||||
else if (rc < 0 && -rc < sys_nerr)
|
||||
fprintf(stderr, "%s: %s\n", func, sys_errlist[-rc]);
|
||||
else
|
||||
fprintf(stderr, "%s: error %d\n", func, rc);
|
||||
|
||||
if (dstfd > 0)
|
||||
close(dstfd);
|
||||
if (dstname)
|
||||
unlink(dstname);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Write complete callback.
|
||||
* Adjust counts and free resources
|
||||
*/
|
||||
static void wr_done(io_context_t ctx, struct iocb *iocb, long res, long res2)
|
||||
{
|
||||
if (res2 != 0) {
|
||||
io_error("aio write", res2);
|
||||
}
|
||||
if (res != iocb->u.c.nbytes) {
|
||||
fprintf(stderr, "write missed bytes expect %d got %d\n", iocb->u.c.nbytes, res2);
|
||||
exit(1);
|
||||
}
|
||||
--tocopy;
|
||||
--busy;
|
||||
free(iocb->u.c.buf);
|
||||
|
||||
memset(iocb, 0xff, sizeof(iocb)); // paranoia
|
||||
free(iocb);
|
||||
write(2, "w", 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Read complete callback.
|
||||
* Change read iocb into a write iocb and start it.
|
||||
*/
|
||||
static void rd_done(io_context_t ctx, struct iocb *iocb, long res, long res2)
|
||||
{
|
||||
/* library needs accessors to look at iocb? */
|
||||
int iosize = iocb->u.c.nbytes;
|
||||
char *buf = iocb->u.c.buf;
|
||||
off_t offset = iocb->u.c.offset;
|
||||
|
||||
if (res2 != 0)
|
||||
io_error("aio read", res2);
|
||||
if (res != iosize) {
|
||||
fprintf(stderr, "read missing bytes expect %d got %d\n", iocb->u.c.nbytes, res);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
||||
/* turn read into write */
|
||||
io_prep_pwrite(iocb, dstfd, buf, iosize, offset);
|
||||
io_set_callback(iocb, wr_done);
|
||||
if (1 != (res = io_submit(ctx, 1, &iocb)))
|
||||
io_error("io_submit write", res);
|
||||
write(2, "r", 1);
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char *const *argv)
|
||||
{
|
||||
int srcfd;
|
||||
struct stat st;
|
||||
off_t length = 0, offset = 0;
|
||||
io_context_t myctx;
|
||||
|
||||
if (argc != 3 || argv[1][0] == '-') {
|
||||
fprintf(stderr, "Usage: aiocp SOURCE DEST");
|
||||
exit(1);
|
||||
}
|
||||
if ((srcfd = open(srcname = argv[1], O_RDONLY)) < 0) {
|
||||
perror(srcname);
|
||||
exit(1);
|
||||
}
|
||||
if (fstat(srcfd, &st) < 0) {
|
||||
perror("fstat");
|
||||
exit(1);
|
||||
}
|
||||
length = st.st_size;
|
||||
|
||||
if ((dstfd = open(dstname = argv[2], O_WRONLY | O_CREAT, 0666)) < 0) {
|
||||
close(srcfd);
|
||||
perror(dstname);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* initialize state machine */
|
||||
memset(&myctx, 0, sizeof(myctx));
|
||||
io_queue_init(AIO_MAXIO, &myctx);
|
||||
tocopy = howmany(length, AIO_BLKSIZE);
|
||||
|
||||
while (tocopy > 0) {
|
||||
int i, rc;
|
||||
/* Submit as many reads as once as possible upto AIO_MAXIO */
|
||||
int n = MIN(MIN(AIO_MAXIO - busy, AIO_MAXIO / 2),
|
||||
howmany(length - offset, AIO_BLKSIZE));
|
||||
if (n > 0) {
|
||||
struct iocb *ioq[n];
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
struct iocb *io = (struct iocb *) malloc(sizeof(struct iocb));
|
||||
int iosize = MIN(length - offset, AIO_BLKSIZE);
|
||||
char *buf = (char *) malloc(iosize);
|
||||
|
||||
if (NULL == buf || NULL == io) {
|
||||
fprintf(stderr, "out of memory\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
io_prep_pread(io, srcfd, buf, iosize, offset);
|
||||
io_set_callback(io, rd_done);
|
||||
ioq[i] = io;
|
||||
offset += iosize;
|
||||
}
|
||||
|
||||
rc = io_submit(myctx, n, ioq);
|
||||
if (rc < 0)
|
||||
io_error("io_submit", rc);
|
||||
|
||||
busy += n;
|
||||
}
|
||||
|
||||
// Handle IO's that have completed
|
||||
rc = io_queue_run(myctx);
|
||||
if (rc < 0)
|
||||
io_error("io_queue_run", rc);
|
||||
|
||||
// if we have maximum number of i/o's in flight
|
||||
// then wait for one to complete
|
||||
if (busy == AIO_MAXIO) {
|
||||
rc = io_queue_wait(myctx, NULL);
|
||||
if (rc < 0)
|
||||
io_error("io_queue_wait", rc);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
close(srcfd);
|
||||
close(dstfd);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Results look like:
|
||||
* [alanm@toolbox ~/MOT3]$ ../taio kernel-source-2.4.8-0.4g.ppc.rpm abc
|
||||
* rrrrrrrrrrrrrrrwwwrwrrwwrrwrwwrrwrwrwwrrwrwrrrrwwrwwwrrwrrrwwwwwwwwwwwwwwwww
|
||||
* rrrrrrrrrrrrrrwwwrrwrwrwrwrrwwwwwwwwwwwwwwrrrrrrrrrrrrrrrrrrwwwwrwrwwrwrwrwr
|
||||
* wrrrrrrrwwwwwwwwwwwwwrrrwrrrwrrwrwwwwwwwwwwrrrrwwrwrrrrrrrrrrrwwwwwwwwwwwrww
|
||||
* wwwrrrrrrrrwwrrrwwrwrwrwwwrrrrrrrwwwrrwwwrrwrwwwwwwwwrrrrrrrwwwrrrrrrrwwwwww
|
||||
* wwwwwwwrwrrrrrrrrwrrwrrwrrwrwrrrwrrrwrrrwrwwwwwwwwwwwwwwwwwwrrrwwwrrrrrrrrrr
|
||||
* rrwrrrrrrwrrwwwwwwwwwwwwwwwwrwwwrrwrwwrrrrrrrrrrrrrrrrrrrwwwwwwwwwwwwwwwwwww
|
||||
* rrrrrwrrwrwrwrrwrrrwwwwwwwwrrrrwrrrwrwwrwrrrwrrwrrrrwwwwwwwrwrwwwwrwwrrrwrrr
|
||||
* rrrwwwwwwwrrrrwwrrrrrrrrrrrrwrwrrrrwwwwwwwwwwwwwwrwrrrrwwwwrwrrrrwrwwwrrrwww
|
||||
* rwwrrrrrrrwrrrrrrrrrrrrwwwwrrrwwwrwrrwwwwwwwwwwwwwwwwwwwwwrrrrrrrwwwwwwwrw
|
||||
*/
|
||||
.fi
|
||||
.SH "SEE ALSO"
|
||||
.BR io_cancel(3),
|
||||
.BR io_fsync(3),
|
||||
.BR io_getevents(3),
|
||||
.BR io_prep_fsync(3),
|
||||
.BR io_prep_pread(3),
|
||||
.BR io_prep_pwrite(3),
|
||||
.BR io_queue_init(3),
|
||||
.BR io_queue_release(3),
|
||||
.BR io_queue_run(3),
|
||||
.BR io_queue_wait(3),
|
||||
.BR io_set_callback(3),
|
||||
.BR io_submit(3),
|
||||
.BR errno(3)
|
||||
@@ -0,0 +1,21 @@
|
||||
.\"/* sys_io_cancel:
|
||||
.\" * Attempts to cancel an iocb previously passed to io_submit. If
|
||||
.\" * the operation is successfully cancelled, the resulting event is
|
||||
.\" * copied into the memory pointed to by result without being placed
|
||||
.\" * into the completion queue and 0 is returned. May fail with
|
||||
.\" * -EFAULT if any of the data structures pointed to are invalid.
|
||||
.\" * May fail with -EINVAL if aio_context specified by ctx_id is
|
||||
.\" * invalid. May fail with -EAGAIN if the iocb specified was not
|
||||
.\" * cancelled. Will fail with -ENOSYS if not implemented.
|
||||
.\" */
|
||||
.\"
|
||||
.TH io_cancel 2 2002-09-03 "Linux 2.4" "Linux AIO"
|
||||
.SH NAME
|
||||
io_cancel \- cancel io requests
|
||||
.SH SYNOPSIS
|
||||
.B #include <errno.h>
|
||||
.br
|
||||
.B #include <libaio.h>
|
||||
.LP
|
||||
.BI "int io_submit(io_context_t " ctx ", struct iocb *" iocb ", struct io_event *" result ");"
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
.TH io_cancel 2 2002-09-03 "Linux 2.4" "Linux AIO"
|
||||
.SH NAME
|
||||
io_cancel \- Cancel io requests
|
||||
.SH SYNOPSIS
|
||||
.nf
|
||||
.B #include <errno.h>
|
||||
.sp
|
||||
.br
|
||||
.B #include <libaio.h>
|
||||
.sp
|
||||
.br
|
||||
.BI "int io_cancel(io_context_t ctx, struct iocb *iocb)"
|
||||
.br
|
||||
.sp
|
||||
struct iocb {
|
||||
void *data; /* Return in the io completion event */
|
||||
unsigned key; /* For use in identifying io requests */
|
||||
short aio_lio_opcode;
|
||||
short aio_reqprio; /* Not used */
|
||||
int aio_fildes;
|
||||
};
|
||||
.fi
|
||||
.SH DESCRIPTION
|
||||
Attempts to cancel an iocb previously passed to io_submit. If
|
||||
the operation is successfully cancelled, the resulting event is
|
||||
copied into the memory pointed to by result without being placed
|
||||
into the completion queue.
|
||||
.PP
|
||||
When one or more requests are asynchronously processed, it might be
|
||||
useful in some situations to cancel a selected operation, e.g., if it
|
||||
becomes obvious that the written data is no longer accurate and would
|
||||
have to be overwritten soon. As an example, assume an application, which
|
||||
writes data in files in a situation where new incoming data would have
|
||||
to be written in a file which will be updated by an enqueued request.
|
||||
.SH "RETURN VALUES"
|
||||
0 is returned on success , otherwise returns Errno.
|
||||
.SH ERRORS
|
||||
.TP
|
||||
.B EFAULT
|
||||
If any of the data structures pointed to are invalid.
|
||||
.TP
|
||||
.B EINVAL
|
||||
If aio_context specified by ctx_id is
|
||||
invalid.
|
||||
.TP
|
||||
.B EAGAIN
|
||||
If the iocb specified was not
|
||||
cancelled.
|
||||
.TP
|
||||
.B ENOSYS
|
||||
if not implemented.
|
||||
.SH "SEE ALSO"
|
||||
.BR io(3),
|
||||
.BR io_fsync(3),
|
||||
.BR io_getevents(3),
|
||||
.BR io_prep_fsync(3),
|
||||
.BR io_prep_pread(3),
|
||||
.BR io_prep_pwrite(3),
|
||||
.BR io_queue_init(3),
|
||||
.BR io_queue_release(3),
|
||||
.BR io_queue_run(3),
|
||||
.BR io_queue_wait(3),
|
||||
.BR io_set_callback(3),
|
||||
.BR io_submit(3),
|
||||
.BR errno(3)
|
||||
@@ -0,0 +1,17 @@
|
||||
.\"/* sys_io_destroy:
|
||||
.\" * Destroy the aio_context specified. May cancel any outstanding
|
||||
.\" * AIOs and block on completion. Will fail with -ENOSYS if not
|
||||
.\" * implemented. May fail with -EFAULT if the context pointed to
|
||||
.\" * is invalid.
|
||||
.\" */
|
||||
.\" libaio provides this as io_queue_release.
|
||||
.TH io_destroy 2 2002-09-03 "Linux 2.4" "Linux AIO"
|
||||
.SH NAME
|
||||
io_destroy \- destroy an io context
|
||||
.SH SYNOPSIS
|
||||
.B #include <errno.h>
|
||||
.br
|
||||
.B #include <libaio.h>
|
||||
.LP
|
||||
.BI "int io_destroy(io_context_t " ctx ");"
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
./" static inline int io_fsync(io_context_t ctx, struct iocb *iocb, io_callback_t cb, int fd)
|
||||
./" {
|
||||
./" io_prep_fsync(iocb, fd);
|
||||
./" io_set_callback(iocb, cb);
|
||||
./" return io_submit(ctx, 1, &iocb);
|
||||
./" }
|
||||
.TH io_fsync 3 2002-09-12 "Linux 2.4" Linux AIO"
|
||||
.SH NAME
|
||||
io_fsync \- Synchronize a file's complete in-core state with that on disk
|
||||
.SH SYNOPSYS
|
||||
.nf
|
||||
.B #include <errno.h>
|
||||
.sp
|
||||
.br
|
||||
.B #include <libaio.h>
|
||||
.sp
|
||||
.br
|
||||
.BI "int io_fsync(io_context_t ctx, struct iocb *iocb, io_callback_t cb, int fd)"
|
||||
.sp
|
||||
struct iocb {
|
||||
void *data;
|
||||
unsigned key;
|
||||
short aio_lio_opcode;
|
||||
short aio_reqprio;
|
||||
int aio_fildes;
|
||||
};
|
||||
.sp
|
||||
typedef void (*io_callback_t)(io_context_t ctx, struct iocb *iocb, long res, long res2);
|
||||
.sp
|
||||
.fi
|
||||
.SH DESCRIPTION
|
||||
When dealing with asynchronous operations it is sometimes necessary to
|
||||
get into a consistent state. This would mean for AIO that one wants to
|
||||
know whether a certain request or a group of request were processed.
|
||||
This could be done by waiting for the notification sent by the system
|
||||
after the operation terminated, but this sometimes would mean wasting
|
||||
resources (mainly computation time).
|
||||
.PP
|
||||
Calling this function forces all I/O operations operating queued at the
|
||||
time of the function call operating on the file descriptor
|
||||
.IR "iocb->io_fildes"
|
||||
into the synchronized I/O completion state . The
|
||||
.IR "io_fsync"
|
||||
function returns
|
||||
immediately but the notification through the method described in
|
||||
.IR "io_callback"
|
||||
will happen only after all requests for this
|
||||
file descriptor have terminated and the file is synchronized. This also
|
||||
means that requests for this very same file descriptor which are queued
|
||||
after the synchronization request are not affected.
|
||||
.SH "RETURN VALUES"
|
||||
Returns 0, otherwise returns errno.
|
||||
.SH ERRORS
|
||||
.TP
|
||||
.B EFAULT
|
||||
.I iocbs
|
||||
referenced data outside of the program's accessible address space.
|
||||
.TP
|
||||
.B EINVAL
|
||||
.I ctx
|
||||
refers to an unitialized aio context, the iocb pointed to by
|
||||
.I iocbs
|
||||
contains an improperly initialized iocb,
|
||||
.TP
|
||||
.B EBADF
|
||||
The iocb contains a file descriptor that does not exist.
|
||||
.TP
|
||||
.B EINVAL
|
||||
The file specified in the iocb does not support the given io operation.
|
||||
.SH "SEE ALSO"
|
||||
.BR io(3),
|
||||
.BR io_cancel(3),
|
||||
.BR io_getevents(3),
|
||||
.BR io_prep_pread(3),
|
||||
.BR io_prep_pwrite(3),
|
||||
.BR io_queue_init(3),
|
||||
.BR io_queue_release(3),
|
||||
.BR io_queue_run(3),
|
||||
.BR io_queue_wait(3),
|
||||
.BR io_set_callback(3),
|
||||
.BR io_submit(3),
|
||||
.BR errno(3)
|
||||
@@ -0,0 +1,29 @@
|
||||
./"/* io_getevents:
|
||||
./" * Attempts to read at least min_nr events and up to nr events from
|
||||
./" * the completion queue for the aio_context specified by ctx_id. May
|
||||
./" * fail with -EINVAL if ctx_id is invalid, if min_nr is out of range,
|
||||
./" * if nr is out of range, if when is out of range. May fail with
|
||||
./" * -EFAULT if any of the memory specified to is invalid. May return
|
||||
./" * 0 or < min_nr if no events are available and the timeout specified
|
||||
./" * by when has elapsed, where when == NULL specifies an infinite
|
||||
./" * timeout. Note that the timeout pointed to by when is relative and
|
||||
./" * will be updated if not NULL and the operation blocks. Will fail
|
||||
./" * with -ENOSYS if not implemented.
|
||||
./" */
|
||||
./"asmlinkage long sys_io_getevents(io_context_t ctx_id,
|
||||
./" long min_nr,
|
||||
./" long nr,
|
||||
./" struct io_event *events,
|
||||
./" struct timespec *timeout)
|
||||
./"
|
||||
.TH io_getevents 2 2002-09-03 "Linux 2.4" "Linux AIO"
|
||||
.SH NAME
|
||||
io_getevents \- read resulting events from io requests
|
||||
.SH SYNOPSIS
|
||||
.B #include <errno.h>
|
||||
.br
|
||||
.B #include <libaio.h>
|
||||
.sp
|
||||
.BI "int io_getevents(io_context_t " ctx ", long " min_nr ", long " nr ", struct io_events *" events "[], struct timespec *" timeout ");"
|
||||
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
./"/* io_getevents:
|
||||
./" * Attempts to read at least min_nr events and up to nr events from
|
||||
./" * the completion queue for the aio_context specified by ctx_id. May
|
||||
./" * fail with -EINVAL if ctx_id is invalid, if min_nr is out of range,
|
||||
./" * if nr is out of range, if when is out of range. May fail with
|
||||
./" * -EFAULT if any of the memory specified to is invalid. May return
|
||||
./" * 0 or < min_nr if no events are available and the timeout specified
|
||||
./" * by when has elapsed, where when == NULL specifies an infinite
|
||||
./" * timeout. Note that the timeout pointed to by when is relative and
|
||||
./" * will be updated if not NULL and the operation blocks. Will fail
|
||||
./" * with -ENOSYS if not implemented.
|
||||
./" */
|
||||
./"asmlinkage long sys_io_getevents(io_context_t ctx_id,
|
||||
./" long min_nr,
|
||||
./" long nr,
|
||||
./" struct io_event *events,
|
||||
./" struct timespec *timeout)
|
||||
./"
|
||||
.TH io_getevents 2 2002-09-03 "Linux 2.4" "Linux AIO"
|
||||
.SH NAME
|
||||
io_getevents \- Read resulting events from io requests
|
||||
.SH SYNOPSIS
|
||||
.nf
|
||||
.B #include <errno.h>
|
||||
.sp
|
||||
.br
|
||||
.B #include <libaio.h>
|
||||
.br
|
||||
.sp
|
||||
struct iocb {
|
||||
void *data;
|
||||
unsigned key;
|
||||
short aio_lio_opcode;
|
||||
short aio_reqprio;
|
||||
int aio_fildes;
|
||||
};
|
||||
.sp
|
||||
struct io_event {
|
||||
unsigned PADDED(data, __pad1);
|
||||
unsigned PADDED(obj, __pad2);
|
||||
unsigned PADDED(res, __pad3);
|
||||
unsigned PADDED(res2, __pad4);
|
||||
};
|
||||
.sp
|
||||
.BI "int io_getevents(io_context_t " ctx ", long " nr ", struct io_event *" events "[], struct timespec *" timeout ");"
|
||||
|
||||
.fi
|
||||
.SH DESCRIPTION
|
||||
Attempts to read up to nr events from
|
||||
the completion queue for the aio_context specified by ctx.
|
||||
.SH "RETURN VALUES"
|
||||
May return
|
||||
0 if no events are available and the timeout specified
|
||||
by when has elapsed, where when == NULL specifies an infinite
|
||||
timeout. Note that the timeout pointed to by when is relative and
|
||||
will be updated if not NULL and the operation blocks. Will fail
|
||||
with ENOSYS if not implemented.
|
||||
.SH ERRORS
|
||||
.TP
|
||||
.B EINVAL
|
||||
if ctx_id is invalid, if min_nr is out of range,
|
||||
if nr is out of range, if when is out of range.
|
||||
.TP
|
||||
.B EFAULT
|
||||
if any of the memory specified to is invalid.
|
||||
.SH "SEE ALSO"
|
||||
.BR io(3),
|
||||
.BR io_cancel(3),
|
||||
.BR io_fsync(3),
|
||||
.BR io_prep_fsync(3),
|
||||
.BR io_prep_pread(3),
|
||||
.BR io_prep_pwrite(3),
|
||||
.BR io_queue_init(3),
|
||||
.BR io_queue_release(3),
|
||||
.BR io_queue_run(3),
|
||||
.BR io_queue_wait(3),
|
||||
.BR io_set_callback(3),
|
||||
.BR io_submit(3),
|
||||
.BR errno(3)
|
||||
@@ -0,0 +1,89 @@
|
||||
./" static inline void io_prep_fsync(struct iocb *iocb, int fd)
|
||||
./" {
|
||||
./" memset(iocb, 0, sizeof(*iocb));
|
||||
./" iocb->aio_fildes = fd;
|
||||
./" iocb->aio_lio_opcode = IO_CMD_FSYNC;
|
||||
./" iocb->aio_reqprio = 0;
|
||||
./" }
|
||||
.TH io_prep_fsync 3 2002-09-12 "Linux 2.4" Linux AIO"
|
||||
.SH NAME
|
||||
io_prep_fsync \- Synchronize a file's complete in-core state with that on disk
|
||||
.SH SYNOPSYS
|
||||
.nf
|
||||
.B #include <errno.h>
|
||||
.br
|
||||
.sp
|
||||
.B #include <libaio.h>
|
||||
.br
|
||||
.sp
|
||||
.BI "static inline void io_prep_fsync(struct iocb *iocb, int fd)"
|
||||
.sp
|
||||
struct iocb {
|
||||
void *data;
|
||||
unsigned key;
|
||||
short aio_lio_opcode;
|
||||
short aio_reqprio;
|
||||
int aio_fildes;
|
||||
};
|
||||
.sp
|
||||
.fi
|
||||
.SH DESCRIPTION
|
||||
This is an inline convenience function for setting up an iocbv for a FSYNC request.
|
||||
.br
|
||||
The file for which
|
||||
.TP
|
||||
.IR "iocb->aio_fildes = fd"
|
||||
is a descriptor is set up with
|
||||
the command
|
||||
.TP
|
||||
.IR "iocb->aio_lio_opcode = IO_CMD_FSYNC:
|
||||
.
|
||||
.PP
|
||||
The io_prep_fsync() function shall set up an IO_CMD_FSYNC operation
|
||||
to asynchronously force all I/O
|
||||
operations associated with the file indicated by the file
|
||||
descriptor aio_fildes member of the iocb structure referenced by
|
||||
the iocb argument and queued at the time of the call to
|
||||
io_submit() to the synchronized I/O completion state. The function
|
||||
call shall return when the synchronization request has been
|
||||
initiated or queued to the file or device (even when the data
|
||||
cannot be synchronized immediately).
|
||||
|
||||
All currently queued I/O operations shall be completed as if by a call
|
||||
to fsync(); that is, as defined for synchronized I/O file
|
||||
integrity completion. If the
|
||||
operation queued by io_prep_fsync() fails, then, as for fsync(),
|
||||
outstanding I/O operations are not guaranteed to have
|
||||
been completed.
|
||||
|
||||
If io_prep_fsync() succeeds, then it is only the I/O that was queued
|
||||
at the time of the call to io_submit() that is guaranteed to be
|
||||
forced to the relevant completion state. The completion of
|
||||
subsequent I/O on the file descriptor is not guaranteed to be
|
||||
completed in a synchronized fashion.
|
||||
.PP
|
||||
This function returns immediately . To schedule the operation, the
|
||||
function
|
||||
.IR io_submit
|
||||
must be called.
|
||||
.PP
|
||||
Simultaneous asynchronous operations using the same iocb produce
|
||||
undefined results.
|
||||
.SH "RETURN VALUES"
|
||||
None
|
||||
.SH ERRORS
|
||||
None
|
||||
.SH "SEE ALSO"
|
||||
.BR io(3),
|
||||
.BR io_cancel(3),
|
||||
.BR io_fsync(3),
|
||||
.BR io_getevents(3),
|
||||
.BR io_prep_pread(3),
|
||||
.BR io_prep_pwrite(3),
|
||||
.BR io_queue_init(3),
|
||||
.BR io_queue_release(3),
|
||||
.BR io_queue_run(3),
|
||||
.BR io_queue_wait(3),
|
||||
.BR io_set_callback(3),
|
||||
.BR io_submit(3),
|
||||
.BR errno(3)
|
||||
@@ -0,0 +1,79 @@
|
||||
./" static inline void io_prep_pread(struct iocb *iocb, int fd, void *buf, size_t count, long long offset)
|
||||
./" {
|
||||
./" memset(iocb, 0, sizeof(*iocb));
|
||||
./" iocb->aio_fildes = fd;
|
||||
./" iocb->aio_lio_opcode = IO_CMD_PREAD;
|
||||
./" iocb->aio_reqprio = 0;
|
||||
./" iocb->u.c.buf = buf;
|
||||
./" iocb->u.c.nbytes = count;
|
||||
./" iocb->u.c.offset = offset;
|
||||
./" }
|
||||
.TH io_prep_pread 3 2002-09-12 "Linux 2.4" Linux AIO"
|
||||
.SH NAME
|
||||
io_prep_pread \- Set up asynchronous read
|
||||
.SH SYNOPSYS
|
||||
.nf
|
||||
.B #include <errno.h>
|
||||
.sp
|
||||
.br
|
||||
.B #include <libaio.h>
|
||||
.br
|
||||
.sp
|
||||
.BI "inline void io_prep_pread(struct iocb *iocb, int fd, void *buf, size_t count, long long offset)
|
||||
"
|
||||
.sp
|
||||
struct iocb {
|
||||
void *data;
|
||||
unsigned key;
|
||||
short aio_lio_opcode;
|
||||
short aio_reqprio;
|
||||
int aio_fildes;
|
||||
};
|
||||
.fi
|
||||
.SH DESCRIPTION
|
||||
.IR io_prep_pread
|
||||
is an inline convenience function designed to facilitate the initialization of
|
||||
the iocb for an asynchronous read operation.
|
||||
|
||||
The first
|
||||
.TP
|
||||
.IR "iocb->u.c.nbytes = count"
|
||||
bytes of the file for which
|
||||
.TP
|
||||
.IR "iocb->aio_fildes = fd"
|
||||
is a descriptor are written to the buffer
|
||||
starting at
|
||||
.TP
|
||||
.IR "iocb->u.c.buf = buf"
|
||||
.
|
||||
.br
|
||||
Reading starts at the absolute position
|
||||
.TP
|
||||
.IR "ioc->u.c.offset = offset"
|
||||
in the file.
|
||||
.PP
|
||||
This function returns immediately . To schedule the operation, the
|
||||
function
|
||||
.IR io_submit
|
||||
must be called.
|
||||
.PP
|
||||
Simultaneous asynchronous operations using the same iocb produce
|
||||
undefined results.
|
||||
.SH "RETURN VALUES"
|
||||
None
|
||||
.SH ERRORS
|
||||
None
|
||||
.SH "SEE ALSO"
|
||||
.BR io(3),
|
||||
.BR io_cancel(3),
|
||||
.BR io_fsync(3),
|
||||
.BR io_getevents(3),
|
||||
.BR io_prep_fsync(3),
|
||||
.BR io_prep_pwrite(3),
|
||||
.BR io_queue_init(3),
|
||||
.BR io_queue_release(3),
|
||||
.BR io_queue_run(3),
|
||||
.BR io_queue_wait(3),
|
||||
.BR io_set_callback(3),
|
||||
.BR io_submit(3),
|
||||
.BR errno(3)
|
||||
@@ -0,0 +1,77 @@
|
||||
./" static inline void io_prep_pwrite(struct iocb *iocb, int fd, void *buf, size_t count, long long offset)
|
||||
./" {
|
||||
./" memset(iocb, 0, sizeof(*iocb));
|
||||
./" iocb->aio_fildes = fd;
|
||||
./" iocb->aio_lio_opcode = IO_CMD_PWRITE;
|
||||
./" iocb->aio_reqprio = 0;
|
||||
./" iocb->u.c.buf = buf;
|
||||
./" iocb->u.c.nbytes = count;
|
||||
./" iocb->u.c.offset = offset;
|
||||
./" }
|
||||
.TH io_prep_pwrite 3 2002-09-12 "Linux 2.4" Linux AIO"
|
||||
.SH NAME
|
||||
io_prep_pwrite \- Set up iocb for asynchronous writes
|
||||
.SH SYNOPSYS
|
||||
.nf
|
||||
.B #include <errno.h>
|
||||
.br
|
||||
.sp
|
||||
.B #include <libaio.h>
|
||||
.br
|
||||
.sp
|
||||
.BI "inline void io_prep_pwrite(struct iocb *iocb, int fd, void *buf, size_t count, long long offset)
|
||||
"
|
||||
.sp
|
||||
struct iocb {
|
||||
void *data;
|
||||
unsigned key;
|
||||
short aio_lio_opcode;
|
||||
short aio_reqprio;
|
||||
int aio_fildes;
|
||||
};
|
||||
.fi
|
||||
.SH DESCRIPTION
|
||||
io_prep_write is a convenicence function for setting up parallel writes.
|
||||
|
||||
The first
|
||||
.TP
|
||||
.IR "iocb->u.c.nbytes = count"
|
||||
bytes of the file for which
|
||||
.TP
|
||||
.IR "iocb->aio_fildes = fd"
|
||||
is a descriptor are written from the buffer
|
||||
starting at
|
||||
.TP
|
||||
.IR "iocb->u.c.buf = buf"
|
||||
.
|
||||
.br
|
||||
Writing starts at the absolute position
|
||||
.TP
|
||||
.IR "ioc->u.c.offset = offset"
|
||||
in the file.
|
||||
.PP
|
||||
This function returns immediately . To schedule the operation, the
|
||||
function
|
||||
.IR io_submit
|
||||
must be called.
|
||||
.PP
|
||||
Simultaneous asynchronous operations using the same iocb produce
|
||||
undefined results.
|
||||
.SH "RETURN VALUES"
|
||||
None
|
||||
.SH ERRORS
|
||||
None
|
||||
.SH "SEE ALSO"
|
||||
.BR io(3),
|
||||
.BR io_cancel(3),
|
||||
.BR io_fsync(3),
|
||||
.BR io_getevents(3),
|
||||
.BR io_prep_fsync(3),
|
||||
.BR io_prep_pread(3),
|
||||
.BR io_queue_init(3),
|
||||
.BR io_queue_release(3),
|
||||
.BR io_queue_run(3),
|
||||
.BR io_queue_wait(3),
|
||||
.BR io_set_callback(3),
|
||||
.BR io_submit(3),
|
||||
.BR errno(3)
|
||||
@@ -0,0 +1,63 @@
|
||||
.TH io_queue_init 2 2002-09-03 "Linux 2.4" "Linux AIO"
|
||||
.SH NAME
|
||||
io_queue_init \- Initialize asynchronous io state machine
|
||||
|
||||
.SH SYNOPSIS
|
||||
.nf
|
||||
.B #include <errno.h>
|
||||
.br
|
||||
.sp
|
||||
.B #include <libaio.h>
|
||||
.br
|
||||
.sp
|
||||
.BI "int io_queue_init(int maxevents, io_context_t *ctx );"
|
||||
.sp
|
||||
.fi
|
||||
.SH DESCRIPTION
|
||||
.B io_queue_init
|
||||
Attempts to create an aio context capable of receiving at least
|
||||
.IR maxevents
|
||||
events.
|
||||
.IR ctx
|
||||
must point to an aio context that already exists and must be initialized
|
||||
to
|
||||
.IR 0
|
||||
before the call.
|
||||
If the operation is successful, *cxtp is filled with the resulting handle.
|
||||
.SH "RETURN VALUES"
|
||||
On success,
|
||||
.B io_queue_init
|
||||
returns 0. Otherwise, -error is return, where
|
||||
error is one of the Exxx values defined in the Errors section.
|
||||
.SH ERRORS
|
||||
.TP
|
||||
.B EFAULT
|
||||
.I iocbs
|
||||
referenced data outside of the program's accessible address space.
|
||||
.TP
|
||||
.B EINVAL
|
||||
.I maxevents
|
||||
is <= 0 or
|
||||
.IR ctx
|
||||
is an invalid memory locattion.
|
||||
.TP
|
||||
.B ENOSYS
|
||||
Not implemented
|
||||
.TP
|
||||
.B EAGAIN
|
||||
.IR "maxevents > max_aio_reqs"
|
||||
where max_aio_reqs is a tunable value.
|
||||
.SH "SEE ALSO"
|
||||
.BR io(3),
|
||||
.BR io_cancel(3),
|
||||
.BR io_fsync(3),
|
||||
.BR io_getevents(3),
|
||||
.BR io_prep_fsync(3),
|
||||
.BR io_prep_pread(3),
|
||||
.BR io_prep_pwrite(3),
|
||||
.BR io_queue_release(3),
|
||||
.BR io_queue_run(3),
|
||||
.BR io_queue_wait(3),
|
||||
.BR io_set_callback(3),
|
||||
.BR io_submit(3),
|
||||
.BR errno(3)
|
||||
@@ -0,0 +1,48 @@
|
||||
.TH io_queue_release 2 2002-09-03 "Linux 2.4" "Linux AIO"
|
||||
.SH NAME
|
||||
io_queue_release \- Release the context associated with the userspace handle
|
||||
.SH SYNOPSIS
|
||||
.nf
|
||||
.B #include <errno.h>
|
||||
.br
|
||||
.B #include <libaio.h>
|
||||
.br
|
||||
.sp
|
||||
.BI "int io_queue_release(io_context_t ctx)"
|
||||
.sp
|
||||
.SH DESCRIPTION
|
||||
.B io_queue_release
|
||||
destroys the context associated with the userspace handle. May cancel any outstanding
|
||||
AIOs and block on completion.
|
||||
|
||||
.B cts.
|
||||
.SH "RETURN VALUES"
|
||||
On success,
|
||||
.B io_queue_release
|
||||
returns 0. Otherwise, -error is return, where
|
||||
error is one of the Exxx values defined in the Errors section.
|
||||
.SH ERRORS
|
||||
.TP
|
||||
.B EINVAL
|
||||
.I ctx
|
||||
refers to an unitialized aio context, the iocb pointed to by
|
||||
.I iocbs
|
||||
contains an improperly initialized iocb,
|
||||
.TP
|
||||
.B ENOSYS
|
||||
Not implemented
|
||||
.SH "SEE ALSO"
|
||||
.BR io(3),
|
||||
.BR io_cancel(3),
|
||||
.BR io_fsync(3),
|
||||
.BR io_getevents(3),
|
||||
.BR io_prep_fsync(3),
|
||||
.BR io_prep_pread(3),
|
||||
.BR io_prep_pwrite(3),
|
||||
.BR io_queue_init(3),
|
||||
.BR io_queue_run(3),
|
||||
.BR io_queue_wait(3),
|
||||
.BR io_set_callback(3),
|
||||
.BR io_submit(3),
|
||||
.BR errno(3)
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
.TH io_queue_run 2 2002-09-03 "Linux 2.4" "Linux AIO"
|
||||
.SH NAME
|
||||
io_queue_run \- Handle completed io requests
|
||||
.SH SYNOPSIS
|
||||
.nf
|
||||
.B #include <errno.h>
|
||||
.br
|
||||
.sp
|
||||
.B #include <libaio.h>
|
||||
.br
|
||||
.sp
|
||||
.BI "int io_queue_run(io_context_t ctx );"
|
||||
.sp
|
||||
.fi
|
||||
.SH DESCRIPTION
|
||||
.B io_queue_run
|
||||
Attempts to read all the events events from
|
||||
the completion queue for the aio_context specified by ctx_id.
|
||||
.SH "RETURN VALUES"
|
||||
May return
|
||||
0 if no events are available.
|
||||
Will fail with -ENOSYS if not implemented.
|
||||
.SH ERRORS
|
||||
.TP
|
||||
.B EFAULT
|
||||
.I iocbs
|
||||
referenced data outside of the program's accessible address space.
|
||||
.TP
|
||||
.B EINVAL
|
||||
.I ctx
|
||||
refers to an unitialized aio context, the iocb pointed to by
|
||||
.I iocbs
|
||||
contains an improperly initialized iocb,
|
||||
.TP
|
||||
.B ENOSYS
|
||||
Not implemented
|
||||
.SH "SEE ALSO"
|
||||
.BR io(3),
|
||||
.BR io_cancel(3),
|
||||
.BR io_fsync(3),
|
||||
.BR io_getevents(3),
|
||||
.BR io_prep_fsync(3),
|
||||
.BR io_prep_pread(3),
|
||||
.BR io_prep_pwrite(3),
|
||||
.BR io_queue_init(3),
|
||||
.BR io_queue_release(3),
|
||||
.BR io_queue_wait(3),
|
||||
.BR io_set_callback(3),
|
||||
.BR io_submit(3),
|
||||
.BR errno(3)
|
||||
@@ -0,0 +1,56 @@
|
||||
.TH io_queue_wait 2 2002-09-03 "Linux 2.4" "Linux AIO"
|
||||
.SH NAME
|
||||
io_queue_wait \- Wait for io requests to complete
|
||||
.SH SYNOPSIS
|
||||
.nf
|
||||
.B #include <errno.h>
|
||||
.br
|
||||
.sp
|
||||
.B #include <libaio.h>
|
||||
.br
|
||||
.sp
|
||||
.BI "int io_queue_wait(io_context_t ctx, const struct timespec *timeout);"
|
||||
.fi
|
||||
.SH DESCRIPTION
|
||||
Attempts to read an event from
|
||||
the completion queue for the aio_context specified by ctx_id.
|
||||
.SH "RETURN VALUES"
|
||||
May return
|
||||
0 if no events are available and the timeout specified
|
||||
by when has elapsed, where when == NULL specifies an infinite
|
||||
timeout. Note that the timeout pointed to by when is relative and
|
||||
will be updated if not NULL and the operation blocks. Will fail
|
||||
with -ENOSYS if not implemented.
|
||||
.SH "RETURN VALUES"
|
||||
On success,
|
||||
.B io_queue_wait
|
||||
returns 0. Otherwise, -error is return, where
|
||||
error is one of the Exxx values defined in the Errors section.
|
||||
.SH ERRORS
|
||||
.TP
|
||||
.B EFAULT
|
||||
.I iocbs
|
||||
referenced data outside of the program's accessible address space.
|
||||
.TP
|
||||
.B EINVAL
|
||||
.I ctx
|
||||
refers to an unitialized aio context, the iocb pointed to by
|
||||
.I iocbs
|
||||
contains an improperly initialized iocb,
|
||||
.TP
|
||||
.B ENOSYS
|
||||
Not implemented
|
||||
.SH "SEE ALSO"
|
||||
.BR io(3),
|
||||
.BR io_cancel(3),
|
||||
.BR io_fsync(3),
|
||||
.BR io_getevents(3),
|
||||
.BR io_prep_fsync(3),
|
||||
.BR io_prep_pread(3),
|
||||
.BR io_prep_pwrite(3),
|
||||
.BR io_queue_init(3),
|
||||
.BR io_queue_release(3),
|
||||
.BR io_queue_run(3),
|
||||
.BR io_set_callback(3),
|
||||
.BR io_submit(3),
|
||||
.BR errno(3)
|
||||
@@ -0,0 +1,44 @@
|
||||
./"static inline void io_set_callback(struct iocb *iocb, io_callback_t cb)
|
||||
.TH io_set_callback 3 2002-09-12 "Linux 2.4" Linux AIO"
|
||||
.SH NAME
|
||||
io_set_callback \- Set up io completion callback function
|
||||
.SH SYNOPSYS
|
||||
.nf
|
||||
.B #include <errno.h>
|
||||
.br
|
||||
.sp
|
||||
.B #include <libaio.h>
|
||||
.br
|
||||
.sp
|
||||
.BI "static inline void io_set_callback(struct iocb *iocb, io_callback_t cb)"
|
||||
.sp
|
||||
struct iocb {
|
||||
void *data;
|
||||
unsigned key;
|
||||
short aio_lio_opcode;
|
||||
short aio_reqprio;
|
||||
int aio_fildes;
|
||||
};
|
||||
.sp
|
||||
typedef void (*io_callback_t)(io_context_t ctx, struct iocb *iocb, long res, long res2);
|
||||
.sp
|
||||
.fi
|
||||
.SH DESCRIPTION
|
||||
The callback is not done if the caller uses raw events from
|
||||
io_getevents, only with the library helpers
|
||||
.SH "RETURN VALUES"
|
||||
.SH ERRORS
|
||||
.SH "SEE ALSO"
|
||||
.BR io(3),
|
||||
.BR io_cancel(3),
|
||||
.BR io_fsync(3),
|
||||
.BR io_getevents(3),
|
||||
.BR io_prep_fsync(3),
|
||||
.BR io_prep_pread(3),
|
||||
.BR io_prep_pwrite(3),
|
||||
.BR io_queue_init(3),
|
||||
.BR io_queue_release(3),
|
||||
.BR io_queue_run(3),
|
||||
.BR io_queue_wait(3),
|
||||
.BR io_submit(3),
|
||||
.BR errno(3)
|
||||
@@ -0,0 +1,15 @@
|
||||
./"/* sys_io_setup:
|
||||
./" * Create an aio_context capable of receiving at least nr_events.
|
||||
./" * ctxp must not point to an aio_context that already exists, and
|
||||
./" * must be initialized to 0 prior to the call. On successful
|
||||
./" * creation of the aio_context, *ctxp is filled in with the resulting
|
||||
./" * handle. May fail with -EINVAL if *ctxp is not initialized,
|
||||
./" * if the specified nr_events exceeds internal limits. May fail
|
||||
./" * with -EAGAIN if the specified nr_events exceeds the user's limit
|
||||
./" * of available events. May fail with -ENOMEM if insufficient kernel
|
||||
./" * resources are available. May fail with -EFAULT if an invalid
|
||||
./" * pointer is passed for ctxp. Will fail with -ENOSYS if not
|
||||
./" * implemented.
|
||||
./" */
|
||||
./" -- note: libaio is actually providing io_queue_init and io_queue_grow
|
||||
./" as separate functions. For now io_setup is the same as io_queue_grow.
|
||||
Loaded 100 of 133 files, more files were not shown because too many files have changed in this diff.
Show more