@@ -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);
+ /* Commit accounting now, as later errors won't undo any changes. */
+ acc_commit(conn);
+
if ( len > XENSTORE_PAYLOAD_MAX ) {
send_error(conn, E2BIG);
return;
@@ -2195,6 +2201,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);
@@ -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;
@@ -100,7 +100,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 +1070,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 +1091,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 +1123,41 @@ 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;
+ struct buffered_data *in = conn->in;
+
+ /*
+ * Make sure domain_acc_add() below can't add additional data to
+ * to be committed accounting records.
+ */
+ conn->in = NULL;
+
+ 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);
+ }
+
+ conn->in = in;
+}
+
int domain_nbentry_inc(struct connection *conn, unsigned int domid)
{
return (domain_acc_add(conn, domid, ACC_NODES, 1, false) < 0)
@@ -25,8 +25,10 @@
* 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_CHD_N = ACC_TR_N, /* max(ACC_REQ_N, ACC_TR_N), for changed dom. */
ACC_N = ACC_TR_N, /* Number of elements per domain. */
};
@@ -113,6 +115,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 */
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) V5: - set conn->in to NULL only locally in acc_commit() (Julien Grall) - define ACC_CHD_N in enum (Julien Grall) --- tools/xenstore/xenstored_core.c | 7 ++++ tools/xenstore/xenstored_core.h | 3 ++ tools/xenstore/xenstored_domain.c | 54 ++++++++++++++++++++++++++++++- tools/xenstore/xenstored_domain.h | 6 +++- 4 files changed, 68 insertions(+), 2 deletions(-)