Add a callback mechanism as a side door of sorts to the event
mgmt functions that are triggered when a node_device object is
added or removed. This includes a mechanism to enumerate already
added devices for those stateInitialize functions called after
the initial nodedevRegister is called. If there is no enumeration
callback, a failure would be returned since the caller would
seem to need that.
Signed-off-by: John Ferlan <jferlan(a)redhat.com>
---
src/check-aclrules.pl | 3 +-
src/check-driverimpls.pl | 1 +
src/conf/node_device_conf.c | 112 ++++++++++++++++++++++++++++++++++++-
src/conf/node_device_conf.h | 18 ++++++
src/libvirt_private.syms | 3 +
src/node_device/node_device_udev.c | 29 ++++++++++
6 files changed, 164 insertions(+), 2 deletions(-)
diff --git a/src/check-aclrules.pl b/src/check-aclrules.pl
index 8739cda..161d954 100755
--- a/src/check-aclrules.pl
+++ b/src/check-aclrules.pl
@@ -243,7 +243,8 @@ while (<>) {
} elsif (/^(?:static\s+)?(vir(?:\w+)?Driver)\s+/) {
if ($1 ne "virNWFilterCallbackDriver" &&
$1 ne "virNWFilterTechDriver" &&
- $1 ne "virDomainConfNWFilterDriver") {
+ $1 ne "virDomainConfNWFilterDriver" &&
+ $1 ne "virNodeDeviceCallbackDriver") {
$intable = 1;
$table = $1;
}
diff --git a/src/check-driverimpls.pl b/src/check-driverimpls.pl
index e320558..b707fc8 100755
--- a/src/check-driverimpls.pl
+++ b/src/check-driverimpls.pl
@@ -70,6 +70,7 @@ while (<>) {
} elsif (/^(?:static\s+)?(vir(?:\w+)?Driver)\s+/) {
next if $1 eq "virNWFilterCallbackDriver" ||
$1 eq "virNWFilterTechDriver" ||
+ $1 eq "virNodeDeviceCallbackDriver" ||
$1 eq "virConnectDriver";
$intable = 1;
$table = $1;
diff --git a/src/conf/node_device_conf.c b/src/conf/node_device_conf.c
index c802840..061f013 100644
--- a/src/conf/node_device_conf.c
+++ b/src/conf/node_device_conf.c
@@ -278,6 +278,99 @@ void virNodeDeviceDefFree(virNodeDeviceDefPtr def)
VIR_FREE(def);
}
+
+/* Provide callback infrastructure that is expected to be called when
+ * devices are added/removed from a node_device subsystem that supports
+ * the callback mechanism. This provides a way for drivers to register
+ * to be notified when a node_device is added/removed. */
+virNodedevEnumerateAddDevices virNodedevEnumerateAddDevicesCb = NULL;
+int nodeDeviceCallbackDriver;
+#define MAX_CALLBACK_DRIVER 10
+static virNodeDeviceCallbackDriverPtr nodeDeviceDrvArray[MAX_CALLBACK_DRIVER];
+
+
+/**
+ * @cb: Driver function to be called.
+ *
+ * Set a callback at driver initialization to provide a callback to a
+ * driver specific function that can handle the enumeration of the existing
+ * devices and the addition of those devices for the registering driver.
+ *
+ * The initial node_device enumeration is done prior to other drivers, thus
+ * this provides a mechanism to load the existing data since the functions
+ * virNodeDeviceAssignDef and virNodeDeviceObjRemove would typically
+ * only be called when a new device is added/removed after the initial
+ * enumeration. The registering driver will need to handle duplication
+ * of data.
+ */
+void
+virNodeDeviceConfEnumerateInit(virNodedevEnumerateAddDevices cb)
+{
+ virNodedevEnumerateAddDevicesCb = cb;
+}
+
+
+/**
+ * @cbd: Driver callback pointers to add/remove devices
+ *
+ * Register a callback function in the registering driver to be called
+ * when devices are added or removed. Additionally, ensure the initial
+ * enumeration of the devices is completed taking care to do it after
+ * setting the callbacks
+ *
+ * Returns address of enumerate callback on success, NULL on failure
+ */
+virNodedevEnumerateAddDevices
+virNodeDeviceRegisterCallbackDriver(virNodeDeviceCallbackDriverPtr cbd)
+{
+ if (nodeDeviceCallbackDriver >= MAX_CALLBACK_DRIVER) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("no space available for another callback driver"));
+ return NULL;
+ }
+
+ if (!cbd->nodeDeviceAdd || !cbd->nodeDeviceRemove) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("callback requires both add and remove functions"));
+ return NULL;
+ }
+
+ if (!virNodedevEnumerateAddDevicesCb) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("no enumeration callback API is registered"));
+ return NULL;
+ }
+
+ nodeDeviceDrvArray[nodeDeviceCallbackDriver++] = cbd;
+
+ return virNodedevEnumerateAddDevicesCb;
+}
+
+
+/**
+ * @cb: Driver function to be called.
+ *
+ * Clear the callback function. It'll be up to the calling driver to
+ * clear it's own data properly
+ */
+void
+virNodeDeviceUnregisterCallbackDriver(virNodeDeviceCallbackDriverPtr cbd)
+{
+ size_t i = 0;
+
+ while (i < nodeDeviceCallbackDriver && nodeDeviceDrvArray[i] != cbd)
+ i++;
+
+ if (i < nodeDeviceCallbackDriver) {
+ memmove(&nodeDeviceDrvArray[i], &nodeDeviceDrvArray[i+1],
+ (nodeDeviceCallbackDriver - i - 1) *
+ sizeof(nodeDeviceDrvArray[i]));
+ nodeDeviceDrvArray[i] = NULL;
+ nodeDeviceCallbackDriver--;
+ }
+}
+
+
void virNodeDeviceObjFree(virNodeDeviceObjPtr dev)
{
if (!dev)
@@ -304,6 +397,7 @@ void virNodeDeviceObjListFree(virNodeDeviceObjListPtr devs)
virNodeDeviceObjPtr virNodeDeviceAssignDef(virNodeDeviceObjListPtr devs,
virNodeDeviceDefPtr def)
{
+ size_t i;
virNodeDeviceObjPtr device;
if ((device = virNodeDeviceFindByName(devs, def->name))) {
@@ -330,6 +424,13 @@ virNodeDeviceObjPtr virNodeDeviceAssignDef(virNodeDeviceObjListPtr
devs,
}
device->def = def;
+ /* Call any registered drivers that want to be notified of a new device */
+ for (i = 0; i < nodeDeviceCallbackDriver; i++) {
+ if (nodeDeviceDrvArray[i]->nodeDeviceAdd(def, false) < 0)
+ VIR_WARN("Failed to add device name '%s' parent '%s' for
"
+ "register callback %zu", def->name, def->parent,
i);
+ }
+
return device;
}
@@ -337,13 +438,22 @@ virNodeDeviceObjPtr virNodeDeviceAssignDef(virNodeDeviceObjListPtr
devs,
void virNodeDeviceObjRemove(virNodeDeviceObjListPtr devs,
virNodeDeviceObjPtr *dev)
{
- size_t i;
+ size_t i, j;
virNodeDeviceObjUnlock(*dev);
for (i = 0; i < devs->count; i++) {
virNodeDeviceObjLock(*dev);
if (devs->objs[i] == *dev) {
+ /* Call any registered drivers that want to be notified of a
+ * removed device */
+ for (j = 0; j < nodeDeviceCallbackDriver; j++) {
+ if (nodeDeviceDrvArray[j]->nodeDeviceRemove((*dev)->def) < 0) {
+ VIR_WARN("failed to remove name='%s' parent='%s'
from "
+ "callback driver, continuing",
+ (*dev)->def->name, (*dev)->def->parent);
+ }
+ }
virNodeDeviceObjUnlock(*dev);
virNodeDeviceObjFree(devs->objs[i]);
*dev = NULL;
diff --git a/src/conf/node_device_conf.h b/src/conf/node_device_conf.h
index 8213c27..b1d878a 100644
--- a/src/conf/node_device_conf.h
+++ b/src/conf/node_device_conf.h
@@ -234,6 +234,24 @@ struct _virNodeDeviceDef {
virNodeDevCapsDefPtr caps; /* optional device capabilities */
};
+/* Callback mechanism to add/remove node device's by name/parent
+ * to a target driver that cares to know */
+typedef int (*virNodeDeviceAdd)(virNodeDeviceDefPtr def, bool enumerate);
+typedef int (*virNodeDeviceRemove)(virNodeDeviceDefPtr def);
+
+typedef struct _virNodeDeviceCallbackDriver virNodeDeviceCallbackDriver;
+typedef virNodeDeviceCallbackDriver *virNodeDeviceCallbackDriverPtr;
+struct _virNodeDeviceCallbackDriver {
+ const char *name;
+ virNodeDeviceAdd nodeDeviceAdd;
+ virNodeDeviceRemove nodeDeviceRemove;
+};
+
+typedef int (*virNodedevEnumerateAddDevices)(virNodeDeviceAdd deviceAddCb);
+void virNodeDeviceConfEnumerateInit(virNodedevEnumerateAddDevices cb);
+
+virNodedevEnumerateAddDevices
virNodeDeviceRegisterCallbackDriver(virNodeDeviceCallbackDriverPtr);
+void virNodeDeviceUnregisterCallbackDriver(virNodeDeviceCallbackDriverPtr);
typedef struct _virNodeDeviceObj virNodeDeviceObj;
typedef virNodeDeviceObj *virNodeDeviceObjPtr;
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index e6ccd69..f426c76 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -695,6 +695,7 @@ virNodeDevCapsDefFree;
virNodeDevCapTypeFromString;
virNodeDevCapTypeToString;
virNodeDeviceAssignDef;
+virNodeDeviceConfEnumerateInit;
virNodeDeviceDefFormat;
virNodeDeviceDefFree;
virNodeDeviceDefParseFile;
@@ -711,6 +712,8 @@ virNodeDeviceObjListFree;
virNodeDeviceObjLock;
virNodeDeviceObjRemove;
virNodeDeviceObjUnlock;
+virNodeDeviceRegisterCallbackDriver;
+virNodeDeviceUnregisterCallbackDriver;
# conf/node_device_event.h
diff --git a/src/node_device/node_device_udev.c b/src/node_device/node_device_udev.c
index 1016075..52e5e97 100644
--- a/src/node_device/node_device_udev.c
+++ b/src/node_device/node_device_udev.c
@@ -1529,6 +1529,33 @@ static int udevPCITranslateInit(bool privileged ATTRIBUTE_UNUSED)
return 0;
}
+
+/*
+ * @deviceAddCb: Callback routine for adding a device
+ *
+ * Enumerate all known devices calling the add device callback function
+ *
+ * Returns 0 on success, -1 on failure
+ */
+static int
+udevEnumerateAddDevices(virNodeDeviceAdd deviceAddCb)
+{
+ size_t i;
+ int ret = 0;
+
+ nodeDeviceLock();
+ for (i = 0; i < driver->devs.count && ret >= 0; i++) {
+ virNodeDeviceObjPtr obj = driver->devs.objs[i];
+ virNodeDeviceObjLock(obj);
+ ret = deviceAddCb(obj->def, true);
+ virNodeDeviceObjUnlock(obj);
+ }
+ nodeDeviceUnlock();
+
+ return ret;
+}
+
+
static int nodeStateInitialize(bool privileged,
virStateInhibitCallback callback ATTRIBUTE_UNUSED,
void *opaque ATTRIBUTE_UNUSED)
@@ -1606,6 +1633,8 @@ static int nodeStateInitialize(bool privileged,
if (udevEnumerateDevices(udev) != 0)
goto cleanup;
+ virNodeDeviceConfEnumerateInit(udevEnumerateAddDevices);
+
ret = 0;
cleanup:
--
2.9.3