diff mbox series

[v4,04/13] tools/xenstore: add framework to commit accounting data on success only

Message ID 20230405070349.25293-5-jgross@suse.com (mailing list archive)
State Superseded
Headers show
Series tools/xenstore: rework internal accounting | expand

Commit Message

Jürgen Groß April 5, 2023, 7:03 a.m. UTC
Instead of modifying accounting data and undo those modifications in
case of an error during further processing, add a framework for
collecting the needed changes and commit them only when the whole
operation has succeeded.

This scheme can reuse large parts of the per transaction accounting.
The changed_domain handling can be reused, but the array size of the
accounting data should be possible to be different for both use cases.

Signed-off-by: Juergen Gross <jgross@suse.com>
---
V3:
- call acc_commit() earlier (Julien Grall)
- add assert() to acc_commit()
- use fixed sized acc array in struct changed_domain (Julien Grall)
---
 tools/xenstore/xenstored_core.c   |  9 ++++--
 tools/xenstore/xenstored_core.h   |  3 ++
 tools/xenstore/xenstored_domain.c | 53 ++++++++++++++++++++++++++++++-
 tools/xenstore/xenstored_domain.h |  5 ++-
 4 files changed, 66 insertions(+), 4 deletions(-)

Comments

Julien Grall April 25, 2023, 5:52 p.m. UTC | #1
Hi Juergen,

On 05/04/2023 08:03, Juergen Gross wrote:
> Instead of modifying accounting data and undo those modifications in
> case of an error during further processing, add a framework for
> collecting the needed changes and commit them only when the whole
> operation has succeeded.
> 
> This scheme can reuse large parts of the per transaction accounting.
> The changed_domain handling can be reused, but the array size of the
> accounting data should be possible to be different for both use cases.
> 
> Signed-off-by: Juergen Gross <jgross@suse.com>
> ---
> V3:
> - call acc_commit() earlier (Julien Grall)
> - add assert() to acc_commit()
> - use fixed sized acc array in struct changed_domain (Julien Grall)
> ---
>   tools/xenstore/xenstored_core.c   |  9 ++++--
>   tools/xenstore/xenstored_core.h   |  3 ++
>   tools/xenstore/xenstored_domain.c | 53 ++++++++++++++++++++++++++++++-
>   tools/xenstore/xenstored_domain.h |  5 ++-
>   4 files changed, 66 insertions(+), 4 deletions(-)
> 
> diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c
> index 3ca68681e3..84335f5f3d 100644
> --- a/tools/xenstore/xenstored_core.c
> +++ b/tools/xenstore/xenstored_core.c
> @@ -1023,6 +1023,9 @@ static void send_error(struct connection *conn, int error)
>   			break;
>   		}
>   	}
> +
> +	acc_drop(conn);
> +
>   	send_reply(conn, XS_ERROR, xsd_errors[i].errstring,
>   			  strlen(xsd_errors[i].errstring) + 1);
>   }
> @@ -1034,6 +1037,9 @@ void send_reply(struct connection *conn, enum xsd_sockmsg_type type,
>   
>   	assert(type != XS_WATCH_EVENT);
>   
> +	conn->in = NULL;

AFAIU, you are setting conn->in to NULL in order to please..

> +	acc_commit(conn);

... this call. However in case of an error like...

> +
>   	if ( len > XENSTORE_PAYLOAD_MAX ) { >   		send_error(conn, E2BIG);

... here, send_reply() will be called again. But the error will not be 
set because conn->in is NULL.

So I think you want to restore conn->in rewrite acc_commit(). The 
ordering would also deserve an explanation in a comment.

>   		return;
> @@ -1059,8 +1065,6 @@ void send_reply(struct connection *conn, enum xsd_sockmsg_type type,
>   		}
>   	}
>   
> -	conn->in = NULL;
> -
>   	/* Update relevant header fields and fill in the message body. */
>   	bdata->hdr.msg.type = type;
>   	bdata->hdr.msg.len = len;
> @@ -2195,6 +2199,7 @@ struct connection *new_connection(const struct interface_funcs *funcs)
>   	new->is_stalled = false;
>   	new->transaction_started = 0;
>   	INIT_LIST_HEAD(&new->out_list);
> +	INIT_LIST_HEAD(&new->acc_list);
>   	INIT_LIST_HEAD(&new->ref_list);
>   	INIT_LIST_HEAD(&new->watches);
>   	INIT_LIST_HEAD(&new->transaction_list);
> diff --git a/tools/xenstore/xenstored_core.h b/tools/xenstore/xenstored_core.h
> index c59b06551f..1f811f38cb 100644
> --- a/tools/xenstore/xenstored_core.h
> +++ b/tools/xenstore/xenstored_core.h
> @@ -139,6 +139,9 @@ struct connection
>   	struct list_head out_list;
>   	uint64_t timeout_msec;
>   
> +	/* Not yet committed accounting data (valid if in != NULL). */
> +	struct list_head acc_list;
> +
>   	/* Referenced requests no longer pending. */
>   	struct list_head ref_list;
>   
> diff --git a/tools/xenstore/xenstored_domain.c b/tools/xenstore/xenstored_domain.c
> index 30fb9acec6..144cbafb73 100644
> --- a/tools/xenstore/xenstored_domain.c
> +++ b/tools/xenstore/xenstored_domain.c
> @@ -91,6 +91,8 @@ struct domain
>   	bool wrl_delay_logged;
>   };
>   
> +#define ACC_CHD_N (ACC_TR_N < ACC_REQ_N ? ACC_REQ_N : ACC_TR_N)

Both ACC_TR_N and ACC_REQ_N are fixed. Can you explain why we need this 
magic?

Related, wouldn't it be better to define it in the enum?

> +
>   struct changed_domain
>   {
>   	/* List of all changed domains. */
> @@ -100,7 +102,7 @@ struct changed_domain
>   	unsigned int domid;
>   
>   	/* Accounting data. */
> -	int acc[ACC_TR_N];
> +	int acc[ACC_CHD_N];
>   };
>   
>   static struct hashtable *domhash;
> @@ -1070,6 +1072,7 @@ static int domain_acc_add(struct connection *conn, unsigned int domid,
>   			  enum accitem what, int add, bool no_dom_alloc)
>   {
>   	struct domain *d;
> +	struct changed_domain *cd;
>   	struct list_head *head;
>   	int ret;
>   
> @@ -1090,6 +1093,22 @@ static int domain_acc_add(struct connection *conn, unsigned int domid,
>   		}
>   	}
>   
> +	/* Temporary accounting data until final commit? */
> +	if (conn && conn->in && what < ACC_REQ_N) {
> +		/* Consider transaction local data. */
> +		ret = 0;
> +		if (conn->transaction && what < ACC_TR_N) {
> +			head = transaction_get_changed_domains(
> +				conn->transaction);
> +			cd = acc_find_changed_domain(head, domid);
> +			if (cd)
> +				ret = cd->acc[what];
> +		}
> +		ret += acc_add_changed_dom(conn->in, &conn->acc_list, what,
> +					   add, domid);
> +		return errno ? -1 : domain_acc_add_valid(d, what, ret);
> +	}
> +
>   	if (conn && conn->transaction && what < ACC_TR_N) {
>   		head = transaction_get_changed_domains(conn->transaction);
>   		ret = acc_add_changed_dom(conn->transaction, head, what,
> @@ -1106,6 +1125,38 @@ static int domain_acc_add(struct connection *conn, unsigned int domid,
>   	return d->acc[what];
>   }
>   
> +void acc_drop(struct connection *conn)
> +{
> +	struct changed_domain *cd;
> +
> +	while ((cd = list_top(&conn->acc_list, struct changed_domain, list))) {
> +		list_del(&cd->list);
> +		talloc_free(cd);
> +	}
> +}
> +
> +void acc_commit(struct connection *conn)
> +{
> +	struct changed_domain *cd;
> +	enum accitem what;
> +
> +	/*
> +	 * Make sure domain_acc_add() below can't add additional data to
> +	 * to be committed accounting records.
> +	 */
> +	assert(!conn->in);
> +
> +	while ((cd = list_top(&conn->acc_list, struct changed_domain, list))) {
> +		list_del(&cd->list);
> +		for (what = 0; what < ACC_REQ_N; what++)
> +			if (cd->acc[what])
> +				domain_acc_add(conn, cd->domid, what,
> +					       cd->acc[what], true);
> +
> +		talloc_free(cd);
> +	}
> +}
> +
>   int domain_nbentry_inc(struct connection *conn, unsigned int domid)
>   {
>   	return (domain_acc_add(conn, domid, ACC_NODES, 1, false) < 0)
> diff --git a/tools/xenstore/xenstored_domain.h b/tools/xenstore/xenstored_domain.h
> index 9d05eb01da..6355ad4f37 100644
> --- a/tools/xenstore/xenstored_domain.h
> +++ b/tools/xenstore/xenstored_domain.h
> @@ -25,7 +25,8 @@
>    * a per transaction array.
>    */
>   enum accitem {
> -	ACC_NODES,
> +	ACC_REQ_N,		/* Number of elements per request. */
> +	ACC_NODES = ACC_REQ_N,
>   	ACC_TR_N,		/* Number of elements per transaction. */
>   	ACC_N = ACC_TR_N,	/* Number of elements per domain. */
>   };
> @@ -113,6 +114,8 @@ int domain_get_quota(const void *ctx, struct connection *conn,
>    * If "update" is true, "chk_quota" is ignored.
>    */
>   int acc_fix_domains(struct list_head *head, bool chk_quota, bool update);
> +void acc_drop(struct connection *conn);
> +void acc_commit(struct connection *conn);
>   
>   /* Write rate limiting */
>   

Cheers,
Jürgen Groß April 26, 2023, 7:08 a.m. UTC | #2
On 25.04.23 19:52, Julien Grall wrote:
> Hi Juergen,
> 
> On 05/04/2023 08:03, Juergen Gross wrote:
>> Instead of modifying accounting data and undo those modifications in
>> case of an error during further processing, add a framework for
>> collecting the needed changes and commit them only when the whole
>> operation has succeeded.
>>
>> This scheme can reuse large parts of the per transaction accounting.
>> The changed_domain handling can be reused, but the array size of the
>> accounting data should be possible to be different for both use cases.
>>
>> Signed-off-by: Juergen Gross <jgross@suse.com>
>> ---
>> V3:
>> - call acc_commit() earlier (Julien Grall)
>> - add assert() to acc_commit()
>> - use fixed sized acc array in struct changed_domain (Julien Grall)
>> ---
>>   tools/xenstore/xenstored_core.c   |  9 ++++--
>>   tools/xenstore/xenstored_core.h   |  3 ++
>>   tools/xenstore/xenstored_domain.c | 53 ++++++++++++++++++++++++++++++-
>>   tools/xenstore/xenstored_domain.h |  5 ++-
>>   4 files changed, 66 insertions(+), 4 deletions(-)
>>
>> diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c
>> index 3ca68681e3..84335f5f3d 100644
>> --- a/tools/xenstore/xenstored_core.c
>> +++ b/tools/xenstore/xenstored_core.c
>> @@ -1023,6 +1023,9 @@ static void send_error(struct connection *conn, int error)
>>               break;
>>           }
>>       }
>> +
>> +    acc_drop(conn);
>> +
>>       send_reply(conn, XS_ERROR, xsd_errors[i].errstring,
>>                 strlen(xsd_errors[i].errstring) + 1);
>>   }
>> @@ -1034,6 +1037,9 @@ void send_reply(struct connection *conn, enum 
>> xsd_sockmsg_type type,
>>       assert(type != XS_WATCH_EVENT);
>> +    conn->in = NULL;
> 
> AFAIU, you are setting conn->in to NULL in order to please..
> 
>> +    acc_commit(conn);
> 
> ... this call. However in case of an error like...
> 
>> +
>>       if ( len > XENSTORE_PAYLOAD_MAX ) { >           send_error(conn, E2BIG);
> 
> ... here, send_reply() will be called again. But the error will not be set 
> because conn->in is NULL.
> 
> So I think you want to restore conn->in rewrite acc_commit(). The ordering would 
> also deserve an explanation in a comment.

Just to make sure I understand you correctly (I have some difficulties
parsing "So I think you want to restore conn->in rewrite acc_commit()."
completely):

You are suggesting to move setting conn->in to NULL to acc_commit() and
to restore it before returning? I'm fine with that.

> 
>>           return;
>> @@ -1059,8 +1065,6 @@ void send_reply(struct connection *conn, enum 
>> xsd_sockmsg_type type,
>>           }
>>       }
>> -    conn->in = NULL;
>> -
>>       /* Update relevant header fields and fill in the message body. */
>>       bdata->hdr.msg.type = type;
>>       bdata->hdr.msg.len = len;
>> @@ -2195,6 +2199,7 @@ struct connection *new_connection(const struct 
>> interface_funcs *funcs)
>>       new->is_stalled = false;
>>       new->transaction_started = 0;
>>       INIT_LIST_HEAD(&new->out_list);
>> +    INIT_LIST_HEAD(&new->acc_list);
>>       INIT_LIST_HEAD(&new->ref_list);
>>       INIT_LIST_HEAD(&new->watches);
>>       INIT_LIST_HEAD(&new->transaction_list);
>> diff --git a/tools/xenstore/xenstored_core.h b/tools/xenstore/xenstored_core.h
>> index c59b06551f..1f811f38cb 100644
>> --- a/tools/xenstore/xenstored_core.h
>> +++ b/tools/xenstore/xenstored_core.h
>> @@ -139,6 +139,9 @@ struct connection
>>       struct list_head out_list;
>>       uint64_t timeout_msec;
>> +    /* Not yet committed accounting data (valid if in != NULL). */
>> +    struct list_head acc_list;
>> +
>>       /* Referenced requests no longer pending. */
>>       struct list_head ref_list;
>> diff --git a/tools/xenstore/xenstored_domain.c 
>> b/tools/xenstore/xenstored_domain.c
>> index 30fb9acec6..144cbafb73 100644
>> --- a/tools/xenstore/xenstored_domain.c
>> +++ b/tools/xenstore/xenstored_domain.c
>> @@ -91,6 +91,8 @@ struct domain
>>       bool wrl_delay_logged;
>>   };
>> +#define ACC_CHD_N (ACC_TR_N < ACC_REQ_N ? ACC_REQ_N : ACC_TR_N)
> 
> Both ACC_TR_N and ACC_REQ_N are fixed. Can you explain why we need this magic?
> 
> Related, wouldn't it be better to define it in the enum?

I can do that, of course. I just didn't want to make the enum even more
complex. :-)

But with a comment this should be okay IMO.


Juergen
Julien Grall April 27, 2023, 4:32 p.m. UTC | #3
Hi,

On 26/04/2023 08:08, Juergen Gross wrote:
> On 25.04.23 19:52, Julien Grall wrote:
>> Hi Juergen,
>>
>> On 05/04/2023 08:03, Juergen Gross wrote:
>>> Instead of modifying accounting data and undo those modifications in
>>> case of an error during further processing, add a framework for
>>> collecting the needed changes and commit them only when the whole
>>> operation has succeeded.
>>>
>>> This scheme can reuse large parts of the per transaction accounting.
>>> The changed_domain handling can be reused, but the array size of the
>>> accounting data should be possible to be different for both use cases.
>>>
>>> Signed-off-by: Juergen Gross <jgross@suse.com>
>>> ---
>>> V3:
>>> - call acc_commit() earlier (Julien Grall)
>>> - add assert() to acc_commit()
>>> - use fixed sized acc array in struct changed_domain (Julien Grall)
>>> ---
>>>   tools/xenstore/xenstored_core.c   |  9 ++++--
>>>   tools/xenstore/xenstored_core.h   |  3 ++
>>>   tools/xenstore/xenstored_domain.c | 53 ++++++++++++++++++++++++++++++-
>>>   tools/xenstore/xenstored_domain.h |  5 ++-
>>>   4 files changed, 66 insertions(+), 4 deletions(-)
>>>
>>> diff --git a/tools/xenstore/xenstored_core.c 
>>> b/tools/xenstore/xenstored_core.c
>>> index 3ca68681e3..84335f5f3d 100644
>>> --- a/tools/xenstore/xenstored_core.c
>>> +++ b/tools/xenstore/xenstored_core.c
>>> @@ -1023,6 +1023,9 @@ static void send_error(struct connection *conn, 
>>> int error)
>>>               break;
>>>           }
>>>       }
>>> +
>>> +    acc_drop(conn);
>>> +
>>>       send_reply(conn, XS_ERROR, xsd_errors[i].errstring,
>>>                 strlen(xsd_errors[i].errstring) + 1);
>>>   }
>>> @@ -1034,6 +1037,9 @@ void send_reply(struct connection *conn, enum 
>>> xsd_sockmsg_type type,
>>>       assert(type != XS_WATCH_EVENT);
>>> +    conn->in = NULL;
>>
>> AFAIU, you are setting conn->in to NULL in order to please..
>>
>>> +    acc_commit(conn);
>>
>> ... this call. However in case of an error like...
>>
>>> +
>>>       if ( len > XENSTORE_PAYLOAD_MAX ) { >           
>>> send_error(conn, E2BIG);
>>
>> ... here, send_reply() will be called again. But the error will not be 
>> set because conn->in is NULL.
>>
>> So I think you want to restore conn->in rewrite acc_commit(). The 
>> ordering would also deserve an explanation in a comment.
> 
> Just to make sure I understand you correctly (I have some difficulties
> parsing "So I think you want to restore conn->in rewrite acc_commit()."
> completely):

Hmmm... Not sure why I wrote "rewrite". I was meant to say that you want 
to restore conn->in after acc_commit() is called.

> 
> You are suggesting to move setting conn->in to NULL to acc_commit() and
> to restore it before returning? I'm fine with that.

Either that or what I wrote above. It depends on whether you expect 
other caller to be in the same situation.

> 
>>
>>>           return;
>>> @@ -1059,8 +1065,6 @@ void send_reply(struct connection *conn, enum 
>>> xsd_sockmsg_type type,
>>>           }
>>>       }
>>> -    conn->in = NULL;
>>> -
>>>       /* Update relevant header fields and fill in the message body. */
>>>       bdata->hdr.msg.type = type;
>>>       bdata->hdr.msg.len = len;
>>> @@ -2195,6 +2199,7 @@ struct connection *new_connection(const struct 
>>> interface_funcs *funcs)
>>>       new->is_stalled = false;
>>>       new->transaction_started = 0;
>>>       INIT_LIST_HEAD(&new->out_list);
>>> +    INIT_LIST_HEAD(&new->acc_list);
>>>       INIT_LIST_HEAD(&new->ref_list);
>>>       INIT_LIST_HEAD(&new->watches);
>>>       INIT_LIST_HEAD(&new->transaction_list);
>>> diff --git a/tools/xenstore/xenstored_core.h 
>>> b/tools/xenstore/xenstored_core.h
>>> index c59b06551f..1f811f38cb 100644
>>> --- a/tools/xenstore/xenstored_core.h
>>> +++ b/tools/xenstore/xenstored_core.h
>>> @@ -139,6 +139,9 @@ struct connection
>>>       struct list_head out_list;
>>>       uint64_t timeout_msec;
>>> +    /* Not yet committed accounting data (valid if in != NULL). */
>>> +    struct list_head acc_list;
>>> +
>>>       /* Referenced requests no longer pending. */
>>>       struct list_head ref_list;
>>> diff --git a/tools/xenstore/xenstored_domain.c 
>>> b/tools/xenstore/xenstored_domain.c
>>> index 30fb9acec6..144cbafb73 100644
>>> --- a/tools/xenstore/xenstored_domain.c
>>> +++ b/tools/xenstore/xenstored_domain.c
>>> @@ -91,6 +91,8 @@ struct domain
>>>       bool wrl_delay_logged;
>>>   };
>>> +#define ACC_CHD_N (ACC_TR_N < ACC_REQ_N ? ACC_REQ_N : ACC_TR_N)
>>
>> Both ACC_TR_N and ACC_REQ_N are fixed. Can you explain why we need 
>> this magic?
>>
>> Related, wouldn't it be better to define it in the enum?
> 
> I can do that, of course. I just didn't want to make the enum even more
> complex. :-)

My concern is there is a disconnect between the enum and this macro. 
What would happen if we increase the enum past ACC_REQ_N/ACC_TR_N?
Would it be necessary to update ACC_CHD_N?

Cheers,
Jürgen Groß April 28, 2023, 7:20 a.m. UTC | #4
On 27.04.23 18:32, Julien Grall wrote:
> Hi,
> 
> On 26/04/2023 08:08, Juergen Gross wrote:
>> On 25.04.23 19:52, Julien Grall wrote:
>>> Hi Juergen,
>>>
>>> On 05/04/2023 08:03, Juergen Gross wrote:
>>>> Instead of modifying accounting data and undo those modifications in
>>>> case of an error during further processing, add a framework for
>>>> collecting the needed changes and commit them only when the whole
>>>> operation has succeeded.
>>>>
>>>> This scheme can reuse large parts of the per transaction accounting.
>>>> The changed_domain handling can be reused, but the array size of the
>>>> accounting data should be possible to be different for both use cases.
>>>>
>>>> Signed-off-by: Juergen Gross <jgross@suse.com>
>>>> ---
>>>> V3:
>>>> - call acc_commit() earlier (Julien Grall)
>>>> - add assert() to acc_commit()
>>>> - use fixed sized acc array in struct changed_domain (Julien Grall)
>>>> ---
>>>>   tools/xenstore/xenstored_core.c   |  9 ++++--
>>>>   tools/xenstore/xenstored_core.h   |  3 ++
>>>>   tools/xenstore/xenstored_domain.c | 53 ++++++++++++++++++++++++++++++-
>>>>   tools/xenstore/xenstored_domain.h |  5 ++-
>>>>   4 files changed, 66 insertions(+), 4 deletions(-)
>>>>
>>>> diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c
>>>> index 3ca68681e3..84335f5f3d 100644
>>>> --- a/tools/xenstore/xenstored_core.c
>>>> +++ b/tools/xenstore/xenstored_core.c
>>>> @@ -1023,6 +1023,9 @@ static void send_error(struct connection *conn, int 
>>>> error)
>>>>               break;
>>>>           }
>>>>       }
>>>> +
>>>> +    acc_drop(conn);
>>>> +
>>>>       send_reply(conn, XS_ERROR, xsd_errors[i].errstring,
>>>>                 strlen(xsd_errors[i].errstring) + 1);
>>>>   }
>>>> @@ -1034,6 +1037,9 @@ void send_reply(struct connection *conn, enum 
>>>> xsd_sockmsg_type type,
>>>>       assert(type != XS_WATCH_EVENT);
>>>> +    conn->in = NULL;
>>>
>>> AFAIU, you are setting conn->in to NULL in order to please..
>>>
>>>> +    acc_commit(conn);
>>>
>>> ... this call. However in case of an error like...
>>>
>>>> +
>>>>       if ( len > XENSTORE_PAYLOAD_MAX ) { > send_error(conn, E2BIG);
>>>
>>> ... here, send_reply() will be called again. But the error will not be set 
>>> because conn->in is NULL.
>>>
>>> So I think you want to restore conn->in rewrite acc_commit(). The ordering 
>>> would also deserve an explanation in a comment.
>>
>> Just to make sure I understand you correctly (I have some difficulties
>> parsing "So I think you want to restore conn->in rewrite acc_commit()."
>> completely):
> 
> Hmmm... Not sure why I wrote "rewrite". I was meant to say that you want to 
> restore conn->in after acc_commit() is called.
> 
>>
>> You are suggesting to move setting conn->in to NULL to acc_commit() and
>> to restore it before returning? I'm fine with that.
> 
> Either that or what I wrote above. It depends on whether you expect other caller 
> to be in the same situation.

I think it should be local to acc_commit(), as conn->in being NULL is a
requirement of the acc_commit() handling.

> 
>>
>>>
>>>>           return;
>>>> @@ -1059,8 +1065,6 @@ void send_reply(struct connection *conn, enum 
>>>> xsd_sockmsg_type type,
>>>>           }
>>>>       }
>>>> -    conn->in = NULL;
>>>> -
>>>>       /* Update relevant header fields and fill in the message body. */
>>>>       bdata->hdr.msg.type = type;
>>>>       bdata->hdr.msg.len = len;
>>>> @@ -2195,6 +2199,7 @@ struct connection *new_connection(const struct 
>>>> interface_funcs *funcs)
>>>>       new->is_stalled = false;
>>>>       new->transaction_started = 0;
>>>>       INIT_LIST_HEAD(&new->out_list);
>>>> +    INIT_LIST_HEAD(&new->acc_list);
>>>>       INIT_LIST_HEAD(&new->ref_list);
>>>>       INIT_LIST_HEAD(&new->watches);
>>>>       INIT_LIST_HEAD(&new->transaction_list);
>>>> diff --git a/tools/xenstore/xenstored_core.h b/tools/xenstore/xenstored_core.h
>>>> index c59b06551f..1f811f38cb 100644
>>>> --- a/tools/xenstore/xenstored_core.h
>>>> +++ b/tools/xenstore/xenstored_core.h
>>>> @@ -139,6 +139,9 @@ struct connection
>>>>       struct list_head out_list;
>>>>       uint64_t timeout_msec;
>>>> +    /* Not yet committed accounting data (valid if in != NULL). */
>>>> +    struct list_head acc_list;
>>>> +
>>>>       /* Referenced requests no longer pending. */
>>>>       struct list_head ref_list;
>>>> diff --git a/tools/xenstore/xenstored_domain.c 
>>>> b/tools/xenstore/xenstored_domain.c
>>>> index 30fb9acec6..144cbafb73 100644
>>>> --- a/tools/xenstore/xenstored_domain.c
>>>> +++ b/tools/xenstore/xenstored_domain.c
>>>> @@ -91,6 +91,8 @@ struct domain
>>>>       bool wrl_delay_logged;
>>>>   };
>>>> +#define ACC_CHD_N (ACC_TR_N < ACC_REQ_N ? ACC_REQ_N : ACC_TR_N)
>>>
>>> Both ACC_TR_N and ACC_REQ_N are fixed. Can you explain why we need this magic?
>>>
>>> Related, wouldn't it be better to define it in the enum?
>>
>> I can do that, of course. I just didn't want to make the enum even more
>> complex. :-)
> 
> My concern is there is a disconnect between the enum and this macro. What would 
> happen if we increase the enum past ACC_REQ_N/ACC_TR_N?

This expansion is happening in later patches.

> Would it be necessary to update ACC_CHD_N?

With the #define above: no.

With including it in the enum: depends on the values of ACC_REQ_N and ACC_TR_N.
ACC_CHD_N must always be equal to the larger one of both.

In my current V6 variant I've added it to the enum with a comment:

+       ACC_CHD_N = ACC_TR_N,   /* max(ACC_REQ_N, ACC_TR_N), for changed dom. */


Juergen
diff mbox series

Patch

diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c
index 3ca68681e3..84335f5f3d 100644
--- a/tools/xenstore/xenstored_core.c
+++ b/tools/xenstore/xenstored_core.c
@@ -1023,6 +1023,9 @@  static void send_error(struct connection *conn, int error)
 			break;
 		}
 	}
+
+	acc_drop(conn);
+
 	send_reply(conn, XS_ERROR, xsd_errors[i].errstring,
 			  strlen(xsd_errors[i].errstring) + 1);
 }
@@ -1034,6 +1037,9 @@  void send_reply(struct connection *conn, enum xsd_sockmsg_type type,
 
 	assert(type != XS_WATCH_EVENT);
 
+	conn->in = NULL;
+	acc_commit(conn);
+
 	if ( len > XENSTORE_PAYLOAD_MAX ) {
 		send_error(conn, E2BIG);
 		return;
@@ -1059,8 +1065,6 @@  void send_reply(struct connection *conn, enum xsd_sockmsg_type type,
 		}
 	}
 
-	conn->in = NULL;
-
 	/* Update relevant header fields and fill in the message body. */
 	bdata->hdr.msg.type = type;
 	bdata->hdr.msg.len = len;
@@ -2195,6 +2199,7 @@  struct connection *new_connection(const struct interface_funcs *funcs)
 	new->is_stalled = false;
 	new->transaction_started = 0;
 	INIT_LIST_HEAD(&new->out_list);
+	INIT_LIST_HEAD(&new->acc_list);
 	INIT_LIST_HEAD(&new->ref_list);
 	INIT_LIST_HEAD(&new->watches);
 	INIT_LIST_HEAD(&new->transaction_list);
diff --git a/tools/xenstore/xenstored_core.h b/tools/xenstore/xenstored_core.h
index c59b06551f..1f811f38cb 100644
--- a/tools/xenstore/xenstored_core.h
+++ b/tools/xenstore/xenstored_core.h
@@ -139,6 +139,9 @@  struct connection
 	struct list_head out_list;
 	uint64_t timeout_msec;
 
+	/* Not yet committed accounting data (valid if in != NULL). */
+	struct list_head acc_list;
+
 	/* Referenced requests no longer pending. */
 	struct list_head ref_list;
 
diff --git a/tools/xenstore/xenstored_domain.c b/tools/xenstore/xenstored_domain.c
index 30fb9acec6..144cbafb73 100644
--- a/tools/xenstore/xenstored_domain.c
+++ b/tools/xenstore/xenstored_domain.c
@@ -91,6 +91,8 @@  struct domain
 	bool wrl_delay_logged;
 };
 
