summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to '0056-tools-xenstore-add-infrastructure-to-keep-track-of-p.patch')
-rw-r--r--0056-tools-xenstore-add-infrastructure-to-keep-track-of-p.patch289
1 files changed, 289 insertions, 0 deletions
diff --git a/0056-tools-xenstore-add-infrastructure-to-keep-track-of-p.patch b/0056-tools-xenstore-add-infrastructure-to-keep-track-of-p.patch
new file mode 100644
index 0000000..b92c61c
--- /dev/null
+++ b/0056-tools-xenstore-add-infrastructure-to-keep-track-of-p.patch
@@ -0,0 +1,289 @@
+From 2e406cf5fbb817341dc860473158382057e13de5 Mon Sep 17 00:00:00 2001
+From: Juergen Gross <jgross@suse.com>
+Date: Tue, 13 Sep 2022 07:35:09 +0200
+Subject: [PATCH 56/87] tools/xenstore: add infrastructure to keep track of per
+ domain memory usage
+
+The amount of memory a domain can consume in Xenstore is limited by
+various quota today, but even with sane quota a domain can still
+consume rather large memory quantities.
+
+Add the infrastructure for keeping track of the amount of memory a
+domain is consuming in Xenstore. Note that this is only the memory a
+domain has direct control over, so any internal administration data
+needed by Xenstore only is not being accounted for.
+
+There are two quotas defined: a soft quota which will result in a
+warning issued via syslog() when it is exceeded, and a hard quota
+resulting in a stop of accepting further requests or watch events as
+long as the hard quota would be violated by accepting those.
+
+Setting any of those quotas to 0 will disable it.
+
+As default values use 2MB per domain for the soft limit (this basically
+covers the allowed case to create 1000 nodes needing 2kB each), and
+2.5MB for the hard limit.
+
+This is part of XSA-326.
+
+Signed-off-by: Juergen Gross <jgross@suse.com>
+Reviewed-by: Julien Grall <jgrall@amazon.com>
+(cherry picked from commit 0d4a8ec7a93faedbe54fd197db146de628459e77)
+---
+ tools/xenstore/xenstored_core.c | 30 ++++++++--
+ tools/xenstore/xenstored_core.h | 2 +
+ tools/xenstore/xenstored_domain.c | 93 +++++++++++++++++++++++++++++++
+ tools/xenstore/xenstored_domain.h | 20 +++++++
+ 4 files changed, 139 insertions(+), 6 deletions(-)
+
+diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c
+index 5171d34c947e..b2bf6740d430 100644
+--- a/tools/xenstore/xenstored_core.c
++++ b/tools/xenstore/xenstored_core.c
+@@ -109,6 +109,8 @@ int quota_nb_perms_per_node = 5;
+ int quota_trans_nodes = 1024;
+ int quota_max_path_len = XENSTORE_REL_PATH_MAX;
+ int quota_req_outstanding = 20;
++int quota_memory_per_domain_soft = 2 * 1024 * 1024; /* 2 MB */
++int quota_memory_per_domain_hard = 2 * 1024 * 1024 + 512 * 1024; /* 2.5 MB */
+
+ unsigned int timeout_watch_event_msec = 20000;
+
+@@ -2406,7 +2408,14 @@ static void usage(void)
+ " quotas are:\n"
+ " transaction-nodes: number of accessed node per\n"
+ " transaction\n"
++" memory: total used memory per domain for nodes,\n"
++" transactions, watches and requests, above\n"
++" which Xenstore will stop talking to domain\n"
+ " outstanding: number of outstanding requests\n"
++" -q, --quota-soft <what>=<nb> set a soft quota <what> to the value <nb>,\n"
++" causing a warning to be issued via syslog() if the\n"
++" limit is violated, allowed quotas are:\n"
++" memory: see above\n"
+ " -w, --timeout <what>=<seconds> set the timeout in seconds for <what>,\n"
+ " allowed timeout candidates are:\n"
+ " watch-event: time a watch-event is kept pending\n"
+@@ -2433,6 +2442,7 @@ static struct option options[] = {
+ { "perm-nb", 1, NULL, 'A' },
+ { "path-max", 1, NULL, 'M' },
+ { "quota", 1, NULL, 'Q' },
++ { "quota-soft", 1, NULL, 'q' },
+ { "timeout", 1, NULL, 'w' },
+ { "no-recovery", 0, NULL, 'R' },
+ { "internal-db", 0, NULL, 'I' },
+@@ -2480,7 +2490,7 @@ static void set_timeout(const char *arg)
+ barf("unknown timeout \"%s\"\n", arg);
+ }
+
+-static void set_quota(const char *arg)
++static void set_quota(const char *arg, bool soft)
+ {
+ const char *eq = strchr(arg, '=');
+ int val;
+@@ -2488,11 +2498,16 @@ static void set_quota(const char *arg)
+ if (!eq)
+ barf("quotas must be specified via <what>=<nb>\n");
+ val = get_optval_int(eq + 1);
+- if (what_matches(arg, "outstanding"))
++ if (what_matches(arg, "outstanding") && !soft)
+ quota_req_outstanding = val;
+- else if (what_matches(arg, "transaction-nodes"))
++ else if (what_matches(arg, "transaction-nodes") && !soft)
+ quota_trans_nodes = val;
+- else
++ else if (what_matches(arg, "memory")) {
++ if (soft)
++ quota_memory_per_domain_soft = val;
++ else
++ quota_memory_per_domain_hard = val;
++ } else
+ barf("unknown quota \"%s\"\n", arg);
+ }
+
+@@ -2510,7 +2525,7 @@ int main(int argc, char *argv[])
+ orig_argc = argc;
+ orig_argv = argv;
+
+- while ((opt = getopt_long(argc, argv, "DE:F:HNPS:t:A:M:Q:T:RVW:w:U",
++ while ((opt = getopt_long(argc, argv, "DE:F:HNPS:t:A:M:Q:q:T:RVW:w:U",
+ options, NULL)) != -1) {
+ switch (opt) {
+ case 'D':
+@@ -2561,7 +2576,10 @@ int main(int argc, char *argv[])
+ quota_max_path_len);
+ break;
+ case 'Q':
+- set_quota(optarg);
++ set_quota(optarg, false);
++ break;
++ case 'q':
++ set_quota(optarg, true);
+ break;
+ case 'w':
+ set_timeout(optarg);
+diff --git a/tools/xenstore/xenstored_core.h b/tools/xenstore/xenstored_core.h
+index 2c77ec7ee0f4..373af18297bf 100644
+--- a/tools/xenstore/xenstored_core.h
++++ b/tools/xenstore/xenstored_core.h
+@@ -270,6 +270,8 @@ extern int priv_domid;
+ extern int quota_nb_entry_per_domain;
+ extern int quota_req_outstanding;
+ extern int quota_trans_nodes;
++extern int quota_memory_per_domain_soft;
++extern int quota_memory_per_domain_hard;
+
+ extern unsigned int timeout_watch_event_msec;
+
+diff --git a/tools/xenstore/xenstored_domain.c b/tools/xenstore/xenstored_domain.c
+index f04b7aae8a32..94fd561e9de4 100644
+--- a/tools/xenstore/xenstored_domain.c
++++ b/tools/xenstore/xenstored_domain.c
+@@ -76,6 +76,13 @@ struct domain
+ /* number of entry from this domain in the store */
+ int nbentry;
+
++ /* Amount of memory allocated for this domain. */
++ int memory;
++ bool soft_quota_reported;
++ bool hard_quota_reported;
++ time_t mem_last_msg;
++#define MEM_WARN_MINTIME_SEC 10
++
+ /* number of watch for this domain */
+ int nbwatch;
+
+@@ -192,6 +199,9 @@ static bool domain_can_read(struct connection *conn)
+ return false;
+ if (conn->domain->nboutstanding >= quota_req_outstanding)
+ return false;
++ if (conn->domain->memory >= quota_memory_per_domain_hard &&
++ quota_memory_per_domain_hard)
++ return false;
+ }
+
+ return (intf->req_cons != intf->req_prod);
+@@ -950,6 +960,89 @@ int domain_entry(struct connection *conn)
+ : 0;
+ }
+
++static bool domain_chk_quota(struct domain *domain, int mem)
++{
++ time_t now;
++
++ if (!domain || !domid_is_unprivileged(domain->domid) ||
++ (domain->conn && domain->conn->is_ignored))
++ return false;
++
++ now = time(NULL);
++
++ if (mem >= quota_memory_per_domain_hard &&
++ quota_memory_per_domain_hard) {
++ if (domain->hard_quota_reported)
++ return true;
++ syslog(LOG_ERR, "Domain %u exceeds hard memory quota, Xenstore interface to domain stalled\n",
++ domain->domid);
++ domain->mem_last_msg = now;
++ domain->hard_quota_reported = true;
++ return true;
++ }
++
++ if (now - domain->mem_last_msg >= MEM_WARN_MINTIME_SEC) {
++ if (domain->hard_quota_reported) {
++ domain->mem_last_msg = now;
++ domain->hard_quota_reported = false;
++ syslog(LOG_INFO, "Domain %u below hard memory quota again\n",
++ domain->domid);
++ }
++ if (mem >= quota_memory_per_domain_soft &&
++ quota_memory_per_domain_soft &&
++ !domain->soft_quota_reported) {
++ domain->mem_last_msg = now;
++ domain->soft_quota_reported = true;
++ syslog(LOG_WARNING, "Domain %u exceeds soft memory quota\n",
++ domain->domid);
++ }
++ if (mem < quota_memory_per_domain_soft &&
++ domain->soft_quota_reported) {
++ domain->mem_last_msg = now;
++ domain->soft_quota_reported = false;
++ syslog(LOG_INFO, "Domain %u below soft memory quota again\n",
++ domain->domid);
++ }
++
++ }
++
++ return false;
++}
++
++int domain_memory_add(unsigned int domid, int mem, bool no_quota_check)
++{
++ struct domain *domain;
++
++ domain = find_domain_struct(domid);
++ if (domain) {
++ /*
++ * domain_chk_quota() will print warning and also store whether
++ * the soft/hard quota has been hit. So check no_quota_check
++ * *after*.
++ */
++ if (domain_chk_quota(domain, domain->memory + mem) &&
++ !no_quota_check)
++ return ENOMEM;
++ domain->memory += mem;
++ } else {
++ /*
++ * The domain the memory is to be accounted for should always
++ * exist, as accounting is done either for a domain related to
++ * the current connection, or for the domain owning a node
++ * (which is always existing, as the owner of the node is
++ * tested to exist and replaced by domid 0 if not).
++ * So not finding the related domain MUST be an error in the
++ * data base.
++ */
++ errno = ENOENT;
++ corrupt(NULL, "Accounting called for non-existing domain %u\n",
++ domid);
++ return ENOENT;
++ }
++
++ return 0;
++}
++
+ void domain_watch_inc(struct connection *conn)
+ {
+ if (!conn || !conn->domain)
+diff --git a/tools/xenstore/xenstored_domain.h b/tools/xenstore/xenstored_domain.h
+index d6519904d831..633c9a0a0a1f 100644
+--- a/tools/xenstore/xenstored_domain.h
++++ b/tools/xenstore/xenstored_domain.h
+@@ -61,6 +61,26 @@ int domain_entry_inc(struct connection *conn, struct node *);
+ void domain_entry_dec(struct connection *conn, struct node *);
+ int domain_entry_fix(unsigned int domid, int num, bool update);
+ int domain_entry(struct connection *conn);
++int domain_memory_add(unsigned int domid, int mem, bool no_quota_check);
++
++/*
++ * domain_memory_add_chk(): to be used when memory quota should be checked.
++ * Not to be used when specifying a negative mem value, as lowering the used
++ * memory should always be allowed.
++ */
++static inline int domain_memory_add_chk(unsigned int domid, int mem)
++{
++ return domain_memory_add(domid, mem, false);
++}
++/*
++ * domain_memory_add_nochk(): to be used when memory quota should not be
++ * checked, e.g. when lowering memory usage, or in an error case for undoing
++ * a previous memory adjustment.
++ */
++static inline void domain_memory_add_nochk(unsigned int domid, int mem)
++{
++ domain_memory_add(domid, mem, true);
++}
+ void domain_watch_inc(struct connection *conn);
+ void domain_watch_dec(struct connection *conn);
+ int domain_watch(struct connection *conn);
+--
+2.37.4
+