[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [Xen-tools] b0893b876c8c4c4eb507d48fc1c4af4268ddecde and watch behaviour
On Thu, 2005-09-22 at 16:35 +1000, Rusty Russell wrote: > Hi Christian! > > I was looking through b0893b876c8c4c4eb507d48fc1c4af4268ddecde, and it > took me a while to figure out what the code was doing. I think there > are two issues here: > > 1) Directories implicitly created by mkdir/write don't fire watches (eg. > mkdir /a/sub/dir only fires a watch event for /a/sub/dir, even if it > also created /a/sub), and > 2) Rm only fires a single event for a watch, as an optimization. > > The first one is a bug, I think, and I'm testing a patch as I type this. Indeed, here is the patch. Applies on top of those 3 tdb-conversion patches... Rusty. # HG changeset patch # User Rusty Russell <rusty@xxxxxxxxxxxxxxx> # Node ID 5ea1c70f0adb9625a5640b61c9068923a95cb8d7 # Parent 6ec88332e05dd2433ca568de619b3e809f66ca41 Fire watches on implicitly-created directories. This patch separates watch event creation and firing, which improves robustness a little should we run out of memory, as well as allowing us to build up a series of different events (such as for each directory created in a write/mkdir which creates more than one), and fire them on successful completion. Signed-off-by: Rusty Russell <rusty@xxxxxxxxxxxxxxx> diff -r 6ec88332e05d -r 5ea1c70f0adb tools/xenstore/testsuite/07watch.test --- a/tools/xenstore/testsuite/07watch.test Wed Sep 21 05:44:48 2005 +++ b/tools/xenstore/testsuite/07watch.test Thu Sep 22 06:42:54 2005 @@ -184,6 +184,7 @@ expect 1:/test/subnode:token 1 waitwatch 1 ackwatch token +1 close # Watch should not double-send after we ack, even if we did something in between. 1 watch /test2 token @@ -195,3 +196,32 @@ 1 ackwatch token expect 1: waitwatch failed: Connection timed out 1 waitwatch +1 close + +# Watch fires on implicitly-created directories (mkdir) +1 watch /test2 token +2 mkdir /test2/sub/dir/dir2 +expect 1:/test2/sub:token +1 waitwatch +1 ackwatch token +expect 1:/test2/sub/dir:token +1 waitwatch +1 ackwatch token +expect 1:/test2/sub/dir/dir2:token +1 waitwatch +1 ackwatch token +1 close + +# Watch fires on implicitly-created directories (write) +1 watch /test2 token +2 write /test2/sub2/dir/entry contents +expect 1:/test2/sub2:token +1 waitwatch +1 ackwatch token +expect 1:/test2/sub2/dir:token +1 waitwatch +1 ackwatch token +expect 1:/test2/sub2/dir/entry:token +1 waitwatch +1 ackwatch token +1 close diff -r 6ec88332e05d -r 5ea1c70f0adb tools/xenstore/xenstored_core.c --- a/tools/xenstore/xenstored_core.c Wed Sep 21 05:44:48 2005 +++ b/tools/xenstore/xenstored_core.c Thu Sep 22 06:42:54 2005 @@ -747,7 +747,8 @@ return strrchr(name, '/') + 1; } -static struct node *construct_node(struct connection *conn, const char *name) +static struct node *construct_node(struct connection *conn, const char *name, + struct events **ev) { const char *base; unsigned int baselen; @@ -757,7 +758,7 @@ /* If parent doesn't exist, create it. */ parent = read_node(conn, parentname); if (!parent) - parent = construct_node(conn, parentname); + parent = construct_node(conn, parentname, ev); if (!parent) return NULL; @@ -774,6 +775,7 @@ node = talloc(name, struct node); node->tdb = tdb_context(conn); node->name = talloc_strdup(node, name); + *ev = add_events(conn, name, false, *ev); /* Inherit permissions, except domains own what they create */ node->num_perms = parent->num_perms; @@ -808,11 +810,12 @@ * This helps fsck if we die during this. */ static struct node *create_node(struct connection *conn, const char *name, - void *data, unsigned int datalen) + void *data, unsigned int datalen, + struct events **ev) { struct node *node, *i; - node = construct_node(conn, name); + node = construct_node(conn, name, ev); if (!node) return NULL; @@ -838,6 +841,7 @@ { unsigned int offset, datalen; struct node *node; + struct events *ev = NULL; char *vec[1] = { NULL }; /* gcc4 + -W + -Werror fucks code. */ char *name; @@ -858,7 +862,8 @@ send_error(conn, errno); return; } - node = create_node(conn, name, in->buffer + offset, datalen); + node = create_node(conn, name, in->buffer + offset, datalen, + &ev); if (!node) { send_error(conn, errno); return; @@ -866,6 +871,7 @@ } else { node->data = in->buffer + offset; node->datalen = datalen; + ev = add_events(conn, name, false, NULL); if (!write_node(conn, node)){ send_error(conn, errno); return; @@ -873,13 +879,14 @@ } add_change_node(conn->transaction, name, false); - fire_watches(conn, name, false); + fire_events(ev); send_ack(conn, XS_WRITE); } static void do_mkdir(struct connection *conn, const char *name) { struct node *node; + struct events *ev = NULL; name = canonicalize(conn, name); node = get_node(conn, name, XS_PERM_WRITE); @@ -891,13 +898,13 @@ send_error(conn, errno); return; } - node = create_node(conn, name, NULL, 0); + node = create_node(conn, name, NULL, 0, &ev); if (!node) { send_error(conn, errno); return; } add_change_node(conn->transaction, name, false); - fire_watches(conn, name, false); + fire_events(ev); } send_ack(conn, XS_MKDIR); } @@ -948,6 +955,7 @@ static void do_rm(struct connection *conn, const char *name) { struct node *node, *parent; + struct events *ev = NULL; name = canonicalize(conn, name); node = get_node(conn, name, XS_PERM_WRITE); @@ -978,6 +986,7 @@ return; } + ev = add_events(conn, name, true, NULL); if (!delete_child(conn, parent, basename(name))) { send_error(conn, EINVAL); return; @@ -985,7 +994,7 @@ delete_node(conn, node); add_change_node(conn->transaction, name, true); - fire_watches(conn, name, true); + fire_events(ev); send_ack(conn, XS_RM); } @@ -1014,6 +1023,7 @@ unsigned int num; char *name, *permstr; struct node *node; + struct events *ev; num = xs_count_strings(in->buffer, in->used); if (num < 2) { @@ -1033,6 +1043,7 @@ return; } + ev = add_events(conn, name, false, NULL); node->perms = talloc_array(node, struct xs_permissions, num); node->num_perms = num; if (!xs_strings_to_perms(node->perms, num, permstr)) { @@ -1045,7 +1056,7 @@ } add_change_node(conn->transaction, name, false); - fire_watches(conn, name, false); + fire_events(ev); send_ack(conn, XS_SET_PERMS); } diff -r 6ec88332e05d -r 5ea1c70f0adb tools/xenstore/xenstored_domain.c --- a/tools/xenstore/xenstored_domain.c Wed Sep 21 05:44:48 2005 +++ b/tools/xenstore/xenstored_domain.c Thu Sep 22 06:42:54 2005 @@ -235,7 +235,7 @@ } if (released) - fire_watches(NULL, "@releaseDomain", false); + special_event("@releaseDomain"); } /* We scan all domains rather than use the information given here. */ @@ -330,8 +330,7 @@ /* Now domain belongs to its connection. */ talloc_steal(domain->conn, domain); - fire_watches(conn, "@introduceDomain", false); - + special_event("@introduceDomain"); send_ack(conn, XS_INTRODUCE); } @@ -378,10 +377,9 @@ send_error(conn, EINVAL); return; } - talloc_free(domain->conn); - fire_watches(conn, "@releaseDomain", false); + special_event("@releaseDomain"); send_ack(conn, XS_RELEASE); } diff -r 6ec88332e05d -r 5ea1c70f0adb tools/xenstore/xenstored_transaction.c --- a/tools/xenstore/xenstored_transaction.c Wed Sep 21 05:44:48 2005 +++ b/tools/xenstore/xenstored_transaction.c Thu Sep 22 06:42:54 2005 @@ -161,11 +161,18 @@ talloc_steal(arg, trans); if (streq(arg, "T")) { + struct events *ev = NULL; + /* FIXME: Merge, rather failing on any change. */ if (trans->generation != generation) { send_error(conn, EAGAIN); return; } + + /* Allocate watch events to send *before* commit. */ + list_for_each_entry(i, &trans->changes, list) + ev = add_events(conn, i->node, i->recurse, ev); + if (!replace_tdb(trans->tdb_name, trans->tdb)) { send_error(conn, errno); return; @@ -173,9 +180,7 @@ /* Don't close this: we won! */ trans->tdb = NULL; - /* Fire off the watches for everything that changed. */ - list_for_each_entry(i, &trans->changes, list) - fire_watches(conn, i->node, i->recurse); + fire_events(ev); generation++; } send_ack(conn, XS_TRANSACTION_END); diff -r 6ec88332e05d -r 5ea1c70f0adb tools/xenstore/xenstored_watch.c --- a/tools/xenstore/xenstored_watch.c Wed Sep 21 05:44:48 2005 +++ b/tools/xenstore/xenstored_watch.c Thu Sep 22 06:42:54 2005 @@ -38,6 +38,8 @@ /* The events on this watch. */ struct list_head list; + struct watch *watch; + /* Data to send (node\0token\0). */ unsigned int len; char *data; @@ -54,8 +56,17 @@ /* Is this relative to connnection's implicit path? */ const char *relative_path; + /* Connection which placed this watch. */ + struct connection *conn; + char *token; char *node; +}; + +struct events +{ + unsigned int num; + struct watch_event **events; }; /* Look through our watches: if any of them have an event, queue it. */ @@ -91,11 +102,13 @@ { struct watch_event *event = _event; + list_del(&event->list); trace_destroy(event, "watch_event"); return 0; } -static void add_event(struct connection *conn, +static void add_event(struct events *ev, + struct connection *conn, struct watch *watch, const char *name) { @@ -116,7 +129,10 @@ name++; } - event = talloc(watch, struct watch_event); + ev->events = talloc_realloc(ev, ev->events, struct watch_event *, + ev->num + 1); + ev->events[ev->num++] = event = talloc(ev, struct watch_event); + event->watch = watch; event->len = strlen(name) + 1 + strlen(watch->token) + 1; event->data = talloc_array(event, char, event->len); strcpy(event->data, name); @@ -126,30 +142,38 @@ trace_create(event, "watch_event"); } -/* FIXME: we fail to fire on out of memory. Should drop connections. */ -void fire_watches(struct connection *conn, const char *name, bool recurse) +static struct events *new_events(const void *ctx) +{ + struct events *ev = talloc(ctx, struct events); + ev->num = 0; + ev->events = NULL; + return ev; +} + +struct events *add_events(struct connection *conn, + const char *name, + bool recurse, + struct events *old) { struct connection *i; struct watch *watch; /* During transactions, don't fire watches. */ - if (conn && conn->transaction) - return; - - /* Create an event for each watch. */ + if (conn->transaction) + return NULL; + + if (!old) + old = new_events(name); + list_for_each_entry(i, &connections, list) { list_for_each_entry(watch, &i->watches, list) { if (is_child(name, watch->node)) - add_event(i, watch, name); + add_event(old, i, watch, name); else if (recurse && is_child(watch->node, name)) - add_event(i, watch, watch->node); - else - continue; - /* If connection not doing anything, queue this. */ - if (i->state == OK) - queue_next_event(i); - } - } + add_event(old, i, watch, watch->node); + } + } + return old; } static int destroy_watch(void *_watch) @@ -158,11 +182,40 @@ return 0; } +/* Create a single event for this watch. */ +static struct events *single_event(void *ctx, + struct connection *conn, + struct watch *watch) +{ + struct events *ev = new_events(ctx); + + add_event(ev, conn, watch, watch->node); + return ev; +} + +/* No convenient context to hang this off, so free immediately. */ +void special_event(const char *eventname) +{ + struct connection *i; + struct watch *watch; + struct events *ev = new_events(talloc_autofree_context()); + + list_for_each_entry(i, &connections, list) { + list_for_each_entry(watch, &i->watches, list) { + if (streq(eventname, watch->node)) + add_event(ev, i, watch, watch->node); + } + } + fire_events(ev); + talloc_free(ev); +} + void do_watch(struct connection *conn, struct buffered_data *in) { struct watch *watch; char *vec[2]; bool relative; + struct events *ev; if (get_strings(in, vec, ARRAY_SIZE(vec)) != ARRAY_SIZE(vec)) { send_error(conn, EINVAL); @@ -184,6 +237,7 @@ watch = talloc(conn, struct watch); watch->node = talloc_strdup(watch, vec[0]); watch->token = talloc_strdup(watch, vec[1]); + watch->conn = conn; if (relative) watch->relative_path = get_implicit_path(conn); else @@ -194,16 +248,30 @@ list_add_tail(&watch->list, &conn->watches); trace_create(watch, "watch"); talloc_set_destructor(watch, destroy_watch); + /* We fire once up front: simplifies clients and restart. */ + ev = single_event(in, conn, watch); send_ack(conn, XS_WATCH); - - /* We fire once up front: simplifies clients and restart. */ - add_event(conn, watch, watch->node); + fire_events(ev); +} + +void fire_events(struct events *ev) +{ + unsigned int i; + + if (!ev) + return; + + /* Reparent each event to the corresponding watch. */ + for (i = 0; i < ev->num; i++) { + talloc_steal(ev->events[i]->watch, ev->events[i]); + /* If connection not doing anything, queue this. */ + if (ev->events[i]->watch->conn->state == OK) + queue_next_event(ev->events[i]->watch->conn); + } } void do_watch_ack(struct connection *conn, const char *token) { - struct watch_event *event; - if (!token) { send_error(conn, EINVAL); return; @@ -222,10 +290,8 @@ } /* Remove event: after ack sent, core will call queue_next_event */ - event = list_top(&conn->waiting_for_ack->events, struct watch_event, - list); - list_del(&event->list); - talloc_free(event); + talloc_free(list_top(&conn->waiting_for_ack->events, + struct watch_event, list)); conn->waiting_for_ack = NULL; send_ack(conn, XS_WATCH_ACK); diff -r 6ec88332e05d -r 5ea1c70f0adb tools/xenstore/xenstored_watch.h --- a/tools/xenstore/xenstored_watch.h Wed Sep 21 05:44:48 2005 +++ b/tools/xenstore/xenstored_watch.h Thu Sep 22 06:42:54 2005 @@ -32,9 +32,20 @@ /* Look through our watches: if any of them have an event, queue it. */ void queue_next_event(struct connection *conn); -/* Fire all watches: recurse means all the children are affected (ie. rm). - */ -void fire_watches(struct connection *conn, const char *name, bool recurse); +struct events; + +/* Create watch events: recurse means all the children are effected (ie. rm). + * Add to old events, if that is non-NULL. */ +struct events *add_events(struct connection *conn, + const char *name, + bool recurse, + struct events *old); + +/* Actually fire those watch events */ +void fire_events(struct events *events); + +/* Fire a special event (introduce or release). */ +void special_event(const char *eventname); void dump_watches(struct connection *conn); -- A bad analogy is like a leaky screwdriver -- Richard Braakman _______________________________________________ Xen-tools mailing list Xen-tools@xxxxxxxxxxxxxxxxxxx http://lists.xensource.com/xen-tools
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |