|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-devel] [PATCH v3 09/15] libxl: synchronise configuration when we hotplug a device
We update JSON version first, then write to xenstore, so that we
maintain the following invariant: any device which is present in
xenstore has a corresponding entry in JSON.
The workflow is as followed:
lock json config
read json config
update in-memory json config with new entry, rewrite
if there's stale entry
for loop
open xs transaction
check device existence, abort if it exists
write in-memory json config to disk
commit xs transaction
end for loop
unlock json config
Please see comment in libxl_internal.h for correctness proof.
As those routines are called both during domain creation and device
hotplug, we add a flag to indicate whether we need to update JSON
config. This flag is only set to true when we hotplug a device. We
cannot update JSON config during domain creation as JSON config is
committed to disk only when domain creation finishes.
Signed-off-by: Wei Liu <wei.liu2@xxxxxxxxxx>
---
tools/libxl/libxl.c | 99 +++++++++++++++++++++++++++++++
tools/libxl/libxl_internal.h | 131 ++++++++++++++++++++++++++++++++++++++++++
tools/libxl/libxl_pci.c | 24 ++++++++
3 files changed, 254 insertions(+)
diff --git a/tools/libxl/libxl.c b/tools/libxl/libxl.c
index ad3495a..fa7d5d5 100644
--- a/tools/libxl/libxl.c
+++ b/tools/libxl/libxl.c
@@ -1894,6 +1894,13 @@ void libxl__device_vtpm_add(libxl__egc *egc, uint32_t
domid,
libxl__device *device;
int rc;
xs_transaction_t t = XBT_NULL;
+ libxl_domain_config d_config;
+ libxl_device_vtpm vtpm_saved;
+ libxl__carefd *lock = NULL;
+
+ libxl_domain_config_init(&d_config);
+ libxl_device_vtpm_init(&vtpm_saved);
+ libxl_device_vtpm_copy(CTX, &vtpm_saved, vtpm);
rc = libxl__device_vtpm_setdefault(gc, vtpm);
if (rc) goto out;
@@ -1908,6 +1915,8 @@ void libxl__device_vtpm_add(libxl__egc *egc, uint32_t
domid,
}
}
+ libxl__update_config_vtpm(gc, &vtpm_saved, vtpm);
+
GCNEW(device);
rc = libxl__device_from_vtpm(gc, domid, vtpm, device);
if ( rc != 0 ) goto out;
@@ -1933,6 +1942,19 @@ void libxl__device_vtpm_add(libxl__egc *egc, uint32_t
domid,
flexarray_append(front, "handle");
flexarray_append(front, GCSPRINTF("%d", vtpm->devid));
+ if (aodev->update_json) {
+ lock = libxl__lock_domain_userdata(gc, domid);
+ if (!lock) {
+ rc = ERROR_LOCK_FAIL;
+ goto out;
+ }
+
+ rc = libxl__get_domain_configuration(gc, domid, &d_config);
+ if (rc) goto out;
+
+ DEVICE_ADD(vtpm, vtpms, domid, &vtpm_saved, COMPARE_DEVID, &d_config);
+ }
+
for (;;) {
rc = libxl__xs_transaction_start(gc, &t);
if (rc) goto out;
@@ -1946,6 +1968,11 @@ void libxl__device_vtpm_add(libxl__egc *egc, uint32_t
domid,
goto out;
}
+ if (aodev->update_json) {
+ rc = libxl__set_domain_configuration(gc, domid, &d_config);
+ if (rc) goto out;
+ }
+
libxl__device_generic_add(gc, t, device,
libxl__xs_kvs_of_flexarray(gc, back,
back->count),
@@ -1965,6 +1992,9 @@ void libxl__device_vtpm_add(libxl__egc *egc, uint32_t
domid,
rc = 0;
out:
libxl__xs_transaction_abort(gc, &t);
+ if (lock) libxl__unlock_domain_userdata(lock);
+ libxl_device_vtpm_dispose(&vtpm_saved);
+ libxl_domain_config_dispose(&d_config);
aodev->rc = rc;
if(rc) aodev->callback(egc, aodev);
return;
@@ -2197,6 +2227,13 @@ static void device_disk_add(libxl__egc *egc, uint32_t
domid,
int rc;
libxl_ctx *ctx = gc->owner;
xs_transaction_t t = XBT_NULL;
+ libxl_domain_config d_config;
+ libxl_device_disk disk_saved;
+ libxl__carefd *lock = NULL;
+
+ libxl_domain_config_init(&d_config);
+ libxl_device_disk_init(&disk_saved);
+ libxl_device_disk_copy(ctx, &disk_saved, disk);
libxl_domain_type type = libxl__domain_type(gc, domid);
if (type == LIBXL_DOMAIN_TYPE_INVALID) {
@@ -2204,6 +2241,29 @@ static void device_disk_add(libxl__egc *egc, uint32_t
domid,
goto out;
}
+ /*
+ * get_vdev != NULL -> local attach
+ * get_vdev == NULL -> block attach
+ *
+ * We don't care about local attach state because it's only
+ * intermediate state. What's nice is that vdev won't change in
+ * "block-attach" case so the macro works as expected -- it can
+ * safely skip an existing entry. See similar check in the for
+ * loop below.
+ */
+ if (!get_vdev && aodev->update_json) {
+ lock = libxl__lock_domain_userdata(gc, domid);
+ if (!lock) {
+ rc = ERROR_LOCK_FAIL;
+ goto out;
+ }
+
+ rc = libxl__get_domain_configuration(gc, domid, &d_config);
+ if (rc) goto out;
+
+ DEVICE_ADD(disk, disks, domid, &disk_saved, COMPARE_DISK, &d_config);
+ }
+
for (;;) {
rc = libxl__xs_transaction_start(gc, &t);
if (rc) goto out;
@@ -2356,6 +2416,11 @@ static void device_disk_add(libxl__egc *egc, uint32_t
domid,
}
}
+ if (!get_vdev && aodev->update_json) {
+ rc = libxl__set_domain_configuration(gc, domid, &d_config);
+ if (rc) goto out;
+ }
+
libxl__device_generic_add(gc, t, device,
libxl__xs_kvs_of_flexarray(gc, back,
back->count),
libxl__xs_kvs_of_flexarray(gc, front,
front->count),
@@ -2374,6 +2439,9 @@ static void device_disk_add(libxl__egc *egc, uint32_t
domid,
out:
libxl__xs_transaction_abort(gc, &t);
+ if (lock) libxl__unlock_domain_userdata(lock);
+ libxl_device_disk_dispose(&disk_saved);
+ libxl_domain_config_dispose(&d_config);
aodev->rc = rc;
if (rc) aodev->callback(egc, aodev);
return;
@@ -3030,6 +3098,13 @@ void libxl__device_nic_add(libxl__egc *egc, uint32_t
domid,
libxl__device *device;
int rc;
xs_transaction_t t = XBT_NULL;
+ libxl_domain_config d_config;
+ libxl_device_nic nic_saved;
+ libxl__carefd *lock = NULL;
+
+ libxl_domain_config_init(&d_config);
+ libxl_device_nic_init(&nic_saved);
+ libxl_device_nic_copy(CTX, &nic_saved, nic);
rc = libxl__device_nic_setdefault(gc, nic, domid);
if (rc) goto out;
@@ -3044,6 +3119,8 @@ void libxl__device_nic_add(libxl__egc *egc, uint32_t
domid,
}
}
+ libxl__update_config_nic(gc, &nic_saved, nic);
+
GCNEW(device);
rc = libxl__device_from_nic(gc, domid, nic, device);
if ( rc != 0 ) goto out;
@@ -3101,6 +3178,19 @@ void libxl__device_nic_add(libxl__egc *egc, uint32_t
domid,
flexarray_append(front, libxl__sprintf(gc,
LIBXL_MAC_FMT, LIBXL_MAC_BYTES(nic->mac)));
+ if (aodev->update_json) {
+ lock = libxl__lock_domain_userdata(gc, domid);
+ if (!lock) {
+ rc = ERROR_LOCK_FAIL;
+ goto out;
+ }
+
+ rc = libxl__get_domain_configuration(gc, domid, &d_config);
+ if (rc) goto out;
+
+ DEVICE_ADD(nic, nics, domid, &nic_saved, COMPARE_DEVID, &d_config);
+ }
+
for (;;) {
rc = libxl__xs_transaction_start(gc, &t);
if (rc) goto out;
@@ -3114,6 +3204,11 @@ void libxl__device_nic_add(libxl__egc *egc, uint32_t
domid,
goto out;
}
+ if (aodev->update_json) {
+ rc = libxl__set_domain_configuration(gc, domid, &d_config);
+ if (rc) goto out;
+ }
+
libxl__device_generic_add(gc, t, device,
libxl__xs_kvs_of_flexarray(gc, back,
back->count),
@@ -3133,6 +3228,9 @@ void libxl__device_nic_add(libxl__egc *egc, uint32_t
domid,
rc = 0;
out:
libxl__xs_transaction_abort(gc, &t);
+ if (lock) libxl__unlock_domain_userdata(lock);
+ libxl_device_nic_dispose(&nic_saved);
+ libxl_domain_config_dispose(&d_config);
aodev->rc = rc;
if (rc) aodev->callback(egc, aodev);
return;
@@ -3686,6 +3784,7 @@ DEFINE_DEVICE_REMOVE(vtpm, destroy, 1)
GCNEW(aodev); \
libxl__prepare_ao_device(ao, aodev); \
aodev->callback = device_addrm_aocomplete; \
+ aodev->update_json = true; \
libxl__device_##type##_add(egc, domid, type, aodev); \
\
return AO_INPROGRESS; \
diff --git a/tools/libxl/libxl_internal.h b/tools/libxl/libxl_internal.h
index fafef5a..2546f88 100644
--- a/tools/libxl/libxl_internal.h
+++ b/tools/libxl/libxl_internal.h
@@ -2146,6 +2146,8 @@ struct libxl__ao_device {
int num_exec;
/* for calling hotplug scripts */
libxl__async_exec_state aes;
+ /* If we need to udpate JSON config */
+ bool update_json;
};
/*
@@ -2271,6 +2273,78 @@ struct libxl__multidev {
*
* Once finished, aodev->callback will be executed.
*/
+/*
+ * As of Xen 4.5 we maintain various infomation, including hotplug
+ * device information, in JSON files, so that we can use this JSON
+ * file as a template to reconstruct domain configuration.
+ *
+ * In essnse there are now two views of device state, one is xenstore,
+ * the other is JSON file. We use xenstore as primary reference.
+ *
+ * Here we maintain one invariant: every device in xenstore must have
+ * an entry in JSON file.
+ *
+ * All device hotplug routines should comply to following pattern:
+ * lock json config (json_lock)
+ * read json config
+ * update in-memory json config with new entry, rewrite
+ * if there's stale entry
+ * for loop -- xs transaction
+ * open xs transaction
+ * check device existence, abort if it exists
+ * write in-memory json config to disk
+ * commit xs transaction
+ * end for loop
+ * unlock json config
+ *
+ * Device removal routines are not touched.
+ *
+ * Here is the proof that we always maintain that invariant and we
+ * don't leak files during interaction of hotplug thread and other
+ * threads / processes.
+ *
+ * # Safe against parallel add
+ *
+ * When another thread / process tries to add same device, it's
+ * blocked by json_lock. The loser of two threads will bail at
+ * existence check, so that we don't overwrite anything.
+ *
+ * # Safe against domain destruction
+ *
+ * When another thread / process tries to destroy domain, it's blocked
+ * by json_lock. If domain destruction thread is loser, it deletes
+ * every userdata file after it requires the lock. If hotplug thread
+ * is loser, it bails at acquiring lock, no device is added. Either
+ * way, no file is leaked.
+ *
+ * # Safe against parallel removal
+ *
+ * When another thread / process tries to remove a device, it's _NOT_
+ * blocked by json_lock, but xenstore transaction can help maintain
+ * invariant. The removal threads either a) sees that device in
+ * xenstore, b) doesn't see that device in xenstore.
+ *
+ * In a), it sees that device in xenstore. At that point hotplug is
+ * already finished (both JSON and xenstore change committed). So that
+ * device can be safely removed. JSON entry is left untouched and
+ * becomes stale, but this is a valid state -- next time when a
+ * device with same identifier gets added, the stale entry gets
+ * overwritten.
+ *
+ * In b), it doesn't see that device in xenstore, but it will commence
+ * anyway. Eventually a force removal is initiated, which will forcely
+ * remove xenstore entry.
+ *
+ * If hotplug threads creates xenstore entry (therefore JSON entry as
+ * well) before force removal, that xenstore entry is removed. We're
+ * left with JSON stale entry but not xenstore entry, which is a valid
+ * state.
+ *
+ * If hotplug thread has not created xenstore entry when the removal
+ * is committed, we're obviously safe. Hotplug thread will add in
+ * xenstore entry afterwards. We have both JSON and xenstore entry,
+ * it's a valid state.
+ */
_hidden void libxl__device_disk_add(libxl__egc *egc, uint32_t domid,
libxl_device_disk *disk,
libxl__ao_device *aodev);
@@ -3273,6 +3347,63 @@ static inline void libxl__update_config_vtpm(libxl__gc
*gc,
libxl_uuid_copy(CTX, &dst->uuid, &src->uuid);
}
+/* Macros used to compare device identifier. Returns true if the two
+ * devices have same identifier. */
+#define COMPARE_DEVID(a, b) ((a)->devid == (b)->devid)
+#define COMPARE_DISK(a, b) (!strcmp((a)->vdev, (b)->vdev))
+#define COMPARE_PCI(a, b) ((a)->func == (b)->func && \
+ (a)->bus == (b)->bus && \
+ (a)->dev == (b)->dev)
+
+/* DEVICE_ADD
+ *
+ * Add a device in libxl_domain_config structure
+ *
+ * It takes 6 parameters:
+ * type: the type of the device, say nic, vtpm, disk, pci etc
+ * ptr: pointer to the start of the array, the array must be
+ * of type libxl_device_#type
+ * domid: domain id of target domain
+ * dev: the device that is to be added / removed / updated
+ * compare: the COMPARE_* macro used to compare @dev's identifier to
+ * those in the array pointed to by @ptr
+ * d_config: pointer to template domain config
+ *
+ * For most device types (nic, vtpm), the array pointer @ptr can be
+ * derived from @type, pci device being the exception, hence we need
+ * to have @ptr.
+ *
+ * If there is already a device with the same identifier in d_config,
+ * that entry is updated.
+ */
+#define DEVICE_ADD(type, ptr, domid, dev, compare, d_config) \
+ ({ \
+ int DA_x; \
+ libxl_device_##type *DA_p = NULL; \
+ \
+ /* Check for existing device */ \
+ for (DA_x = 0; DA_x < (d_config)->num_##ptr; DA_x++) { \
+ if (compare(&(d_config)->ptr[DA_x], (dev))) { \
+ DA_p = &(d_config)->ptr[DA_x]; \
+ break; \
+ } \
+ } \
+ \
+ if (!DA_p) { \
+ (d_config)->ptr = \
+ libxl__realloc(NOGC, (d_config)->ptr, \
+ ((d_config)->num_##ptr + 1) * \
+ sizeof(libxl_device_##type)); \
+ DA_p = &(d_config)->ptr[(d_config)->num_##ptr]; \
+ (d_config)->num_##ptr++; \
+ } else { \
+ libxl_device_##type##_dispose(DA_p); \
+ } \
+ \
+ libxl_device_##type##_init(DA_p); \
+ libxl_device_##type##_copy(CTX, DA_p, (dev)); \
+ })
+
#endif
/*
diff --git a/tools/libxl/libxl_pci.c b/tools/libxl/libxl_pci.c
index 38a9642..b87819f 100644
--- a/tools/libxl/libxl_pci.c
+++ b/tools/libxl/libxl_pci.c
@@ -126,6 +126,13 @@ static int libxl__device_pci_add_xenstore(libxl__gc *gc,
uint32_t domid, libxl_d
xs_transaction_t t;
libxl__device *device;
int rc;
+ libxl_domain_config d_config;
+ libxl_device_pci pcidev_saved;
+ libxl__carefd *lock = NULL;
+
+ libxl_domain_config_init(&d_config);
+ libxl_device_pci_init(&pcidev_saved);
+ libxl_device_pci_copy(CTX, &pcidev_saved, pcidev);
be_path = libxl__sprintf(gc, "%s/backend/pci/%d/0",
libxl__xs_get_dompath(gc, 0), domid);
num_devs = libxl__xs_read(gc, XBT_NULL, libxl__sprintf(gc, "%s/num_devs",
be_path));
@@ -153,6 +160,17 @@ static int libxl__device_pci_add_xenstore(libxl__gc *gc,
uint32_t domid, libxl_d
GCNEW(device);
libxl__device_from_pcidev(gc, domid, pcidev, device);
+ lock = libxl__lock_domain_userdata(gc, domid);
+ if (!lock) {
+ rc = ERROR_LOCK_FAIL;
+ goto out;
+ }
+
+ rc = libxl__get_domain_configuration(gc, domid, &d_config);
+ if (rc) goto out;
+
+ DEVICE_ADD(pci, pcidevs, domid, &pcidev_saved, COMPARE_PCI, &d_config);
+
for (;;) {
rc = libxl__xs_transaction_start(gc, &t);
if (rc) goto out;
@@ -165,6 +183,9 @@ static int libxl__device_pci_add_xenstore(libxl__gc *gc,
uint32_t domid, libxl_d
goto out;
}
+ rc = libxl__set_domain_configuration(gc, domid, &d_config);
+ if (rc) goto out;
+
libxl__xs_writev(gc, t, be_path,
libxl__xs_kvs_of_flexarray(gc, back, back->count));
@@ -175,6 +196,9 @@ static int libxl__device_pci_add_xenstore(libxl__gc *gc,
uint32_t domid, libxl_d
out:
libxl__xs_transaction_abort(gc, &t);
+ if (lock) libxl__unlock_domain_userdata(lock);
+ libxl_device_pci_dispose(&pcidev_saved);
+ libxl_domain_config_dispose(&d_config);
return rc;
}
--
1.7.10.4
_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
http://lists.xen.org/xen-devel
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |