|
[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 30/03/17 17:41, Wei Liu wrote:
> 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.
Right.
>
>> 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.
This is due to the different purpose of write_node_raw() in both
cases:
In the read case we write an _additional_ node into the transaction,
while in the write case it is just the user's operation.
>
>> + 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...
As I already said: in the read case writing the node is depending
on the context and lifting it up would seem rather strange: why
should reading a node contain a write_node_raw() call?
In the write case I believe we should keep write_node_raw() where
it belongs: in the appropriate function modifying the node.
All actions in access_node() are transaction specific and _additional_
in the transaction case.
>
>> + }
>> +
>> + 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.
Right. Will change. IIRC the main reason to do it this way was to
avoid making remember_string global.
>
>> + 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.
That's a valid question. The iteration over trans->accessed is missing.
> 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.
Juergen
_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
https://lists.xen.org/xen-devel
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |