On Tue, Feb 19, 2013 at 08:27:42PM +0800, Osier Yang wrote:
The hash entry is changed from "ref" to {ref, @domains}.
With this, the
caller can simply call qemuRemoveSharedDisk, without afraid of removing
the entry belongs to other domains. qemuProcessStart will obviously
benifit from it on error codepath (which calls qemuProcessStop to do
the cleanup).
---
src/qemu/qemu_conf.c | 163 +++++++++++++++++++++++++++++++++++++++++------
src/qemu/qemu_conf.h | 26 ++++++-
src/qemu/qemu_driver.c | 6 +-
src/qemu/qemu_process.c | 4 +-
4 files changed, 171 insertions(+), 28 deletions(-)
diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c
index e54227f..a0477b3 100644
--- a/src/qemu/qemu_conf.c
+++ b/src/qemu/qemu_conf.c
@@ -858,10 +858,9 @@ static int
qemuCheckSharedDisk(virHashTablePtr sharedDisks,
virDomainDiskDefPtr disk)
{
- int val;
- size_t *ref = NULL;
char *sysfs_path = NULL;
char *key = NULL;
+ int val;
int ret = 0;
/* The only conflicts between shared disk we care about now
@@ -881,7 +880,6 @@ qemuCheckSharedDisk(virHashTablePtr sharedDisks,
if (!virFileExists(sysfs_path))
goto cleanup;
-
if (!(key = qemuGetSharedDiskKey(disk->src))) {
ret = -1;
goto cleanup;
@@ -890,7 +888,7 @@ qemuCheckSharedDisk(virHashTablePtr sharedDisks,
/* It can't be conflict if no other domain is
* is sharing it.
*/
- if (!(ref = virHashLookup(sharedDisks, key)))
+ if (!(virHashLookup(sharedDisks, key)))
goto cleanup;
if (virGetDeviceUnprivSGIO(disk->src, NULL, &val) < 0) {
@@ -916,14 +914,83 @@ cleanup:
return ret;
}
-/* Increase ref count if the entry already exists, otherwise
- * add a new entry.
+bool
+qemuSharedDiskEntryDomainExists(qemuSharedDiskEntryPtr entry,
+ const char *name,
+ int *idx)
+{
+ int i;
+
+ for (i = 0; i < entry->ref; i++) {
+ if (STREQ(entry->domains[i], name)) {
+ if (idx)
+ *idx = i;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void
+qemuSharedDiskEntryFree(void *payload, const void *name ATTRIBUTE_UNUSED)
+{
+ qemuSharedDiskEntryPtr entry = (qemuSharedDiskEntryPtr)payload;
+ int i;
+
+ for (i = 0; i < entry->ref; i++) {
+ VIR_FREE(entry->domains[i]);
+ }
+ VIR_FREE(entry->domains);
+}
+
+static qemuSharedDiskEntryPtr
+qemuSharedDiskEntryCopy(const qemuSharedDiskEntryPtr entry)
+{
+ qemuSharedDiskEntryPtr ret = NULL;
+ int i;
+
+ if (VIR_ALLOC(ret) < 0) {
+ virReportOOMError();
+ return NULL;
+ }
+
+ if (VIR_ALLOC_N(ret->domains, entry->ref) < 0) {
+ virReportOOMError();
+ goto cleanup;
+ }
+
+ for (i = 0; i < entry->ref; i++) {
+ if (!(ret->domains[i] = strdup(entry->domains[i]))) {
+ virReportOOMError();
+ goto cleanup;
+ }
+ ret->ref++;
+ }
+
+ return ret;
+
+cleanup:
+ qemuSharedDiskEntryFree(ret, NULL);
+ return NULL;
+}
+
+/* qemuAddSharedDisk:
+ * @driver: Pointer to qemu driver struct
+ * @disk: The disk def
+ * @name: The domain name
+ *
+ * Increase ref count and add the domain name into the list which
+ * records all the domains that use the shared disk if the entry
+ * already exists, otherwise add a new entry.
*/
int
qemuAddSharedDisk(virQEMUDriverPtr driver,
- virDomainDiskDefPtr disk)
+ virDomainDiskDefPtr disk,
+ const char *name)
{
- size_t *ref = NULL;
+ qemuSharedDiskEntry *entry = NULL;
+ qemuSharedDiskEntry *new_entry = NULL;
char *key = NULL;
int ret = -1;
@@ -942,11 +1009,40 @@ qemuAddSharedDisk(virQEMUDriverPtr driver,
if (!(key = qemuGetSharedDiskKey(disk->src)))
goto cleanup;
- if ((ref = virHashLookup(driver->sharedDisks, key))) {
- if (virHashUpdateEntry(driver->sharedDisks, key, ++ref) < 0)
- goto cleanup;
+ if ((entry = virHashLookup(driver->sharedDisks, key))) {
+ /* Nothing to do if the shared disk is already recorded
+ * in the table.
+ */
+ if (qemuSharedDiskEntryDomainExists(entry, name, NULL)) {
+ ret = 0;
+ goto cleanup;
+ }
+
+ if (!(new_entry = qemuSharedDiskEntryCopy(entry)))
+ goto cleanup;
+
+ if ((VIR_EXPAND_N(new_entry->domains, new_entry->ref, 1) < 0) ||
+ !(new_entry->domains[new_entry->ref - 1] = strdup(name))) {
+ qemuSharedDiskEntryFree(new_entry, NULL);
+ virReportOOMError();
+ goto cleanup;
+ }
+
+ if (virHashUpdateEntry(driver->sharedDisks, key, new_entry) < 0) {
+ qemuSharedDiskEntryFree(new_entry, NULL);
+ goto cleanup;
+ }
} else {
- if (virHashAddEntry(driver->sharedDisks, key, (void *)0x1))
+ if ((VIR_ALLOC(entry) < 0) ||
+ (VIR_ALLOC_N(entry->domains, 1) < 0) ||
+ !(entry->domains[0] = strdup(name))) {
+ virReportOOMError();
+ goto cleanup;
+ }
+
+ entry->ref = 1;
+
+ if (virHashAddEntry(driver->sharedDisks, key, entry))
goto cleanup;
}
@@ -957,16 +1053,25 @@ cleanup:
return ret;
}
-/* Decrease the ref count if the entry already exists, otherwise
- * remove the entry.
+/* qemuRemoveSharedDisk:
+ * @driver: Pointer to qemu driver struct
+ * @disk: The disk def
+ * @name: The domain name
+ *
+ * Decrease ref count and remove the domain name from the list which
+ * records all the domains that use the shared disk if ref is not 1,
+ * otherwise remove the entry.
*/
int
qemuRemoveSharedDisk(virQEMUDriverPtr driver,
- virDomainDiskDefPtr disk)
+ virDomainDiskDefPtr disk,
+ const char *name)
{
- size_t *ref = NULL;
+ qemuSharedDiskEntryPtr entry = NULL;
+ qemuSharedDiskEntryPtr new_entry = NULL;
char *key = NULL;
int ret = -1;
+ int idx;
if (disk->type != VIR_DOMAIN_DISK_TYPE_BLOCK ||
!disk->shared || !disk->src)
@@ -976,12 +1081,32 @@ qemuRemoveSharedDisk(virQEMUDriverPtr driver,
if (!(key = qemuGetSharedDiskKey(disk->src)))
goto cleanup;
- if (!(ref = virHashLookup(driver->sharedDisks, key)))
+ if (!(entry = virHashLookup(driver->sharedDisks, key)))
+ goto cleanup;
+
+ /* Nothing to do if the shared disk is not recored in
+ * the table.
+ */
+ if (!qemuSharedDiskEntryDomainExists(entry, name, &idx)) {
+ ret = 0;
goto cleanup;
+ }
+
+ if (entry->ref != 1) {
+ if (!(new_entry = qemuSharedDiskEntryCopy(entry)))
+ goto cleanup;
+
+ if (idx != new_entry->ref - 1)
+ memmove(&new_entry->domains[idx],
+ &new_entry->domains[idx + 1],
+ sizeof(*new_entry->domains) * (new_entry->ref - idx - 1));
- if (ref != (void *)0x1) {
- if (virHashUpdateEntry(driver->sharedDisks, key, --ref) < 0)
+ VIR_SHRINK_N(new_entry->domains, new_entry->ref, 1);
+
+ if (virHashUpdateEntry(driver->sharedDisks, key, new_entry) < 0){
+ qemuSharedDiskEntryFree(new_entry, NULL);
goto cleanup;
+ }
} else {
if (virHashRemoveEntry(driver->sharedDisks, key) < 0)
goto cleanup;
diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h
index 81a001b..d3cd0a1 100644
--- a/src/qemu/qemu_conf.h
+++ b/src/qemu/qemu_conf.h
@@ -272,16 +272,34 @@ qemuDriverCloseCallback qemuDriverCloseCallbackGet(virQEMUDriverPtr
driver,
void qemuDriverCloseCallbackRunAll(virQEMUDriverPtr driver,
virConnectPtr conn);
-int qemuAddSharedDisk(virQEMUDriverPtr driver,
- virDomainDiskDefPtr disk)
+typedef struct _qemuSharedDiskEntry qemuSharedDiskEntry;
+typedef qemuSharedDiskEntry *qemuSharedDiskEntryPtr;
+struct _qemuSharedDiskEntry {
+ size_t ref;
+ char **domains; /* array of domain names */
+};
This shouldn't be in the header file, since nothing outside the
.c file should ever touch the hash table contents directly.
Daniel
--
|:
http://berrange.com -o-
http://www.flickr.com/photos/dberrange/ :|
|:
http://libvirt.org -o-
http://virt-manager.org :|
|:
http://autobuild.org -o-
http://search.cpan.org/~danberr/ :|
|:
http://entangle-photo.org -o-
http://live.gnome.org/gtk-vnc :|