commit cc0612464896e7ecf93dd9a52c4c4348fcf33d0e
Author: Juergen Gross <jgross@suse.com>
Date:   Mon Dec 5 08:48:52 2016 +0100

    xenstore: add small default data buffer to internal struct
    
    Instead of always allocating a data buffer for incoming or outgoing
    xenstore wire data add a small buffer to the buffered_data structure
    of xenstored. This has the advantage that especially sending simple
    response messages like errors or "OK" will no longer need allocating
    a data buffer. This requires adding a memory context where the
    allocated buffer was used for that purpose.
    
    In order to avoid allocating a new buffered_data structure for each
    response reuse the structure of the original request. This in turn
    will avoid any new memory allocations for sending e.g. an ENOMEM
    response making it possible to send it at all. To do this the
    allocation of the buffered_data structure for the incoming request
    must be done when a new request is recognized instead of doing it
    when accepting a new connect.
    
    Signed-off-by: Juergen Gross <jgross@suse.com>
    Acked-by: Wei Liu <wei.liu2@citrix.com>

Index: xen-4.5.5-testing/tools/xenstore/xenstored_core.c
===================================================================
--- xen-4.5.5-testing.orig/tools/xenstore/xenstored_core.c
+++ xen-4.5.5-testing/tools/xenstore/xenstored_core.c
@@ -653,17 +653,20 @@ void send_reply(struct connection *conn,
 		return;
 	}
 
-	/* Message is a child of the connection context for auto-cleanup. */
-	bdata = new_buffer(conn);
-	bdata->buffer = talloc_array(bdata, char, len);
-
-	/* Echo request header in reply unless this is an async watch event. */
+	/* Replies reuse the request buffer, events need a new one. */
 	if (type != XS_WATCH_EVENT) {
-		memcpy(&bdata->hdr.msg, &conn->in->hdr.msg,
-		       sizeof(struct xsd_sockmsg));
+		bdata = conn->in;
+		bdata->inhdr = true;
+		bdata->used = 0;
+		conn->in = NULL;
 	} else {
-		memset(&bdata->hdr.msg, 0, sizeof(struct xsd_sockmsg));
+		/* Message is a child of the connection for auto-cleanup. */
+		bdata = new_buffer(conn);
 	}
+	if (len <= DEFAULT_BUFFER_SIZE)
+		bdata->buffer = bdata->default_buffer;
+	else
+		bdata->buffer = talloc_array(bdata, char, len);
 
 	/* Update relevant header fields and fill in the message body. */
 	bdata->hdr.msg.type = type;
@@ -739,7 +742,7 @@ static char *perms_to_strings(const void
 	return strings;
 }
 
-char *canonicalize(struct connection *conn, const char *node)
+char *canonicalize(struct connection *conn, const void *ctx, const char *node)
 {
 	const char *prefix;
 
@@ -747,7 +750,7 @@ char *canonicalize(struct connection *co
 		return (char *)node;
 	prefix = get_implicit_path(conn);
 	if (prefix)
-		return talloc_asprintf(node, "%s/%s", prefix, node);
+		return talloc_asprintf(ctx, "%s/%s", prefix, node);
 	return (char *)node;
 }
 
@@ -761,7 +764,7 @@ static struct node *get_node_canonicaliz
 
 	if (!canonical_name)
 		canonical_name = &tmp_name;
-	*canonical_name = canonicalize(conn, name);
+	*canonical_name = canonicalize(conn, ctx, name);
 	return get_node(conn, ctx, *canonical_name, perm);
 }
 
@@ -871,17 +874,18 @@ static char *basename(const char *name)
 	return strrchr(name, '/') + 1;
 }
 
-static struct node *construct_node(struct connection *conn, const char *name)
+static struct node *construct_node(struct connection *conn, const void *ctx,
+				   const char *name)
 {
 	const char *base;
 	unsigned int baselen;
 	struct node *parent, *node;
-	char *children, *parentname = get_parent(name, name);
+	char *children, *parentname = get_parent(ctx, name);
 
 	/* If parent doesn't exist, create it. */
 	parent = read_node(conn, parentname, parentname);
 	if (!parent)
-		parent = construct_node(conn, parentname);
+		parent = construct_node(conn, ctx, parentname);
 	if (!parent)
 		return NULL;
 
@@ -891,7 +895,7 @@ static struct node *construct_node(struc
 	/* Add child to parent. */
 	base = basename(name);
 	baselen = strlen(base) + 1;
-	children = talloc_array(name, char, parent->childlen + baselen);
+	children = talloc_array(ctx, char, parent->childlen + baselen);
 	if (!children)
 		goto nomem;
 	memcpy(children, parent->children, parent->childlen);
@@ -900,7 +904,7 @@ static struct node *construct_node(struc
 	parent->childlen += baselen;
 
 	/* Allocate node */
-	node = talloc(name, struct node);
+	node = talloc(ctx, struct node);
 	if (!node)
 		goto nomem;
 	node->tdb = tdb_context(conn);
@@ -944,13 +948,13 @@ static int destroy_node(void *_node)
 	return 0;
 }
 
-static struct node *create_node(struct connection *conn, 
+static struct node *create_node(struct connection *conn, const void *ctx,
 				const char *name,
 				void *data, unsigned int datalen)
 {
 	struct node *node, *i;
 
-	node = construct_node(conn, name);
+	node = construct_node(conn, ctx, name);
 	if (!node)
 		return NULL;
 
@@ -993,7 +997,8 @@ static int do_write(struct connection *c
 		/* No permissions, invalid input? */
 		if (errno != ENOENT)
 			return errno;
-		node = create_node(conn, name, in->buffer + offset, datalen);
+		node = create_node(conn, in, name, in->buffer + offset,
+				   datalen);
 		if (!node)
 			return errno;
 	} else {
@@ -1023,7 +1028,7 @@ static int do_mkdir(struct connection *c
 		/* No permissions? */
 		if (errno != ENOENT)
 			return errno;
-		node = create_node(conn, name, NULL, 0);
+		node = create_node(conn, in, name, NULL, 0);
 		if (!node)
 			return errno;
 		wrl_apply_debit_direct(conn);
@@ -1095,12 +1100,13 @@ static bool delete_child(struct connecti
 }
 
 
-static int _rm(struct connection *conn, struct node *node, const char *name)
+static int _rm(struct connection *conn, const void *ctx, struct node *node,
+	       const char *name)
 {
 	/* Delete from parent first, then if we crash, the worst that can
 	   happen is the child will continue to take up space, but will
 	   otherwise be unreachable. */
-	struct node *parent = read_node(conn, name, get_parent(name, name));
+	struct node *parent = read_node(conn, ctx, get_parent(ctx, name));
 	if (!parent)
 		return EINVAL;
 
@@ -1117,7 +1123,7 @@ static void internal_rm(const char *name
 	char *tname = talloc_strdup(NULL, name);
 	struct node *node = read_node(NULL, tname, tname);
 	if (node)
-		_rm(NULL, node, tname);
+		_rm(NULL, tname, node, tname);
 	talloc_free(node);
 	talloc_free(tname);
 }
@@ -1148,7 +1154,7 @@ static int do_rm(struct connection *conn
 	if (streq(name, "/"))
 		return EINVAL;
 
-	ret = _rm(conn, node, name);
+	ret = _rm(conn, in, node, name);
 	if (ret)
 		return ret;
 
@@ -1322,8 +1328,7 @@ static void consider_message(struct conn
 
 	process_message(conn, conn->in);
 
-	talloc_free(conn->in);
-	conn->in = new_buffer(conn);
+	assert(conn->in == NULL);
 }
 
 /* Errors in reading or allocating here mean we get out of sync, so we
@@ -1331,7 +1336,15 @@ static void consider_message(struct conn
 static void handle_input(struct connection *conn)
 {
 	int bytes;
-	struct buffered_data *in = conn->in;
+	struct buffered_data *in;
+
+	if (!conn->in) {
+		conn->in = new_buffer(conn);
+		/* In case of no memory just try it next time again. */
+		if (!conn->in)
+			return;
+	}
+	in = conn->in;
 
 	/* Not finished header yet? */
 	if (in->inhdr) {
@@ -1349,7 +1362,10 @@ static void handle_input(struct connecti
 			goto bad_client;
 		}
 
-		in->buffer = talloc_array(in, char, in->hdr.msg.len);
+		if (in->hdr.msg.len <= DEFAULT_BUFFER_SIZE)
+			in->buffer = in->default_buffer;
+		else
+			in->buffer = talloc_array(in, char, in->hdr.msg.len);
 		if (!in->buffer)
 			goto bad_client;
 		in->used = 0;
@@ -1398,12 +1414,6 @@ struct connection *new_connection(connwr
 	INIT_LIST_HEAD(&new->watches);
 	INIT_LIST_HEAD(&new->transaction_list);
 
-	new->in = new_buffer(new);
-	if (new->in == NULL) {
-		talloc_free(new);
-		return NULL;
-	}
-
 	list_add_tail(&new->list, &connections);
 	talloc_set_destructor(new, destroy_conn);
 	trace_create(new, "connection");
@@ -1543,7 +1553,7 @@ static void setup_structure(void)
 
 		if (remove_local) {
 			internal_rm("/local");
-			create_node(NULL, tlocal, NULL, 0);
+			create_node(NULL, NULL, tlocal, NULL, 0);
 
 			check_store();
 		}
Index: xen-4.5.5-testing/tools/xenstore/xenstored_core.h
===================================================================
--- xen-4.5.5-testing.orig/tools/xenstore/xenstored_core.h
+++ xen-4.5.5-testing/tools/xenstore/xenstored_core.h
@@ -37,6 +37,9 @@ typedef int32_t wrl_creditt;
 #define WRL_CREDIT_MAX (1000*1000*1000)
 /* ^ satisfies non-overflow condition for wrl_xfer_credit */
 
+/* DEFAULT_BUFFER_SIZE should be large enough for each errno string. */
+#define DEFAULT_BUFFER_SIZE 16
+
 struct buffered_data
 {
 	struct list_head list;
@@ -54,6 +57,7 @@ struct buffered_data
 
 	/* The actual data. */
 	char *buffer;
+	char default_buffer[DEFAULT_BUFFER_SIZE];
 };
 
 struct connection;
@@ -143,7 +147,7 @@ void send_reply(struct connection *conn,
 void send_ack(struct connection *conn, enum xsd_sockmsg_type type);
 
 /* Canonicalize this path if possible. */
-char *canonicalize(struct connection *conn, const char *node);
+char *canonicalize(struct connection *conn, const void *ctx, const char *node);
 
 /* Get this node, checking we have permissions. */
 struct node *get_node(struct connection *conn,
Index: xen-4.5.5-testing/tools/xenstore/xenstored_domain.c
===================================================================
--- xen-4.5.5-testing.orig/tools/xenstore/xenstored_domain.c
+++ xen-4.5.5-testing/tools/xenstore/xenstored_domain.c
@@ -348,9 +348,7 @@ static void domain_conn_reset(struct dom
 		talloc_free(out);
 	}
 
-	talloc_free(conn->in->buffer);
-	memset(conn->in, 0, sizeof(*conn->in));
-	conn->in->inhdr = true;
+	talloc_free(conn->in);
 
 	domain->interface->req_cons = domain->interface->req_prod = 0;
 	domain->interface->rsp_cons = domain->interface->rsp_prod = 0;
Index: xen-4.5.5-testing/tools/xenstore/xenstored_transaction.c
===================================================================
--- xen-4.5.5-testing.orig/tools/xenstore/xenstored_transaction.c
+++ xen-4.5.5-testing/tools/xenstore/xenstored_transaction.c
@@ -212,8 +212,8 @@ int do_transaction_end(struct connection
 	list_del(&trans->list);
 	conn->transaction_started--;
 
-	/* Attach transaction to arg for auto-cleanup */
-	talloc_steal(arg, trans);
+	/* Attach transaction to in for auto-cleanup */
+	talloc_steal(in, trans);
 
 	if (streq(arg, "T")) {
 		wrl_apply_debit_trans_commit(conn);
Index: xen-4.5.5-testing/tools/xenstore/xenstored_watch.c
===================================================================
--- xen-4.5.5-testing.orig/tools/xenstore/xenstored_watch.c
+++ xen-4.5.5-testing/tools/xenstore/xenstored_watch.c
@@ -165,7 +165,7 @@ int do_watch(struct connection *conn, st
 		/* check if valid event */
 	} else {
 		relative = !strstarts(vec[0], "/");
-		vec[0] = canonicalize(conn, vec[0]);
+		vec[0] = canonicalize(conn, in, vec[0]);
 		if (!is_valid_nodename(vec[0]))
 			return EINVAL;
 	}
@@ -210,7 +210,7 @@ int do_unwatch(struct connection *conn,
 	if (get_strings(in, vec, ARRAY_SIZE(vec)) != ARRAY_SIZE(vec))
 		return EINVAL;
 
-	node = canonicalize(conn, vec[0]);
+	node = canonicalize(conn, in, vec[0]);
 	list_for_each_entry(watch, &conn->watches, list) {
 		if (streq(watch->node, node) && streq(watch->token, vec[1])) {
 			list_del(&watch->list);
