mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-09-09 09:14:57 +00:00
The rule generating the HTML docs passing the --html flag to xsltproc. This makes it use the legacy HTML parser, which either ignores or tries to fix all sorts of broken XML tags. There's no reason why we should be writing broken XML in the first place, so removing --html and adding the XHTML doctype to all files forces us to create good XML. This adds the XHTML doc type and fixes many, many XML tag problems it exposes. Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
268 lines
7.8 KiB
XML
268 lines
7.8 KiB
XML
<?xml version="1.0" encoding="UTF-8"?>
|
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
|
<html xmlns="http://www.w3.org/1999/xhtml">
|
|
<body>
|
|
<h1>Resource Lock Manager</h1>
|
|
|
|
<ul id="toc"></ul>
|
|
|
|
<p>
|
|
This page describes the design of the resource lock manager
|
|
that is used for locking disk images, to ensure exclusive
|
|
access to content.
|
|
</p>
|
|
|
|
<h2><a name="goals">Goals</a></h2>
|
|
|
|
<p>
|
|
The high level goal is to prevent the same disk image being
|
|
used by more than one QEMU instance at a time (unless the
|
|
disk is marked as shareable, or readonly). The scenarios
|
|
to be prevented are thus:
|
|
</p>
|
|
|
|
<ol>
|
|
<li>
|
|
Two different guests running configured to point at the
|
|
same disk image.
|
|
</li>
|
|
<li>
|
|
One guest being started more than once on two different
|
|
machines due to admin mistake
|
|
</li>
|
|
<li>
|
|
One guest being started more than once on a single machine
|
|
due to libvirt driver bug on a single machine.
|
|
</li>
|
|
</ol>
|
|
|
|
<h2><a name="requirement">Requirements</a></h2>
|
|
|
|
<p>
|
|
The high level goal leads to a set of requirements
|
|
for the lock manager design
|
|
</p>
|
|
|
|
<ol>
|
|
<li>
|
|
A lock must be held on a disk whenever a QEMU process
|
|
has the disk open
|
|
</li>
|
|
<li>
|
|
The lock scheme must allow QEMU to be configured with
|
|
readonly, shared write, or exclusive writable disks
|
|
</li>
|
|
<li>
|
|
A lock handover must be performed during the migration
|
|
process where 2 QEMU processes will have the same disk
|
|
open concurrently.
|
|
</li>
|
|
<li>
|
|
The lock manager must be able to identify and kill the
|
|
process accessing the resource if the lock is revoked.
|
|
</li>
|
|
<li>
|
|
Locks can be acquired for arbitrary VM related resources,
|
|
as determined by the management application.
|
|
</li>
|
|
</ol>
|
|
|
|
<h2><a name="design">Design</a></h2>
|
|
|
|
<p>
|
|
Within a lock manager the following series of operations
|
|
will need to be supported.
|
|
</p>
|
|
|
|
<ul>
|
|
<li>
|
|
<strong>Register object</strong>
|
|
Register the identity of an object against which
|
|
locks will be acquired
|
|
</li>
|
|
<li>
|
|
<strong>Add resource</strong>
|
|
Associate a resource with an object for future
|
|
lock acquisition / release
|
|
</li>
|
|
<li>
|
|
<strong>Acquire locks</strong>
|
|
Acquire the locks for all resources associated
|
|
with the object
|
|
</li>
|
|
<li>
|
|
<strong>Release locks</strong>
|
|
Release the locks for all resources associated
|
|
with the object
|
|
</li>
|
|
<li>
|
|
<strong>Inquire locks</strong>
|
|
Get a representation of the state of the locks
|
|
for all resources associated with the object
|
|
</li>
|
|
</ul>
|
|
|
|
<h2><a name="impl">Plugin Implementations</a></h2>
|
|
|
|
<p>
|
|
Lock manager implementations are provided as LGPLv2+
|
|
licensed, dlopen()able library modules. The plugins
|
|
will be loadable from the following location:
|
|
</p>
|
|
|
|
<pre>
|
|
/usr/{lib,lib64}/libvirt/lock_manager/$NAME.so
|
|
</pre>
|
|
|
|
<p>
|
|
The lock manager plugin must export a single ELF
|
|
symbol named <code>virLockDriverImpl</code>, which is
|
|
a static instance of the <code>virLockDriver</code>
|
|
struct. The struct is defined in the header file
|
|
</p>
|
|
|
|
<pre>
|
|
#include <libvirt/plugins/lock_manager.h>
|
|
</pre>
|
|
|
|
<p>
|
|
All callbacks in the struct must be initialized
|
|
to non-NULL pointers. The semantics of each
|
|
callback are defined in the API docs embedded
|
|
in the previously mentioned header file
|
|
</p>
|
|
|
|
<h2><a name="qemuIntegrate">QEMU Driver integration</a></h2>
|
|
|
|
<p>
|
|
With the QEMU driver, the lock plugin will be set
|
|
in the <code>/etc/libvirt/qemu.conf</code> configuration
|
|
file by specifying the lock manager name.
|
|
</p>
|
|
|
|
<pre>
|
|
lockManager="sanlock"
|
|
</pre>
|
|
|
|
<p>
|
|
By default the lock manager will be a 'no op' implementation
|
|
for backwards compatibility
|
|
</p>
|
|
|
|
<h2><a name="usagePatterns">Lock usage patterns</a></h2>
|
|
|
|
<p>
|
|
The following pseudo code illustrates the common
|
|
patterns of operations invoked on the lock
|
|
manager plugin callbacks.
|
|
</p>
|
|
|
|
<h3><a name="usageLockAcquire">Lock acquisition</a></h3>
|
|
|
|
<p>
|
|
Initial lock acquisition will be performed from the
|
|
process that is to own the lock. This is typically
|
|
the QEMU child process, in between the fork+exec
|
|
pairing. When adding further resources on the fly,
|
|
to an existing object holding locks, this will be
|
|
done from the libvirtd process.
|
|
</p>
|
|
|
|
<pre>
|
|
virLockManagerParam params[] = {
|
|
{ .type = VIR_LOCK_MANAGER_PARAM_TYPE_UUID,
|
|
.key = "uuid",
|
|
},
|
|
{ .type = VIR_LOCK_MANAGER_PARAM_TYPE_STRING,
|
|
.key = "name",
|
|
.value = { .str = dom->def->name },
|
|
},
|
|
{ .type = VIR_LOCK_MANAGER_PARAM_TYPE_UINT,
|
|
.key = "id",
|
|
.value = { .i = dom->def->id },
|
|
},
|
|
{ .type = VIR_LOCK_MANAGER_PARAM_TYPE_UINT,
|
|
.key = "pid",
|
|
.value = { .i = dom->pid },
|
|
},
|
|
{ .type = VIR_LOCK_MANAGER_PARAM_TYPE_CSTRING,
|
|
.key = "uri",
|
|
.value = { .cstr = driver->uri },
|
|
},
|
|
};
|
|
mgr = virLockManagerNew(lockPlugin,
|
|
VIR_LOCK_MANAGER_TYPE_DOMAIN,
|
|
ARRAY_CARDINALITY(params),
|
|
params,
|
|
0)));
|
|
|
|
foreach (initial disks)
|
|
virLockManagerAddResource(mgr,
|
|
VIR_LOCK_MANAGER_RESOURCE_TYPE_DISK,
|
|
$path, 0, NULL, $flags);
|
|
|
|
if (virLockManagerAcquire(lock, NULL, 0) < 0);
|
|
...abort...
|
|
</pre>
|
|
|
|
<h3><a name="usageLockAttach">Lock release</a></h3>
|
|
|
|
<p>
|
|
The locks are all implicitly released when the process
|
|
that acquired them exits, however, a process may
|
|
voluntarily give up the lock by running
|
|
</p>
|
|
|
|
<pre>
|
|
char *state = NULL;
|
|
virLockManagerParam params[] = {
|
|
{ .type = VIR_LOCK_MANAGER_PARAM_TYPE_UUID,
|
|
.key = "uuid",
|
|
},
|
|
{ .type = VIR_LOCK_MANAGER_PARAM_TYPE_STRING,
|
|
.key = "name",
|
|
.value = { .str = dom->def->name },
|
|
},
|
|
{ .type = VIR_LOCK_MANAGER_PARAM_TYPE_UINT,
|
|
.key = "id",
|
|
.value = { .i = dom->def->id },
|
|
},
|
|
{ .type = VIR_LOCK_MANAGER_PARAM_TYPE_UINT,
|
|
.key = "pid",
|
|
.value = { .i = dom->pid },
|
|
},
|
|
{ .type = VIR_LOCK_MANAGER_PARAM_TYPE_CSTRING,
|
|
.key = "uri",
|
|
.value = { .cstr = driver->uri },
|
|
},
|
|
};
|
|
mgr = virLockManagerNew(lockPlugin,
|
|
VIR_LOCK_MANAGER_TYPE_DOMAIN,
|
|
ARRAY_CARDINALITY(params),
|
|
params,
|
|
0)));
|
|
|
|
foreach (initial disks)
|
|
virLockManagerAddResource(mgr,
|
|
VIR_LOCK_MANAGER_RESOURCE_TYPE_DISK,
|
|
$path, 0, NULL, $flags);
|
|
|
|
virLockManagerRelease(mgr, & state, 0);
|
|
</pre>
|
|
|
|
<p>
|
|
The returned state string can be passed to the
|
|
<code>virLockManagerAcquire</code> method to
|
|
later re-acquire the exact same locks. This
|
|
state transfer is commonly used when performing
|
|
live migration of virtual machines. By validating
|
|
the state the lock manager can ensure no other
|
|
VM has re-acquire the same locks on a different
|
|
host. The state can also be obtained without
|
|
releasing the locks, by calling the
|
|
<code>virLockManagerInquire</code> method.
|
|
</p>
|
|
|
|
</body>
|
|
</html>
|