mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2025-01-30 16:35:24 +00:00
virclosecallbacks: Add new close callbacks APIs
The new APIs store the list of callbacks for a VM inside the virDomainObj and also allow registering multiple callbacks for a single domain and also for multiple connections. For now this code is dormant until each driver using the old APIs is not refactored to use the new APIs. Signed-off-by: Peter Krempa <pkrempa@redhat.com> Reviewed-by: Pavel Hrdina <phrdina@redhat.com>
This commit is contained in:
parent
2cb13113c2
commit
cb195c19b7
@ -310,3 +310,339 @@ virCloseCallbacksRun(virCloseCallbacks *closeCallbacks,
|
||||
VIR_FREE(list->entries);
|
||||
VIR_FREE(list);
|
||||
}
|
||||
|
||||
|
||||
struct _virCloseCallbacksDomainData {
|
||||
virConnectPtr conn;
|
||||
virCloseCallback cb;
|
||||
};
|
||||
typedef struct _virCloseCallbacksDomainData virCloseCallbacksDomainData;
|
||||
|
||||
|
||||
static void
|
||||
virCloseCallbacksDomainDataFree(virCloseCallbacksDomainData* data)
|
||||
{
|
||||
g_free(data);
|
||||
}
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(virCloseCallbacksDomainData, virCloseCallbacksDomainDataFree);
|
||||
|
||||
|
||||
virClass *virCloseCallbacksDomainListClass;
|
||||
|
||||
struct _virCloseCallbacksDomainList {
|
||||
virObjectLockable parent;
|
||||
|
||||
GList *callbacks;
|
||||
};
|
||||
typedef struct _virCloseCallbacksDomainList virCloseCallbacksDomainList;
|
||||
|
||||
|
||||
static void
|
||||
virCloseCallbacksDomainListDispose(void *obj G_GNUC_UNUSED)
|
||||
{
|
||||
virCloseCallbacksDomainList *cc = obj;
|
||||
|
||||
g_list_free_full(cc->callbacks, (GDestroyNotify) virCloseCallbacksDomainDataFree);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
virCloseCallbacksDomainListOnceInit(void)
|
||||
{
|
||||
if (!(VIR_CLASS_NEW(virCloseCallbacksDomainList, virClassForObjectLockable())))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
VIR_ONCE_GLOBAL_INIT(virCloseCallbacksDomainList);
|
||||
|
||||
|
||||
/**
|
||||
* virCloseCallbacksDomainAlloc:
|
||||
*
|
||||
* Allocates and returns a data structure for holding close callback data in
|
||||
* a virDomainObj.
|
||||
*/
|
||||
virObject *
|
||||
virCloseCallbacksDomainAlloc(void)
|
||||
{
|
||||
if (virCloseCallbacksDomainListInitialize() < 0)
|
||||
abort();
|
||||
|
||||
return virObjectNew(virCloseCallbacksDomainListClass);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* virCloseCallbacksDomainAdd:
|
||||
* @vm: domain object
|
||||
* @conn: pointer to the connection which should trigger the close callback
|
||||
* @cb: pointer to the callback function
|
||||
*
|
||||
* Registers @cb as a connection close callback for the @conn connection with
|
||||
* the @vm domain. Duplicate registrations are ignored.
|
||||
*
|
||||
* Caller must hold lock on @vm.
|
||||
*/
|
||||
void
|
||||
virCloseCallbacksDomainAdd(virDomainObj *vm,
|
||||
virConnectPtr conn,
|
||||
virCloseCallback cb)
|
||||
{
|
||||
virCloseCallbacksDomainList *cc = (virCloseCallbacksDomainList *) vm->closecallbacks;
|
||||
|
||||
if (!conn || !cb)
|
||||
return;
|
||||
|
||||
VIR_WITH_OBJECT_LOCK_GUARD(cc) {
|
||||
virCloseCallbacksDomainData *data;
|
||||
GList *n;
|
||||
|
||||
for (n = cc->callbacks; n; n = n->next) {
|
||||
data = n->data;
|
||||
|
||||
if (data->cb == cb && data->conn == conn)
|
||||
return;
|
||||
}
|
||||
|
||||
data = g_new0(virCloseCallbacksDomainData, 1);
|
||||
data->conn = conn;
|
||||
data->cb = cb;
|
||||
|
||||
cc->callbacks = g_list_prepend(cc->callbacks, data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* virCloseCallbacksDomainMatch:
|
||||
* @data: pointer to a close callback data structure
|
||||
* @conn: connection pointer matched against @data
|
||||
* @cb: callback pointer matched against @data
|
||||
*
|
||||
* Returns true if the @data callback structure matches the requested @conn
|
||||
* and/or @cb parameters. If either of @conn/@cb is NULL it is interpreted as
|
||||
* a wildcard.
|
||||
*/
|
||||
static bool
|
||||
virCloseCallbacksDomainMatch(virCloseCallbacksDomainData *data,
|
||||
virConnectPtr conn,
|
||||
virCloseCallback cb)
|
||||
{
|
||||
if (conn && cb)
|
||||
return data->conn == conn && data->cb == cb;
|
||||
|
||||
if (conn)
|
||||
return data->conn == conn;
|
||||
|
||||
if (cb)
|
||||
return data->cb == cb;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* virCloseCallbacksDomainIsRegistered:
|
||||
* @vm: domain object
|
||||
* @conn: connection pointer
|
||||
* @cb: callback pointer
|
||||
*
|
||||
* Returns true if @vm has one or more matching (see virCloseCallbacksDomainMatch)
|
||||
* callback(s) registered. Caller must hold lock on @vm.
|
||||
*/
|
||||
bool
|
||||
virCloseCallbacksDomainIsRegistered(virDomainObj *vm,
|
||||
virConnectPtr conn,
|
||||
virCloseCallback cb)
|
||||
{
|
||||
virCloseCallbacksDomainList *cc = (virCloseCallbacksDomainList *) vm->closecallbacks;
|
||||
|
||||
VIR_WITH_OBJECT_LOCK_GUARD(cc) {
|
||||
GList *n;
|
||||
|
||||
for (n = cc->callbacks; n; n = n->next) {
|
||||
virCloseCallbacksDomainData *data = n->data;
|
||||
|
||||
if (virCloseCallbacksDomainMatch(data, conn, cb))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* virCloseCallbacksDomainRemove:
|
||||
* @vm: domain object
|
||||
* @conn: connection pointer
|
||||
* @cb: callback pointer
|
||||
*
|
||||
* Removes all the registered matching (see virCloseCallbacksDomainMatch)
|
||||
* callbacks for @vm. Caller must hold lock on @vm.
|
||||
*/
|
||||
void
|
||||
virCloseCallbacksDomainRemove(virDomainObj *vm,
|
||||
virConnectPtr conn,
|
||||
virCloseCallback cb)
|
||||
{
|
||||
virCloseCallbacksDomainList *cc = (virCloseCallbacksDomainList *) vm->closecallbacks;
|
||||
|
||||
VIR_WITH_OBJECT_LOCK_GUARD(cc) {
|
||||
GList *n = cc->callbacks;
|
||||
|
||||
while (n) {
|
||||
GList *cur = n;
|
||||
|
||||
n = n->next;
|
||||
|
||||
if (virCloseCallbacksDomainMatch(cur->data, conn, cb)) {
|
||||
cc->callbacks = g_list_remove_link(cc->callbacks, cur);
|
||||
g_list_free_full(cur, (GDestroyNotify) virCloseCallbacksDomainDataFree);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* virCloseCallbacksDomainFetchForConn:
|
||||
* @vm: domain object
|
||||
* @conn: pointer to connection being closed
|
||||
*
|
||||
* Fetches connection close callbacks for @conn from @vm. The fetched close
|
||||
* callbacks are removed from the list of callbacks of @vm. This function
|
||||
* must be called with lock on @vm held. Caller is responsible for freeing the
|
||||
* returned list.
|
||||
*/
|
||||
static GList *
|
||||
virCloseCallbacksDomainFetchForConn(virDomainObj *vm,
|
||||
virConnectPtr conn)
|
||||
{
|
||||
virCloseCallbacksDomainList *cc = (virCloseCallbacksDomainList *) vm->closecallbacks;
|
||||
GList *conncallbacks = NULL;
|
||||
|
||||
VIR_WITH_OBJECT_LOCK_GUARD(cc) {
|
||||
GList *n;
|
||||
|
||||
for (n = cc->callbacks; n;) {
|
||||
virCloseCallbacksDomainData *data = n->data;
|
||||
GList *cur = n;
|
||||
|
||||
n = n->next;
|
||||
|
||||
if (data->conn == conn) {
|
||||
cc->callbacks = g_list_remove_link(cc->callbacks, cur);
|
||||
conncallbacks = g_list_concat(cur, conncallbacks);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return conncallbacks;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* virCloseCallbacksDomainRun
|
||||
* @vm: domain object
|
||||
* @conn: pointer to connection being closed
|
||||
*
|
||||
* Fetches and sequentially calls all connection close callbacks for @conn from
|
||||
* @vm. This function must be called with lock on @vm held.
|
||||
*/
|
||||
static void
|
||||
virCloseCallbacksDomainRun(virDomainObj *vm,
|
||||
virConnectPtr conn)
|
||||
{
|
||||
g_autolist(virCloseCallbacksDomainData) callbacks = NULL;
|
||||
GList *n;
|
||||
|
||||
callbacks = virCloseCallbacksDomainFetchForConn(vm, conn);
|
||||
|
||||
for (n = callbacks; n; n = n->next) {
|
||||
virCloseCallbacksDomainData *data = n->data;
|
||||
|
||||
VIR_DEBUG("vm='%s' cb='%p'", vm->def->name, data->cb);
|
||||
|
||||
(data->cb)(vm, conn);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* virCloseCallbacksDomainHasCallbackForConn:
|
||||
* @vm: domain object
|
||||
* @conn: connection being closed
|
||||
*
|
||||
* Returns true if @vm has a callback registered for the @conn connection. This
|
||||
* function doesn't require a lock being held on @vm.
|
||||
*/
|
||||
static bool
|
||||
virCloseCallbacksDomainHasCallbackForConn(virDomainObj *vm,
|
||||
virConnectPtr conn)
|
||||
{
|
||||
/* we can access vm->closecallbacks as it's a immutable pointer */
|
||||
virCloseCallbacksDomainList *cc = (virCloseCallbacksDomainList *) vm->closecallbacks;
|
||||
|
||||
if (!cc)
|
||||
return false;
|
||||
|
||||
VIR_WITH_OBJECT_LOCK_GUARD(cc) {
|
||||
GList *n;
|
||||
|
||||
for (n = cc->callbacks; n; n = n->next) {
|
||||
virCloseCallbacksDomainData *data = n->data;
|
||||
|
||||
if (data->conn == conn)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* virCloseCallbacksDomainRunForConn:
|
||||
* @domains: domain list object
|
||||
* @conn: connection being closed
|
||||
*
|
||||
* Finds all domains in @domains which registered one or more connection close
|
||||
* callbacks for @conn and calls the callbacks. This function is designed to
|
||||
* be called from virDrvConnectClose function of individual drivers.
|
||||
*
|
||||
* To minimize lock contention the function first fetches a list of all domain
|
||||
* objects, then checks whether a connect close callback is actually registered
|
||||
* for the domain object and just then acquires the lock on the VM object.
|
||||
*/
|
||||
void
|
||||
virCloseCallbacksDomainRunForConn(virDomainObjList *domains,
|
||||
virConnectPtr conn)
|
||||
{
|
||||
virDomainObj **vms = NULL;
|
||||
size_t nvms;
|
||||
size_t i;
|
||||
|
||||
VIR_DEBUG("conn=%p", conn);
|
||||
|
||||
virDomainObjListCollectAll(domains, &vms, &nvms);
|
||||
|
||||
for (i = 0; i < nvms; i++) {
|
||||
virDomainObj *vm = vms[i];
|
||||
|
||||
if (!virCloseCallbacksDomainHasCallbackForConn(vm, conn))
|
||||
continue;
|
||||
|
||||
VIR_WITH_OBJECT_LOCK_GUARD(vm) {
|
||||
/* VIR_WITH_OBJECT_LOCK_GUARD is a for loop, so this break applies to that */
|
||||
if (vm->removing)
|
||||
break;
|
||||
|
||||
virCloseCallbacksDomainRun(vm, conn);
|
||||
}
|
||||
}
|
||||
|
||||
virObjectListFreeCount(vms, nvms);
|
||||
}
|
||||
|
@ -49,3 +49,27 @@ void
|
||||
virCloseCallbacksRun(virCloseCallbacks *closeCallbacks,
|
||||
virConnectPtr conn,
|
||||
virDomainObjList *domains);
|
||||
|
||||
/* ---- */
|
||||
|
||||
virObject *
|
||||
virCloseCallbacksDomainAlloc(void);
|
||||
|
||||
void
|
||||
virCloseCallbacksDomainAdd(virDomainObj *vm,
|
||||
virConnectPtr conn,
|
||||
virCloseCallback cb);
|
||||
|
||||
void
|
||||
virCloseCallbacksDomainRemove(virDomainObj *vm,
|
||||
virConnectPtr conn,
|
||||
virCloseCallback cb);
|
||||
|
||||
bool
|
||||
virCloseCallbacksDomainIsRegistered(virDomainObj *vm,
|
||||
virConnectPtr conn,
|
||||
virCloseCallback cb);
|
||||
|
||||
void
|
||||
virCloseCallbacksDomainRunForConn(virDomainObjList *domains,
|
||||
virConnectPtr conn);
|
||||
|
@ -1607,6 +1607,11 @@ virDomainDriverSetupPersistentDefBlkioParams;
|
||||
|
||||
|
||||
# hypervisor/virclosecallbacks.h
|
||||
virCloseCallbacksDomainAdd;
|
||||
virCloseCallbacksDomainAlloc;
|
||||
virCloseCallbacksDomainIsRegistered;
|
||||
virCloseCallbacksDomainRemove;
|
||||
virCloseCallbacksDomainRunForConn;
|
||||
virCloseCallbacksGet;
|
||||
virCloseCallbacksNew;
|
||||
virCloseCallbacksRun;
|
||||
|
Loading…
x
Reference in New Issue
Block a user