On Thu, May 19, 2011 at 07:24:18AM -0400, Daniel P. Berrange wrote: > Define the basic framework lock manager plugins. The > basic plugin API for 3rd parties to implemented is > defined in > > src/locking/lock_driver.h > > This allows dlopen()able modules for alternative locking > schemes, however, we do not install the header. This > requires lock plugins to be in-tree allowing changing of > the lock manager plugin API in future. > > The libvirt code for loading & calling into plugins > is in > > src/locking/lock_manager.{c,h} > > * include/libvirt/virterror.h, src/util/virterror.c: Add > VIR_FROM_LOCKING > * src/locking/lock_driver.h: API for lock driver plugins > to implement > * src/locking/lock_manager.c, src/locking/lock_manager.h: > Internal API for managing locking > * src/Makefile.am: Add locking code > --- > include/libvirt/virterror.h | 1 + > po/POTFILES.in | 1 + > src/Makefile.am | 3 +- > src/libvirt_private.syms | 14 ++ > src/locking/README | 158 +++++++++++++++++++ > src/locking/lock_driver.h | 293 +++++++++++++++++++++++++++++++++++ > src/locking/lock_manager.c | 357 +++++++++++++++++++++++++++++++++++++++++++ > src/locking/lock_manager.h | 65 ++++++++ > src/util/virterror.c | 3 + > 9 files changed, 894 insertions(+), 1 deletions(-) > create mode 100644 src/locking/README > create mode 100644 src/locking/lock_driver.h > create mode 100644 src/locking/lock_manager.c > create mode 100644 src/locking/lock_manager.h > > diff --git a/include/libvirt/virterror.h b/include/libvirt/virterror.h > index 0708e02..efa4796 100644 > --- a/include/libvirt/virterror.h > +++ b/include/libvirt/virterror.h > @@ -81,6 +81,7 @@ typedef enum { > VIR_FROM_VMWARE = 39, /* Error from VMware driver */ > VIR_FROM_EVENT = 40, /* Error from event loop impl */ > VIR_FROM_LIBXL = 41, /* Error from libxenlight driver */ > + VIR_FROM_LOCKING = 42, /* Error from lock manager */ > } virErrorDomain; > > > diff --git a/po/POTFILES.in b/po/POTFILES.in > index dd44da2..9c3d287 100644 > --- a/po/POTFILES.in > +++ b/po/POTFILES.in > @@ -31,6 +31,7 @@ src/fdstream.c > src/interface/netcf_driver.c > src/internal.h > src/libvirt.c > +src/locking/lock_manager.c > src/lxc/lxc_container.c > src/lxc/lxc_conf.c > src/lxc/lxc_controller.c > diff --git a/src/Makefile.am b/src/Makefile.am > index 58eb2a7..a27838b 100644 > --- a/src/Makefile.am > +++ b/src/Makefile.am > @@ -93,7 +93,8 @@ DRIVER_SOURCES = \ > datatypes.c datatypes.h \ > fdstream.c fdstream.h \ > $(NODE_INFO_SOURCES) \ > - libvirt.c libvirt_internal.h > + libvirt.c libvirt_internal.h \ > + locking/lock_manager.c locking/lock_manager.h > > > # XML configuration format handling sources > diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms > index 1b13c5c..1784c0d 100644 > --- a/src/libvirt_private.syms > +++ b/src/libvirt_private.syms > @@ -588,6 +588,20 @@ virRegisterSecretDriver; > virRegisterStorageDriver; > > > +# locking.h > +virLockManagerAcquire; > +virLockManagerAddResource; > +virLockManagerFree; > +virLockManagerInquire; > +virLockManagerNew; > +virLockManagerPluginNew; > +virLockManagerPluginRef; > +virLockManagerPluginUnref; > +virLockManagerPluginUsesState; > +virLockManagerPluginGetName; > +virLockManagerRelease; > + > + > # logging.h > virLogDefineFilter; > virLogDefineOutput; > diff --git a/src/locking/README b/src/locking/README > new file mode 100644 > index 0000000..4fa4f89 > --- /dev/null > +++ b/src/locking/README > @@ -0,0 +1,158 @@ > + > +At libvirtd startup: > + > + plugin = virLockManagerPluginLoad("sync-manager"); > + > + > +At libvirtd shtudown: > + > + virLockManagerPluginUnload(plugin) > + > + > +At guest startup: > + > + manager = virLockManagerNew(plugin, > + VIR_LOCK_MANAGER_OBJECT_DOMAIN, > + 0); > + > + virLockManagerSetParameter(manager, "id", id); > + virLockManagerSetParameter(manager, "uuid", uuid); > + virLockManagerSetParameter(manager, "name", name); > + > + foreach disk > + virLockManagerRegisterResource(manager, > + VIR_LOCK_MANAGER_RESOURCE_TYPE_DISK, > + disk.path, > + ..flags...); > + > + if (!virLockManagerAcquireObject(manager)) > + abort.. > + > + run QEMU > + > + > +At guest shutdown: > + > + ...send QEMU 'quit' monitor command, and/or kill(qemupid)... > + > + if (!virLockManagerShutdown(manager)) > + kill(supervisorpid); /* XXX or leave it running ??? */ > + > + virLockManagerFree(manager); > + > + > + > +At libvirtd restart with running guests: > + > + foreach still running guest > + manager = virLockManagerNew(driver, > + VIR_LOCK_MANAGER_START_DOMAIN, > + VIR_LOCK_MANAGER_NEW_ATTACH); > + virLockManagerSetParameter(manager, "id", id); > + virLockManagerSetParameter(manager, "uuid", uuid); > + virLockManagerSetParameter(manager, "name", name); > + > + if (!virLockManagerGetChild(manager, &qemupid)) > + kill(supervisorpid); /* XXX or leave it running ??? */ > + > + > + > +With disk hotplug: > + > + if (virLockManagerAcquireResource(manager, > + VIR_LOCK_MANAGER_RESOURCE_TYPE_DISK, > + disk.path > + ..flags..)) > + ...abort hotplug attempt ... > + > + ...hotplug the device... > + > + > + > +With disk unhotplug: > + > + ...hotunplug the device... > + > + if (virLockManagerReleaseResource(manager, > + VIR_LOCK_MANAGER_RESOURCE_TYPE_DISK, > + disk.path > + ..flags..)) > + ...log warning ... > + > + > + > +During migration: > + > + 1. On source host > + > + if (!virLockManagerPrepareMigrate(manager, hosturi)) > + ..don't start migration.. > + > + 2. On dest host > + > + manager = virLockManagerNew(driver, > + VIR_LOCK_MANAGER_START_DOMAIN, > + VIR_LOCK_MANAGER_NEW_MIGRATE); > + virLockManagerSetParameter(manager, "id", id); > + virLockManagerSetParameter(manager, "uuid", uuid); > + virLockManagerSetParameter(manager, "name", name); > + > + foreach disk > + virLockManagerRegisterResource(manager, > + VIR_LOCK_MANAGER_RESOURCE_TYPE_DISK, > + disk.path, > + ..flags...); > + > + char **supervisorargv; > + int supervisorargc; > + > + supervisor = virLockManagerGetSupervisorPath(manager); > + virLockManagerGetSupervisorArgs(&argv, &argc); > + > + cmd = qemuBuildCommandLine(supervisor, supervisorargv, supervisorargv); > + > + supervisorpid = virCommandExec(cmd); > + > + if (!virLockManagerGetChild(manager, &qemupid)) > + kill(supervisorpid); /* XXX or leave it running ??? */ > + > + 3. Initiate migration in QEMU on source and wait for completion > + > + 4a. On failure > + > + 4a1 On target > + > + virLockManagerCompleteMigrateIn(manager, > + VIR_LOCK_MANAGER_MIGRATE_CANCEL); > + virLockManagerShutdown(manager); > + virLockManagerFree(manager); > + > + 4a2 On source > + > + virLockManagerCompleteMigrateIn(manager, > + VIR_LOCK_MANAGER_MIGRATE_CANCEL); > + > + 4b. On succcess > + > + > + 4b1 On target > + > + virLockManagerCompleteMigrateIn(manager, 0); > + > + 42 On source > + > + virLockManagerCompleteMigrateIn(manager, 0); > + virLockManagerShutdown(manager); > + virLockManagerFree(manager); > + > + > +Notes: > + > + - If a lock manager impl does just VM level leases, it can > + ignore all the resource paths at startup. > + > + - If a lock manager impl does not support migrate > + it can return an error from all migrate calls > + > + - If a lock manger impl does not support hotplug > + it can return an error from all resource acquire/release calls > diff --git a/src/locking/lock_driver.h b/src/locking/lock_driver.h > new file mode 100644 > index 0000000..40a55f6 > --- /dev/null > +++ b/src/locking/lock_driver.h > @@ -0,0 +1,293 @@ > +/* > + * lock_driver.h: Defines the lock driver plugin API > + * > + * Copyright (C) 2010-2011 Red Hat, Inc. > + * > + * 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.1 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 > + * Please add * Author: Daniel P. Berrange <berrange@xxxxxxxxxx> well I assume you wrote it :-) > + */ > + > +#ifndef __VIR_PLUGINS_LOCK_DRIVER_H__ > +# define __VIR_PLUGINS_LOCK_DRIVER_H__ > + > +# include "internal.h" > + > +typedef struct _virLockManager virLockManager; > +typedef virLockManager *virLockManagerPtr; > + > +typedef struct _virLockDriver virLockDriver; > +typedef virLockDriver *virLockDriverPtr; > + > +typedef struct _virLockManagerParam virLockManagerParam; > +typedef virLockManagerParam *virLockManagerParamPtr; > + > +typedef enum { > + /* State passing is used to re-acquire existing leases */ > + VIR_LOCK_MANAGER_USES_STATE = (1 << 0) > +} virLockManagerFlags; > + > +typedef enum { > + /* The managed object is a virtual guest domain */ > + VIR_LOCK_MANAGER_OBJECT_TYPE_DOMAIN = 0, > +} virLockManagerObjectType; > + > +typedef enum { > + /* The resource to be locked is a virtual disk */ > + VIR_LOCK_MANAGER_RESOURCE_TYPE_DISK = 0, > + /* A lease against an arbitrary resource */ > + VIR_LOCK_MANAGER_RESOURCE_TYPE_LEASE = 1, > +} virLockManagerResourceType; > + > +typedef enum { > + /* The resource is assigned in readonly mode */ > + VIR_LOCK_MANAGER_RESOURCE_READONLY = (1 << 0), > + /* The resource is assigned in shared, writable mode */ > + VIR_LOCK_MANAGER_RESOURCE_SHARED = (1 << 1), > +} virLockManagerResourceFlags; > + > +typedef enum { > + /* Don't acquire the resources, just register the object PID */ > + VIR_LOCK_MANAGER_ACQUIRE_REGISTER_ONLY = (1 << 0) > +} virLockManagerAcquireFlags; > + > +enum { > + VIR_LOCK_MANAGER_PARAM_TYPE_STRING, > + VIR_LOCK_MANAGER_PARAM_TYPE_INT, > + VIR_LOCK_MANAGER_PARAM_TYPE_LONG, > + VIR_LOCK_MANAGER_PARAM_TYPE_UINT, > + VIR_LOCK_MANAGER_PARAM_TYPE_ULONG, > + VIR_LOCK_MANAGER_PARAM_TYPE_DOUBLE, > + VIR_LOCK_MANAGER_PARAM_TYPE_UUID, > +}; > + > +struct _virLockManagerParam { > + int type; > + const char *key; > + union { > + int i; > + long long l; > + unsigned int ui; > + unsigned long long ul; > + double d; > + char *str; > + unsigned char uuid[16]; > + } value; > +}; > + > + > +/* > + * Changes in major version denote incompatible ABI changes > + * Changes in minor version denote new compatible API entry points > + * Changes in micro version denote new compatible flags > + */ > +# define VIR_LOCK_MANAGER_VERSION_MAJOR 1 > +# define VIR_LOCK_MANAGER_VERSION_MINOR 0 > +# define VIR_LOCK_MANAGER_VERSION_MICRO 0 > + > +# define VIR_LOCK_MANAGER_VERSION \ > + ((VIR_LOCK_MANAGER_VERSION_MAJOR * 1000 * 1000) + \ > + (VIR_LOCK_MANAGER_VERSION_MINOR * 1000) + \ > + (VIR_LOCK_MANAGER_VERSION_MICRO)) > + > + > + > +/** > + * virLockDriverInit: > + * @version: the libvirt requested plugin ABI version > + * @flags: the libvirt requested plugin optional extras > + * > + * Allow the plugin to validate the libvirt requested > + * plugin version / flags. This allows the plugin impl > + * to block its use in versions of libvirtd which are > + * too old to support key features. > + * > + * NB: A plugin may be loaded multiple times, for different > + * libvirt drivers (eg QEMU, LXC, UML) > + * > + * Returns -1 if the requested version/flags were inadequate > + */ > +typedef int (*virLockDriverInit)(unsigned int version, > + unsigned int flags); > + > +/** > + * virLockDriverDeinit: > + * > + * Called to release any resources prior to the plugin > + * being unloaded from memory. Returns -1 to prevent > + * plugin from being unloaded from memory. > + */ > +typedef int (*virLockDriverDeinit)(void); > + > +/** > + * virLockManagerNew: > + * @man: the lock manager context > + * @type: the type of process to be supervised > + * @nparams: number of metadata parameters > + * @params: extra metadata parameters > + * @flags: optional flags, currently unused > + * > + * Initialize a new context to supervise a process, usually > + * a virtual machine. The lock driver implementation can use > + * the <code>privateData</code> field of <code>man</code> > + * to store a pointer to any driver specific state. > + * > + * A process of VIR_LOCK_MANAGER_START_DOMAIN will be > + * given the following parameters > + * > + * - id: the domain unique id (unsigned int) > + * - uuid: the domain uuid (uuid) > + * - name: the domain name (string) > + * - pid: process ID to own/owning the lock (unsigned int) > + * > + * Returns 0 if successful initialized a new context, -1 on error > + */ > +typedef int (*virLockDriverNew)(virLockManagerPtr man, > + unsigned int type, > + size_t nparams, > + virLockManagerParamPtr params, > + unsigned int flags); > + > +/** > + * virLockDriverFree: > + * @manager: the lock manager context > + * > + * Release any resources associated with the lock manager > + * context private data > + */ > +typedef void (*virLockDriverFree)(virLockManagerPtr man); > + > +/** > + * virLockDriverAddResource: > + * @manager: the lock manager context > + * @type: the resource type virLockManagerResourceType > + * @name: the resource name > + * @nparams: number of metadata parameters > + * @params: extra metadata parameters > + * @flags: the resource access flags > + * > + * Assign a resource to a managed object. This will > + * only be called prior to the object is being locked > + * when it is inactive. eg, to set the initial boot > + * time disk assignments on a VM > + * The format of @name varies according to > + * the resource @type. A VIR_LOCK_MANAGER_RESOURCE_TYPE_DISK > + * will have the fully qualified file path, while a resource > + * of type VIR_LOCK_MANAGER_RESOURCE_TYPE_LEASE will have the > + * unique name of the lease > + * > + * A resource of type VIR_LOCK_MANAGER_RESOURCE_TYPE_LEASE > + * will receive at least the following extra parameters > + * > + * - 'path': a fully qualified path to the lockspace > + * - 'lockspace': globally string identifying the lockspace name > + * - 'offset': byte offset within the lease (unsigned long long) > + * > + * If no flags are given, the resource is assumed to be > + * used in exclusive, read-write mode. Access can be > + * relaxed to readonly, or shared read-write. > + * > + * Returns 0 on success, or -1 on failure > + */ > +typedef int (*virLockDriverAddResource)(virLockManagerPtr man, > + unsigned int type, > + const char *name, > + size_t nparams, > + virLockManagerParamPtr params, > + unsigned int flags); > + > +/** > + * virLockDriverAcquire: > + * @manager: the lock manager context > + * @state: the current lock state > + * @flags: optional flags, currently unused > + * > + * Start managing resources for the object. This > + * must be called from the PID that represents the > + * object to be managed. If the lock is lost at any > + * time, the PID will be killed off by the lock manager. > + * The optional state contains information about the > + * locks previously held for the object. > + * > + * Returns 0 on success, or -1 on failure > + */ > +typedef int (*virLockDriverAcquire)(virLockManagerPtr man, > + const char *state, > + unsigned int flags); > + > +/** > + * virLockDriverRelease: > + * @manager: the lock manager context > + * @state: pointer to be filled with lock state > + * @flags: optional flags > + * > + * Inform the lock manager that the supervised process has > + * been, or can be stopped. > + * > + * Returns 0 on success, or -1 on failure > + */ > +typedef int (*virLockDriverRelease)(virLockManagerPtr man, > + char **state, > + unsigned int flags); > + > +/** > + * virLockDriverInquire: > + * @manager: the lock manager context > + * @state: pointer to be filled with lock state > + * @flags: optional flags, currently unused > + * > + * Retrieve the current lock state. The returned > + * lock state may be NULL if none is required. The > + * caller is responsible for freeing the lock > + * state string when it is no longer required > + * > + * Returns 0 on success, or -1 on failure. > + */ > +typedef int (*virLockDriverInquire)(virLockManagerPtr man, > + char **state, > + unsigned int flags); > + > + > +struct _virLockManager { > + virLockDriverPtr driver; > + void *privateData; > +}; > + > +/** > + * The plugin must export a static instance of this > + * driver table, with the name 'virLockDriverImpl' > + */ > +struct _virLockDriver { > + /** > + * @version: the newest implemented plugin ABI version > + * @flags: optional flags, currently unused > + */ > + unsigned int version; > + unsigned int flags; > + > + virLockDriverInit drvInit; > + virLockDriverDeinit drvDeinit; > + > + virLockDriverNew drvNew; > + virLockDriverFree drvFree; > + > + virLockDriverAddResource drvAddResource; > + > + virLockDriverAcquire drvAcquire; > + virLockDriverRelease drvRelease; > + virLockDriverInquire drvInquire; > +}; > + > + > +#endif /* __VIR_PLUGINS_LOCK_DRIVER_H__ */ > diff --git a/src/locking/lock_manager.c b/src/locking/lock_manager.c > new file mode 100644 > index 0000000..cb96091 > --- /dev/null > +++ b/src/locking/lock_manager.c > @@ -0,0 +1,357 @@ > +/* > + * lock_manager.c: Implements the internal lock manager API > + * > + * Copyright (C) 2010-2011 Red Hat, Inc. > + * > + * 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.1 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 > + * Same thing :-) > + */ > + > +#include <config.h> > + > +#include "lock_manager.h" > +#include "virterror_internal.h" > +#include "logging.h" > +#include "util.h" > +#include "memory.h" > +#include "uuid.h" > + > +#include <dlfcn.h> > +#include <stdlib.h> > +#include <unistd.h> > + > +#include "configmake.h" V> + > +#define VIR_FROM_THIS VIR_FROM_LOCKING [...] > +/** > + * virLockManagerPluginRef: > + * @plugin: the plugin implementation to ref > + * > + * Acquires an additional reference on the plugin. > + */ > +void virLockManagerPluginRef(virLockManagerPluginPtr plugin) > +{ > + plugin->refs++; > +} > + > + > +/** > + * virLockManagerPluginUnref: > + * @plugin: the plugin implementation to unref > + * > + * Releases a reference on the plugin. When the last reference > + * is released, it will attempt to unload the plugin from memory. > + * The plugin may refuse to allow unloading if this would > + * result in an unsafe scenario. > + * > + */ > +void virLockManagerPluginUnref(virLockManagerPluginPtr plugin) > +{ > + if (!plugin) > + return; > + > + plugin->refs--; Shoudn't we protect those ref/unrefs with a global lock ? Chances of entering the race there sounds small but this looks racy Could be done as an improvement. > + if (plugin->refs > 0) > + return; besides minor comments, ACK, Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@xxxxxxxxxxxx | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/ -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list