@@ -415,7 +415,7 @@ CONTROL <command>|[<parameters>|]
print|<string>
print <string> to syslog (xenstore runs as daemon) or
to console (xenstore runs as stubdom)
- quota|[set <name> <val>|<domid>]
+ quota|[set <name> <val>|<domid>|max [-r]]
without parameters: print the current quota settings
with "set <name> <val>": set the quota <name> to new value
<val> (The admin should make sure all the domain usage is
@@ -424,6 +424,9 @@ CONTROL <command>|[<parameters>|]
violating the new quota setting isn't increased further)
with "<domid>": print quota related accounting data for
the domain <domid>
+ with "max [-r]": show global per-domain maximum values of all
+ unprivileged domains, optionally reset the values by adding
+ "-r"
quota-soft|[set <name> <val>]
like the "quota" command, but for soft-quota.
trace|[+|-<switch>]
@@ -306,6 +306,22 @@ static int quota_get(const void *ctx, struct connection *conn,
return domain_get_quota(ctx, conn, atoi(vec[0]));
}
+static int quota_max(const void *ctx, struct connection *conn,
+ char **vec, int num)
+{
+ if (num > 1)
+ return EINVAL;
+
+ if (num == 1) {
+ if (!strcmp(vec[0], "-r"))
+ domain_reset_global_acc();
+ else
+ return EINVAL;
+ }
+
+ return domain_max_global_acc(ctx, conn);
+}
+
static int do_control_quota(const void *ctx, struct connection *conn,
char **vec, int num)
{
@@ -315,6 +331,9 @@ static int do_control_quota(const void *ctx, struct connection *conn,
if (!strcmp(vec[0], "set"))
return quota_set(ctx, conn, vec + 1, num - 1, hard_quotas);
+ if (!strcmp(vec[0], "max"))
+ return quota_max(ctx, conn, vec + 1, num - 1);
+
return quota_get(ctx, conn, vec, num);
}
@@ -978,7 +997,8 @@ static struct cmd_s cmds[] = {
{ "memreport", do_control_memreport, "[<file>]" },
#endif
{ "print", do_control_print, "<string>" },
- { "quota", do_control_quota, "[set <name> <val>|<domid>]" },
+ { "quota", do_control_quota,
+ "[set <name> <val>|<domid>|max [-r]]" },
{ "quota-soft", do_control_quota_s, "[set <name> <val>]" },
{ "help", do_control_help, "" },
};
@@ -43,6 +43,8 @@ static evtchn_port_t virq_port;
xenevtchn_handle *xce_handle = NULL;
+static unsigned int acc_global_max[ACC_N];
+
struct domain
{
/* The id of this domain */
@@ -70,7 +72,10 @@ struct domain
bool introduced;
/* Accounting data for this domain. */
- unsigned int acc[ACC_N];
+ struct acc {
+ unsigned int val;
+ unsigned int max;
+ } acc[ACC_N];
/* Memory quota data for this domain. */
bool soft_quota_reported;
@@ -199,9 +204,9 @@ static bool domain_can_read(struct connection *conn)
if (domain_is_unprivileged(conn)) {
if (domain->wrl_credit < 0)
return false;
- if (domain->acc[ACC_OUTST] >= quota_req_outstanding)
+ if (domain->acc[ACC_OUTST].val >= quota_req_outstanding)
return false;
- if (domain->acc[ACC_MEM] >= quota_memory_per_domain_hard &&
+ if (domain->acc[ACC_MEM].val >= quota_memory_per_domain_hard &&
quota_memory_per_domain_hard)
return false;
}
@@ -264,7 +269,7 @@ static int domain_tree_remove_sub(const void *ctx, struct connection *conn,
ret = WALK_TREE_SKIP_CHILDREN;
}
- return domain->acc[ACC_NODES] ? ret : WALK_TREE_SUCCESS_STOP;
+ return domain->acc[ACC_NODES].val ? ret : WALK_TREE_SUCCESS_STOP;
}
static void domain_tree_remove(struct domain *domain)
@@ -272,7 +277,7 @@ static void domain_tree_remove(struct domain *domain)
int ret;
struct walk_funcs walkfuncs = { .enter = domain_tree_remove_sub };
- if (domain->acc[ACC_NODES]) {
+ if (domain->acc[ACC_NODES].val) {
ret = walk_node_tree(domain, NULL, "/", &walkfuncs, domain);
if (ret == WALK_TREE_ERROR_STOP)
syslog(LOG_ERR,
@@ -430,14 +435,41 @@ int domain_get_quota(const void *ctx, struct connection *conn,
return ENOMEM;
#define ent(t, e) \
- resp = talloc_asprintf_append(resp, "%-16s: %8d\n", #t, e); \
+ resp = talloc_asprintf_append(resp, "%-16s: %8u (max: %8u\n", #t, \
+ d->acc[e].val, d->acc[e].max); \
+ if (!resp) return ENOMEM
+
+ ent(nodes, ACC_NODES);
+ ent(watches, ACC_WATCH);
+ ent(transactions, ACC_TRANS);
+ ent(outstanding, ACC_OUTST);
+ ent(memory, ACC_MEM);
+
+#undef ent
+
+ send_reply(conn, XS_CONTROL, resp, strlen(resp) + 1);
+
+ return 0;
+}
+
+int domain_max_global_acc(const void *ctx, struct connection *conn)
+{
+ char *resp;
+
+ resp = talloc_asprintf(ctx, "Max. seen accounting values:\n");
+ if (!resp)
+ return ENOMEM;
+
+#define ent(t, e) \
+ resp = talloc_asprintf_append(resp, "%-16s: %8u\n", #t, \
+ acc_global_max[e]); \
if (!resp) return ENOMEM
- ent(nodes, d->acc[ACC_NODES]);
- ent(watches, d->acc[ACC_WATCH]);
- ent(transactions, d->acc[ACC_TRANS]);
- ent(outstanding, d->acc[ACC_OUTST]);
- ent(memory, d->acc[ACC_MEM]);
+ ent(nodes, ACC_NODES);
+ ent(watches, ACC_WATCH);
+ ent(transactions, ACC_TRANS);
+ ent(outstanding, ACC_OUTST);
+ ent(memory, ACC_MEM);
#undef ent
@@ -1055,14 +1087,22 @@ int domain_adjust_node_perms(struct node *node)
static int domain_acc_add_chk(struct domain *d, unsigned int what, int add,
unsigned int domid)
{
- if ((add < 0 && -add > d->acc[what]) ||
- (d->acc[what] + add) > INT_MAX) {
+ unsigned int val;
+
+ if ((add < 0 && -add > d->acc[what].val) ||
+ (d->acc[what].val + add) > INT_MAX) {
syslog(LOG_ERR, "Accounting error: domain %u type %u val %u\n",
- domid, what, d->acc[what] + add);
+ domid, what, d->acc[what].val + add);
return (add < 0) ? 0 : INT_MAX;
}
- return d->acc[what] + add;
+ val = d->acc[what].val + add;
+ if (val > d->acc[what].max)
+ d->acc[what].max = val;
+ if (val > acc_global_max[what] && domid_is_unprivileged(domid))
+ acc_global_max[what] = val;
+
+ return val;
}
static int domain_acc_add(struct connection *conn, unsigned int domid,
@@ -1118,10 +1158,10 @@ static int domain_acc_add(struct connection *conn, unsigned int domid,
}
trace_acc("global change domid %u: what=%u %u add %d\n", domid, what,
- d->acc[what], add);
- d->acc[what] = domain_acc_add_chk(d, what, add, domid);
+ d->acc[what].val, add);
+ d->acc[what].val = domain_acc_add_chk(d, what, add, domid);
- return d->acc[what];
+ return d->acc[what].val;
}
void acc_drop(struct connection *conn)
@@ -1154,6 +1194,28 @@ void acc_commit(struct connection *conn)
conn->in = in;
}
+static int domain_reset_global_acc_sub(void *k, void *v, void *arg)
+{
+ struct domain *d = v;
+ unsigned int i;
+
+ for (i = 0; i < ACC_N; i++)
+ d->acc[i].max = d->acc[i].val;
+
+ return 0;
+}
+
+void domain_reset_global_acc(void)
+{
+ unsigned int i;
+
+ for (i = 0; i < ACC_N; i++)
+ acc_global_max[i] = 0;
+
+ /* Set current max values seen. */
+ hashtable_iterate(domhash, domain_reset_global_acc_sub, NULL);
+}
+
int domain_nbentry_inc(struct connection *conn, unsigned int domid)
{
return (domain_acc_add(conn, domid, ACC_NODES, 1, false) < 0)
@@ -1654,7 +1716,7 @@ static int domain_check_acc_init_sub(void *k, void *v, void *arg)
* If everything is correct incrementing the value for each node will
* result in dom->nodes being 0 at the end.
*/
- dom->nodes = -d->acc[ACC_NODES];
+ dom->nodes = -d->acc[ACC_NODES].val;
if (!hashtable_insert(domains, &dom->domid, dom)) {
talloc_free(dom);
@@ -1709,7 +1771,7 @@ static int domain_check_acc_sub(void *k, void *v, void *arg)
if (!d)
return 0;
- d->acc[ACC_NODES] += dom->nodes;
+ d->acc[ACC_NODES].val += dom->nodes;
return 0;
}
@@ -116,6 +116,8 @@ int domain_get_quota(const void *ctx, struct connection *conn,
int acc_fix_domains(struct list_head *head, bool update);
void acc_drop(struct connection *conn);
void acc_commit(struct connection *conn);
+int domain_max_global_acc(const void *ctx, struct connection *conn);
+void domain_reset_global_acc(void);
/* Write rate limiting */
Add saving the maximum values of the different accounting data seen per domain and (for unprivileged domains) globally, and print those values via the xenstore-control quota command. Add a sub-command for resetting the global maximum values seen. This should help for a decision how to set the related quotas. Signed-off-by: Juergen Gross <jgross@suse.com> --- docs/misc/xenstore.txt | 5 +- tools/xenstore/xenstored_control.c | 22 ++++++- tools/xenstore/xenstored_domain.c | 102 +++++++++++++++++++++++------ tools/xenstore/xenstored_domain.h | 2 + 4 files changed, 109 insertions(+), 22 deletions(-)