blktap3: Introduce the tapback daemon (most of blkback in user-space).
PATCH v3 1/1 blktap3: Introduce the tapback daemon (most of blkback in user-space).
This patch series introduces the tapback daemon, the user space daemon that acts as a device's back-end, essentially most of blkback in user space. The daemon is responsible for coordinating the front-end and tapdisk. It creates tapdisk process as needed, instructs them to connect to/disconnect from the shared ring, and manages the state of the back-end.
The shared ring between the front-end and the tapdisk is provided by a piece of code that lives inside the tapdisk and will be introduced by the next patch series.
---
Changed since v1:
The series has been largely reorganised:
* Renamed the daemon from xenio to tapback.
* Improved description in patch 0.
* Merged structures and functions.
* Disaggregated functionality from the core daemon source file to smaller ones in order to facilitate the review process and improve maintenance.
Changed since v2:
* Added a new patch that ignores tapback binaries.
* For the rest of the patches, see the description in each patch.
PATCH v3 1/1 blktap3/tapback: Introduce core defines and structure definitions
---
Changed since v2:
* Removed BUG_ON macro.
* Fixed whitespace in tapback_backend_find_device.
* Clarified back-end name (xenio).
* Removed references to the "serial" thing.
* Remove unused member dev from struct vbd.
* Introduce prototype of function XenbusState2str.
tools/blktap3/tapback/tapback.h new file +244 -0
@@ -0,0 +1,244 @@ +/* +* Copyright (C) 2012 Citrix Ltd. +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* 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 General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, +* USA. +*/ + +#ifndef __TAPBACK_H__ +#define __TAPBACK_H__ + +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <assert.h> +#include <string.h> + +#include <xen/xen.h> +#include <xenstore.h> +#include <xen/io/xenbus.h> +#include <xen/event_channel.h> +#include <xen/grant_table.h> + +#include "tap-ctl.h" + +void tapback_log (int prio,const char *fmt, ...); +void (*tapback_vlog) (int prio,const char *fmt,va_list ap); + +#define DBG(_fmt, _args...) tapback_log(LOG_DEBUG, "%s:%d "_fmt, __FILE__, \ + __LINE__, ##_args) +#define INFO(_fmt, _args...) tapback_log(LOG_INFO, _fmt, ##_args) +#define WARN(_fmt, _args...) tapback_log(LOG_WARNING, "%s:%d "_fmt, __FILE__, \ + __LINE__, ##_args) + +#define WARN_ON (_cond, fmt, ...) \ + if (unlikely(_cond)) { \ + printf(fmt, ##__VA_ARGS__); \ + } + +/* +* Pre-defined XenStore path components used for running the XenBus protocol. +* +* To avoid confusion with blktap2, we'll use a new kind of device for libxl +* defining it in tools/libxl/libxl_types_internal.idl. This will be done by +* the patch that adds libxl support for blktap3. The temporary back-end name +* is "xenio". Once blktap3 becomes the default back-end, its back-end name +* should be "vbd" and "xenio" will be removed. TODO When that patch is sent, +* use the definition from there instead of hard-coding it here. +*/ +#define XENSTORE_BACKEND "backend" +#define BLKTAP3_BACKEND_NAME "xenio" +#define BLKTAP3_BACKEND_PATH XENSTORE_BACKEND"/"BLKTAP3_BACKEND_NAME +#define BLKTAP3_BACKEND_TOKEN XENSTORE_BACKEND"-"BLKTAP3_BACKEND_NAME +#define BLKTAP3_FRONTEND_TOKEN "otherend-state" + +/* +* TODO Put the rest of the front-end nodes defined in blkif.h here and group +* them. e.g. FRONTEND_NODE_xxx. +*/ +#define RING_REF "ring-ref" +#define FEAT_PERSIST "feature-persistent" + +/** +* A Virtual Block Device (VBD), represents a block device in a guest VM. +* Contains all relevant information. +*/ +typedef struct vbd { + +/** +* Device name, as retrieved from XenStore at probe-time. +*/ +char *name; + +/** +* The device ID. Same as vbd.name, we keep it around because the tapdisk +* control libreary wants it as an int and not as a string. +*/ +int devid; + +/** +* For linked lists. +*/ +TAILQ_ENTRY (vbd) backend_entry; + +/** +* The domain ID this VBD belongs to. +*/ +domid_t domid; + +/** +* The root directory in XenStore for this VBD. This is where all +* directories and key/value pairs related to this VBD are stored. +*/ +char *frontend_path; + +/** +* XenStore path to the VBD's state. This is just +* vbd.frontend_path + "/state", we keep it around so we don't have to +* allocate/free memory all the time. +*/ +char *frontend_state_path; + +/** +* Indicates whether the tapdisk is connected to the shared ring. +*/ +bool connected; + +/** +* Descriptor of the tapdisk process serving this virtual block device. We +* need this until the end of the VBD's lifetime in order to disconnect +* the tapdisk from the shared ring. +*/ +tap_list_t tap; + +/* +* XXX We keep sector_size, sectors, and info because we need to +* communicate them to the front-end not only when the front-end goes to +* XenbusStateInitialised, but to XenbusStateConnected as well. +*/ + +/** +* Sector size, supplied by the tapdisk, communicated to blkfront. +*/ +unsigned int sector_size; + +/** +* Number of sectors, supplied by the tapdisk, communicated to blkfront. +*/ +unsigned long long sectors; + +/** +* VDISK_???, defined in include/xen/interface/io/blkif.h. +*/ +unsigned int info; + +}vbd_t ; + +TAILQ_HEAD (tqh_vbd, vbd); + +/** +* The collection of all necessary handles and descriptors. +*/ +struct _blktap3_daemon { + +/** +* A handle to XenStore. +*/ +struct xs_handle *xs; + +/** +* For executing transacted operations on XenStore. +*/ +xs_transaction_t xst; + +/** +* The list of virtual block devices. +* +* TODO We sometimes have to scan the whole list to find the device/domain +* we're interested in, should we optimize this? E.g. use a hash table +* for O(1) access? +* TODO Replace with a hash table (hcreate etc.)? +*/ +struct tqh_vbd devices; + +/** +* TODO From xen/include/public/io/blkif.h: "The maximum supported size of +* the request ring buffer" +*/ +int max_ring_page_order; +}; + +extern struct _blktap3_daemon blktap3_daemon; + +#define tapback_backend_for_each_device (_device, _next) \ + TAILQ_FOREACH_SAFE(_device, &blktap3_daemon.devices, backend_entry, _next) + +/** +* Iterates over all devices and returns the one for which the condition is +* true. +*/ +#define tapback_backend_find_device (_device, _cond) \ +do { \ + vbd_t *__next; \ + int found = 0; \ + tapback_backend_for_each_device(_device, __next) { \ + if (_cond) { \ + found = 1; \ + break; \ + } \ + } \ + if (!found) \ + _device = NULL; \ +} while (0) + +/** +* Act in response to a change in the front-end XenStore path. +* +* @param path the front-end's XenStore path that changed +* @returns 0 on success, an error code otherwise +* +* XXX Only called by tapback_read_watch +*/ +int +tapback_backend_handle_otherend_watch (const char *const path); + +/** +* Act in response to a change in the back-end directory in XenStore. +* +* If the path is "/backend" or "/backend/<backend name>", all devices are +* probed. Otherwise, the path should be +* "backend/<backend name>/<domid>/<device name>" +* (i.e. backend/<backend name>/1/51712), and in this case this specific device +* is probed. +* +* @param path the back-end's XenStore path that changed @returns 0 on success, +* an error code otherwise +* +* XXX Only called by tapback_read_watch. +*/ +int +tapback_backend_handle_backend_watch (char *const path); + +/** +* Converts XenbusState values to a printable string, e.g. XenbusStateConnected +* corresponds to "connected". +* +* @param xbs the XenbusState to convert +* @returns a printable string +*/ +char * +XenbusState2str (const XenbusState xbs); + +#endif /* __TAPBACK_H__ */
PATCH v3 1/1 blktap3/tapback: Introduces functionality required to access XenStore
In reply to Thanos MakatosThis patch introduces convenience functions that read/write values from/to XenStore.
---
Changed since v2:
* Introduced makefile to facilitate development.
* Removed functions (v)mprintf as (v)asprintf suffice.
* Ensure tapback_xs_vread (a) returns a NULL terminated string, and (b) the returned string doesn't contain NULL characters, apart from the NULL-terminating one.
* Removed unnecessary variable initialisations.
* Minor whitespace clean up.
* Fixed typo in tapback_xs_vread.
* function tapback_xs_vread: handle corner case where the value returned by xs_read is a zero-length string.
tools/blktap3/tapback/Makefile new file +31 -0
@@ -0,0 +1,31 @@ +XEN_ROOT := $(CURDIR)/../../../ +include $(XEN_ROOT)/tools/Rules.mk + +BLKTAP_ROOT := .. + +# -D_GNU_SOURCE is required by vasprintf. +override CFLAGS += \ + -I$(BLKTAP_ROOT)/include \ + -I$(BLKTAP_ROOT)/control \ + -D_GNU_SOURCE \ + $(CFLAGS_libxenstore) \ + $(CFLAGS_libxenctrl) \ + $(CFLAGS_xeninclude) \ + -Wall \ + -Wextra \ + -Werror + +# FIXME cause trouble +override CFLAGS += \ + -Wno-old-style-declaration \ + -Wno-sign-compare \ + -Wno-type-limits + +override LDFLAGS += \ + $(LDLIBS_libxenstore) \ + $(LDFLAGS_libxenctrl) + +clean: + rm -f *.o *.o.d .*.o.d + +.PHONY: clean install
tools/blktap3/tapback/xenstore.c new file +195 -0
195 lines not on this page · raw message
tools/blktap3/tapback/xenstore.h new file +95 -0
95 lines not on this page · raw message
PATCH v3 1/1 blktap3/tapback: Logging for the tapback daemon and libxenio
In reply to Thanos Makatos---
Changed since v2:
* minor whitespace clean up
tools/blktap3/tapback/log.c new file +33 -0
@@ -0,0 +1,33 @@ +/* +* Copyright (C) 2012 Citrix Ltd. +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* 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 General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, +* USA. +*/ + +#include "compiler.h" +#include <stdarg.h> +#include <syslog.h> + +void (*tapback_vlog) (int prio,const char *fmt,va_list ap) = vsyslog; + +__printf(2, 3) void +tapback_log (int prio,const char *fmt, ...) +{ +va_list ap; +va_start (ap, fmt); +tapback_vlog (prio, fmt, ap); +va_end (ap); +}
PATCH v3 1/1 blktap3/tapback: Introduce back-end XenStore path handler
In reply to Thanos MakatosThis patch introduces the handler executed when the back-end XenStore path gets modified. A back-end XenStore path is modified as a result of a device creation/removal request. The device is created/removed depending on whether its path exists/does not exist in XenStore.
Creating a device comprises creating the in-memory representation of it and adding it to the list of devices, locating the tapdisk designated to serve this VBD, and setting a XenStore watch to the front-end path of the newly-created device.
Deleting a device comprises removing that XenStore watch and deallocating its in-memory representation.
---
Changed since v2:
* Removed the "serial" thing.
* Function tapback_backend_handle_backend_watch doesn't always have to scan
* the entire back-end sub-tree, this is left as a future optimisation (relevant comment inside code updated).
* Replaced mprintf with asprintf.
* Check for failures in tapback_backend_scan.
* Minor code and whitespace clean up.
* The tapback daemon searches for a suitable tapdisk and creates one if none is found. It also kills it when the front-end disconnects.
tools/blktap3/tapback/backend.c new file +503 -0
@@ -0,0 +1,503 @@ +/* +* Copyright (C) 2012 Citrix Ltd. +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* 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 General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, +* USA. +* +* This file contains the handler executed when the back-end XenStore path gets +* modified. +*/ + +#include "tapback.h" +#include "xenstore.h" +#include "tap-ctl-info.h" + +/** +* Removes the XenStore watch from the front-end. +* +* @param device the VBD whose front-end XenStore path should stop being +* watched +*/ +static void +tapback_device_unwatch_frontend_state (vbd_t *const device) +{ +assert (device); + +if (device->frontend_state_path) +xs_unwatch (blktap3_daemon.xs, device->frontend_state_path, +BLKTAP3_FRONTEND_TOKEN ); + +free (device->frontend_state_path); + device->frontend_state_path =NULL ; +} + +/** +* Destroys and deallocates the back-end part of a VBD. +* +* @param device the VBD to destroy +*/ +static void +tapback_backend_destroy_device (vbd_t *const device) +{ +int err; + +assert (device); + +TAILQ_REMOVE (&blktap3_daemon.devices, device, backend_entry); + +tapback_device_unwatch_frontend_state (device); + +/* +* kill the tapdisk +*/ + err =tap_ctl_destroy (device->tap.pid, device->tap.minor,0 ,NULL ); +if (err) +WARN ("failed to destroy tapdisk %d/%d: %s (error ignored)\n" , + device->tap.pid, device->tap.minor,strerror (err)); + +free (device->frontend_path); +free (device->name); + +/* +* XXX device->bdev is expected to have been freed +*/ + +free (device); +} + +/** +* Retrieves the tapdisk designated to serve this device, storing this +* information in the supplied VBD handle. +* +* @param param <type>:/path/to/file +* @param tap output parameter that receives the tapdisk process information. +* The parameter is undefined when the function returns a non-zero value. +* @returns 0 if a suitable tapdisk is found, ESRCH if no suitable tapdisk is +* found, and an error code in case of error +* +* TODO rename function +* +* XXX Only called by blkback_probe. +*/ +static inline int +blkback_find_tapdisk (const char *params,tap_list_t *tap) +{ +struct tqh_tap_list list; +tap_list_t *_tap; +int err; +char *type; +char *path; +char *_params =NULL ; + +assert (params); +assert (tap); + + _params =strdup (params); +if (!_params) { +WARN ("failed to allocate memory\n" ); + err =ENOMEM ; + goto out; + } + + type =strtok (_params,":" ); +if (type) { + path =strtok (NULL ,":" ); + } +if (!type || !path) { +WARN ("malformed params \'%s\'\n" , params); + err =EINVAL ; + goto out; + } + + err =tap_ctl_list (&list); +if (err) { +WARN ("error listing tapdisks: %s\n" ,strerror (err)); + goto out; + } + + err =ESRCH ; +if (!TAILQ_EMPTY (&list)) { +tap_list_for_each_entry (_tap, &list) { +if (_tap->type && !strcmp (_tap->type, type) && _tap->path + && !strcmp (_tap->path, path)) { + err =0 ; +memcpy (tap, _tap,sizeof (tap)); +break ; + } + } +tap_ctl_list_free (&list); + }else +DBG ("no tapdisks\n" ); + +out: +free (_params); +return err; +} + +/** +* Creates a device and adds it to the list of devices. +* Initiates a XenStore watch to the front-end state. +* +* Creating the device implies initializing the handle and retrieving all the +* information of the tapdisk serving this VBD. +* +* @param domid the ID of the domain where the VBD is created +* @param name the name of the device +* @returns 0 on success, an error code otherwise +*/ +static inline int +tapback_backend_create_device (const domid_t domid,const char *const name) +{ +vbd_t *device =NULL ; +int err =0 ; +char *params =NULL ; + +assert (name); + +DBG ("creating device %d/%s\n" , domid, name); + +if (!(device =calloc (1 ,sizeof (*device)))) { +WARN ("error allocating memory\n" ); + err = -errno; + goto out; + } + + device->domid = domid; + +TAILQ_INSERT_TAIL (&blktap3_daemon.devices, device, backend_entry); + +if (!(device->name =strdup (name))) { + err = -errno; + goto out; + } + +/* +* Get the front-end path from XenStore. We need this to talk to the +* front-end. +*/ +if (!(device->frontend_path =tapback_device_read (device,"frontend" ))) { + err = errno; +WARN ("failed to read front-end path: %s\n" ,strerror (err)); + goto out; + } + +/* +* Get the file path backing the VBD. +*/ + params =tapback_xs_read (blktap3_daemon.xs, blktap3_daemon.xst, +"%s/%d/%s/params" ,BLKTAP3_BACKEND_PATH , domid, name); +if (!params) { + err = errno; +WARN ("failed to read backing file: %s\n" ,strerror (err)); + goto out; + } +DBG ("need to find tapdisk serving \'%s\'\n" , params); + + err =blkback_find_tapdisk (params, &device->tap); +if (!err) { +DBG ("found tapdisk %d/%d\n" , device->tap.pid, device->tap.minor); + }else if (err ==ESRCH ) { +/* FIXME replace with tap_ctl_create */ +DBG ("no such tapdisk\n" ); + err =tap_ctl_spawn (); +if (err <=0 ) { +WARN ("failed to create tapdisk: %s\n" ,strerror (err)); + goto out; + } + device->tap.pid = err; +DBG ("spawned tapdisk %d\n" , device->tap.pid); + device->tap.minor =0 ; + err =tap_ctl_attach (device->tap.pid, device->tap.minor); +if (err) { +WARN ("failed to attach tapdisk: %s\n" ,strerror (err)); + goto out; + } +DBG ("attached tapdisk %d\n" , device->tap.pid); + err =tap_ctl_open (device->tap.pid,0 , params,0 ,-1 ,NULL ); +if (err) { +WARN ("failed to open tapdisk: %s\n" ,strerror (err)); + goto out; + } +DBG ("opened tapdisk %d\n" , device->tap.pid); + }else { +WARN ("error looking for tapdisk: %s\n" ,strerror (err)); + goto out; + } + +/* +* get the VBD parameters from the tapdisk +*/ +if ((err =tap_ctl_info (device->tap.pid,0 , &device->sectors, + &device->sector_size, &device->info))) { +WARN ("error retrieving disk characteristics: %s\n" ,strerror (-err)); + goto out; + } + +DBG ("got %d-%d with tapdev %d/%d\n" , device->domid, device->devid, + device->tap.pid, device->tap.minor); + +/* +* Finally, watch the front-end path in XenStore for changes, i.e. +* /local/domain/<domid>/device/vbd/<devname>/state +* After this, we wait for the front-end to switch state to continue with +* the initialisation. +*/ +if (asprintf (&device->frontend_state_path,"%s/state" , + device->frontend_path) ==-1 ) { +/* TODO log error */ + err = -errno; + goto out; + } +assert (device->frontend_state_path); + +/* +* We use the same token for all front-end watches. We don't have to use a +* unique token for each front-end watch because when a front-end watch +* fires we are given the XenStore path that changed. +*/ +if (!xs_watch (blktap3_daemon.xs, device->frontend_state_path, +BLKTAP3_FRONTEND_TOKEN )) { +free (device->frontend_state_path); + err = -errno; + goto out; + } + +out: +if (err) { +WARN ("error creating device: domid=%d name=%s err=%d (%s)\n" , + domid, name, err,strerror (err)); +if (device) +tapback_backend_destroy_device (device); + } +free (params); +return err; +} + +/** +* Creates (removes) a device depending on the existence (non-existence) of the +* "backend/<backend name>/@domid/@devname" XenStore path. +* +* @param domid the ID of the domain where the VBD is created +* @param devname device name +* @returns 0 on success, an error code otherwise +*/ +static int +tapback_backend_probe_device (const domid_t domid,const char *const devname) +{ +int should_exist =0 , create =0 , remove =0 ; +vbd_t *device =NULL ; +char * s =NULL ; + +assert (devname); + +DBG ("probing device domid=%d name=%s\n" , domid, devname); + +/* +* Ask XenStore if the device _should_ exist. +*/ + s =tapback_xs_read (blktap3_daemon.xs, blktap3_daemon.xst,"%s/%d/%s" , +BLKTAP3_BACKEND_PATH , domid, devname); + should_exist = s !=NULL ; +free (s); + +/* +* Search the device list for this specific device. +*/ +tapback_backend_find_device (device, + device->domid == domid && !strcmp (device->name, devname)); + +/* +* If XenStore says that the device should exist but it's not in our device +* list, we must create it. If it's the other way round, this is a removal. +*/ + remove = device && !should_exist; + create = !device && should_exist; + +DBG ("should exist=%d device=%p remove=%d create=%d\n" , + should_exist, device, remove, create); + +if (!create && !remove) { +/* +* A watch has triggered on a path we're not interested in. +* TODO Check if we can avoid probing the device completely based on +* the path that triggered. +*/ +DBG ("spurious XenStore watch triggered on back-end\n" ); +return 0 ; + } + +/* +* Remember that remove and create may both be true at the same time, as +* this indicates that the device has been removed and re-created too fast. +* In this case, we too need to remove and re-create the device, +* respectively. +*/ + +if (remove) +tapback_backend_destroy_device (device); + +if (create) { +const int err =tapback_backend_create_device (domid, devname); +if (0 != err) { +WARN ("error creating device %s on domain %d: %s\n" , devname, domid, +strerror (err)); +return err; + } + } + +return 0 ; +} + +/** +* Scans XenStore for all blktap3 devices and probes each one of them. +* +* XXX Only called by tapback_backend_handle_backend_watch. +*/ +static int +tapback_backend_scan (void ) +{ +vbd_t *device =NULL , *next =NULL ; +unsigned int i =0 , j =0 , n =0 , m =0 ; +char **dir =NULL ; +int err =0 ; + +DBG ("scanning the back-end\n" ); + +/* +* scrap all non-existent devices +* FIXME Why do we do this? +* FIXME Is this costly? +*/ + +tapback_backend_for_each_device (device, next) { + err =tapback_backend_probe_device (device->domid, device->name); +if (err) { +WARN ("error probing device %s of domain %d: %s\n" , device->name, + device->domid,strerror (err)); +/* TODO Should we fail in this case of keep probing? */ + goto out; + } + } + +/* +* probe the new ones +* +* FIXME We're checking each and every device in each and every domain, +* could there be a performance issue in the presence of many VMs/VBDs? +* (e.g. boot-storm) +*/ +if (!(dir =xs_directory (blktap3_daemon.xs, blktap3_daemon.xst, +BLKTAP3_BACKEND_PATH , &n))) { + err = errno; +if (err ==ENOENT ) + err =0 ; +else +WARN ("error listing %s: %s\n" ,BLKTAP3_BACKEND_PATH , +strerror (err)); + goto out; + } + +DBG ("probing %d domains\n" , n); + +for (i =0 ; i < n; i++) {/* for each domain */ +char *path =NULL , **sub =NULL , *end =NULL ; +domid_t domid =0 ; + +/* +* Get the domain ID. +*/ + domid =strtoul (dir[i], &end,0 ); +if (*end !=0 || end == dir[i]) +continue ; + +/* +* Read the devices of this domain. +*/ +if (asprintf (&path,"%s/%d" ,BLKTAP3_BACKEND_PATH , domid) ==-1 ) { +/* TODO log error */ + err = errno; + goto out; + } + sub =xs_directory (blktap3_daemon.xs, blktap3_daemon.xst, path, &m); + err = errno; +free (path); + +if (!sub) { +WARN ("error listing %s: %s\n" , path,strerror (err)); + goto out; + } + +/* +* Probe each device. +*/ +for (j =0 ; j < m; j++) { + err =tapback_backend_probe_device (domid, sub[j]); +if (err) { +WARN ("error probing device %s of domain %d: %s\n" , sub[j], + domid,strerror (err)); + goto out; + } + } + +free (sub); + } + +out: +free (dir); +return err; +} + +int +tapback_backend_handle_backend_watch (char *const path) +{ +char *s =NULL , *end =NULL , *name =NULL ; +domid_t domid =0 ; + +assert (path); + +DBG ("handling watch triggered on path \'%s\'\n" , path); + + s =strtok (path,"/" ); +assert (!strcmp (s,XENSTORE_BACKEND )); +if (!(s =strtok (NULL ,"/" ))) +return tapback_backend_scan (); + +assert (!strcmp (s,BLKTAP3_BACKEND_NAME )); +if (!(s =strtok (NULL ,"/" ))) +return tapback_backend_scan (); + + domid =strtoul (s, &end,0 ); +if (*end !=0 || end == s) { +WARN ("invalid domain ID \'%s\'\n" , s); +return EINVAL ; + } + +/* +* TODO Optimisation: since we know which domain changed, we don't have to +* scan the whole thing. Add the domid as an optional parameter to +* tapback_backend_scan. +*/ +if (!(name =strtok (NULL ,"/" ))) +return tapback_backend_scan (); + +/* +* Create or remove a specific device. +* +* TODO tapback_backend_probe_device reads xenstore again to see if the +* device should exist, but we already know that in the current function. +* Optimise this case. +*/ +return tapback_backend_probe_device (domid, name); +}
PATCH v3 1/1 blktap3/tapback: Introduce front-end XenStore path handler
In reply to Thanos MakatosThis patch introduces the handler executed when the front-end XenStore path of a VBD gets modified. This is done when the front-end switches state. The core of this functionality is connecting/disconnecting the tapdisk to/from the shared ring.
When the front-end goes to Initialised or Connected state, the daemon reads all the necessary information from XenStore provided by the front-end, initiates the shared ring creation (the actual creation is performed by a library that will be introduced by another patch series), and instructs the tapdisk designated to serve this VBD to connect to the ring. Finally, it communicates to the front-end all the necessary disk parameters.
When the front-end switches to Closed, the tapdisk is disconnected from the shared ring.
---
Changed since v2:
* Documented the frontend_state_change_map array of callbacks.
* Minor code and whitespace clean up.
* Added some debug prints.
* Don't fail if the front-end doesn't tell us whether or not it supports persistent grants, just assume it doesn't.
tools/blktap3/include/blktap3.h +15 -0
@@ -44,4 +44,19 @@TAILQ_REMOVE (src, node, entry); \TAILQ_INSERT_TAIL (dst, node, entry); +/** +* Block I/O protocol +* +* Taken from linux/drivers/block/xen-blkback/common.h so that blkfront can +* work both with blktap2 and blktap3. +* +* TODO linux/drivers/block/xen-blkback/common.h contains other definitions +* necessary for allowing tapdisk3 to talk directly to blkfront. Find a way to +* use the definitions from there. +*/ +enum blkif_protocol { +BLKIF_PROTOCOL_NATIVE =1 , +BLKIF_PROTOCOL_X86_32 =2 , +BLKIF_PROTOCOL_X86_64 =3 , +};#endif /* __BLKTAP_3_H__ */
tools/blktap3/tapback/frontend.c new file +418 -0
418 lines not on this page · raw message
PATCH v3 1/1 blktap3/tapback: Introduce the tapback daemon
In reply to Thanos MakatosThis patch introduces the core of the tapback daemon, the user space daemon that acts as a device's back-end, essentially most of blkback in user space. Similar to blkback, the daemon monitors XenStore for device creation/removal requests and front-end state changes, and acts in response to them (communicates to the front-end necessary information, switches the back-end's state etc.). The daemon creates/destroys tapdisk processes as needed.
---
Changed since v1:
* minor code clean up
Changed since v2:
* Introduce function XenbusState2str for printing XenbusStates in a more human-friendly way.
* Don't print the log identify if the tapback is run in non-daemon mode, in order to make debug output more concise.
tools/blktap3/tapback/tapback.c new file +301 -0
@@ -0,0 +1,301 @@ +/* +* Copyright (C) 2012 Citrix Ltd. +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* 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 General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, +* USA. +* +* This file contains the core of the tapback daemon, the user space daemon +* that acts as a device's back-end. +*/ + +/* +* TODO Some of these includes may be useless. +* TODO Replace hard-coding strings with defines/const string. +*/ +#include <stdlib.h> +#include <stdarg.h> +#include <assert.h> +#include <syslog.h> +#include <fcntl.h> +#include <unistd.h> +#include <libgen.h> +#include <getopt.h> +#include <sys/ioctl.h> +#include <sys/mount.h> +#include <syslog.h> + +#include "blktap3.h" +#include "stdio.h" /* TODO tap-ctl.h needs to include stdio.h */ +#include "tap-ctl.h" +#include "tapback.h" + +void tapback_log (int prio,const char *fmt, ...); +void (*tapback_vlog) (int prio,const char *fmt,va_list ap); + +struct _blktap3_daemon blktap3_daemon; + +char *XenbusState2str (const XenbusState xbs) +{ +static char *const str[] = { + [XenbusStateUnknown] ="unknown" , + [XenbusStateInitialising] ="initialising" , + [XenbusStateInitWait] ="init wait" , + [XenbusStateInitialised] ="initialised" , + [XenbusStateConnected] ="connected" , + [XenbusStateClosing] ="closing" , + [XenbusStateClosed] ="closed" , + [XenbusStateReconfiguring] ="reconfiguring" , + [XenbusStateReconfigured] ="reconfigured" + }; +return str[xbs]; +} + +/** +* Read changes that occurred on the "backend/<backend name>" XenStore path +* or one of the front-end paths and act accordingly. +*/ +static inline void +tapback_read_watch (void ) +{ +char **watch =NULL , *path =NULL , *token =NULL ; +unsigned int n =0 ; +int err =0 , _abort =0 ; + +/* read the change */ + watch =xs_read_watch (blktap3_daemon.xs, &n); + path = watch[XS_WATCH_PATH ]; + token = watch[XS_WATCH_TOKEN ]; + +/* +* TODO Put the body of "again:" into a loop instead of using goto. +*/ +again: +if (!(blktap3_daemon.xst =xs_transaction_start (blktap3_daemon.xs))) { +WARN ("error starting transaction\n" ); + goto fail; + } + +/* +* The token indicates which XenStore watch triggered, the front-end one or +* the back-end one. +*/ +if (!strcmp (token,BLKTAP3_FRONTEND_TOKEN )) { +DBG ("front-end triggered\n" ); + err =tapback_backend_handle_otherend_watch (path); + }else if (!strcmp (token,BLKTAP3_BACKEND_TOKEN )) { +DBG ("back-end triggered\n" ); + err =tapback_backend_handle_backend_watch (path); + }else { +WARN ("invalid token \'%s\'\n" , token); + err =EINVAL ; + } + + _abort = !!err; +if (_abort) +DBG ("aborting transaction: %s\n" ,strerror (err)); + + err =xs_transaction_end (blktap3_daemon.xs, blktap3_daemon.xst, _abort); + blktap3_daemon.xst =0 ; +if (!err) { + err = -errno; +/* +* This is OK according to xs_transaction_end's semantics. +*/ +if (EAGAIN == errno) + goto again; +DBG ("error ending transaction: %s\n" ,strerror (err)); + } + +fail: +free (watch); +return ; +} + +static void +tapback_backend_destroy (void ) +{ +if (blktap3_daemon.xs) { +xs_daemon_close (blktap3_daemon.xs); + blktap3_daemon.xs =NULL ; + } +} + +/** +* Initializes the back-end descriptor. There is one back-end per tapback +* process. Also, it initiates a watch to XenStore on backend/<backend name>. +* +* @returns 0 on success, an error code otherwise +*/ +static inline int +tapback_backend_create (void ) +{ +int err; + +TAILQ_INIT (&blktap3_daemon.devices); + blktap3_daemon.xst =XBT_NULL ; + +if (!(blktap3_daemon.xs =xs_daemon_open ())) { + err =EINVAL ; + goto fail; + } + +/* +* Watch the back-end. +*/ +if (!xs_watch (blktap3_daemon.xs,BLKTAP3_BACKEND_PATH , +BLKTAP3_BACKEND_TOKEN )) { + err = errno; + goto fail; + } + +return 0 ; + +fail: +tapback_backend_destroy (); + +return err; +} + +/** +* Runs the daemon. +* +* Watches backend/<backend name> and the front-end devices. +*/ +static inline int +tapback_backend_run (void ) +{ +const int fd =xs_fileno (blktap3_daemon.xs); +int err; + +do { +fd_set rfds; +int nfds =0 ; + +FD_ZERO (&rfds); +FD_SET (fd, &rfds); + +/* poll the fd for changes in the XenStore path we're interested in */ +if ((nfds =select (fd +1 , &rfds,NULL ,NULL ,NULL )) <0 ) { +perror ("error monitoring XenStore" ); + err = -errno; +break ; + } + +if (FD_ISSET (fd, &rfds)) +tapback_read_watch (); +DBG ("--\n" ); + }while (1 ); + +return err; +} + +static char *blkback_ident =NULL ; + +static void +blkback_vlog_fprintf (const int prio,const char *const fmt,va_list ap) +{ +static const char *strprio[] = { + [LOG_DEBUG ] ="DBG" , + [LOG_INFO ] ="INF" , + [LOG_WARNING ] ="WRN" + }; + +assert (LOG_DEBUG == prio ||LOG_INFO == prio ||LOG_WARNING == prio); +assert (strprio[prio]); + +fprintf (stderr,"%s[%s] " , blkback_ident, strprio[prio]); +vfprintf (stderr, fmt, ap); +} + +/** +* Print tapback's usage instructions. +*/ +static void +usage (FILE *const stream,const char *const prog) +{ +assert (stream); +assert (prog); + +fprintf (stream, +"usage: %s\n" +"\t[-D|--debug]\n" +"\t[-h|--help]\n" , prog); +} + +int main (int argc,char **argv) +{ +const char *prog =NULL ; +int opt_debug =0 ; +int err =0 ; + + prog =basename (argv[0 ]); + + opt_debug =0 ; + +do { +const struct option longopts[] = { + {"help" ,0 ,NULL ,'h' }, + {"debug" ,0 ,NULL ,'D' }, + }; +int c; + + c =getopt_long (argc, argv,"h:D" , longopts,NULL ); +if (c <0 ) +break ; + +switch (c) { +case 'h' : +usage (stdout, prog); +return 0 ; +case 'D' : + opt_debug =1 ; +break ; +case '?' : + goto usage; + } + }while (1 ); + +if (opt_debug) { + blkback_ident ="" ; + tapback_vlog = blkback_vlog_fprintf; + } +else { + blkback_ident =BLKTAP3_BACKEND_TOKEN ; +openlog (blkback_ident,0 ,LOG_DAEMON ); + } + +if (!opt_debug) { +if ((err =daemon (0 ,0 ))) { + err = -errno; + goto fail; + } + } + +if ((err =tapback_backend_create ())) { +WARN ("error creating blkback: %s\n" ,strerror (err)); + goto fail; + } + + err =tapback_backend_run (); + +tapback_backend_destroy (); + +fail: +return err ? -err :0 ; + +usage: +usage (stderr, prog); +return 1 ; +}
PATCH v3 1/1 blktap3/tapback: Introduce tapback daemon Makefile
In reply to Thanos MakatosThis patch introduces the Makefile that builds the tapback daemon. This Makefile is not yet hooked into the build system.
---
Changed since v2:
* Use $(BINDIR) as the daemon's installation directory.
* Fixed whitespace.
tools/blktap3/tapback/Makefile +18 -1
@@ -3,6 +3,10 @@ include $(XEN_ROOT)/tools/Rules.mk BLKTAP_ROOT := .. +INST_DIR ?= $(BINDIR) + +IBIN = tapback + # -D_GNU_SOURCE is required by vasprintf. override CFLAGS += \ -I$(BLKTAP_ROOT)/include \
@@ -25,7 +29,20 @@ override LDFLAGS += \ $(LDLIBS_libxenstore) \ $(LDFLAGS_libxenctrl) +TAPBACK-OBJS := log.o xenstore.o frontend.o backend.o + +TAPBACK-LIBS := $(BLKTAP_ROOT)/control/libblktapctl.so.1.0.0 + +all: $(IBIN) + +$(IBIN): $(TAPBACK-OBJS) tapback.o + $(CC) -o $@ $^ $(TAPBACK-LIBS) $(LDFLAGS) + +install: all + $(INSTALL_DIR) -p $(DESTDIR)$(INST_DIR) + $(INSTALL_PROG) $(IBIN) $(DESTDIR)$(INST_DIR) + clean: - rm -f *.o *.o.d .*.o.d + rm -f *.o *.o.d .*.o.d $(IBIN) .PHONY: clean install
PATCH v3 1/1 blktap3/tapback: Add the tapback binary to the Mercurial ignore list
In reply to Thanos Makatos.hgignore +3 -0
@@ -371,4 +371,7 @@ ^unmodified_drivers/linux-2.6/.*\.ko$ ^unmodified_drivers/linux-2.6/.*\.mod\.c$ ^LibVNCServer.* + +# blktap3 +^tools/blktap3/tapback/tapback$ ^tools/blktap3/control/_paths.h$