Files
xen/tools/libs/light/libxl_linux.c
T
Jason Andryuk 6d7d8d2b3e libxl: Add support for blktap vbd3
This patch re-introduces blktap support to libxl.  Unlike earlier
versions, it does not link against any blktap library.  libxl changes
are needed to write to the vbd3 backend XenStore nodes.

blktap has three components.  tapdisk is a daemon implementing the disk
IO, NBD (Network Block Device), and Xen PV interfaces.  tap-ctl is a
tool to control tapdisks - creating, starting, stopping and freeing.
tapback manages the XenStore operations and instructs tapdisk to
connect.

It is notable that tapdisk performs the grant and event channel ops, but
doesn't interact with XenStore.  tapback performs XenStore operations
and notifies tapdisks of values and changes.

The flow is: libxl writes to the "vbd3" XenStore nodes and runs the
block-tap script.  The block-tap script runs tap-ctl to create a tapdisk
instance as the physical device.  tapback then sees the tapdisk and
instructs the tapdisk to connect up the PV blkif interface.

This is expected to work without the kernel blktap driver, so the
block-tap script is modified accordingly to write the UNIX NBD path.

backendtype=tap was not fully removed previously, but it would never
succeed since it would hit the hardcoded error in disk_try_backend().
It is reused now.

An example command to attach a vhd:
xl block-attach vm 'vdev=xvdf,backendtype=tap,format=vhd,target=/srv/target.vhd'

Format raw also works to run an "aio:" tapdisk.

Signed-off-by: Jason Andryuk <jandryuk@gmail.com>
Reviewed-by: Anthony PERARD <anthony.perard@citrix.com>
2024-02-13 09:28:58 +01:00

372 lines
10 KiB
C

