[libvirt] [RFC PATCH 0/3] Implement two-tier driver loading

This patchset implements a two-tier driver loading system. I split the hypervisor drivers out into their own tier, which is loaded after the other drivers. This has the net effect of ensuring that things like secrets, networks, etc., are initialized and auto-started before any hypervisors, such as qemu, lxc, etc., are touched. This resolves the race condition present when starting libvirtd while domains are running, which happens when restarting libvirtd after having started at least one domain. This patch will work without my config driver patchset, but does prevent RBD storage pools from auto-starting. It may also affect other pool types, but I only have file and RBD to test with, personally. The RBD storage pool is only affected because it requires a hypervisor connection (prior to this patchset, that connection was hardcoded to be a connection to qemu on localhost) in order to look up secrets. Any pool type that does not use/need data outside of the base storage pool definition should continue to auto-start (file backed pools definitely still work) and also no longer be part of the restart race condition. For anyone who is not familiar with the race condition I mentioned above, the basic description is the upon restarting libvirtd, any running QEMU domains using storage pool backed disks are killed (randomly) due to their storage pool not being online. This is due to storage pool auto-start not having finished before QEMU initalization runs. I would appreciate any comments and suggestions about this patchset. It works for me on 4 machines running three different distros of Linux (Archlinux, Gentoo, and CentOS), so I would imagine it should work most anywhere. Adam Walters (3): driver: Implement new state driver field storage: Fix hardcoded qemu connection libvirt: Implement two-tier driver loading src/config/config_driver.c | 1 + src/driver.h | 6 ++++ src/interface/interface_backend_netcf.c | 1 + src/libvirt.c | 57 ++++++++++++++++++++++++++++----- src/libxl/libxl_driver.c | 1 + src/lxc/lxc_driver.c | 1 + src/network/bridge_driver.c | 1 + src/node_device/node_device_hal.c | 1 + src/node_device/node_device_udev.c | 1 + src/nwfilter/nwfilter_driver.c | 1 + src/qemu/qemu_driver.c | 1 + src/remote/remote_driver.c | 1 + src/secret/secret_driver.c | 1 + src/storage/storage_driver.c | 13 ++++---- src/uml/uml_driver.c | 1 + src/xen/xen_driver.c | 1 + 16 files changed, 75 insertions(+), 14 deletions(-) -- 1.8.5.2

