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@xxxxxxxxxx> --- 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 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list