Files
xen/tools/blktap/lib/xenbus.c
T
Keir Fraser 962649c369 blktap: cleanup
Make functions w/o a prototype static and remove redundant
declaration of xs_fire_next_watch().

Signed-off-by: Christoph Egger <Christoph.Egger@amd.com>
2008-07-11 15:36:40 +01:00

435 lines
10 KiB
C

/*
* xenbus.c
*
* xenbus interface to the blocktap.
*
* this handles the top-half of integration with block devices through the
* store -- the tap driver negotiates the device channel etc, while the
* userland tap client needs to sort out the disk parameters etc.
*
* (c) 2005 Andrew Warfield and Julian Chesterfield
*
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation; or, when distributed
* separately from the Linux kernel or incorporated into other
* software packages, subject to the following license:
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this source file (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy, modify,
* merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <err.h>
#include <stdarg.h>
#include <errno.h>
#include <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. */
deverr = xs_gather(h, bepath, "mode", NULL, &path, NULL);
if (deverr) {
DPRINTF("ERROR: could not find read/write mode\n");
goto fail;
} else if (path[0] == 'r')
be->readonly = 1;
if (be->blkif == NULL) {
/* Front end dir is a number, which is used as the handle. */
p = strrchr(be->frontpath, '/') + 1;
handle = strtoul(p, NULL, 0);
be->blkif = alloc_blkif(be->frontend_id);
if (be->blkif == NULL)
goto fail;
be->blkif->be_id = get_be_id(bepath);
/* Insert device specific info, */
blk = malloc(sizeof(blkif_info_t));
if (!blk) {
DPRINTF("Out of memory - blkif_info_t\n");
goto fail;
}
er = xs_gather(h, bepath, "params", NULL, &blk->params, NULL);
if (er)
goto fail;
be->blkif->info = blk;
if (deverr) {
/*Dev number was not available, try to set manually*/
pdev = convert_dev_name_to_num(blk->params);
be->pdev = pdev;
}
er = blkif_init(be->blkif, handle, be->pdev, be->readonly);
if (er != 0) {
DPRINTF("Unable to open device %s\n",blk->params);
goto fail;
}
DPRINTF("[BECHG]: ADDED A NEW BLKIF (%s)\n", bepath);
}
/* Supply the information about the device to xenstore */
er = xs_printf(h, be->backpath, "sectors", "%llu",
be->blkif->ops->get_size(be->blkif));
if (er == 0) {
DPRINTF("ERROR: Failed writing sectors");
goto fail;
}
er = xs_printf(h, be->backpath, "sector-size", "%lu",
be->blkif->ops->get_secsize(be->blkif));
if (er == 0) {
DPRINTF("ERROR: Failed writing sector-size");
goto fail;
}
er = xs_printf(h, be->backpath, "info", "%u",
be->blkif->ops->get_info(be->blkif));
if (er == 0) {
DPRINTF("ERROR: Failed writing info");
goto fail;
}
be->blkif->state = CONNECTED;
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);
}
/**
*We set a general watch on the backend vbd directory
*ueblktap_probe is called for every update
*Our job is to monitor for new entries. As they
*are created, we initalise the state and attach a disk.
*/
static int add_blockdevice_probe_watch(struct xs_handle *h, const char *domid)
{
char *path;
struct xenbus_watch *vbd_watch;
if (asprintf(&path, "/local/domain/%s/backend/tap", domid) == -1)
return -ENOMEM;
vbd_watch = (struct xenbus_watch *)malloc(sizeof(struct xenbus_watch));
if (!vbd_watch) {
DPRINTF("ERROR: unable to malloc vbd_watch [%s]\n", path);
return -EINVAL;
}
vbd_watch->node = path;
vbd_watch->callback = ueblktap_probe;
if (register_xenbus_watch(h, vbd_watch) != 0) {
DPRINTF("ERROR: adding vbd probe watch %s\n", path);
return -EINVAL;
}
return 0;
}
/* Asynch callback to check for /local/domain/<DOMID>/name */
static void check_dom(struct xs_handle *h, struct xenbus_watch *w,
const char *bepath_im)
{
char *domid;
domid = get_dom_domid(h);
if (domid == NULL)
return;
add_blockdevice_probe_watch(h, domid);
free(domid);
unregister_xenbus_watch(h, w);
}
/* We must wait for xend to register /local/domain/<DOMID> */
static int watch_for_domid(struct xs_handle *h)
{
struct xenbus_watch *domid_watch;
char *path = NULL;
if (asprintf(&path, "/local/domain") == -1)
return -ENOMEM;
domid_watch = malloc(sizeof(struct xenbus_watch));
if (domid_watch == NULL) {
DPRINTF("ERROR: unable to malloc domid_watch [%s]\n", path);
return -EINVAL;
}
domid_watch->node = path;
domid_watch->callback = check_dom;
if (register_xenbus_watch(h, domid_watch) != 0) {
DPRINTF("ERROR: adding vbd probe watch %s\n", path);
return -EINVAL;
}
DPRINTF("Set async watch for /local/domain\n");
return 0;
}
int setup_probe_watch(struct xs_handle *h)
{
char *domid;
int ret;
domid = get_dom_domid(h);
if (domid == NULL)
return watch_for_domid(h);
ret = add_blockdevice_probe_watch(h, domid);
free(domid);
return ret;
}