+#define ACC_CHD_N (ACC_TR_N < ACC_REQ_N ? ACC_REQ_N : ACC_TR_N)
+
 struct changed_domain
 {
 	/* List of all changed domains. */
@@ -100,7 +102,7 @@  struct changed_domain
 	unsigned int domid;
 
 	/* Accounting data. */
-	int acc[ACC_TR_N];
+	int acc[ACC_CHD_N];
 };
 
 static struct hashtable *domhash;
@@ -1070,6 +1072,7 @@  static int domain_acc_add(struct connection *conn, unsigned int domid,
 			  enum accitem what, int add, bool no_dom_alloc)
 {
 	struct domain *d;
+	struct changed_domain *cd;
 	struct list_head *head;
 	int ret;
 
@@ -1090,6 +1093,22 @@  static int domain_acc_add(struct connection *conn, unsigned int domid,
 		}
 	}
 
+	/* Temporary accounting data until final commit? */
+	if (conn && conn->in && what < ACC_REQ_N) {
+		/* Consider transaction local data. */
+		ret = 0;
+		if (conn->transaction && what < ACC_TR_N) {
+			head = transaction_get_changed_domains(
+				conn->transaction);
+			cd = acc_find_changed_domain(head, domid);
+			if (cd)
+				ret = cd->acc[what];
+		}
+		ret += acc_add_changed_dom(conn->in, &conn->acc_list, what,
+					   add, domid);
+		return errno ? -1 : domain_acc_add_valid(d, what, ret);
+	}
+
 	if (conn && conn->transaction && what < ACC_TR_N) {
 		head = transaction_get_changed_domains(conn->transaction);
 		ret = acc_add_changed_dom(conn->transaction, head, what,
@@ -1106,6 +1125,38 @@  static int domain_acc_add(struct connection *conn, unsigned int domid,
 	return d->acc[what];
 }
 
+void acc_drop(struct connection *conn)
+{
+	struct changed_domain *cd;
+
+	while ((cd = list_top(&conn->acc_list, struct changed_domain, list))) {
+		list_del(&cd->list);
+		talloc_free(cd);
+	}
+}
+
+void acc_commit(struct connection *conn)
+{
+	struct changed_domain *cd;
+	enum accitem what;
+
+	/*
+	 * Make sure domain_acc_add() below can't add additional data to
+	 * to be committed accounting records.
+	 */
+	assert(!conn->in);
+
+	while ((cd = list_top(&conn->acc_list, struct changed_domain, list))) {
+		list_del(&cd->list);
+		for (what = 0; what < ACC_REQ_N; what++)
+			if (cd->acc[what])
+				domain_acc_add(conn, cd->domid, what,
+					       cd->acc[what], true);
+
+		talloc_free(cd);
+	}
+}
+
 int domain_nbentry_inc(struct connection *conn, unsigned int domid)
 {
 	return (domain_acc_add(conn, domid, ACC_NODES, 1, false) < 0)
diff --git a/tools/xenstore/xenstored_domain.h b/tools/xenstore/xenstored_domain.h
index 9d05eb01da..6355ad4f37 100644
--- a/tools/xenstore/xenstored_domain.h
+++ b/tools/xenstore/xenstored_domain.h
@@ -25,7 +25,8 @@ 
  * a per transaction array.
  */
 enum accitem {
-	ACC_NODES,
+	ACC_REQ_N,		/* Number of elements per request. */
+	ACC_NODES = ACC_REQ_N,
 	ACC_TR_N,		/* Number of elements per transaction. */
 	ACC_N = ACC_TR_N,	/* Number of elements per domain. */
 };
@@ -113,6 +114,8 @@  int domain_get_quota(const void *ctx, struct connection *conn,
  * If "update" is true, "chk_quota" is ignored.
  */
 int acc_fix_domains(struct list_head *head, bool chk_quota, bool update);
+void acc_drop(struct connection *conn);
+void acc_commit(struct connection *conn);
 
 /* Write rate limiting */