This implements a new field in the virStateDriver struct. In order to prevent possible compilation issues, this patch also implements te new field in all of the existing drivers. Other than in driver.h, the changes are all a single line addition to the files. Signed-off-by: Adam Walters <adam@pandorasboxen.com> --- src/config/config_driver.c | 1 + src/driver.h | 6 ++++++ src/interface/interface_backend_netcf.c | 1 + src/libxl/libxl_driver.c | 1 + src/lxc/lxc_driver.c | 1 + src/network/bridge_driver.c | 1 + src/node_device/node_device_hal.c | 1 + src/node_device/node_device_udev.c | 1 + src/nwfilter/nwfilter_driver.c | 1 + src/qemu/qemu_driver.c | 1 + src/remote/remote_driver.c | 1 + src/secret/secret_driver.c | 1 + src/storage/storage_driver.c | 1 + src/uml/uml_driver.c | 1 + src/xen/xen_driver.c | 1 + 15 files changed, 20 insertions(+) diff --git a/src/config/config_driver.c b/src/config/config_driver.c index a057300..a817c7a 100644 --- a/src/config/config_driver.c +++ b/src/config/config_driver.c @@ -228,6 +228,7 @@ static virStateDriver configStateDriver = { .stateInitialize = configStateInitialize, .stateCleanup = configStateCleanup, .stateReload = configStateReload, + .stateType = VIR_DRV_STATE_DRV_LIBVIRT, }; int configRegister(void) { diff --git a/src/driver.h b/src/driver.h index b6927ea..6863910 100644 --- a/src/driver.h +++ b/src/driver.h @@ -1833,6 +1833,11 @@ typedef int typedef int (*virDrvStateStop)(void); +typedef enum { + VIR_DRV_STATE_DRV_LIBVIRT = 1, + VIR_DRV_STATE_DRV_HYPERVISOR = 2, +} virDrvStateDrvType; + typedef struct _virStateDriver virStateDriver; typedef virStateDriver *virStateDriverPtr; @@ -1843,6 +1848,7 @@ struct _virStateDriver { virDrvStateCleanup stateCleanup; virDrvStateReload stateReload; virDrvStateStop stateStop; + virDrvStateDrvType stateType; }; # endif diff --git a/src/interface/interface_backend_netcf.c b/src/interface/interface_backend_netcf.c index c4e18c4..8b910f9 100644 --- a/src/interface/interface_backend_netcf.c +++ b/src/interface/interface_backend_netcf.c @@ -1157,6 +1157,7 @@ static virStateDriver interfaceStateDriver = { .stateInitialize = netcfStateInitialize, .stateCleanup = netcfStateCleanup, .stateReload = netcfStateReload, + .stateType = VIR_DRV_STATE_DRV_LIBVIRT, }; int netcfIfaceRegister(void) { diff --git a/src/libxl/libxl_driver.c b/src/libxl/libxl_driver.c index d91744f..cce7798 100644 --- a/src/libxl/libxl_driver.c +++ b/src/libxl/libxl_driver.c @@ -4292,6 +4292,7 @@ static virStateDriver libxlStateDriver = { .stateAutoStart = libxlStateAutoStart, .stateCleanup = libxlStateCleanup, .stateReload = libxlStateReload, + .stateType = VIR_DRV_STATE_DRV_HYPERVISOR, }; diff --git a/src/lxc/lxc_driver.c b/src/lxc/lxc_driver.c index e5298d1..3fe88c6 100644 --- a/src/lxc/lxc_driver.c +++ b/src/lxc/lxc_driver.c @@ -4815,6 +4815,7 @@ static virStateDriver lxcStateDriver = { .stateAutoStart = lxcStateAutoStart, .stateCleanup = lxcStateCleanup, .stateReload = lxcStateReload, + .stateType = VIR_DRV_STATE_DRV_HYPERVISOR, }; int lxcRegister(void) diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c index 3e10758..be49c78 100644 --- a/src/network/bridge_driver.c +++ b/src/network/bridge_driver.c @@ -3131,6 +3131,7 @@ static virStateDriver networkStateDriver = { .stateAutoStart = networkStateAutoStart, .stateCleanup = networkStateCleanup, .stateReload = networkStateReload, + .stateType = VIR_DRV_STATE_DRV_LIBVIRT, }; int networkRegister(void) { diff --git a/src/node_device/node_device_hal.c b/src/node_device/node_device_hal.c index fafd520..23cee5e 100644 --- a/src/node_device/node_device_hal.c +++ b/src/node_device/node_device_hal.c @@ -827,6 +827,7 @@ static virStateDriver halStateDriver = { .stateInitialize = nodeStateInitialize, /* 0.5.0 */ .stateCleanup = nodeStateCleanup, /* 0.5.0 */ .stateReload = nodeStateReload, /* 0.5.0 */ + .stateType = VIR_DRV_STATE_DRV_LIBVIRT, }; int diff --git a/src/node_device/node_device_udev.c b/src/node_device/node_device_udev.c index 5d49968..6282072 100644 --- a/src/node_device/node_device_udev.c +++ b/src/node_device/node_device_udev.c @@ -1816,6 +1816,7 @@ static virStateDriver udevStateDriver = { .stateInitialize = nodeStateInitialize, /* 0.7.3 */ .stateCleanup = nodeStateCleanup, /* 0.7.3 */ .stateReload = nodeStateReload, /* 0.7.3 */ + .stateType = VIR_DRV_STATE_DRV_LIBVIRT, }; int udevNodeRegister(void) diff --git a/src/nwfilter/nwfilter_driver.c b/src/nwfilter/nwfilter_driver.c index d21dd82..a427a0b 100644 --- a/src/nwfilter/nwfilter_driver.c +++ b/src/nwfilter/nwfilter_driver.c @@ -702,6 +702,7 @@ static virStateDriver stateDriver = { .stateInitialize = nwfilterStateInitialize, .stateCleanup = nwfilterStateCleanup, .stateReload = nwfilterStateReload, + .stateType = VIR_DRV_STATE_DRV_LIBVIRT, }; diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 45d11cd..8d68220 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -16190,6 +16190,7 @@ static virStateDriver qemuStateDriver = { .stateCleanup = qemuStateCleanup, .stateReload = qemuStateReload, .stateStop = qemuStateStop, + .stateType = VIR_DRV_STATE_DRV_HYPERVISOR, }; int qemuRegister(void) { diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index be282d6..5e2fbd1 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -7299,6 +7299,7 @@ static virNWFilterDriver nwfilter_driver = { static virStateDriver state_driver = { .name = "Remote", .stateInitialize = remoteStateInitialize, + .stateType = VIR_DRV_STATE_DRV_LIBVIRT, }; #endif diff --git a/src/secret/secret_driver.c b/src/secret/secret_driver.c index 9f7f946..32ee81d 100644 --- a/src/secret/secret_driver.c +++ b/src/secret/secret_driver.c @@ -1189,6 +1189,7 @@ static virStateDriver stateDriver = { .stateInitialize = secretStateInitialize, .stateCleanup = secretStateCleanup, .stateReload = secretStateReload, + .stateType = VIR_DRV_STATE_DRV_LIBVIRT, }; int diff --git a/src/storage/storage_driver.c b/src/storage/storage_driver.c index baba9c9..a614279 100644 --- a/src/storage/storage_driver.c +++ b/src/storage/storage_driver.c @@ -2646,6 +2646,7 @@ static virStateDriver stateDriver = { .stateAutoStart = storageStateAutoStart, .stateCleanup = storageStateCleanup, .stateReload = storageStateReload, + .stateType = VIR_DRV_STATE_DRV_LIBVIRT, }; int storageRegister(void) diff --git a/src/uml/uml_driver.c b/src/uml/uml_driver.c index 1784eb5..336ffde 100644 --- a/src/uml/uml_driver.c +++ b/src/uml/uml_driver.c @@ -2900,6 +2900,7 @@ static virStateDriver umlStateDriver = { .stateAutoStart = umlStateAutoStart, .stateCleanup = umlStateCleanup, .stateReload = umlStateReload, + .stateType = VIR_DRV_STATE_DRV_HYPERVISOR, }; int umlRegister(void) { diff --git a/src/xen/xen_driver.c b/src/xen/xen_driver.c index 4103dc9..97084ed 100644 --- a/src/xen/xen_driver.c +++ b/src/xen/xen_driver.c @@ -279,6 +279,7 @@ static virStateDriver state_driver = { .name = "Xen", .stateInitialize = xenUnifiedStateInitialize, .stateCleanup = xenUnifiedStateCleanup, + .stateType = VIR_DRV_STATE_DRV_HYPERVISOR, }; /*----- Dispatch functions. -----*/ -- 1.8.5.2

On 12/21/13 05:14, Adam Walters wrote:
This implements a new field in the virStateDriver struct. In order to prevent possible compilation issues, this patch also implements te new field in all of the existing drivers. Other than in driver.h, the changes are all a single line addition to the files.
Please break this long line as advised in 2/3.
Signed-off-by: Adam Walters <adam@pandorasboxen.com> --- src/config/config_driver.c | 1 + src/driver.h | 6 ++++++ src/interface/interface_backend_netcf.c | 1 + src/libxl/libxl_driver.c | 1 + src/lxc/lxc_driver.c | 1 + src/network/bridge_driver.c | 1 + src/node_device/node_device_hal.c | 1 + src/node_device/node_device_udev.c | 1 + src/nwfilter/nwfilter_driver.c | 1 + src/qemu/qemu_driver.c | 1 + src/remote/remote_driver.c | 1 + src/secret/secret_driver.c | 1 + src/storage/storage_driver.c | 1 + src/uml/uml_driver.c | 1 + src/xen/xen_driver.c | 1 + 15 files changed, 20 insertions(+)
diff --git a/src/config/config_driver.c b/src/config/config_driver.c index a057300..a817c7a 100644 --- a/src/config/config_driver.c +++ b/src/config/config_driver.c @@ -228,6 +228,7 @@ static virStateDriver configStateDriver = { .stateInitialize = configStateInitialize, .stateCleanup = configStateCleanup, .stateReload = configStateReload, + .stateType = VIR_DRV_STATE_DRV_LIBVIRT, };
int configRegister(void) { diff --git a/src/driver.h b/src/driver.h index b6927ea..6863910 100644 --- a/src/driver.h +++ b/src/driver.h @@ -1833,6 +1833,11 @@ typedef int typedef int (*virDrvStateStop)(void);
+typedef enum { + VIR_DRV_STATE_DRV_LIBVIRT = 1, + VIR_DRV_STATE_DRV_HYPERVISOR = 2, +} virDrvStateDrvType; + typedef struct _virStateDriver virStateDriver; typedef virStateDriver *virStateDriverPtr;
@@ -1843,6 +1848,7 @@ struct _virStateDriver { virDrvStateCleanup stateCleanup; virDrvStateReload stateReload; virDrvStateStop stateStop; + virDrvStateDrvType stateType; }; # endif
These additions require patching some of the check scripts too. Please apply the following patch, otherwise this won't pass make check: GEN check-drivername Driver struct field stateType should be named stateDrvType GEN check-driverimpls ./libxl/libxl_driver.c:26463 Bad prefix 'VIR_DRV_STATE_DRV_HYPERVISOR' for API 'stateType', expecting 'libxl' ./libxl/libxl_driver.c:26463 Bad impl name 'VIR_DRV_STATE_DRV_HYPERVISOR' for API 'stateType', expecting 'libxlStateType' ./lxc/lxc_driver.c:37541 Bad prefix 'VIR_DRV_STATE_DRV_HYPERVISOR' for API 'stateType', expecting 'lxc' ./lxc/lxc_driver.c:37541 Bad impl name 'VIR_DRV_STATE_DRV_HYPERVISOR' for API 'stateType', expecting 'lxcStateType' diff --git a/src/check-driverimpls.pl b/src/check-driverimpls.pl index f726403..2266323 100755 --- a/src/check-driverimpls.pl +++ b/src/check-driverimpls.pl @@ -37,7 +37,7 @@ while (<>) { next if $api eq "no"; next if $api eq "name"; + next if $api eq "stateType"; my $suffix = $impl; my $prefix = $impl; diff --git a/src/check-drivername.pl b/src/check-drivername.pl index 5c8de0a..d481128 100755 --- a/src/check-drivername.pl +++ b/src/check-drivername.pl @@ -66,6 +66,8 @@ while (<DRVFILE>) { my $drv = $1; my $field = $2; + next if $field =~ /stateType/; + my $tmp = $drv; $tmp =~ s/virDrv//; $tmp =~ s/^NWFilter/nwfilter/; Or possibly don't apply the last hunk and re-name the field to stateDrvType which sounds a little bit better (not required). Peter

This utilizes the config driver I submitted to resolve the hardcoded qemu connection string. With this, the storage driver no longer has a circular dependency with QEMU. Without this patch, when libvirtd is restarted, QEMU requires storage (when domains are using storage pool backings) and storage requires QEMU. This causes issues during startup. Signed-off-by: Adam Walters <adam@pandorasboxen.com> --- src/storage/storage_driver.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/storage/storage_driver.c b/src/storage/storage_driver.c index a614279..1077a66 100644 --- a/src/storage/storage_driver.c +++ b/src/storage/storage_driver.c @@ -70,12 +70,12 @@ storageDriverAutostart(virStorageDriverStatePtr driver) { size_t i; virConnectPtr conn = NULL; - /* XXX Remove hardcoding of QEMU URI */ - if (driverState->privileged) - conn = virConnectOpen("qemu:///system"); - else - conn = virConnectOpen("qemu:///session"); - /* Ignoring NULL conn - let backends decide */ + conn = virConnectOpen("config:///"); + /* Ignoring NULL conn - let backends decide. * + * As long as the config driver is built, and * + * it should be, since it is default on and * + * not configurable, a NULL conn should never * + * happen. for (i = 0; i < driver->pools.count; i++) { virStoragePoolObjPtr pool = driver->pools.objs[i]; -- 1.8.5.2

On 12/21/13 05:14, Adam Walters wrote:
This utilizes the config driver I submitted to resolve the hardcoded qemu connection string. With this, the storage driver no longer has a circular dependency with QEMU. Without this patch, when libvirtd is restarted, QEMU requires storage (when domains are using storage pool backings) and storage requires QEMU. This causes issues during startup.
Please break the summary into multiple lines. The usual set point is 72 columns so that "git log" fits into the 80 column legacy terminal setting.
Signed-off-by: Adam Walters <adam@pandorasboxen.com> --- src/storage/storage_driver.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/src/storage/storage_driver.c b/src/storage/storage_driver.c index a614279..1077a66 100644 --- a/src/storage/storage_driver.c +++ b/src/storage/storage_driver.c @@ -70,12 +70,12 @@ storageDriverAutostart(virStorageDriverStatePtr driver) { size_t i; virConnectPtr conn = NULL;
- /* XXX Remove hardcoding of QEMU URI */ - if (driverState->privileged) - conn = virConnectOpen("qemu:///system"); - else - conn = virConnectOpen("qemu:///session"); - /* Ignoring NULL conn - let backends decide */ + conn = virConnectOpen("config:///"); + /* Ignoring NULL conn - let backends decide. * + * As long as the config driver is built, and * + * it should be, since it is default on and * + * not configurable, a NULL conn should never * + * happen.
Missing closing of the comment. (fails pretty badly at compile time)
for (i = 0; i < driver->pools.count; i++) { virStoragePoolObjPtr pool = driver->pools.objs[i];
Otherwise looks good. Peter

This implements a two-tier driver loading system into libvirt. The two classes of drivers are "Libvirt" drivers and "Hypervisor" drivers. Hypervisor drivers are fairly self-explanatory, they provide domain services. Libvirt drivers are sort of the backend drivers for those, like the secret and storage drivers. In the two-tier loading system here, the "Libvirt" drivers are all loaded and auto-started. Once those have finished, the "Hypervisor" drivers are loaded and auto-started. By doing things in this manner, we do not have to hard-code a driver loading order or roll our own dynamic dependency-based loading algorithm, while still gaining the benefits of a more orderly driver loading approach, which should help minimize the possibility of a race condition during startup. If another race condition is found, the code can be extended to provide any number of extra tiers without much trouble. Signed-off-by: Adam Walters <adam@pandorasboxen.com> --- src/libvirt.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 49 insertions(+), 8 deletions(-) diff --git a/src/libvirt.c b/src/libvirt.c index 77f481e..9c00491 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -837,31 +837,72 @@ int virStateInitialize(bool privileged, void *opaque) { size_t i; + virStateDriverPtr virLibvirtStateDriverTab[MAX_DRIVERS]; + int virLibvirtStateDriverTabCount = 0; + virStateDriverPtr virHypervisorStateDriverTab[MAX_DRIVERS]; + int virHypervisorStateDriverTabCount = 0; if (virInitialize() < 0) return -1; for (i = 0; i < virStateDriverTabCount; i++) { - if (virStateDriverTab[i]->stateInitialize) { + switch (virStateDriverTab[i]->stateType) { + case VIR_DRV_STATE_DRV_LIBVIRT: + virLibvirtStateDriverTab[virLibvirtStateDriverTabCount++] = + virStateDriverTab[i]; + break; + case VIR_DRV_STATE_DRV_HYPERVISOR: + virHypervisorStateDriverTab[virHypervisorStateDriverTabCount++] = + virStateDriverTab[i]; + break; + } + } + + for (i = 0; i < virLibvirtStateDriverTabCount; i++) { + if (virLibvirtStateDriverTab[i]->stateInitialize) { VIR_DEBUG("Running global init for %s state driver", - virStateDriverTab[i]->name); - if (virStateDriverTab[i]->stateInitialize(privileged, + virLibvirtStateDriverTab[i]->name); + if (virLibvirtStateDriverTab[i]->stateInitialize(privileged, callback, opaque) < 0) { virErrorPtr err = virGetLastError(); VIR_ERROR(_("Initialization of %s state driver failed: %s"), - virStateDriverTab[i]->name, + virLibvirtStateDriverTab[i]->name, err && err->message ? err->message : _("Unknown problem")); return -1; } } } - for (i = 0; i < virStateDriverTabCount; i++) { - if (virStateDriverTab[i]->stateAutoStart) { + for (i = 0; i < virLibvirtStateDriverTabCount; i++) { + if (virLibvirtStateDriverTab[i]->stateAutoStart) { + VIR_DEBUG("Running global auto start for %s state driver", + virLibvirtStateDriverTab[i]->name); + virLibvirtStateDriverTab[i]->stateAutoStart(); + } + } + + for (i = 0; i < virHypervisorStateDriverTabCount; i++) { + if (virHypervisorStateDriverTab[i]->stateInitialize) { + VIR_DEBUG("Running global init for %s state driver", + virHypervisorStateDriverTab[i]->name); + if (virHypervisorStateDriverTab[i]->stateInitialize(privileged, + callback, + opaque) < 0) { + virErrorPtr err = virGetLastError(); + VIR_ERROR(_("Initialization of %s state driver failed: %s"), + virHypervisorStateDriverTab[i]->name, + err && err->message ? err->message : _("Unknown problem")); + return -1; + } + } + } + + for (i = 0; i < virHypervisorStateDriverTabCount; i++) { + if (virHypervisorStateDriverTab[i]->stateAutoStart) { VIR_DEBUG("Running global auto start for %s state driver", - virStateDriverTab[i]->name); - virStateDriverTab[i]->stateAutoStart(); + virHypervisorStateDriverTab[i]->name); + virHypervisorStateDriverTab[i]->stateAutoStart(); } } return 0; -- 1.8.5.2

On 12/21/13 05:14, Adam Walters wrote:
This implements a two-tier driver loading system into libvirt. The two classes of drivers are "Libvirt" drivers and "Hypervisor" drivers. Hypervisor drivers are fairly self-explanatory, they provide domain services. Libvirt drivers are sort of the backend drivers for those, like the secret and storage drivers. In the two-tier loading system here, the "Libvirt" drivers are all loaded and auto-started. Once those have finished, the "Hypervisor" drivers are loaded and auto-started. By doing things in this manner, we do not have to hard-code a driver loading order or roll our own dynamic dependency-based loading algorithm, while still gaining the benefits of a more orderly driver loading approach, which should help minimize the possibility of a race condition during startup. If another race condition is found, the code can be extended to provide any number of extra tiers without much trouble.
Again, as in 2/3, please break the long line into some paragraphs.
Signed-off-by: Adam Walters <adam@pandorasboxen.com> --- src/libvirt.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 49 insertions(+), 8 deletions(-)
diff --git a/src/libvirt.c b/src/libvirt.c index 77f481e..9c00491 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -837,31 +837,72 @@ int virStateInitialize(bool privileged, void *opaque) { size_t i; + virStateDriverPtr virLibvirtStateDriverTab[MAX_DRIVERS]; + int virLibvirtStateDriverTabCount = 0; + virStateDriverPtr virHypervisorStateDriverTab[MAX_DRIVERS]; + int virHypervisorStateDriverTabCount = 0;
if (virInitialize() < 0) return -1;
for (i = 0; i < virStateDriverTabCount; i++) { - if (virStateDriverTab[i]->stateInitialize) { + switch (virStateDriverTab[i]->stateType) { + case VIR_DRV_STATE_DRV_LIBVIRT: + virLibvirtStateDriverTab[virLibvirtStateDriverTabCount++] = + virStateDriverTab[i]; + break; + case VIR_DRV_STATE_DRV_HYPERVISOR: + virHypervisorStateDriverTab[virHypervisorStateDriverTabCount++] = + virStateDriverTab[i]; + break; + } + }
Hmmm, this duplicates the loading code for each driver tier. As we don't really need to copy the driver structure pointers into separate arrays to load each driver tier I'd suggest the following multi-pass algorithm: for (driverTier = 0; driverTier < driverTierLast; driverTier++) { for (i = 0; i < virStateDriverTabCount i++) { if (virStateDriverTab[i]->stateType != driverTier) continue; ... ALL THE EXISTING LOADER CODE ... } } This'd save a lot of the code duplication and still would keep the ordering you are trying to introduce. I like the overal idea of this series. Peter
participants (2)
-
Adam Walters
-
Peter Krempa