mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-08-09 18:44:58 -06:00
hw/xen: Implement XenStore watches
Starts out fairly simple: a hash table of watches based on the path. Except there can be multiple watches on the same path, so the watch ends up being a simple linked list, and the head of that list is in the hash table. Which makes removal a bit of a PITA but it's not so bad; we just special-case "I had to remove the head of the list and now I have to replace it in / remove it from the hash table". And if we don't remove the head, it's a simple linked-list operation. We do need to fire watches on *deleted* nodes, so instead of just a simple xs_node_unref() on the topmost victim, we need to recurse down and fire watches on them all. Signed-off-by: David Woodhouse <dwmw@amazon.co.uk> Reviewed-by: Paul Durrant <paul@xen.org>
This commit is contained in:
parent
3ef7ff83ca
commit
6e1330090d
2 changed files with 323 additions and 15 deletions
|
@ -34,10 +34,14 @@ static void xs_impl_delete(XenstoreImplState *s)
|
|||
{
|
||||
int err;
|
||||
|
||||
xs_impl_reset_watches(s, DOMID_GUEST);
|
||||
g_assert(!s->nr_domu_watches);
|
||||
|
||||
err = xs_impl_rm(s, DOMID_QEMU, XBT_NULL, "/local");
|
||||
g_assert(!err);
|
||||
g_assert(s->nr_nodes == 1);
|
||||
|
||||
g_hash_table_unref(s->watches);
|
||||
xs_node_unref(s->root);
|
||||
g_free(s);
|
||||
|
||||
|
@ -65,6 +69,14 @@ static int write_str(XenstoreImplState *s, unsigned int dom_id,
|
|||
return err;
|
||||
}
|
||||
|
||||
static void watch_cb(void *_str, const char *path, const char *token)
|
||||
{
|
||||
GString *str = _str;
|
||||
|
||||
g_string_append(str, path);
|
||||
g_string_append(str, token);
|
||||
}
|
||||
|
||||
static XenstoreImplState *setup(void)
|
||||
{
|
||||
XenstoreImplState *s = xs_impl_create();
|
||||
|
@ -75,6 +87,7 @@ static XenstoreImplState *setup(void)
|
|||
|
||||
err = write_str(s, DOMID_QEMU, XBT_NULL, abspath, "");
|
||||
g_assert(!err);
|
||||
g_assert(s->nr_nodes == 4);
|
||||
|
||||
g_free(abspath);
|
||||
|
||||
|
@ -93,6 +106,8 @@ static void test_xs_node_simple(void)
|
|||
{
|
||||
GByteArray *data = g_byte_array_new();
|
||||
XenstoreImplState *s = setup();
|
||||
GString *guest_watches = g_string_new(NULL);
|
||||
GString *qemu_watches = g_string_new(NULL);
|
||||
GList *items = NULL;
|
||||
XsNode *old_root;
|
||||
uint64_t gencnt;
|
||||
|
@ -100,6 +115,20 @@ static void test_xs_node_simple(void)
|
|||
|
||||
g_assert(s);
|
||||
|
||||
err = xs_impl_watch(s, DOMID_GUEST, "some", "guestwatch",
|
||||
watch_cb, guest_watches);
|
||||
g_assert(!err);
|
||||
g_assert(guest_watches->len == strlen("someguestwatch"));
|
||||
g_assert(!strcmp(guest_watches->str, "someguestwatch"));
|
||||
g_string_truncate(guest_watches, 0);
|
||||
|
||||
err = xs_impl_watch(s, 0, "/local/domain/1/some", "qemuwatch",
|
||||
watch_cb, qemu_watches);
|
||||
g_assert(!err);
|
||||
g_assert(qemu_watches->len == strlen("/local/domain/1/someqemuwatch"));
|
||||
g_assert(!strcmp(qemu_watches->str, "/local/domain/1/someqemuwatch"));
|
||||
g_string_truncate(qemu_watches, 0);
|
||||
|
||||
/* Read gives ENOENT when it should */
|
||||
err = xs_impl_read(s, DOMID_GUEST, XBT_NULL, "foo", data);
|
||||
g_assert(err == ENOENT);
|
||||
|
@ -109,6 +138,14 @@ static void test_xs_node_simple(void)
|
|||
"something");
|
||||
g_assert(s->nr_nodes == 7);
|
||||
g_assert(!err);
|
||||
g_assert(!strcmp(guest_watches->str,
|
||||
"some/relative/pathguestwatch"));
|
||||
g_assert(!strcmp(qemu_watches->str,
|
||||
"/local/domain/1/some/relative/pathqemuwatch"));
|
||||
|
||||
g_string_truncate(qemu_watches, 0);
|
||||
g_string_truncate(guest_watches, 0);
|
||||
xs_impl_reset_watches(s, 0);
|
||||
|
||||
/* Read gives back what we wrote */
|
||||
err = xs_impl_read(s, DOMID_GUEST, XBT_NULL, "some/relative/path", data);
|
||||
|
@ -123,6 +160,8 @@ static void test_xs_node_simple(void)
|
|||
g_assert(!err);
|
||||
g_assert(data->len == strlen("something"));
|
||||
|
||||
g_assert(!qemu_watches->len);
|
||||
g_assert(!guest_watches->len);
|
||||
/* Keep a copy, to force COW mode */
|
||||
old_root = xs_node_ref(s->root);
|
||||
|
||||
|
@ -132,12 +171,18 @@ static void test_xs_node_simple(void)
|
|||
"something else");
|
||||
g_assert(!err);
|
||||
g_assert(s->nr_nodes == 8);
|
||||
g_assert(!qemu_watches->len);
|
||||
g_assert(!strcmp(guest_watches->str, "some/relative/path2guestwatch"));
|
||||
g_string_truncate(guest_watches, 0);
|
||||
|
||||
/* Overwrite an existing node */
|
||||
err = write_str(s, DOMID_GUEST, XBT_NULL, "some/relative/path",
|
||||
"another thing");
|
||||
g_assert(!err);
|
||||
g_assert(s->nr_nodes == 8);
|
||||
g_assert(!qemu_watches->len);
|
||||
g_assert(!strcmp(guest_watches->str, "some/relative/pathguestwatch"));
|
||||
g_string_truncate(guest_watches, 0);
|
||||
|
||||
/* We can list the two files we wrote */
|
||||
err = xs_impl_directory(s, DOMID_GUEST, XBT_NULL, "some/relative", &gencnt,
|
||||
|
@ -151,9 +196,38 @@ static void test_xs_node_simple(void)
|
|||
g_assert(!items->next->next);
|
||||
g_list_free_full(items, g_free);
|
||||
|
||||
err = xs_impl_unwatch(s, DOMID_GUEST, "some", "guestwatch",
|
||||
watch_cb, guest_watches);
|
||||
g_assert(!err);
|
||||
|
||||
err = xs_impl_unwatch(s, DOMID_GUEST, "some", "guestwatch",
|
||||
watch_cb, guest_watches);
|
||||
g_assert(err == ENOENT);
|
||||
|
||||
err = xs_impl_watch(s, DOMID_GUEST, "some/relative/path2", "watchp2",
|
||||
watch_cb, guest_watches);
|
||||
g_assert(!err);
|
||||
g_assert(guest_watches->len == strlen("some/relative/path2watchp2"));
|
||||
g_assert(!strcmp(guest_watches->str, "some/relative/path2watchp2"));
|
||||
g_string_truncate(guest_watches, 0);
|
||||
|
||||
err = xs_impl_watch(s, DOMID_GUEST, "/local/domain/1/some/relative",
|
||||
"watchrel", watch_cb, guest_watches);
|
||||
g_assert(!err);
|
||||
g_assert(guest_watches->len ==
|
||||
strlen("/local/domain/1/some/relativewatchrel"));
|
||||
g_assert(!strcmp(guest_watches->str,
|
||||
"/local/domain/1/some/relativewatchrel"));
|
||||
g_string_truncate(guest_watches, 0);
|
||||
|
||||
/* Write somewhere else which already existed */
|
||||
err = write_str(s, DOMID_GUEST, XBT_NULL, "some/relative", "moredata");
|
||||
g_assert(!err);
|
||||
g_assert(s->nr_nodes == 8);
|
||||
|
||||
g_assert(!strcmp(guest_watches->str,
|
||||
"/local/domain/1/some/relativewatchrel"));
|
||||
g_string_truncate(guest_watches, 0);
|
||||
|
||||
g_byte_array_set_size(data, 0);
|
||||
err = xs_impl_read(s, DOMID_GUEST, XBT_NULL, "some/relative", data);
|
||||
|
@ -164,6 +238,7 @@ static void test_xs_node_simple(void)
|
|||
/* Overwrite existing data */
|
||||
err = write_str(s, DOMID_GUEST, XBT_NULL, "some/relative", "otherdata");
|
||||
g_assert(!err);
|
||||
g_string_truncate(guest_watches, 0);
|
||||
|
||||
g_byte_array_set_size(data, 0);
|
||||
err = xs_impl_read(s, DOMID_GUEST, XBT_NULL, "some/relative", data);
|
||||
|
@ -176,11 +251,21 @@ static void test_xs_node_simple(void)
|
|||
g_assert(!err);
|
||||
g_assert(s->nr_nodes == 5);
|
||||
|
||||
/* Each watch fires with the least specific relevant path */
|
||||
g_assert(strstr(guest_watches->str,
|
||||
"some/relative/path2watchp2"));
|
||||
g_assert(strstr(guest_watches->str,
|
||||
"/local/domain/1/some/relativewatchrel"));
|
||||
g_string_truncate(guest_watches, 0);
|
||||
|
||||
g_byte_array_set_size(data, 0);
|
||||
err = xs_impl_read(s, DOMID_GUEST, XBT_NULL, "some/relative", data);
|
||||
g_assert(err == ENOENT);
|
||||
g_byte_array_unref(data);
|
||||
|
||||
xs_impl_reset_watches(s, DOMID_GUEST);
|
||||
g_string_free(qemu_watches, true);
|
||||
g_string_free(guest_watches, true);
|
||||
xs_node_unref(old_root);
|
||||
xs_impl_delete(s);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue