[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Xen-devel] [PATCH v4 3/4] xenstore: rework of transaction handling



On Thu, Mar 30, 2017 at 03:11:48PM +0200, Juergen Gross wrote:
[...]
> +
> +/*
> + * A node has been accessed.
> + *
> + * Modifying accesses (write, delete) always update the generation (global 
> and
> + * node->generation).
> + *
> + * Accesses in a transaction will be added to the list of accessed nodes
> + * if not already done. Read type accesses will copy the node to the
> + * transaction specific data base part, write type accesses go there
> + * anyway.
> + *
> + * If not NULL, key will be supplied with name and length of name of the node
> + * to be accessed in the data base.
> + */
> +int access_node(struct connection *conn, struct node *node,
> +             enum node_access_type type, TDB_DATA *key)
> +{
> +     struct accessed_node *i = NULL;
>       struct transaction *trans;
> +     TDB_DATA local_key;
> +     const char *trans_name = NULL;
> +     int ret;
> +     bool introduce = false;
>  
> -     if (!conn || !conn->transaction) {
> +     if (type != NODE_ACCESS_READ) {
>               /* They're changing the global database. */

This comment should be moved below.

>               node->generation = generation++;
> -             return;
> +             if (conn && !conn->transaction)
> +                     wrl_apply_debit_direct(conn);
> +     }
> +
> +     if (!conn || !conn->transaction) {
> +             if (key)
> +                     set_tdb_key(node->name, key);
> +             return 0;
>       }
>  
>       trans = conn->transaction;
>  
> -     node->generation = generation + trans->trans_gen++;
> +     trans_name = transaction_get_node_name(node, trans, node->name);
> +     if (!trans_name)
> +             goto nomem;
>  
> -     list_for_each_entry(i, &trans->changes, list) {
> -             if (streq(i->node, node->name)) {
> -                     if (recurse)
> -                             i->recurse = recurse;
> -                     return;
> -             }
> -     }
> -
> -     i = talloc(trans, struct changed_node);
> +     i = find_accessed_node(trans, node->name);
>       if (!i) {
> -             /* All we can do is let the transaction fail. */
> -             generation++;
> -             return;
> +             i = talloc_zero(trans, struct accessed_node);
> +             if (!i)
> +                     goto nomem;
> +             i->node = talloc_strdup(i, node->name);
> +             if (!i->node)
> +                     goto nomem;
> +
> +             introduce = true;
> +             i->ta_node = false;
> +
> +             /* We have to verify read nodes only if we didn't write them. */
> +             if (type == NODE_ACCESS_READ) {
> +                     i->generation = node->generation;
> +                     i->check_gen = true;
> +                     if (node->generation != NO_GENERATION) {
> +                             set_tdb_key(trans_name, &local_key);
> +                             ret = write_node_raw(conn, &local_key, node);
> +                             if (ret)
> +                                     goto err;
> +                             i->ta_node = true;
> +                     }
> +             }

I think the current code structure is a bit confusing.

For write types, the call to write_node_raw is outside of this function
but for read type it is inside this function.

> +             list_add_tail(&i->list, &trans->accessed);
>       }
> -     i->node = talloc_strdup(i, node->name);
> -     if (!i->node) {
> -             /* All we can do is let the transaction fail. */
> -             generation++;
> +
> +     if (type != NODE_ACCESS_READ)
> +             i->modified = true;
> +
> +     if (introduce && type == NODE_ACCESS_DELETE)
> +             /* Nothing to delete. */
> +             return -1;
> +
> +     if (key) {
> +             set_tdb_key(trans_name, key);
> +             if (type == NODE_ACCESS_WRITE)
> +                     i->ta_node = true;
> +             if (type == NODE_ACCESS_DELETE)
> +                     i->ta_node = false;

The same caveat made me wonder if ta_node was really what it meant to
be because I couldn't find where write_node_raw was.

Can I suggest you make them consistent? Either lift the write_node_raw
for read to read_node or push down the write_node_raw in delete_node and
write_node here?

If I misunderstand how this works, please tell me...

> +     }
> +
> +     return 0;
> +
> +nomem:
> +     ret = ENOMEM;
> +err:
> +     talloc_free((void *)trans_name);
> +     talloc_free(i);
> +     trans->fail = true;
> +     errno = ret;
> +     return ret;
> +}
> +
> +/*
> + * Finalize transaction:
> + * Walk through accessed nodes and check generation against global data.
> + * If all entries match, read the transaction entries and write them without
> + * transaction prepended. Delete all transaction specific nodes in the data
> + * base.
> + */
[...]
> @@ -269,7 +525,7 @@ void transaction_entry_inc(struct transaction *trans, 
> unsigned int domid)
>       d = talloc(trans, struct changed_domain);
>       if (!d) {
>               /* Let the transaction fail. */
> -             generation++;
> +             trans->fail = true;
>               return;
>       }
>       d->domid = domid;
> @@ -290,7 +546,7 @@ void transaction_entry_dec(struct transaction *trans, 
> unsigned int domid)
>       d = talloc(trans, struct changed_domain);
>       if (!d) {
>               /* Let the transaction fail. */
> -             generation++;
> +             trans->fail = true;
>               return;
>       }
>       d->domid = domid;
> @@ -313,6 +569,29 @@ void conn_delete_all_transactions(struct connection 
> *conn)
>       conn->transaction_started = 0;
>  }
>  
> +int check_transactions(int (*func)(void *, const char *), void *par)
> +{

I think you can make the func take hashtable * and the second parameter
hashtable *.

You can then avoid changing remember_string.

Or even better, you don't need to take a callback function at all
because AFAICT only remember_string is called.

> +     struct connection *conn;
> +     struct transaction *trans;
> +     char *tname;
> +
> +     list_for_each_entry(conn, &connections, list) {
> +             list_for_each_entry(trans, &conn->transaction_list, list) {
> +                     tname = talloc_asprintf(trans, "%"PRIu64,
> +                                             trans->generation);
> +                     if (!tname)
> +                             return ENOMEM;
> +
> +                     if (!func(par, tname))
> +                             return ENOMEM;
> +

I guess a bigger question is why calling remember_string here would
constitute a check on all transactions.

Sorry this is taking a bit long. I am still struggling with the code. I
haven't got around to check if the code matches the algorithm yet.
I expect a few iterations are needed.

Wei.

_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
https://lists.xen.org/xen-devel

 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.