/*
* Copyright (C) 2011
* Author Roger Pau Monne <roger.pau@entel.upc.edu>
*
* This program 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; version 2.1 only. with the special
* exception on linking described in file LICENSE.
*
* This program 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.
*/
#include "libxl_osdeps.h" /* must come before any other headers */
#include <sys/resource.h>
#include "libxl_internal.h"
/* Workarounds for Linux-specific lacks can go here: */
#ifndef CLONE_NEWIPC /* Available as of Linux 2.6.19 / glibc 2.8 */
# define CLONE_NEWIPC 0x08000000
#endif
int libxl__try_phy_backend(mode_t st_mode)
{
if (S_ISBLK(st_mode) || S_ISREG(st_mode)) {
return 1;
}
return 0;
}
char *libxl__devid_to_localdev(libxl__gc *gc, int devid)
{
return libxl__devid_to_vdev(gc, devid);
}
/* Hotplug scripts helpers */
static char **get_hotplug_env(libxl__gc *gc,
char *script, libxl__device *dev)
{
const char *type = libxl__device_kind_to_string(dev->backend_kind);
char *be_path = libxl__device_backend_path(gc, dev);
char **env;
int nr = 0;
const int arraysize = 15;
GCNEW_ARRAY(env, arraysize);
env[nr++] = "script";
env[nr++] = script;
env[nr++] = "XENBUS_TYPE";
env[nr++] = (char *) type;
env[nr++] = "XENBUS_PATH";
env[nr++] = GCSPRINTF("backend/%s/%u/%d", type, dev->domid, dev->devid);
env[nr++] = "XENBUS_BASE_PATH";
env[nr++] = "backend";
if (dev->backend_kind == LIBXL__DEVICE_KIND_VIF) {
libxl_nic_type nictype;
char *gatewaydev;
gatewaydev = libxl__xs_read(gc, XBT_NULL,
GCSPRINTF("%s/%s", be_path, "gatewaydev"));
env[nr++] = "netdev";
env[nr++] = gatewaydev ? : "";
if (libxl__nic_type(gc, dev, &nictype)) {
LOGD(ERROR, dev->domid, "unable to get nictype");
return NULL;
}
switch (nictype) {
case LIBXL_NIC_TYPE_VIF_IOEMU:
env[nr++] = "INTERFACE";
env[nr++] = (char *) libxl__device_nic_devname(gc, dev->domid,
dev->devid,
LIBXL_NIC_TYPE_VIF_IOEMU);
/*
* We need to fall through because for PV_IOEMU nic types we need
* to execute both the vif and the tap hotplug script, and we
* don't know which one we are executing in this call, so provide
* both env variables.
*/
case LIBXL_NIC_TYPE_VIF:
env[nr++] = "vif";
env[nr++] = (char *) libxl__device_nic_devname(gc, dev->domid,
dev->devid,
LIBXL_NIC_TYPE_VIF);
break;
default:
return NULL;
}
}
env[nr++] = NULL;
assert(nr <= arraysize);
return env;
}
/* Hotplug scripts caller functions */
static int libxl__hotplug_nic(libxl__gc *gc, libxl__device *dev,
char ***args, char ***env,
libxl__device_action action, int num_exec)
{
char *be_path = libxl__device_backend_path(gc, dev);
char *script;
int nr = 0, rc = 0;
libxl_nic_type nictype;
script = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/%s", be_path,
"script"));
if (!script) {
LOGED(ERROR, dev->domid,
"unable to read script from %s", be_path);
rc = ERROR_FAIL;
goto out;
}
rc = libxl__nic_type(gc, dev, &nictype);
if (rc) {
LOGD(ERROR, dev->domid, "error when fetching nic type");
rc = ERROR_FAIL;
goto out;
}
if (nictype == LIBXL_NIC_TYPE_VIF && num_exec != 0) {
rc = 0;
goto out;
}
*env = get_hotplug_env(gc, script, dev);
if (!*env) {
rc = ERROR_FAIL;
goto out;
}
const int arraysize = 4;
GCNEW_ARRAY(*args, arraysize);
(*args)[nr++] = script;
if (nictype == LIBXL_NIC_TYPE_VIF_IOEMU && num_exec) {
(*args)[nr++] = (char *) libxl__device_action_to_string(action);
(*args)[nr++] = "type_if=tap";
(*args)[nr++] = NULL;
} else {
(*args)[nr++] = action == LIBXL__DEVICE_ACTION_ADD ? "online" :
"offline";
(*args)[nr++] = "type_if=vif";
(*args)[nr++] = NULL;
}
assert(nr == arraysize);
rc = 1;
out:
return rc;
}
static int libxl__hotplug_disk(libxl__gc *gc, libxl__device *dev,
char ***args, char ***env,
libxl__device_action action)
{
char *be_path = libxl__device_backend_path(gc, dev);
char *script;
int nr = 0, rc = 0;
script = libxl__xs_read(gc, XBT_NULL,
GCSPRINTF("%s/%s", be_path, "script"));
if (!script) {
LOGEVD(ERROR, errno, dev->domid,
"unable to read script from %s", be_path);
rc = ERROR_FAIL;
goto error;
}
*env = get_hotplug_env(gc, script, dev);
if (!*env) {
LOGD(ERROR, dev->domid, "Failed to get hotplug environment");
rc = ERROR_FAIL;
goto error;
}
const int arraysize = 3;
GCNEW_ARRAY(*args, arraysize);
(*args)[nr++] = script;
(*args)[nr++] = (char *) libxl__device_action_to_string(action);
(*args)[nr++] = NULL;
assert(nr == arraysize);
LOGD(DEBUG, dev->domid, "Args and environment ready");
rc = 1;
error:
return rc;
}
int libxl__get_hotplug_script_info(libxl__gc *gc, libxl__device *dev,
char ***args, char ***env,
libxl__device_action action,
int num_exec)
{
int rc;
switch (dev->backend_kind) {
case LIBXL__DEVICE_KIND_VBD:
case LIBXL__DEVICE_KIND_VBD3:
if (num_exec != 0) {
LOGD(DEBUG, dev->domid,
"num_exec %d, not running hotplug scripts", num_exec);
rc = 0;
goto out;
}
rc = libxl__hotplug_disk(gc, dev, args, env, action);
break;
case LIBXL__DEVICE_KIND_VIF:
/*
* If domain has a stubdom we don't have to execute hotplug scripts
* for emulated interfaces
*/
if ((num_exec > 1) ||
(libxl_get_stubdom_id(CTX, dev->domid) && num_exec)) {
LOGD(DEBUG, dev->domid,
"num_exec %d, not running hotplug scripts", num_exec);
rc = 0;
goto out;
}
rc = libxl__hotplug_nic(gc, dev, args, env, action, num_exec);
break;
default:
/* No need to execute any hotplug scripts */
LOGD(DEBUG, dev->domid, "backend_kind %s, no need to execute scripts",
libxl__device_kind_to_string(dev->backend_kind));
rc = 0;
break;
}
out:
return rc;
}
int libxl__pci_numdevs(libxl__gc *gc)
{
DIR *dir;
struct dirent *entry;
int num_devs = 0;
dir = opendir("/sys/bus/pci/devices");
if (!dir) {
LOGE(ERROR, "Cannot open /sys/bus/pci/devices");
return ERROR_FAIL;
}
while ((entry = readdir(dir))) {
if (entry->d_name[0] == '.')
continue;
num_devs++;
}
closedir(dir);
return num_devs;
}
int libxl__pci_topology_init(libxl__gc *gc,
physdev_pci_device_t *devs,
int num_devs)
{
DIR *dir;
struct dirent *entry;
int i, err = 0;
dir = opendir("/sys/bus/pci/devices");
if (!dir) {
LOGE(ERROR, "Cannot open /sys/bus/pci/devices");
return ERROR_FAIL;
}
i = 0;
while ((entry = readdir(dir))) {
unsigned int dom, bus, dev, func;
if (entry->d_name[0] == '.')
continue;
if (i == num_devs) {
LOG(ERROR, "Too many devices");
err = ERROR_FAIL;
errno = ENOSPC;
goto out;
}
if (sscanf(entry->d_name, "%x:%x:%x.%d", &dom, &bus, &dev, &func) < 4) {
LOGE(ERROR, "Error processing /sys/bus/pci/devices");
err = ERROR_FAIL;
goto out;
}
devs[i].seg = dom;
devs[i].bus = bus;
devs[i].devfn = ((dev & 0x1f) << 3) | (func & 7);
i++;
}
out:
closedir(dir);
return err;
}
static struct {
int resource;
rlim_t limit;
} rlimits[] = {
#define RLIMIT_ENTRY(r, l) \
{ .resource = r, .limit = l }
/* Big enough for log files, not big enough for a DoS */
RLIMIT_ENTRY(RLIMIT_FSIZE, 256*1024),
/* Shouldn't need any of these */
RLIMIT_ENTRY(RLIMIT_CORE, 0),
RLIMIT_ENTRY(RLIMIT_MSGQUEUE, 0),
RLIMIT_ENTRY(RLIMIT_LOCKS, 0),
RLIMIT_ENTRY(RLIMIT_MEMLOCK, 0),
/* End-of-list marker */
RLIMIT_ENTRY(RLIMIT_NLIMITS, 0),
#undef RLIMIT_ENTRY
};
int libxl__local_dm_preexec_restrict(libxl__gc *gc)
{
int r;
unsigned i;
/* Unshare mount and IPC namespaces. These are unused by QEMU. */
r = unshare(CLONE_NEWNS | CLONE_NEWIPC);
if (r) {
LOGE(ERROR, "libxl: unshare Mount and IPC namespace failed");
return ERROR_FAIL;
}
/* Set various "easy" rlimits */
for (i = 0; rlimits[i].resource != RLIMIT_NLIMITS; i++) {
struct rlimit rlim;
rlim.rlim_cur = rlim.rlim_max = rlimits[i].limit;
r = setrlimit(rlimits[i].resource, &rlim);
if (r < 0) {
LOGE(ERROR, "Setting rlimit %d to %llu failed\n",
rlimits[i].resource,
(unsigned long long)rlimits[i].limit);
return ERROR_FAIL;
}
}
return 0;
}
/*
* Local variables:
* mode: C
* c-basic-offset: 4
* indent-tabs-mode: nil
* End:
*/