* [PATCH v2 00/12] tools: add support for per-domain xenstore quota
@ 2026-03-20 15:01 Juergen Gross
2026-03-20 15:01 ` [PATCH v2 01/12] tools/libs/store: add get- and set-quota related functions Juergen Gross
` (11 more replies)
0 siblings, 12 replies; 20+ messages in thread
From: Juergen Gross @ 2026-03-20 15:01 UTC (permalink / raw)
To: xen-devel
Cc: Juergen Gross, Julien Grall, Anthony PERARD, Nick Rosbrook,
George Dunlap
This series is adding support for per-domain Xenstore quota to:
- xenstored
- libxenstore
- libxl
- xl
With this it is possible to e.g. allow larger limits for driver
domains.
Changes in V2:
- addressed all comments
- new patch 2
Juergen Gross (12):
tools/libs/store: add get- and set-quota related functions
tools/xenstored: add helper to parse domid
tools/xenstored: add central quota check functions
tools/xenstored: rework hard_quotas and soft_quotas arrays
tools/xenstored: add GLOBAL_QUOTA_DATA record for live update
tools/xenstored: split acc[] array in struct domain
tools/xenstored: use per-domain quota settings
tools/xenstored: implement the GET/SET_QUOTA commands
tools/libxl: add functions for retrieving and setting xenstore quota
tools/libxl: add support for xenstore quota in domain_config
tools/xl: add xl commands for xenstore quota operations
tools/xl: add support for xenstore quota setting via domain config
docs/man/xl.cfg.5.pod.in | 13 +
tools/golang/xenlight/helpers.gen.go | 84 +++++
tools/golang/xenlight/types.gen.go | 10 +
tools/include/libxl.h | 21 ++
tools/include/xenstore.h | 37 ++
tools/libs/light/Makefile | 1 +
tools/libs/light/libxl_dom.c | 8 +
tools/libs/light/libxl_domain.c | 11 +
tools/libs/light/libxl_types.idl | 10 +
tools/libs/light/libxl_xsquota.c | 116 ++++++
tools/libs/store/Makefile | 2 +-
tools/libs/store/libxenstore.map | 8 +
tools/libs/store/xs.c | 111 ++++++
tools/xenstored/control.c | 24 +-
tools/xenstored/core.c | 41 ++-
tools/xenstored/domain.c | 519 +++++++++++++++++++++------
tools/xenstored/domain.h | 28 +-
tools/xenstored/lu.c | 6 +
tools/xenstored/transaction.c | 2 +-
tools/xenstored/watch.c | 4 +-
tools/xl/Makefile | 1 +
tools/xl/xl.h | 2 +
tools/xl/xl_cmdtable.c | 10 +
tools/xl/xl_parse.c | 47 ++-
tools/xl/xl_parse.h | 1 +
tools/xl/xl_xsquota.c | 90 +++++
26 files changed, 1057 insertions(+), 150 deletions(-)
create mode 100644 tools/libs/light/libxl_xsquota.c
create mode 100644 tools/xl/xl_xsquota.c
--
2.53.0
^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH v2 01/12] tools/libs/store: add get- and set-quota related functions
2026-03-20 15:01 [PATCH v2 00/12] tools: add support for per-domain xenstore quota Juergen Gross
@ 2026-03-20 15:01 ` Juergen Gross
2026-03-23 12:16 ` Anthony PERARD
2026-03-20 15:01 ` [PATCH v2 02/12] tools/xenstored: add helper to parse domid Juergen Gross
` (10 subsequent siblings)
11 siblings, 1 reply; 20+ messages in thread
From: Juergen Gross @ 2026-03-20 15:01 UTC (permalink / raw)
To: xen-devel; +Cc: Juergen Gross, Julien Grall, Anthony PERARD
Add functions for getting and setting Xenstore quota to libxenstore:
xs_get_quota_names(): get the names of the supported quota.
xs_get_global_quota(): get the value of one global quota.
xs_set_global_quota(): set the value of one global quota.
xs_get_domain_quota(): get the value of one quota of a domain.
xs_set_domain_quota(): set the value of one quota of a domain.
Signed-off-by: Juergen Gross <jgross@suse.com>
---
V2:
- make string parameters const (Anthony Perard)
- add function descriptions to header (Anthony Perard)
---
tools/include/xenstore.h | 37 +++++++++++
tools/libs/store/Makefile | 2 +-
tools/libs/store/libxenstore.map | 8 +++
tools/libs/store/xs.c | 111 +++++++++++++++++++++++++++++++
4 files changed, 157 insertions(+), 1 deletion(-)
diff --git a/tools/include/xenstore.h b/tools/include/xenstore.h
index 423422dc50..bf6d767a22 100644
--- a/tools/include/xenstore.h
+++ b/tools/include/xenstore.h
@@ -277,6 +277,43 @@ bool xs_get_features_domain(struct xs_handle *h, unsigned int domid,
bool xs_set_features_domain(struct xs_handle *h, unsigned int domid,
unsigned int features);
+/* Get names of supported quota.
+ * Returns an array of quota names supported by Xenstore. On return
+ * num will contain the number of quota names in the array.
+ * Will return NULL on failure.
+ * The returned array should be freed via free() after usage, this will
+ * free the array AND the strings of the quota names.
+ */
+const char **xs_get_quota_names(struct xs_handle *h, unsigned int *num);
+
+/* Get the value of one global quota.
+ * Returns the global quota value specified by "quota" in "value".
+ * Return false on failure.
+ */
+bool xs_get_global_quota(struct xs_handle *h, const char *quota,
+ unsigned int *value);
+
+/* Set the value of one global quota.
+ * Sets the global quota value specified by "quota" to "value".
+ * Return false on failure.
+ */
+bool xs_set_global_quota(struct xs_handle *h, const char *quota,
+ unsigned int value);
+
+/* Get the value of one domain quota.
+ * Returns a domain's quota value specified by "quota" in "value".
+ * Return false on failure.
+ */
+bool xs_get_domain_quota(struct xs_handle *h, unsigned int domid,
+ const char *quota, unsigned int *value);
+
+/* Set the value of one domain quota.
+ * Sets a domain's quota value specified by "quota" to "value".
+ * Return false on failure.
+ */
+bool xs_set_domain_quota(struct xs_handle *h, unsigned int domid,
+ const char *quota, unsigned int value);
+
char *xs_control_command(struct xs_handle *h, const char *cmd,
void *data, unsigned int len);
/* Deprecated: use xs_control_command() instead. */
diff --git a/tools/libs/store/Makefile b/tools/libs/store/Makefile
index fed43b0008..b52d1f35ad 100644
--- a/tools/libs/store/Makefile
+++ b/tools/libs/store/Makefile
@@ -2,7 +2,7 @@ XEN_ROOT=$(CURDIR)/../../..
include $(XEN_ROOT)/tools/Rules.mk
MAJOR = 4
-MINOR = 1
+MINOR = 2
version-script := libxenstore.map
ifeq ($(CONFIG_Linux),y)
diff --git a/tools/libs/store/libxenstore.map b/tools/libs/store/libxenstore.map
index cd9df86749..a08ddd549f 100644
--- a/tools/libs/store/libxenstore.map
+++ b/tools/libs/store/libxenstore.map
@@ -45,3 +45,11 @@ VERS_4.1 {
xs_get_features_domain;
xs_set_features_domain;
} VERS_4.0;
+VERS_4.2 {
+ global:
+ xs_get_quota_names;
+ xs_get_global_quota;
+ xs_set_global_quota;
+ xs_get_domain_quota;
+ xs_set_domain_quota;
+} VERS_4.1;
diff --git a/tools/libs/store/xs.c b/tools/libs/store/xs.c
index 8f4b90a3cf..06462445e0 100644
--- a/tools/libs/store/xs.c
+++ b/tools/libs/store/xs.c
@@ -1456,6 +1456,117 @@ bool xs_set_features_domain(struct xs_handle *h, unsigned int domid,
return xs_bool(xs_talkv(h, iov, ARRAY_SIZE(iov), NULL));
}
+const char **xs_get_quota_names(struct xs_handle *h, unsigned int *num)
+{
+ struct xsd_sockmsg msg = { .type = XS_GET_QUOTA };
+ struct iovec iov[1];
+ const char **quota;
+ char *reply;
+ char *c;
+ unsigned int i;
+
+ iov[0].iov_base = &msg;
+ iov[0].iov_len = sizeof(msg);
+
+ reply = xs_talkv(h, iov, ARRAY_SIZE(iov), NULL);
+ if (!reply)
+ return NULL;
+
+ *num = 1;
+ for (c = reply; *c; c++)
+ if (*c == ' ')
+ (*num)++;
+
+ quota = malloc(*num * sizeof(char *) + strlen(reply) + 1);
+ c = (char *)(quota + *num);
+ strcpy(c, reply);
+ for (i = 0; i < *num; i++) {
+ quota[i] = c;
+ c = strchr(c, ' ');
+ if (c) {
+ *c = 0;
+ c++;
+ }
+ }
+
+ return quota;
+}
+
+bool xs_get_global_quota(struct xs_handle *h, const char *quota,
+ unsigned int *value)
+{
+ struct xsd_sockmsg msg = { .type = XS_GET_QUOTA };
+ struct iovec iov[2];
+
+ iov[0].iov_base = &msg;
+ iov[0].iov_len = sizeof(msg);
+ iov[1].iov_base = (char *)quota;
+ iov[1].iov_len = strlen(quota) + 1;
+
+ return xs_uint(xs_talkv(h, iov, ARRAY_SIZE(iov), NULL), value);
+}
+
+bool xs_set_global_quota(struct xs_handle *h, const char *quota,
+ unsigned int value)
+{
+ struct xsd_sockmsg msg = { .type = XS_SET_QUOTA };
+ char val_str[MAX_STRLEN(value)];
+ struct iovec iov[3];
+
+ snprintf(val_str, sizeof(val_str), "%u", value);
+
+ iov[0].iov_base = &msg;
+ iov[0].iov_len = sizeof(msg);
+ iov[1].iov_base = (char *)quota;
+ iov[1].iov_len = strlen(quota) + 1;
+ iov[2].iov_base = val_str;
+ iov[2].iov_len = strlen(val_str) + 1;
+
+ return xs_bool(xs_talkv(h, iov, ARRAY_SIZE(iov), NULL));
+}
+
+bool xs_get_domain_quota(struct xs_handle *h, unsigned int domid,
+ const char *quota, unsigned int *value)
+{
+ struct xsd_sockmsg msg = { .type = XS_GET_QUOTA };
+ char domid_str[MAX_STRLEN(domid)];
+ struct iovec iov[3];
+
+ snprintf(domid_str, sizeof(domid_str), "%u", domid);
+
+ iov[0].iov_base = &msg;
+ iov[0].iov_len = sizeof(msg);
+ iov[1].iov_base = domid_str;
+ iov[1].iov_len = strlen(domid_str) + 1;
+ iov[2].iov_base = (char *)quota;
+ iov[2].iov_len = strlen(quota) + 1;
+
+ return xs_uint(xs_talkv(h, iov, ARRAY_SIZE(iov), NULL), value);
+}
+
+bool xs_set_domain_quota(struct xs_handle *h, unsigned int domid,
+ const char *quota, unsigned int value)
+{
+ struct xsd_sockmsg msg = { .type = XS_SET_QUOTA };
+ char domid_str[MAX_STRLEN(domid)];
+ char val_str[MAX_STRLEN(value)];
+ struct iovec iov[4];
+
+ snprintf(domid_str, sizeof(domid_str), "%u", domid);
+ snprintf(val_str, sizeof(val_str), "%u", value);
+
+ iov[0].iov_base = &msg;
+ iov[0].iov_len = sizeof(msg);
+ iov[1].iov_base = domid_str;
+ iov[1].iov_len = strlen(domid_str) + 1;
+ iov[2].iov_base = (char *)quota;
+ iov[2].iov_len = strlen(quota) + 1;
+ iov[3].iov_base = val_str;
+ iov[3].iov_len = strlen(val_str) + 1;
+
+ return xs_bool(xs_talkv(h, iov, ARRAY_SIZE(iov), NULL));
+}
+
char *xs_control_command(struct xs_handle *h, const char *cmd,
void *data, unsigned int len)
{
--
2.53.0
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH v2 02/12] tools/xenstored: add helper to parse domid
2026-03-20 15:01 [PATCH v2 00/12] tools: add support for per-domain xenstore quota Juergen Gross
2026-03-20 15:01 ` [PATCH v2 01/12] tools/libs/store: add get- and set-quota related functions Juergen Gross
@ 2026-03-20 15:01 ` Juergen Gross
2026-03-23 12:30 ` Anthony PERARD
2026-03-20 15:01 ` [PATCH v2 03/12] tools/xenstored: add central quota check functions Juergen Gross
` (9 subsequent siblings)
11 siblings, 1 reply; 20+ messages in thread
From: Juergen Gross @ 2026-03-20 15:01 UTC (permalink / raw)
To: xen-devel; +Cc: Juergen Gross, Julien Grall, Anthony PERARD
Today a domid passed in by a command is parsed using atoi(). This
will still "succeed" even with a domid like "x", resulting in "0" to
be used instead.
Use a common domid parser instead rejecting everything but integers
in the range 0..65535 like specified in docs/misc/xenstore.txt.
Signed-off-by: Juergen Gross <jgross@suse.com>
---
V2:
- new patch (kind of suggested by Anthony Perard)
---
tools/xenstored/domain.c | 47 +++++++++++++++++++++++++++++++++-------
1 file changed, 39 insertions(+), 8 deletions(-)
diff --git a/tools/xenstored/domain.c b/tools/xenstored/domain.c
index e453b3061f..a70acddf94 100644
--- a/tools/xenstored/domain.c
+++ b/tools/xenstored/domain.c
@@ -732,6 +732,18 @@ static char *talloc_domain_path(const void *context, unsigned int domid)
return talloc_asprintf(context, "/local/domain/%u", domid);
}
+/* Parse a domid. Sets errno either to 0 or EINVAL. */
+static unsigned int parse_domid(const char *input)
+{
+ unsigned long domid;
+ char *endptr;
+
+ domid = strtoul(input, &endptr, 10);
+ errno = (*endptr != 0 || domid > 65535) ? EINVAL : 0;
+
+ return domid;
+}
+
int domain_get_quota(const void *ctx, struct connection *conn,
unsigned int domid)
{
@@ -1077,7 +1089,10 @@ int do_introduce(const void *ctx, struct connection *conn,
if (get_strings(in, vec, ARRAY_SIZE(vec)) < ARRAY_SIZE(vec))
return EINVAL;
- domid = atoi(vec[0]);
+ domid = parse_domid(vec[0]);
+ if (errno)
+ return errno;
+
/* Ignore the gfn, we don't need it. */
port = atoi(vec[2]);
@@ -1124,8 +1139,12 @@ int do_set_target(const void *ctx, struct connection *conn,
if (get_strings(in, vec, ARRAY_SIZE(vec)) < ARRAY_SIZE(vec))
return EINVAL;
- domid = atoi(vec[0]);
- tdomid = atoi(vec[1]);
+ domid = parse_domid(vec[0]);
+ if (errno)
+ return errno;
+ tdomid = parse_domid(vec[1]);
+ if (errno)
+ return errno;
domain = find_connected_domain(domid);
if (IS_ERR(domain))
@@ -1152,7 +1171,9 @@ static struct domain *onearg_domain(struct connection *conn,
if (!domid_str)
return ERR_PTR(-EINVAL);
- domid = atoi(domid_str);
+ domid = parse_domid(domid_str);
+ if (errno)
+ return ERR_PTR(-errno);
if (domid == store_domid || domid == priv_domid)
return ERR_PTR(-EINVAL);
@@ -1200,11 +1221,15 @@ int do_get_domain_path(const void *ctx, struct connection *conn,
{
char *path;
const char *domid_str = onearg(in);
+ unsigned int domid;
if (!domid_str)
return EINVAL;
- path = talloc_domain_path(ctx, atoi(domid_str));
+ domid = parse_domid(domid_str);
+ if (errno)
+ return errno;
+ path = talloc_domain_path(ctx, domid);
if (!path)
return errno;
@@ -1223,7 +1248,9 @@ int do_is_domain_introduced(const void *ctx, struct connection *conn,
if (!domid_str)
return EINVAL;
- domid = atoi(domid_str);
+ domid = parse_domid(domid_str);
+ if (errno)
+ return errno;
if (domid == DOMID_SELF)
result = 1;
else
@@ -1261,7 +1288,9 @@ int do_get_feature(const void *ctx, struct connection *conn,
return EINVAL;
if (n_args == 1) {
- domid = atoi(vec[0]);
+ domid = parse_domid(vec[0]);
+ if (errno)
+ return errno;
domain = find_or_alloc_existing_domain(domid);
if (!domain)
return ENOENT;
@@ -1289,7 +1318,9 @@ int do_set_feature(const void *ctx, struct connection *conn,
if (get_strings(in, vec, ARRAY_SIZE(vec)) != ARRAY_SIZE(vec))
return EINVAL;
- domid = atoi(vec[0]);
+ domid = parse_domid(vec[0]);
+ if (errno)
+ return errno;
features = atoi(vec[1]);
domain = find_or_alloc_existing_domain(domid);
if (!domain)
--
2.53.0
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH v2 03/12] tools/xenstored: add central quota check functions
2026-03-20 15:01 [PATCH v2 00/12] tools: add support for per-domain xenstore quota Juergen Gross
2026-03-20 15:01 ` [PATCH v2 01/12] tools/libs/store: add get- and set-quota related functions Juergen Gross
2026-03-20 15:01 ` [PATCH v2 02/12] tools/xenstored: add helper to parse domid Juergen Gross
@ 2026-03-20 15:01 ` Juergen Gross
2026-03-20 15:01 ` [PATCH v2 04/12] tools/xenstored: rework hard_quotas and soft_quotas arrays Juergen Gross
` (8 subsequent siblings)
11 siblings, 0 replies; 20+ messages in thread
From: Juergen Gross @ 2026-03-20 15:01 UTC (permalink / raw)
To: xen-devel; +Cc: Juergen Gross, Julien Grall, Anthony PERARD
Add central functions for checking a value (either an absolute one or
the current domain value plus an offset) against a specific quota.
This is in preparation of introducing per-domain quota.
The required changes allow to drop the "update" parameter from
domain_nbentry_fix().
While at it, make the returned error for exceeded quota consistent by
changing one instance from "E2BIG" to "ENOSPC".
Signed-off-by: Juergen Gross <jgross@suse.com>
Reviewed-by: Anthony PERARD <anthony.perard@vates.tech>
---
V2:
- expand commit message (Anthony Perard)
- rename domain_check_quota_*() to domain_quota_*_exceeds()
(Jason Andryuk)
---
tools/xenstored/core.c | 4 +-
tools/xenstored/domain.c | 74 +++++++++++++++++------------------
tools/xenstored/domain.h | 6 +--
tools/xenstored/transaction.c | 2 +-
tools/xenstored/watch.c | 4 +-
5 files changed, 42 insertions(+), 48 deletions(-)
diff --git a/tools/xenstored/core.c b/tools/xenstored/core.c
index e4d2fd4876..e86d947be4 100644
--- a/tools/xenstored/core.c
+++ b/tools/xenstored/core.c
@@ -1539,7 +1539,7 @@ static struct node *create_node(struct connection *conn, const void *ctx,
for (i = node; i; i = i->parent) {
/* i->parent is set for each new node, so check quota. */
if (i->parent &&
- domain_nbentry(conn) >= hard_quotas[ACC_NODES].val) {
+ domain_quota_add_exceeds(conn->domain, ACC_NODES, 1)) {
ret = ENOSPC;
goto err;
}
@@ -2321,7 +2321,7 @@ void setup_structure(bool live_update)
manual_node("/tool/xenstored", NULL);
manual_node("@releaseDomain", NULL);
manual_node("@introduceDomain", NULL);
- domain_nbentry_fix(priv_domid, 5, true);
+ domain_nbentry_fix(priv_domid, 5);
}
}
diff --git a/tools/xenstored/domain.c b/tools/xenstored/domain.c
index a70acddf94..21f6f13f83 100644
--- a/tools/xenstored/domain.c
+++ b/tools/xenstored/domain.c
@@ -389,6 +389,25 @@ void wrl_apply_debit_trans_commit(struct connection *conn)
wrl_apply_debit_actual(conn->domain);
}
+static bool domain_quota_val_exceeds(struct domain *d, enum accitem what,
+ unsigned int val)
+{
+ unsigned int quota = hard_quotas[what].val;
+
+ if (!quota || !domid_is_unprivileged(d->domid))
+ return false;
+
+ return val >= quota;
+}
+
+bool domain_quota_add_exceeds(struct domain *d, enum accitem what, int add)
+{
+ if (add < 0 || !d)
+ return false;
+
+ return domain_quota_val_exceeds(d, what, d->acc[what].val + add);
+}
+
static bool check_indexes(XENSTORE_RING_IDX cons, XENSTORE_RING_IDX prod)
{
return ((prod - cons) <= XENSTORE_RING_SIZE);
@@ -490,10 +509,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].val >= hard_quotas[ACC_OUTST].val)
+ if (domain_quota_add_exceeds(domain, ACC_OUTST, 0))
return false;
- if (domain->acc[ACC_MEM].val >= hard_quotas[ACC_MEM].val &&
- hard_quotas[ACC_MEM].val)
+ if (domain_quota_add_exceeds(domain, ACC_MEM, 0))
return false;
}
@@ -916,16 +934,20 @@ do { \
int acc_fix_domains(struct list_head *head, bool chk_quota, bool update)
{
+ struct domain *d;
struct changed_domain *cd;
- int cnt;
list_for_each_entry(cd, head, list) {
- cnt = domain_nbentry_fix(cd->domid, cd->acc[ACC_NODES], update);
- if (!update) {
- if (chk_quota && cnt >= hard_quotas[ACC_NODES].val)
- return ENOSPC;
- if (cnt < 0)
+ if (update) {
+ domain_nbentry_fix(cd->domid, cd->acc[ACC_NODES]);
+ } else if (chk_quota) {
+ d = find_or_alloc_existing_domain(cd->domid);
+
+ if (!d)
return ENOMEM;
+ if (domain_quota_add_exceeds(d, ACC_NODES,
+ cd->acc[ACC_NODES]))
+ return ENOSPC;
}
}
@@ -1763,7 +1785,7 @@ bool domain_max_chk(const struct connection *conn, enum accitem what,
if (!conn || !conn->domain)
return false;
- if (domain_is_unprivileged(conn) && val > hard_quotas[what].val)
+ if (domain_quota_val_exceeds(conn->domain, what, val))
return true;
domain_acc_valid_max(conn->domain, what, val);
@@ -1783,21 +1805,9 @@ int domain_nbentry_dec(struct connection *conn, unsigned int domid)
? errno : 0;
}
-int domain_nbentry_fix(unsigned int domid, int num, bool update)
-{
- int ret;
-
- ret = domain_acc_add(NULL, domid, ACC_NODES, update ? num : 0, update);
- if (ret < 0 || update)
- return ret;
-
- return domid_is_unprivileged(domid) ? ret + num : 0;
-}
-
-unsigned int domain_nbentry(struct connection *conn)
+int domain_nbentry_fix(unsigned int domid, int num)
{
- return domain_is_unprivileged(conn)
- ? domain_acc_add(conn, conn->id, ACC_NODES, 0, true) : 0;
+ return domain_acc_add(NULL, domid, ACC_NODES, num, true);
}
static bool domain_chk_quota(struct connection *conn, unsigned int mem)
@@ -1812,7 +1822,7 @@ static bool domain_chk_quota(struct connection *conn, unsigned int mem)
domain = conn->domain;
now = time(NULL);
- if (mem >= hard_quotas[ACC_MEM].val && hard_quotas[ACC_MEM].val) {
+ if (domain_quota_val_exceeds(domain, ACC_MEM, mem)) {
if (domain->hard_quota_reported)
return true;
syslog(LOG_ERR, "Domain %u exceeds hard memory quota, Xenstore interface to domain stalled\n",
@@ -1888,13 +1898,6 @@ void domain_watch_dec(struct connection *conn)
domain_acc_add(conn, conn->id, ACC_WATCH, -1, true);
}
-int domain_watch(struct connection *conn)
-{
- return (domain_is_unprivileged(conn))
- ? domain_acc_add(conn, conn->id, ACC_WATCH, 0, true)
- : 0;
-}
-
void domain_outstanding_inc(struct connection *conn)
{
domain_acc_add(conn, conn->id, ACC_OUTST, 1, true);
@@ -1915,13 +1918,6 @@ void domain_transaction_dec(struct connection *conn)
domain_acc_add(conn, conn->id, ACC_TRANS, -1, true);
}
-unsigned int domain_transaction_get(struct connection *conn)
-{
- return (domain_is_unprivileged(conn))
- ? domain_acc_add(conn, conn->id, ACC_TRANS, 0, true)
- : 0;
-}
-
const char *dump_state_connections(FILE *fp)
{
const char *ret = NULL;
diff --git a/tools/xenstored/domain.h b/tools/xenstored/domain.h
index 28186ccac0..29b91fc783 100644
--- a/tools/xenstored/domain.h
+++ b/tools/xenstored/domain.h
@@ -113,8 +113,7 @@ int domain_alloc_permrefs(struct node_perms *perms);
/* Quota manipulation */
int domain_nbentry_inc(struct connection *conn, unsigned int domid);
int domain_nbentry_dec(struct connection *conn, unsigned int domid);
-int domain_nbentry_fix(unsigned int domid, int num, bool update);
-unsigned int domain_nbentry(struct connection *conn);
+int domain_nbentry_fix(unsigned int domid, int num);
int domain_memory_add(struct connection *conn, unsigned int domid, int mem,
bool no_quota_check);
@@ -141,12 +140,10 @@ static inline void domain_memory_add_nochk(struct connection *conn,
}
void domain_watch_inc(struct connection *conn);
void domain_watch_dec(struct connection *conn);
-int domain_watch(struct connection *conn);
void domain_outstanding_inc(struct connection *conn);
void domain_outstanding_dec(struct connection *conn, unsigned int domid);
void domain_transaction_inc(struct connection *conn);
void domain_transaction_dec(struct connection *conn);
-unsigned int domain_transaction_get(struct connection *conn);
int domain_get_quota(const void *ctx, struct connection *conn,
unsigned int domid);
@@ -161,6 +158,7 @@ int domain_max_global_acc(const void *ctx, struct connection *conn);
void domain_reset_global_acc(void);
bool domain_max_chk(const struct connection *conn, enum accitem what,
unsigned int val);
+bool domain_quota_add_exceeds(struct domain *d, enum accitem what, int add);
extern long wrl_ntransactions;
diff --git a/tools/xenstored/transaction.c b/tools/xenstored/transaction.c
index 167cd597fd..47cd6ecd3c 100644
--- a/tools/xenstored/transaction.c
+++ b/tools/xenstored/transaction.c
@@ -470,7 +470,7 @@ int do_transaction_start(const void *ctx, struct connection *conn,
if (conn->transaction)
return EBUSY;
- if (domain_transaction_get(conn) > hard_quotas[ACC_TRANS].val)
+ if (domain_quota_add_exceeds(conn->domain, ACC_TRANS, 1))
return ENOSPC;
/* Attach transaction to ctx for autofree until it's complete */
diff --git a/tools/xenstored/watch.c b/tools/xenstored/watch.c
index b66a9f1a39..becb9c339d 100644
--- a/tools/xenstored/watch.c
+++ b/tools/xenstored/watch.c
@@ -220,8 +220,8 @@ int do_watch(const void *ctx, struct connection *conn, struct buffered_data *in)
return EEXIST;
}
- if (domain_watch(conn) > hard_quotas[ACC_WATCH].val)
- return E2BIG;
+ if (domain_quota_add_exceeds(conn->domain, ACC_WATCH, 1))
+ return ENOSPC;
watch = add_watch(conn, vec[0], vec[1], relative, false);
if (!watch)
--
2.53.0
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH v2 04/12] tools/xenstored: rework hard_quotas and soft_quotas arrays
2026-03-20 15:01 [PATCH v2 00/12] tools: add support for per-domain xenstore quota Juergen Gross
` (2 preceding siblings ...)
2026-03-20 15:01 ` [PATCH v2 03/12] tools/xenstored: add central quota check functions Juergen Gross
@ 2026-03-20 15:01 ` Juergen Gross
2026-03-20 15:01 ` [PATCH v2 05/12] tools/xenstored: add GLOBAL_QUOTA_DATA record for live update Juergen Gross
` (7 subsequent siblings)
11 siblings, 0 replies; 20+ messages in thread
From: Juergen Gross @ 2026-03-20 15:01 UTC (permalink / raw)
To: xen-devel; +Cc: Juergen Gross, Julien Grall, Anthony PERARD
Instead of having one array for hard quotas and one for soft quotas,
split them differently: have one array with the quota names and
descriptions, and one with the quota values (soft and hard) and the
maximum value so far.
This is in preparation of supporting per-domain quotas, as the layout
of the second array elements will be reused in the domain data.
While at it add an accessor for getting a soft quota value, as this
will be needed for per-domain quotas, too.
Signed-off-by: Juergen Gross <jgross@suse.com>
Reviewed-by: Anthony PERARD <anthony.perard@vates.tech>
---
tools/xenstored/control.c | 24 ++++++++-------
tools/xenstored/core.c | 33 +++++++++++---------
tools/xenstored/domain.c | 65 +++++++++++++++++++++------------------
tools/xenstored/domain.h | 15 +++++----
4 files changed, 76 insertions(+), 61 deletions(-)
diff --git a/tools/xenstored/control.c b/tools/xenstored/control.c
index 2611a6fade..ca59d30d05 100644
--- a/tools/xenstored/control.c
+++ b/tools/xenstored/control.c
@@ -101,7 +101,7 @@ static int do_control_log(const void *ctx, struct connection *conn,
}
static int quota_show_current(const void *ctx, struct connection *conn,
- const struct quota *quotas)
+ unsigned int idx)
{
char *resp;
unsigned int i;
@@ -111,11 +111,12 @@ static int quota_show_current(const void *ctx, struct connection *conn,
return ENOMEM;
for (i = 0; i < ACC_N; i++) {
- if (!quotas[i].name)
+ if (!quota_adm[i].name || quotas[i].val[idx] == Q_VAL_DISABLED)
continue;
resp = talloc_asprintf_append(resp, "%-17s: %8d %s\n",
- quotas[i].name, quotas[i].val,
- quotas[i].descr);
+ quota_adm[i].name,
+ quotas[i].val[idx],
+ quota_adm[i].descr);
if (!resp)
return ENOMEM;
}
@@ -126,7 +127,7 @@ static int quota_show_current(const void *ctx, struct connection *conn,
}
static int quota_set(const void *ctx, struct connection *conn,
- const char **vec, int num, struct quota *quotas)
+ const char **vec, int num, unsigned int idx)
{
unsigned int i;
int val;
@@ -139,8 +140,9 @@ static int quota_set(const void *ctx, struct connection *conn,
return EINVAL;
for (i = 0; i < ACC_N; i++) {
- if (quotas[i].name && !strcmp(vec[0], quotas[i].name)) {
- quotas[i].val = val;
+ if (quota_adm[i].name && !strcmp(vec[0], quota_adm[i].name) &&
+ quotas[i].val[idx] != Q_VAL_DISABLED) {
+ quotas[i].val[idx] = val;
send_ack(conn, XS_CONTROL);
return 0;
}
@@ -178,10 +180,10 @@ static int do_control_quota(const void *ctx, struct connection *conn,
const char **vec, int num)
{
if (num == 0)
- return quota_show_current(ctx, conn, hard_quotas);
+ return quota_show_current(ctx, conn, Q_IDX_HARD);
if (!strcmp(vec[0], "set"))
- return quota_set(ctx, conn, vec + 1, num - 1, hard_quotas);
+ return quota_set(ctx, conn, vec + 1, num - 1, Q_IDX_HARD);
if (!strcmp(vec[0], "max"))
return quota_max(ctx, conn, vec + 1, num - 1);
@@ -193,10 +195,10 @@ static int do_control_quota_s(const void *ctx, struct connection *conn,
const char **vec, int num)
{
if (num == 0)
- return quota_show_current(ctx, conn, soft_quotas);
+ return quota_show_current(ctx, conn, Q_IDX_SOFT);
if (!strcmp(vec[0], "set"))
- return quota_set(ctx, conn, vec + 1, num - 1, soft_quotas);
+ return quota_set(ctx, conn, vec + 1, num - 1, Q_IDX_SOFT);
return EINVAL;
}
diff --git a/tools/xenstored/core.c b/tools/xenstored/core.c
index e86d947be4..dc63c97658 100644
--- a/tools/xenstored/core.c
+++ b/tools/xenstored/core.c
@@ -2614,10 +2614,9 @@ static void set_timeout(const char *arg)
barf("unknown timeout \"%s\"\n", arg);
}
-static void set_quota(const char *arg, bool soft)
+static void set_quota(const char *arg, unsigned int idx)
{
const char *eq = strchr(arg, '=');
- struct quota *q = soft ? soft_quotas : hard_quotas;
unsigned int val;
unsigned int i;
@@ -2626,8 +2625,9 @@ static void set_quota(const char *arg, bool soft)
val = get_optval_uint(eq + 1);
for (i = 0; i < ACC_N; i++) {
- if (what_matches(arg, q[i].name)) {
- q[i].val = val;
+ if (what_matches(arg, quota_adm[i].name) &&
+ quotas[i].val[idx] != Q_VAL_DISABLED) {
+ quotas[i].val[idx] = val;
return;
}
}
@@ -2635,6 +2635,11 @@ static void set_quota(const char *arg, bool soft)
barf("unknown quota \"%s\"\n", arg);
}
+static void set_one_quota(const char *arg, unsigned int idx, enum accitem what)
+{
+ quotas[what].val[idx] = get_optval_uint(arg);
+}
+
/* Sorted by bit values of TRACE_* flags. Flag is (1u << index). */
const char *const trace_switches[] = {
"obj", "io", "wrl", "acc", "tdb",
@@ -2688,7 +2693,7 @@ int main(int argc, char *argv[])
options, NULL)) != -1) {
switch (opt) {
case 'E':
- hard_quotas[ACC_NODES].val = get_optval_uint(optarg);
+ set_one_quota(optarg, Q_IDX_HARD, ACC_NODES);
break;
case 'F':
pidfile = optarg;
@@ -2700,10 +2705,10 @@ int main(int argc, char *argv[])
dofork = false;
break;
case 'S':
- hard_quotas[ACC_NODESZ].val = get_optval_uint(optarg);
+ set_one_quota(optarg, Q_IDX_HARD, ACC_NODESZ);
break;
case 't':
- hard_quotas[ACC_TRANS].val = get_optval_uint(optarg);
+ set_one_quota(optarg, Q_IDX_HARD, ACC_TRANS);
break;
case 'T':
tracefile = optarg;
@@ -2716,22 +2721,22 @@ int main(int argc, char *argv[])
keep_orphans = true;
break;
case 'W':
- hard_quotas[ACC_WATCH].val = get_optval_uint(optarg);
+ set_one_quota(optarg, Q_IDX_HARD, ACC_WATCH);
break;
case 'A':
- hard_quotas[ACC_NPERM].val = get_optval_uint(optarg);
+ set_one_quota(optarg, Q_IDX_HARD, ACC_NPERM);
break;
case 'M':
- hard_quotas[ACC_PATHLEN].val = get_optval_uint(optarg);
- hard_quotas[ACC_PATHLEN].val =
+ set_one_quota(optarg, Q_IDX_HARD, ACC_PATHLEN);
+ quotas[ACC_PATHLEN].val[Q_IDX_HARD] =
min((unsigned int)XENSTORE_REL_PATH_MAX,
- hard_quotas[ACC_PATHLEN].val);
+ quotas[ACC_PATHLEN].val[Q_IDX_HARD]);
break;
case 'Q':
- set_quota(optarg, false);
+ set_quota(optarg, Q_IDX_HARD);
break;
case 'q':
- set_quota(optarg, true);
+ set_quota(optarg, Q_IDX_SOFT);
break;
case 'w':
set_timeout(optarg);
diff --git a/tools/xenstored/domain.c b/tools/xenstored/domain.c
index 21f6f13f83..4e696a81a8 100644
--- a/tools/xenstored/domain.c
+++ b/tools/xenstored/domain.c
@@ -51,60 +51,60 @@ static evtchn_port_t virq_port;
xenevtchn_handle *xce_handle = NULL;
-struct quota hard_quotas[ACC_N] = {
+struct quotaadm quota_adm[ACC_N] = {
[ACC_NODES] = {
.name = "nodes",
.descr = "Nodes per domain",
- .val = 1000,
},
[ACC_WATCH] = {
.name = "watches",
.descr = "Watches per domain",
- .val = 128,
},
[ACC_OUTST] = {
.name = "outstanding",
.descr = "Outstanding requests per domain",
- .val = 20,
},
[ACC_MEM] = {
.name = "memory",
- .descr = "Total Xenstore memory per domain (error level)",
- .val = 2 * 1024 * 1024 + 512 * 1024, /* 2.5 MB */
+ .descr = "Total Xenstore memory per domain",
},
[ACC_TRANS] = {
.name = "transactions",
.descr = "Active transactions per domain",
- .val = 10,
},
[ACC_TRANSNODES] = {
.name = "transaction-nodes",
.descr = "Max. number of accessed nodes per transaction",
- .val = 1024,
},
[ACC_NPERM] = {
.name = "node-permissions",
.descr = "Max. number of permissions per node",
- .val = 5,
},
[ACC_PATHLEN] = {
.name = "path-max",
.descr = "Max. length of a node path",
- .val = XENSTORE_REL_PATH_MAX,
},
[ACC_NODESZ] = {
.name = "node-size",
.descr = "Max. size of a node",
- .val = 2048,
},
};
-struct quota soft_quotas[ACC_N] = {
- [ACC_MEM] = {
- .name = "memory",
- .descr = "Total Xenstore memory per domain (warning level)",
- .val = 2 * 1024 * 1024, /* 2.0 MB */
+struct quota quotas[ACC_N] = {
+ [ACC_NODES] = { .val = { 1000, Q_VAL_DISABLED }, },
+ [ACC_WATCH] = { .val = { 128, Q_VAL_DISABLED }, },
+ [ACC_OUTST] = { .val = { 20, Q_VAL_DISABLED }, },
+ [ACC_MEM] = {
+ .val = { 2 * 1024 * 1024 + 512 * 1024, /* 2.5 MB */
+ 2 * 1024 * 1024 /* 2.0 MB */ },
},
+ [ACC_TRANS] = { .val = { 10, Q_VAL_DISABLED }, },
+ [ACC_TRANSNODES] = { .val = { 1024, Q_VAL_DISABLED }, },
+ [ACC_NPERM] = { .val = { 5, Q_VAL_DISABLED }, },
+ [ACC_PATHLEN] = {
+ .val = { XENSTORE_REL_PATH_MAX, Q_VAL_DISABLED },
+ },
+ [ACC_NODESZ] = { .val = { 2048, Q_VAL_DISABLED }, },
};
typedef int32_t wrl_creditt;
@@ -389,10 +389,15 @@ void wrl_apply_debit_trans_commit(struct connection *conn)
wrl_apply_debit_actual(conn->domain);
}
+static unsigned int domain_get_soft_quota(struct domain *d, enum accitem what)
+{
+ return quotas[what].val[Q_IDX_SOFT];
+}
+
static bool domain_quota_val_exceeds(struct domain *d, enum accitem what,
unsigned int val)
{
- unsigned int quota = hard_quotas[what].val;
+ unsigned int quota = quotas[what].val[Q_IDX_HARD];
if (!quota || !domid_is_unprivileged(d->domid))
return false;
@@ -777,10 +782,10 @@ int domain_get_quota(const void *ctx, struct connection *conn,
return ENOMEM;
for (i = 0; i < ACC_N; i++) {
- if (!hard_quotas[i].name)
+ if (!quota_adm[i].name)
continue;
resp = talloc_asprintf_append(resp, "%-17s: %8u (max %8u)\n",
- hard_quotas[i].name,
+ quota_adm[i].name,
d->acc[i].val, d->acc[i].max);
if (!resp)
return ENOMEM;
@@ -801,11 +806,10 @@ int domain_max_global_acc(const void *ctx, struct connection *conn)
return ENOMEM;
for (i = 0; i < ACC_N; i++) {
- if (!hard_quotas[i].name)
+ if (!quota_adm[i].name)
continue;
resp = talloc_asprintf_append(resp, "%-17s: %8u\n",
- hard_quotas[i].name,
- hard_quotas[i].max);
+ quota_adm[i].name, quotas[i].max);
if (!resp)
return ENOMEM;
}
@@ -1631,12 +1635,12 @@ static void domain_acc_valid_max(struct domain *d, enum accitem what,
unsigned int val)
{
assert(what < ARRAY_SIZE(d->acc));
- assert(what < ARRAY_SIZE(hard_quotas));
+ assert(what < ARRAY_SIZE(quotas));
if (val > d->acc[what].max)
d->acc[what].max = val;
- if (val > hard_quotas[what].max && domid_is_unprivileged(d->domid))
- hard_quotas[what].max = val;
+ if (val > quotas[what].max && domid_is_unprivileged(d->domid))
+ quotas[what].max = val;
}
static int domain_acc_add_valid(struct domain *d, enum accitem what, int add)
@@ -1773,7 +1777,7 @@ void domain_reset_global_acc(void)
unsigned int i;
for (i = 0; i < ACC_N; i++)
- hard_quotas[i].max = 0;
+ quotas[i].max = 0;
/* Set current max values seen. */
hashtable_iterate(domhash, domain_reset_global_acc_sub, NULL);
@@ -1833,21 +1837,22 @@ static bool domain_chk_quota(struct connection *conn, unsigned int mem)
}
if (now - domain->mem_last_msg >= MEM_WARN_MINTIME_SEC) {
+ unsigned int soft_mem = domain_get_soft_quota(domain, ACC_MEM);
+
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 >= soft_quotas[ACC_MEM].val &&
- soft_quotas[ACC_MEM].val && !domain->soft_quota_reported) {
+ if (mem >= soft_mem && soft_mem &&
+ !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 < soft_quotas[ACC_MEM].val &&
- domain->soft_quota_reported) {
+ if (mem < soft_mem && 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",
diff --git a/tools/xenstored/domain.h b/tools/xenstored/domain.h
index 29b91fc783..3bedadb477 100644
--- a/tools/xenstored/domain.h
+++ b/tools/xenstored/domain.h
@@ -40,15 +40,18 @@ enum accitem {
ACC_N, /* Number of elements per domain. */
};
-struct quota {
+extern struct quotaadm {
const char *name;
const char *descr;
- unsigned int val;
- unsigned int max;
-};
+} quota_adm[ACC_N];
-extern struct quota hard_quotas[ACC_N];
-extern struct quota soft_quotas[ACC_N];
+extern struct quota {
+ unsigned int val[2];
+#define Q_IDX_HARD 0
+#define Q_IDX_SOFT 1
+#define Q_VAL_DISABLED UINT_MAX
+ unsigned int max;
+} quotas[ACC_N];
void handle_event(void);
--
2.53.0
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH v2 05/12] tools/xenstored: add GLOBAL_QUOTA_DATA record for live update
2026-03-20 15:01 [PATCH v2 00/12] tools: add support for per-domain xenstore quota Juergen Gross
` (3 preceding siblings ...)
2026-03-20 15:01 ` [PATCH v2 04/12] tools/xenstored: rework hard_quotas and soft_quotas arrays Juergen Gross
@ 2026-03-20 15:01 ` Juergen Gross
2026-03-23 12:50 ` Anthony PERARD
2026-03-20 15:01 ` [PATCH v2 06/12] tools/xenstored: split acc[] array in struct domain Juergen Gross
` (6 subsequent siblings)
11 siblings, 1 reply; 20+ messages in thread
From: Juergen Gross @ 2026-03-20 15:01 UTC (permalink / raw)
To: xen-devel; +Cc: Juergen Gross, Julien Grall, Anthony PERARD
Communicate the global quota settings via the GLOBAL_QUOTA_DATA
record to the new Xenstore instance.
This avoids to lose global quota settings done via xenstore-control.
In theory it would be possible to drop any quota related command line
parameters in the live update case, but they don't do any harm, as
the record data is applied on top of the command line data.
For soft-quota just prepend "soft-" to the quota name.
Use sub-functions for building and analyzing the quota part of the
migration stream, as they will be reused for per-domain quotas.
Signed-off-by: Juergen Gross <jgross@suse.com>
---
V2:
- add macros for soft-quota name prefix and its length (Anthony Perard)
- don't allow disabled quota in parse_quota_name() (Anthony Perard)
- rename "len" to "rec_len" in dump_state_glb_quota() (Anthony Perard)
- rename build_quota_data() parameter "name" to "names_buf" (Anthony Perard)
- let get_quota_size() start with len 0 (Anthony Perard)
---
tools/xenstored/domain.c | 132 +++++++++++++++++++++++++++++++++++++++
tools/xenstored/domain.h | 2 +
tools/xenstored/lu.c | 6 ++
3 files changed, 140 insertions(+)
diff --git a/tools/xenstored/domain.c b/tools/xenstored/domain.c
index 4e696a81a8..cfc8fd0cb4 100644
--- a/tools/xenstored/domain.c
+++ b/tools/xenstored/domain.c
@@ -107,6 +107,9 @@ struct quota quotas[ACC_N] = {
[ACC_NODESZ] = { .val = { 2048, Q_VAL_DISABLED }, },
};
+#define SOFT_PREFIX "soft-"
+#define SOFT_PREFIX_LEN (sizeof(SOFT_PREFIX) - 1)
+
typedef int32_t wrl_creditt;
struct domain
@@ -1363,6 +1366,29 @@ int do_set_feature(const void *ctx, struct connection *conn,
return 0;
}
+static bool parse_quota_name(const char *name, unsigned int *qidx,
+ unsigned int *idx)
+{
+ unsigned int q;
+
+ if (strncmp(name, SOFT_PREFIX, SOFT_PREFIX_LEN)) {
+ *idx = Q_IDX_HARD;
+ } else {
+ *idx = Q_IDX_SOFT;
+ name += SOFT_PREFIX_LEN;
+ }
+ for (q = 0; q < ACC_N; q++) {
+ if (quota_adm[q].name && !strcmp(quota_adm[q].name, name)) {
+ if (quotas[q].val[*idx] == Q_VAL_DISABLED)
+ return true;
+ *qidx = q;
+ return false;
+ }
+ }
+
+ return true;
+}
+
static int close_xgt_handle(void *_handle)
{
xengnttab_close(*(xengnttab_handle **)_handle);
@@ -2032,6 +2058,64 @@ void read_state_connection(const void *ctx, const void *state)
}
}
+/* Returns number of quota and adds length of quota names to *len. */
+static unsigned int get_quota_size(struct quota *quota, unsigned int *len)
+{
+ unsigned int q;
+ unsigned int n = 0;
+
+ *len = 0;
+ for (q = 0; q < ACC_N; q++) {
+ if (!quota_adm[q].name)
+ continue;
+ if (quota[q].val[Q_IDX_HARD] != Q_VAL_DISABLED) {
+ n++;
+ *len += strlen(quota_adm[q].name) + 1;
+ }
+ if (quota[q].val[Q_IDX_SOFT] != Q_VAL_DISABLED) {
+ n++;
+ *len += strlen(quota_adm[q].name) + SOFT_PREFIX_LEN + 1;
+ }
+ }
+
+ return n;
+}
+
+static void build_quota_data(struct quota *quota, uint32_t *val,
+ char *names_buf)
+{
+ unsigned int q;
+ unsigned int n = 0;
+
+ for (q = 0; q < ACC_N; q++) {
+ if (!quota_adm[q].name)
+ continue;
+ if (quota[q].val[Q_IDX_HARD] != Q_VAL_DISABLED) {
+ val[n++] = quota[q].val[Q_IDX_HARD];
+ strcpy(names_buf, quota_adm[q].name);
+ names_buf += strlen(names_buf) + 1;
+ }
+ if (quota[q].val[Q_IDX_SOFT] != Q_VAL_DISABLED) {
+ val[n++] = quota[q].val[Q_IDX_SOFT];
+ strcpy(names_buf, SOFT_PREFIX);
+ strcpy(names_buf + SOFT_PREFIX_LEN, quota_adm[q].name);
+ names_buf += strlen(names_buf) + 1;
+ }
+ }
+}
+
+static void parse_quota_data(const uint32_t *val, const char *name,
+ unsigned int n, struct quota *quota)
+{
+ unsigned int i, q, idx;
+
+ for (i = 0; i < n; i++) {
+ if (!parse_quota_name(name, &q, &idx))
+ quota[q].val[idx] = val[i];
+ name += strlen(name) + 1;
+ }
+}
+
static int dump_state_domain(const void *k, void *v, void *arg)
{
struct domain *domain = v;
@@ -2080,6 +2164,54 @@ void read_state_domain(const void *ctx, const void *state, unsigned int version)
domain->features = sd->features;
}
+const char *dump_state_glb_quota(FILE *fp)
+{
+ struct xs_state_record_header *head;
+ struct xs_state_glb_quota *glb;
+ void *record;
+ unsigned int n_quota;
+ unsigned int rec_len;
+ size_t ret;
+
+ n_quota = get_quota_size(quotas, &rec_len);
+ rec_len += n_quota * sizeof(glb->quota_val[0]);
+ rec_len += sizeof(*glb);
+ rec_len = ROUNDUP(rec_len, 3);
+
+ record = talloc_size(NULL, rec_len + sizeof(*head));
+ if (!record)
+ return "Dump global quota allocation error";
+
+ head = record;
+ head->type = XS_STATE_TYPE_GLB_QUOTA;
+ head->length = rec_len;
+
+ glb = (struct xs_state_glb_quota *)(head + 1);
+ glb->n_dom_quota = n_quota;
+ glb->n_glob_quota = 0;
+
+ build_quota_data(quotas, glb->quota_val,
+ (char *)(glb->quota_val + n_quota));
+
+ ret = fwrite(record, rec_len + sizeof(*head), 1, fp);
+
+ talloc_free(record);
+
+ if (ret != 1 || dump_state_align(fp))
+ return "Dump global quota error";
+
+ return NULL;
+}
+
+void read_state_glb_quota(const void *ctx, const void *state)
+{
+ const struct xs_state_glb_quota *glb = state;
+ unsigned int n_quota = glb->n_dom_quota + glb->n_glob_quota;
+ const char *name = (const char *)(glb->quota_val + n_quota);
+
+ parse_quota_data(glb->quota_val, name, n_quota, quotas);
+}
+
struct domain_acc {
unsigned int domid;
int nodes;
diff --git a/tools/xenstored/domain.h b/tools/xenstored/domain.h
index 3bedadb477..8f23a82854 100644
--- a/tools/xenstored/domain.h
+++ b/tools/xenstored/domain.h
@@ -172,10 +172,12 @@ void wrl_apply_debit_trans_commit(struct connection *conn);
const char *dump_state_connections(FILE *fp);
const char *dump_state_domains(FILE *fp);
+const char *dump_state_glb_quota(FILE *fp);
void read_state_connection(const void *ctx, const void *state);
void read_state_domain(const void *ctx, const void *state,
unsigned int version);
+void read_state_glb_quota(const void *ctx, const void *state);
struct hashtable *domain_check_acc_init(void);
void domain_check_acc_add(const struct node *node, struct hashtable *domains);
diff --git a/tools/xenstored/lu.c b/tools/xenstored/lu.c
index fa8395eb1e..eaffdbc69e 100644
--- a/tools/xenstored/lu.c
+++ b/tools/xenstored/lu.c
@@ -192,6 +192,9 @@ void lu_read_state(void)
case XS_STATE_TYPE_DOMAIN:
read_state_domain(ctx, state.buf, version);
break;
+ case XS_STATE_TYPE_GLB_QUOTA:
+ read_state_glb_quota(ctx, state.buf);
+ break;
default:
xprintf("live-update: unknown state record %08x\n",
head.type);
@@ -319,6 +322,9 @@ static const char *lu_dump_state(const void *ctx, struct connection *conn)
}
ret = dump_state_global(fp);
+ if (ret)
+ goto out;
+ ret = dump_state_glb_quota(fp);
if (ret)
goto out;
ret = dump_state_connections(fp);
--
2.53.0
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH v2 06/12] tools/xenstored: split acc[] array in struct domain
2026-03-20 15:01 [PATCH v2 00/12] tools: add support for per-domain xenstore quota Juergen Gross
` (4 preceding siblings ...)
2026-03-20 15:01 ` [PATCH v2 05/12] tools/xenstored: add GLOBAL_QUOTA_DATA record for live update Juergen Gross
@ 2026-03-20 15:01 ` Juergen Gross
2026-03-20 15:01 ` [PATCH v2 07/12] tools/xenstored: use per-domain quota settings Juergen Gross
` (5 subsequent siblings)
11 siblings, 0 replies; 20+ messages in thread
From: Juergen Gross @ 2026-03-20 15:01 UTC (permalink / raw)
To: xen-devel; +Cc: Juergen Gross, Julien Grall, Anthony PERARD
Prepare using per-domain quota by splitting the acc[] array in struct
domain into an array with the current accounting data, and an array
of type struct quota for the per-domain quota and the seen max value
of the domain.
Signed-off-by: Juergen Gross <jgross@suse.com>
Reviewed-by: Anthony PERARD <anthony.perard@vates.tech>
---
tools/xenstored/domain.c | 34 ++++++++++++++++------------------
1 file changed, 16 insertions(+), 18 deletions(-)
diff --git a/tools/xenstored/domain.c b/tools/xenstored/domain.c
index cfc8fd0cb4..b82f2d5167 100644
--- a/tools/xenstored/domain.c
+++ b/tools/xenstored/domain.c
@@ -143,10 +143,8 @@ struct domain
bool introduced;
/* Accounting data for this domain. */
- struct acc {
- unsigned int val;
- unsigned int max;
- } acc[ACC_N];
+ unsigned int acc_val[ACC_N];
+ struct quota acc[ACC_N];
/* Memory quota data for this domain. */
bool soft_quota_reported;
@@ -413,7 +411,7 @@ bool domain_quota_add_exceeds(struct domain *d, enum accitem what, int add)
if (add < 0 || !d)
return false;
- return domain_quota_val_exceeds(d, what, d->acc[what].val + add);
+ return domain_quota_val_exceeds(d, what, d->acc_val[what] + add);
}
static bool check_indexes(XENSTORE_RING_IDX cons, XENSTORE_RING_IDX prod)
@@ -585,7 +583,7 @@ static int domain_tree_remove_sub(const void *ctx, struct connection *conn,
ret = WALK_TREE_SKIP_CHILDREN;
}
- return domain->acc[ACC_NODES].val ? ret : WALK_TREE_SUCCESS_STOP;
+ return domain->acc_val[ACC_NODES] ? ret : WALK_TREE_SUCCESS_STOP;
}
static void domain_tree_remove(struct domain *domain)
@@ -593,7 +591,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].val) {
+ if (domain->acc_val[ACC_NODES]) {
ret = walk_node_tree(domain, NULL, "/", &walkfuncs, domain);
if (ret == WALK_TREE_ERROR_STOP)
syslog(LOG_ERR,
@@ -789,7 +787,7 @@ int domain_get_quota(const void *ctx, struct connection *conn,
continue;
resp = talloc_asprintf_append(resp, "%-17s: %8u (max %8u)\n",
quota_adm[i].name,
- d->acc[i].val, d->acc[i].max);
+ d->acc_val[i], d->acc[i].max);
if (!resp)
return ENOMEM;
}
@@ -1673,10 +1671,10 @@ static int domain_acc_add_valid(struct domain *d, enum accitem what, int add)
{
unsigned int val;
- assert(what < ARRAY_SIZE(d->acc));
+ assert(what < ARRAY_SIZE(d->acc_val));
- if ((add < 0 && -add > d->acc[what].val) ||
- (add > 0 && (INT_MAX - d->acc[what].val) < add)) {
+ if ((add < 0 && -add > d->acc_val[what]) ||
+ (add > 0 && (INT_MAX - d->acc_val[what]) < add)) {
/*
* In a transaction when a node is being added/removed AND the
* same node has been added/removed outside the transaction in
@@ -1687,7 +1685,7 @@ static int domain_acc_add_valid(struct domain *d, enum accitem what, int add)
return (add < 0) ? 0 : INT_MAX;
}
- val = d->acc[what].val + add;
+ val = d->acc_val[what] + add;
domain_acc_valid_max(d, what, val);
return val;
@@ -1746,10 +1744,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].val, add);
- d->acc[what].val = domain_acc_add_valid(d, what, add);
+ d->acc_val[what], add);
+ d->acc_val[what] = domain_acc_add_valid(d, what, add);
- return d->acc[what].val;
+ return d->acc_val[what];
}
void acc_drop(struct connection *conn)
@@ -1793,7 +1791,7 @@ static int domain_reset_global_acc_sub(const void *k, void *v, void *arg)
unsigned int i;
for (i = 0; i < ACC_N; i++)
- d->acc[i].max = d->acc[i].val;
+ d->acc[i].max = d->acc_val[i];
return 0;
}
@@ -2233,7 +2231,7 @@ static int domain_check_acc_init_sub(const 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].val;
+ dom->nodes = -d->acc_val[ACC_NODES];
if (hashtable_add(domains, &dom->domid, dom)) {
talloc_free(dom);
@@ -2288,7 +2286,7 @@ static int domain_check_acc_cb(const void *k, void *v, void *arg)
if (!d)
return 0;
- d->acc[ACC_NODES].val += dom->nodes;
+ d->acc_val[ACC_NODES] += dom->nodes;
return 0;
}
--
2.53.0
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH v2 07/12] tools/xenstored: use per-domain quota settings
2026-03-20 15:01 [PATCH v2 00/12] tools: add support for per-domain xenstore quota Juergen Gross
` (5 preceding siblings ...)
2026-03-20 15:01 ` [PATCH v2 06/12] tools/xenstored: split acc[] array in struct domain Juergen Gross
@ 2026-03-20 15:01 ` Juergen Gross
2026-03-20 15:01 ` [PATCH v2 08/12] tools/xenstored: implement the GET/SET_QUOTA commands Juergen Gross
` (4 subsequent siblings)
11 siblings, 0 replies; 20+ messages in thread
From: Juergen Gross @ 2026-03-20 15:01 UTC (permalink / raw)
To: xen-devel; +Cc: Juergen Gross, Julien Grall, Anthony PERARD
Initialize the per-domain quota values with the global ones at domain
introduction. Use the per-domain quota settings when checking for
current values to exceed the quota.
Add per-domain quota support to live update.
Signed-off-by: Juergen Gross <jgross@suse.com>
Reviewed-by: Anthony PERARD <anthony.perard@vates.tech>
---
V2:
- fix commit message (Anthony Perard)
- rename "len" to "rec_len" in dump_state_domain() (Anthony Perard)
---
tools/xenstored/domain.c | 60 ++++++++++++++++++++++++++++++----------
1 file changed, 45 insertions(+), 15 deletions(-)
diff --git a/tools/xenstored/domain.c b/tools/xenstored/domain.c
index b82f2d5167..7a8d285e64 100644
--- a/tools/xenstored/domain.c
+++ b/tools/xenstored/domain.c
@@ -392,6 +392,9 @@ void wrl_apply_debit_trans_commit(struct connection *conn)
static unsigned int domain_get_soft_quota(struct domain *d, enum accitem what)
{
+ if (d && d->acc[what].val[Q_IDX_SOFT] != Q_VAL_DISABLED)
+ return d->acc[what].val[Q_IDX_SOFT];
+
return quotas[what].val[Q_IDX_SOFT];
}
@@ -400,6 +403,9 @@ static bool domain_quota_val_exceeds(struct domain *d, enum accitem what,
{
unsigned int quota = quotas[what].val[Q_IDX_HARD];
+ if (d->acc[what].val[Q_IDX_HARD] != Q_VAL_DISABLED)
+ quota = d->acc[what].val[Q_IDX_HARD];
+
if (!quota || !domid_is_unprivileged(d->domid))
return false;
@@ -824,6 +830,7 @@ static struct domain *alloc_domain(const void *context, unsigned int domid,
uint64_t unique_id)
{
struct domain *domain;
+ unsigned int q;
domain = talloc_zero(context, struct domain);
if (!domain) {
@@ -837,6 +844,11 @@ static struct domain *alloc_domain(const void *context, unsigned int domid,
domain->introduced = false;
domain->features = XENSTORE_FEATURES;
+ for (q = 0; q < ACC_N; q++) {
+ domain->acc[q].val[Q_IDX_HARD] = quotas[q].val[Q_IDX_HARD];
+ domain->acc[q].val[Q_IDX_SOFT] = quotas[q].val[Q_IDX_SOFT];
+ }
+
if (hashtable_add(domhash, &domain->domid, domain)) {
talloc_free(domain);
errno = ENOMEM;
@@ -2118,25 +2130,39 @@ static int dump_state_domain(const void *k, void *v, void *arg)
{
struct domain *domain = v;
FILE *fp = arg;
- struct xs_state_domain sd;
- struct xs_state_record_header head;
-
- head.type = XS_STATE_TYPE_DOMAIN;
- head.length = sizeof(sd);
- memset(&sd, 0, sizeof(sd));
- sd.domain_id = domain->domid;
+ struct xs_state_domain *sd;
+ struct xs_state_record_header *head;
+ void *record;
+ unsigned int n_quota;
+ unsigned int rec_len;
+ size_t ret;
- if (lu_status->version > 1)
- sd.features = domain->features;
+ n_quota = get_quota_size(domain->acc, &rec_len);
+ rec_len += n_quota * sizeof(sd->quota_val[0]);
+ rec_len += sizeof(*sd);
+ rec_len = ROUNDUP(rec_len, 3);
- if (fwrite(&head, sizeof(head), 1, fp) != 1)
- return 1;
- if (fwrite(&sd, sizeof(sd), 1, fp) != 1)
- return 1;
- if (dump_state_align(fp))
+ record = talloc_size(NULL, rec_len + sizeof(*head));
+ if (!record)
return 1;
- return 0;
+ head = record;
+ head->type = XS_STATE_TYPE_DOMAIN;
+ head->length = rec_len;
+
+ sd = (struct xs_state_domain *)(head + 1);
+ sd->domain_id = domain->domid;
+ sd->n_quota = n_quota;
+ sd->features = (lu_status->version > 1) ? domain->features : 0;
+
+ build_quota_data(domain->acc, sd->quota_val,
+ (char *)(sd->quota_val + n_quota));
+
+ ret = fwrite(record, rec_len + sizeof(*head), 1, fp);
+
+ talloc_free(record);
+
+ return (ret != 1 || dump_state_align(fp)) ? 1 : 0;
}
const char *dump_state_domains(FILE *fp)
@@ -2153,6 +2179,8 @@ void read_state_domain(const void *ctx, const void *state, unsigned int version)
{
const struct xs_state_domain *sd = state;
struct domain *domain;
+ unsigned int n_quota = sd->n_quota;
+ const char *name = (const char *)(sd->quota_val + n_quota);
domain = find_domain_struct(sd->domain_id);
if (!domain)
@@ -2160,6 +2188,8 @@ void read_state_domain(const void *ctx, const void *state, unsigned int version)
if (version > 1)
domain->features = sd->features;
+
+ parse_quota_data(sd->quota_val, name, n_quota, domain->acc);
}
const char *dump_state_glb_quota(FILE *fp)
--
2.53.0
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH v2 08/12] tools/xenstored: implement the GET/SET_QUOTA commands
2026-03-20 15:01 [PATCH v2 00/12] tools: add support for per-domain xenstore quota Juergen Gross
` (6 preceding siblings ...)
2026-03-20 15:01 ` [PATCH v2 07/12] tools/xenstored: use per-domain quota settings Juergen Gross
@ 2026-03-20 15:01 ` Juergen Gross
2026-03-23 13:00 ` Anthony PERARD
2026-03-20 15:01 ` [PATCH v2 09/12] tools/libxl: add functions for retrieving and setting xenstore quota Juergen Gross
` (3 subsequent siblings)
11 siblings, 1 reply; 20+ messages in thread
From: Juergen Gross @ 2026-03-20 15:01 UTC (permalink / raw)
To: xen-devel; +Cc: Juergen Gross, Julien Grall, Anthony PERARD
Add the implementation of the GET_QUOTA and SET_QUOTA wire commands.
Signed-off-by: Juergen Gross <jgross@suse.com>
---
V2:
- refuse quota value Q_VAL_DISABLED (Anthony Perard)
- use talloc_strdup() (Anthony Perard)
- drop comments in domain.h (Anthony Perard)
---
tools/xenstored/core.c | 4 ++
tools/xenstored/domain.c | 111 +++++++++++++++++++++++++++++++++++++++
tools/xenstored/domain.h | 5 ++
3 files changed, 120 insertions(+)
diff --git a/tools/xenstored/core.c b/tools/xenstored/core.c
index dc63c97658..4786a2a82e 100644
--- a/tools/xenstored/core.c
+++ b/tools/xenstored/core.c
@@ -2035,6 +2035,10 @@ static struct {
{ "GET_FEATURE", do_get_feature, XS_FLAG_PRIV },
[XS_SET_FEATURE] =
{ "SET_FEATURE", do_set_feature, XS_FLAG_PRIV },
+ [XS_GET_QUOTA] =
+ { "GET_QUOTA", do_get_quota, XS_FLAG_PRIV },
+ [XS_SET_QUOTA] =
+ { "SET_QUOTA", do_set_quota, XS_FLAG_PRIV },
};
static const char *sockmsg_string(enum xsd_sockmsg_type type)
diff --git a/tools/xenstored/domain.c b/tools/xenstored/domain.c
index 7a8d285e64..1684f6dee7 100644
--- a/tools/xenstored/domain.c
+++ b/tools/xenstored/domain.c
@@ -1399,6 +1399,117 @@ static bool parse_quota_name(const char *name, unsigned int *qidx,
return true;
}
+int do_get_quota(const void *ctx, struct connection *conn,
+ struct buffered_data *in)
+{
+ const char *vec[2];
+ unsigned int n_pars;
+ unsigned int domid;
+ unsigned int q;
+ unsigned int idx;
+ char *resp;
+ const char *name;
+ const struct quota *quota;
+ const struct domain *domain;
+
+ n_pars = get_strings(in, vec, ARRAY_SIZE(vec));
+
+ if (n_pars > 2)
+ return EINVAL;
+
+ if (n_pars == 0) {
+ resp = talloc_strdup(ctx, "");
+ if (!resp)
+ return ENOMEM;
+ for (q = 0; q < ACC_N; q++) {
+ if (!quota_adm[q].name)
+ continue;
+ if (quotas[q].val[Q_IDX_HARD] != Q_VAL_DISABLED) {
+ resp = talloc_asprintf_append(resp, "%s%s",
+ *resp ? " " : "", quota_adm[q].name);
+ if (!resp)
+ return ENOMEM;
+ }
+ if (quotas[q].val[Q_IDX_SOFT] != Q_VAL_DISABLED) {
+ resp = talloc_asprintf_append(resp, "%s%s%s",
+ *resp ? " " : "", SOFT_PREFIX,
+ quota_adm[q].name);
+ if (!resp)
+ return ENOMEM;
+ }
+ }
+ } else {
+ if (n_pars == 1) {
+ quota = quotas;
+ name = vec[0];
+ } else {
+ domid = parse_domid(vec[0]);
+ if (errno)
+ return errno;
+ domain = find_or_alloc_existing_domain(domid);
+ if (!domain)
+ return ENOENT;
+ quota = domain->acc;
+ name = vec[1];
+ }
+
+ if (parse_quota_name(name, &q, &idx))
+ return EINVAL;
+
+ resp = talloc_asprintf(ctx, "%u", quota[q].val[idx]);
+ if (!resp)
+ return ENOMEM;
+ }
+
+ send_reply(conn, XS_GET_QUOTA, resp, strlen(resp) + 1);
+
+ return 0;
+}
+
+int do_set_quota(const void *ctx, struct connection *conn,
+ struct buffered_data *in)
+{
+ const char *vec[3];
+ unsigned int n_pars;
+ unsigned int domid;
+ unsigned int q;
+ unsigned int idx;
+ const char *name;
+ unsigned int val;
+ struct quota *quota;
+ struct domain *domain;
+
+ n_pars = get_strings(in, vec, ARRAY_SIZE(vec));
+
+ if (n_pars < 2 || n_pars > 3)
+ return EINVAL;
+
+ if (n_pars == 2) {
+ quota = quotas;
+ name = vec[0];
+ val = atoi(vec[1]);
+ } else {
+ domid = parse_domid(vec[0]);
+ if (errno)
+ return errno;
+ domain = find_or_alloc_existing_domain(domid);
+ if (!domain)
+ return ENOENT;
+ quota = domain->acc;
+ name = vec[1];
+ val = atoi(vec[2]);
+ }
+
+ if (parse_quota_name(name, &q, &idx) || val == Q_VAL_DISABLED)
+ return EINVAL;
+
+ quota[q].val[idx] = val;
+
+ send_ack(conn, XS_SET_QUOTA);
+
+ return 0;
+}
+
static int close_xgt_handle(void *_handle)
{
xengnttab_close(*(xengnttab_handle **)_handle);
diff --git a/tools/xenstored/domain.h b/tools/xenstored/domain.h
index 8f23a82854..ca38b5e0ea 100644
--- a/tools/xenstored/domain.h
+++ b/tools/xenstored/domain.h
@@ -93,6 +93,11 @@ int do_get_feature(const void *ctx, struct connection *conn,
int do_set_feature(const void *ctx, struct connection *conn,
struct buffered_data *in);
+int do_get_quota(const void *ctx, struct connection *conn,
+ struct buffered_data *in);
+int do_set_quota(const void *ctx, struct connection *conn,
+ struct buffered_data *in);
+
void domain_early_init(void);
void domain_init(int evtfd);
void init_domains(bool live_update);
--
2.53.0
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH v2 09/12] tools/libxl: add functions for retrieving and setting xenstore quota
2026-03-20 15:01 [PATCH v2 00/12] tools: add support for per-domain xenstore quota Juergen Gross
` (7 preceding siblings ...)
2026-03-20 15:01 ` [PATCH v2 08/12] tools/xenstored: implement the GET/SET_QUOTA commands Juergen Gross
@ 2026-03-20 15:01 ` Juergen Gross
2026-03-23 13:19 ` Anthony PERARD
2026-03-20 15:01 ` [PATCH v2 10/12] tools/libxl: add support for xenstore quota in domain_config Juergen Gross
` (2 subsequent siblings)
11 siblings, 1 reply; 20+ messages in thread
From: Juergen Gross @ 2026-03-20 15:01 UTC (permalink / raw)
To: xen-devel; +Cc: Juergen Gross, Nick Rosbrook, George Dunlap, Anthony PERARD
Add some functions allowing to retrieve and set Xenstore quota (either
global or domain specific).
Signed-off-by: Juergen Gross <jgross@suse.com>
Acked-by: Nick Rosbrook <enr0n@ubuntu.com> # golang stuff
---
V2:
- rename libxl functions to use "xs_quota" instead of "xsquota"
(Anthony Perard)
- rename the libxl_xs_quota_*_get() "q" parameter to "q_out"
(Anthony Perard)
- rename the struct xs_quota_set to xs_quota_list (Anthony Perard)
- several style changes (Anthony Perard)
---
tools/golang/xenlight/helpers.gen.go | 78 ++++++++++++++++++
tools/golang/xenlight/types.gen.go | 9 +++
tools/include/libxl.h | 20 +++++
tools/libs/light/Makefile | 1 +
tools/libs/light/libxl_types.idl | 9 +++
tools/libs/light/libxl_xsquota.c | 116 +++++++++++++++++++++++++++
6 files changed, 233 insertions(+)
create mode 100644 tools/libs/light/libxl_xsquota.c
diff --git a/tools/golang/xenlight/helpers.gen.go b/tools/golang/xenlight/helpers.gen.go
index 8909fe8a1b..767b9e45f5 100644
--- a/tools/golang/xenlight/helpers.gen.go
+++ b/tools/golang/xenlight/helpers.gen.go
@@ -998,6 +998,84 @@ xc.policy = C.libxl_rdm_reserve_policy(x.Policy)
return nil
}
+// NewXsQuotaItem returns an instance of XsQuotaItem initialized with defaults.
+func NewXsQuotaItem() (*XsQuotaItem, error) {
+var (
+x XsQuotaItem
+xc C.libxl_xs_quota_item)
+
+C.libxl_xs_quota_item_init(&xc)
+defer C.libxl_xs_quota_item_dispose(&xc)
+
+if err := x.fromC(&xc); err != nil {
+return nil, err }
+
+return &x, nil}
+
+func (x *XsQuotaItem) fromC(xc *C.libxl_xs_quota_item) error {
+ x.Name = C.GoString(xc.name)
+x.Val = uint32(xc.val)
+
+ return nil}
+
+func (x *XsQuotaItem) toC(xc *C.libxl_xs_quota_item) (err error){defer func(){
+if err != nil{
+C.libxl_xs_quota_item_dispose(xc)}
+}()
+
+if x.Name != "" {
+xc.name = C.CString(x.Name)}
+xc.val = C.uint32_t(x.Val)
+
+ return nil
+ }
+
+// NewXsQuotaList returns an instance of XsQuotaList initialized with defaults.
+func NewXsQuotaList() (*XsQuotaList, error) {
+var (
+x XsQuotaList
+xc C.libxl_xs_quota_list)
+
+C.libxl_xs_quota_list_init(&xc)
+defer C.libxl_xs_quota_list_dispose(&xc)
+
+if err := x.fromC(&xc); err != nil {
+return nil, err }
+
+return &x, nil}
+
+func (x *XsQuotaList) fromC(xc *C.libxl_xs_quota_list) error {
+ x.Quota = nil
+if n := int(xc.num_quota); n > 0 {
+cQuota := (*[1<<28]C.libxl_xs_quota_item)(unsafe.Pointer(xc.quota))[:n:n]
+x.Quota = make([]XsQuotaItem, n)
+for i, v := range cQuota {
+if err := x.Quota[i].fromC(&v); err != nil {
+return fmt.Errorf("converting field Quota: %v", err) }
+}
+}
+
+ return nil}
+
+func (x *XsQuotaList) toC(xc *C.libxl_xs_quota_list) (err error){defer func(){
+if err != nil{
+C.libxl_xs_quota_list_dispose(xc)}
+}()
+
+if numQuota := len(x.Quota); numQuota > 0 {
+xc.quota = (*C.libxl_xs_quota_item)(C.malloc(C.ulong(numQuota)*C.sizeof_libxl_xs_quota_item))
+xc.num_quota = C.int(numQuota)
+cQuota := (*[1<<28]C.libxl_xs_quota_item)(unsafe.Pointer(xc.quota))[:numQuota:numQuota]
+for i,v := range x.Quota {
+if err := v.toC(&cQuota[i]); err != nil {
+return fmt.Errorf("converting field Quota: %v", err)
+}
+}
+}
+
+ return nil
+ }
+
// NewDomainBuildInfo returns an instance of DomainBuildInfo initialized with defaults.
func NewDomainBuildInfo(dtype DomainType) (*DomainBuildInfo, error) {
var (
diff --git a/tools/golang/xenlight/types.gen.go b/tools/golang/xenlight/types.gen.go
index ab9d4ca7b4..8dd610919d 100644
--- a/tools/golang/xenlight/types.gen.go
+++ b/tools/golang/xenlight/types.gen.go
@@ -543,6 +543,15 @@ Altp2MModeExternal Altp2MMode = 2
Altp2MModeLimited Altp2MMode = 3
)
+type XsQuotaItem struct {
+Name string
+Val uint32
+}
+
+type XsQuotaList struct {
+Quota []XsQuotaItem
+}
+
type DomainBuildInfo struct {
MaxVcpus int
AvailVcpus Bitmap
diff --git a/tools/include/libxl.h b/tools/include/libxl.h
index bc35e412da..6d2910df34 100644
--- a/tools/include/libxl.h
+++ b/tools/include/libxl.h
@@ -1537,6 +1537,18 @@ void libxl_mac_copy(libxl_ctx *ctx, libxl_mac *dst, const libxl_mac *src);
*/
#define LIBXL_HAVE_XEN_PLATFORM_PCI_BAR_UC
+/*
+ * LIBXL_HAVE_XENSTORE_QUOTA
+ *
+ * If this is defined the Xenstore quota related functions
+ * libxl_xs_quota_global_get()
+ * libxl_xs_quota_global_set()
+ * libxl_xs_quota_domain_get()
+ * libxl_xs_quota_domain_set()
+ * are available.
+ */
+#define LIBXL_HAVE_XENSTORE_QUOTA
+
typedef char **libxl_string_list;
void libxl_string_list_dispose(libxl_string_list *sl);
int libxl_string_list_length(const libxl_string_list *sl);
@@ -3011,6 +3023,14 @@ static inline int libxl_qemu_monitor_command_0x041200(libxl_ctx *ctx,
#define libxl_qemu_monitor_command libxl_qemu_monitor_command_0x041200
#endif
+/* Get/set global and per-domain Xenstore quota. */
+int libxl_xs_quota_global_get(libxl_ctx *ctx, libxl_xs_quota_list *q_out);
+int libxl_xs_quota_global_set(libxl_ctx *ctx, libxl_xs_quota_list *q);
+int libxl_xs_quota_domain_get(libxl_ctx *ctx, uint32_t domid,
+ libxl_xs_quota_list *q_out);
+int libxl_xs_quota_domain_set(libxl_ctx *ctx, uint32_t domid,
+ libxl_xs_quota_list *q);
+
#include <libxl_event.h>
/*
diff --git a/tools/libs/light/Makefile b/tools/libs/light/Makefile
index bc60c46558..ca22a40c6c 100644
--- a/tools/libs/light/Makefile
+++ b/tools/libs/light/Makefile
@@ -106,6 +106,7 @@ OBJS-y += libxl_pvcalls.o
OBJS-y += libxl_vsnd.o
OBJS-y += libxl_vkb.o
OBJS-y += libxl_virtio.o
+OBJS-y += libxl_xsquota.o
OBJS-y += libxl_genid.o
OBJS-y += _libxl_types.o
OBJS-y += libxl_flask.o
diff --git a/tools/libs/light/libxl_types.idl b/tools/libs/light/libxl_types.idl
index d64a573ff3..1a63c8af76 100644
--- a/tools/libs/light/libxl_types.idl
+++ b/tools/libs/light/libxl_types.idl
@@ -574,6 +574,15 @@ libxl_altp2m_mode = Enumeration("altp2m_mode", [
(3, "limited"),
], init_val = "LIBXL_ALTP2M_MODE_DISABLED")
+libxl_xs_quota_item = Struct("xs_quota_item", [
+ ("name", string),
+ ("val", uint32),
+ ])
+
+libxl_xs_quota_list = Struct("xs_quota_list", [
+ ("quota", Array(libxl_xs_quota_item, "num_quota"))
+ ])
+
libxl_domain_build_info = Struct("domain_build_info",[
("max_vcpus", integer),
("avail_vcpus", libxl_bitmap),
diff --git a/tools/libs/light/libxl_xsquota.c b/tools/libs/light/libxl_xsquota.c
new file mode 100644
index 0000000000..4524442655
--- /dev/null
+++ b/tools/libs/light/libxl_xsquota.c
@@ -0,0 +1,116 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
+
+/* Xenstore quota handling functions. */
+
+#include "libxl_internal.h"
+
+static int get_quota(libxl_ctx *ctx, unsigned int domid,
+ libxl_xs_quota_list *q_out,
+ bool (func)(struct xs_handle *h, unsigned int domid,
+ const char *quota, unsigned int *value))
+{
+ const char **names;
+ unsigned int num, i;
+ bool ok;
+ int rc;
+ GC_INIT(ctx);
+
+ libxl_xs_quota_list_init(q_out);
+ names = xs_get_quota_names(ctx->xsh, &num);
+ if (!names) {
+ /* Xenstore quota support is optional! */
+ if (errno != ENOSYS) {
+ libxl_xs_quota_list_dispose(q_out);
+ rc = ERROR_FAIL;
+ } else {
+ rc = 0;
+ }
+ goto out;
+ }
+
+ q_out->num_quota = num;
+ q_out->quota = libxl__calloc(NOGC, num, sizeof(*q_out->quota));
+ for (i = 0; i < num; i++) {
+ q_out->quota[i].name = libxl__strdup(NOGC, names[i]);
+ ok = func(ctx->xsh, domid, q_out->quota[i].name, &q_out->quota[i].val);
+ if (!ok) {
+ libxl_xs_quota_list_dispose(q_out);
+ rc = ERROR_FAIL;
+ goto out;
+ }
+ }
+
+ rc = 0;
+
+ out:
+ free(names);
+
+ GC_FREE;
+ return rc;
+}
+
+static int set_quota(libxl_ctx *ctx, unsigned int domid, libxl_xs_quota_list *q,
+ bool (func)(struct xs_handle *h, unsigned int domid,
+ const char *quota, unsigned int value))
+{
+ unsigned int i;
+ bool ok;
+ int rc;
+ GC_INIT(ctx);
+
+ for (i = 0; i < q->num_quota; i++) {
+ ok = func(ctx->xsh, domid, q->quota[i].name, q->quota[i].val);
+ if (!ok) {
+ rc = ERROR_FAIL;
+ goto out;
+ }
+ }
+
+ rc = 0;
+
+ out:
+ GC_FREE;
+ return rc;
+}
+
+static bool get_global_quota(struct xs_handle *h, unsigned int domid,
+ const char *quota, unsigned int *value)
+{
+ return xs_get_global_quota(h, quota, value);
+}
+
+int libxl_xs_quota_global_get(libxl_ctx *ctx, libxl_xs_quota_list *q_out)
+{
+ return get_quota(ctx, 0, q_out, get_global_quota);
+}
+
+static bool set_global_quota(struct xs_handle *h, unsigned int domid,
+ const char *quota, unsigned int value)
+{
+ return xs_set_global_quota(h, quota, value);
+}
+
+int libxl_xs_quota_global_set(libxl_ctx *ctx, libxl_xs_quota_list *q)
+{
+ return set_quota(ctx, 0, q, set_global_quota);;
+}
+
+int libxl_xs_quota_domain_get(libxl_ctx *ctx, uint32_t domid,
+ libxl_xs_quota_list *q_out)
+{
+ return get_quota(ctx, domid, q_out, xs_get_domain_quota);
+}
+
+int libxl_xs_quota_domain_set(libxl_ctx *ctx, uint32_t domid,
+ libxl_xs_quota_list *q)
+{
+ return set_quota(ctx, domid, q, xs_set_domain_quota);
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
--
2.53.0
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH v2 10/12] tools/libxl: add support for xenstore quota in domain_config
2026-03-20 15:01 [PATCH v2 00/12] tools: add support for per-domain xenstore quota Juergen Gross
` (8 preceding siblings ...)
2026-03-20 15:01 ` [PATCH v2 09/12] tools/libxl: add functions for retrieving and setting xenstore quota Juergen Gross
@ 2026-03-20 15:01 ` Juergen Gross
2026-03-23 13:21 ` Anthony PERARD
2026-03-20 15:01 ` [PATCH v2 11/12] tools/xl: add xl commands for xenstore quota operations Juergen Gross
2026-03-20 15:01 ` [PATCH v2 12/12] tools/xl: add support for xenstore quota setting via domain config Juergen Gross
11 siblings, 1 reply; 20+ messages in thread
From: Juergen Gross @ 2026-03-20 15:01 UTC (permalink / raw)
To: xen-devel; +Cc: Juergen Gross, Nick Rosbrook, George Dunlap, Anthony PERARD
Add support for xenstore quota in the struct domain_config. Initially
it will be used only for migration of a domain.
Signed-off-by: Juergen Gross <jgross@suse.com>
Acked-by: Nick Rosbrook <enr0n@ubuntu.com> # golang stuff
---
V2:
- use LOGED() for error logging (Anthony Perard)
- mention additional struct member xenstore_quota in libxl.h
(Anthony Perard)
---
tools/golang/xenlight/helpers.gen.go | 6 ++++++
tools/golang/xenlight/types.gen.go | 1 +
tools/include/libxl.h | 1 +
tools/libs/light/libxl_dom.c | 8 ++++++++
tools/libs/light/libxl_domain.c | 11 +++++++++++
tools/libs/light/libxl_types.idl | 1 +
6 files changed, 28 insertions(+)
diff --git a/tools/golang/xenlight/helpers.gen.go b/tools/golang/xenlight/helpers.gen.go
index 767b9e45f5..b0c09da910 100644
--- a/tools/golang/xenlight/helpers.gen.go
+++ b/tools/golang/xenlight/helpers.gen.go
@@ -1286,6 +1286,9 @@ if err := x.TrapUnmappedAccesses.fromC(&xc.trap_unmapped_accesses);err != nil {
return fmt.Errorf("converting field TrapUnmappedAccesses: %v", err)
}
x.XenstoreFeatureMask = uint32(xc.xenstore_feature_mask)
+if err := x.XenstoreQuota.fromC(&xc.xenstore_quota);err != nil {
+return fmt.Errorf("converting field XenstoreQuota: %v", err)
+}
return nil}
@@ -1825,6 +1828,9 @@ if err := x.TrapUnmappedAccesses.toC(&xc.trap_unmapped_accesses); err != nil {
return fmt.Errorf("converting field TrapUnmappedAccesses: %v", err)
}
xc.xenstore_feature_mask = C.uint32_t(x.XenstoreFeatureMask)
+if err := x.XenstoreQuota.toC(&xc.xenstore_quota); err != nil {
+return fmt.Errorf("converting field XenstoreQuota: %v", err)
+}
return nil
}
diff --git a/tools/golang/xenlight/types.gen.go b/tools/golang/xenlight/types.gen.go
index 8dd610919d..e0fd78ec03 100644
--- a/tools/golang/xenlight/types.gen.go
+++ b/tools/golang/xenlight/types.gen.go
@@ -629,6 +629,7 @@ VmtraceBufKb int
Vpmu Defbool
TrapUnmappedAccesses Defbool
XenstoreFeatureMask uint32
+XenstoreQuota XsQuotaList
}
type DomainBuildInfoTypeUnion interface {
diff --git a/tools/include/libxl.h b/tools/include/libxl.h
index 6d2910df34..80e3ec8de9 100644
--- a/tools/include/libxl.h
+++ b/tools/include/libxl.h
@@ -1545,6 +1545,7 @@ void libxl_mac_copy(libxl_ctx *ctx, libxl_mac *dst, const libxl_mac *src);
* libxl_xs_quota_global_set()
* libxl_xs_quota_domain_get()
* libxl_xs_quota_domain_set()
+ * and the xenstore_quota member of struct domain_build_info
* are available.
*/
#define LIBXL_HAVE_XENSTORE_QUOTA
diff --git a/tools/libs/light/libxl_dom.c b/tools/libs/light/libxl_dom.c
index 05ebc69534..4ff5f65f6f 100644
--- a/tools/libs/light/libxl_dom.c
+++ b/tools/libs/light/libxl_dom.c
@@ -509,6 +509,14 @@ retry_transaction:
xs_introduce_domain(ctx->xsh, domid, state->store_mfn, state->store_port);
+ if (info->xenstore_quota.num_quota) {
+ rc = libxl_xs_quota_domain_set(ctx, domid, &info->xenstore_quota);
+ if (rc) {
+ LOGED(ERROR, domid, "Failed to set Xenstore quota");
+ goto out;
+ }
+ }
+
out:
free(vm_path);
return rc;
diff --git a/tools/libs/light/libxl_domain.c b/tools/libs/light/libxl_domain.c
index 5be47f687f..37fcd92871 100644
--- a/tools/libs/light/libxl_domain.c
+++ b/tools/libs/light/libxl_domain.c
@@ -2533,6 +2533,17 @@ static void retrieve_domain_configuration_end(libxl__egc *egc,
}
}
+ /* Xenstore quota */
+ {
+ libxl_xs_quota_list_dispose(&d_config->b_info.xenstore_quota);
+ rc = libxl_xs_quota_domain_get(CTX, domid,
+ &d_config->b_info.xenstore_quota);
+ if (rc) {
+ LOGED(ERROR, domid, "Fail to get xenstore quota");
+ goto out;
+ }
+ }
+
/* Devices: disk, nic, vtpm, pcidev etc. */
/* The MERGE macro implements following logic:
diff --git a/tools/libs/light/libxl_types.idl b/tools/libs/light/libxl_types.idl
index 1a63c8af76..a7893460f0 100644
--- a/tools/libs/light/libxl_types.idl
+++ b/tools/libs/light/libxl_types.idl
@@ -760,6 +760,7 @@ libxl_domain_build_info = Struct("domain_build_info",[
("vpmu", libxl_defbool),
("trap_unmapped_accesses", libxl_defbool),
("xenstore_feature_mask", uint32, {'init_val': '~0U'}),
+ ("xenstore_quota", libxl_xs_quota_list),
], dir=DIR_IN,
copy_deprecated_fn="libxl__domain_build_info_copy_deprecated",
--
2.53.0
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH v2 11/12] tools/xl: add xl commands for xenstore quota operations
2026-03-20 15:01 [PATCH v2 00/12] tools: add support for per-domain xenstore quota Juergen Gross
` (9 preceding siblings ...)
2026-03-20 15:01 ` [PATCH v2 10/12] tools/libxl: add support for xenstore quota in domain_config Juergen Gross
@ 2026-03-20 15:01 ` Juergen Gross
2026-03-23 14:08 ` Anthony PERARD
2026-03-20 15:01 ` [PATCH v2 12/12] tools/xl: add support for xenstore quota setting via domain config Juergen Gross
11 siblings, 1 reply; 20+ messages in thread
From: Juergen Gross @ 2026-03-20 15:01 UTC (permalink / raw)
To: xen-devel; +Cc: Juergen Gross, Anthony PERARD
Add "xl xenstore-quota-get" and "xl xenstore-quota-set" commands for
retrieving and setting global and per-domain Xenstore quota.
Signed-off-by: Juergen Gross <jgross@suse.com>
---
V2:
- s/quota data/quotas/ in cmdtable (Anthony Perard)
- test for quota value to fit into type (Anthony Perard)
- use libxl_xs_quota_list_init() (Anthony Perard)
- use xcalloc() (Anthony Perard)
---
tools/xl/Makefile | 1 +
tools/xl/xl.h | 2 +
tools/xl/xl_cmdtable.c | 10 +++++
tools/xl/xl_parse.c | 28 +++++++++++++
tools/xl/xl_parse.h | 1 +
tools/xl/xl_xsquota.c | 90 ++++++++++++++++++++++++++++++++++++++++++
6 files changed, 132 insertions(+)
create mode 100644 tools/xl/xl_xsquota.c
diff --git a/tools/xl/Makefile b/tools/xl/Makefile
index 973ff0e1a2..e4eed8be13 100644
--- a/tools/xl/Makefile
+++ b/tools/xl/Makefile
@@ -24,6 +24,7 @@ XL_OBJS += xl_sched.o xl_pci.o xl_vcpu.o xl_cdrom.o xl_mem.o
XL_OBJS += xl_info.o xl_console.o xl_misc.o
XL_OBJS += xl_vmcontrol.o xl_saverestore.o xl_migrate.o
XL_OBJS += xl_vdispl.o xl_vsnd.o xl_vkb.o
+XL_OBJS += xl_xsquota.o
$(XL_OBJS): CFLAGS += $(CFLAGS_libxentoollog)
$(XL_OBJS): CFLAGS += $(CFLAGS_XL)
diff --git a/tools/xl/xl.h b/tools/xl/xl.h
index 9000df00de..0efc07a6ba 100644
--- a/tools/xl/xl.h
+++ b/tools/xl/xl.h
@@ -217,6 +217,8 @@ int main_psr_mba_set(int argc, char **argv);
int main_psr_mba_show(int argc, char **argv);
#endif
int main_qemu_monitor_command(int argc, char **argv);
+int main_xsquota_get(int argc, char **argv);
+int main_xsquota_set(int argc, char **argv);
void help(const char *command);
diff --git a/tools/xl/xl_cmdtable.c b/tools/xl/xl_cmdtable.c
index 06a0039718..5098f72ab4 100644
--- a/tools/xl/xl_cmdtable.c
+++ b/tools/xl/xl_cmdtable.c
@@ -649,6 +649,16 @@ const struct cmd_spec cmd_table[] = {
"-h print this help\n"
},
#endif
+ { "xenstore-quota-get",
+ &main_xsquota_get, 0, 0,
+ "List global or domain specific Xenstore quotas",
+ "<Domain>|-g",
+ },
+ { "xenstore-quota-set",
+ &main_xsquota_set, 0, 1,
+ "Set global or domain specific Xenstore quotas",
+ "<Domain>|-g <quota>=<val>...",
+ },
};
const int cmdtable_len = ARRAY_SIZE(cmd_table);
diff --git a/tools/xl/xl_parse.c b/tools/xl/xl_parse.c
index 1a2ea8b5d5..4b074fdb58 100644
--- a/tools/xl/xl_parse.c
+++ b/tools/xl/xl_parse.c
@@ -1314,6 +1314,34 @@ out:
return ret;
}
+int parse_xsquota_item(const char *buf, struct libxl_xs_quota_item *item)
+{
+ const char *eq;
+ char *endptr;
+ unsigned long val;
+
+ eq = strchr(buf, '=');
+ if (!eq) {
+ fprintf(stderr, "Quota specification \"%s\" lacks \"=\".\n", buf);
+ return ERROR_INVAL;
+ }
+ errno = 0;
+ item->name = strndup(buf, eq - buf);
+ if (!item->name)
+ return ERROR_NOMEM;
+ val = strtoul(eq + 1, &endptr, 0);
+ if (errno || !eq[1] || *endptr || (unsigned int)val != val) {
+ fprintf(stderr,
+ "Quota specification \"%s\" uses illegal value \"%s\".\n",
+ buf, eq + 1);
+ return ERROR_INVAL;
+ }
+
+ item->val = val;
+
+ return 0;
+}
+
void parse_config_data(const char *config_source,
const char *config_data,
int config_len,
diff --git a/tools/xl/xl_parse.h b/tools/xl/xl_parse.h
index fe0d586cdd..57bb43a067 100644
--- a/tools/xl/xl_parse.h
+++ b/tools/xl/xl_parse.h
@@ -36,6 +36,7 @@ int parse_nic_config(libxl_device_nic *nic, XLU_Config **config, char *token);
int parse_vdispl_config(libxl_device_vdispl *vdispl, char *token);
int parse_vsnd_item(libxl_device_vsnd *vsnd, const char *spec);
int parse_vkb_config(libxl_device_vkb *vkb, char *token);
+int parse_xsquota_item(const char *buf, struct libxl_xs_quota_item *item);
int match_option_size(const char *prefix, size_t len,
char *arg, char **argopt);
diff --git a/tools/xl/xl_xsquota.c b/tools/xl/xl_xsquota.c
new file mode 100644
index 0000000000..3533d22dae
--- /dev/null
+++ b/tools/xl/xl_xsquota.c
@@ -0,0 +1,90 @@
+/* SPDX-License-Identifier: LGPL-2.1-only */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <libxl.h>
+#include <libxlutil.h>
+
+#include "xl.h"
+#include "xl_utils.h"
+#include "xl_parse.h"
+
+int main_xsquota_get(int argc, char **argv)
+{
+ libxl_xs_quota_list q;
+ unsigned int i;
+ int rc;
+
+ if (argc != 2) {
+ fprintf(stderr, "Domain or \"-g\" must be specified.\n");
+ return EXIT_FAILURE;
+ }
+
+ libxl_xs_quota_list_init(&q);
+
+ if (!strcmp(argv[1], "-g")) {
+ rc = libxl_xs_quota_global_get(ctx, &q);
+ } else {
+ uint32_t domid = find_domain(argv[1]);
+
+ rc = libxl_xs_quota_domain_get(ctx, domid, &q);
+ }
+
+ if (rc) {
+ libxl_xs_quota_list_dispose(&q);
+ fprintf(stderr, "Quota could not be obtained.\n");
+ return EXIT_FAILURE;
+ }
+
+ printf("Quota name Quota value\n");
+ printf("--------------------------------\n");
+ for (i = 0; i < q.num_quota; i++)
+ printf("%-20s %8u\n", q.quota[i].name, q.quota[i].val);
+
+ libxl_xs_quota_list_dispose(&q);
+
+ return EXIT_SUCCESS;
+}
+
+int main_xsquota_set(int argc, char **argv)
+{
+ unsigned int i;
+ libxl_xs_quota_list q;
+ int rc = EXIT_FAILURE;
+
+ if (argc < 3) {
+ fprintf(stderr, "Not enough parameters.\n");
+ help("xenstore-quota-set");
+ return EXIT_FAILURE;
+ }
+
+ libxl_xs_quota_list_init(&q);
+
+ q.num_quota = argc - 2;
+ q.quota = xcalloc(q.num_quota, sizeof(*q.quota));
+
+ for (i = 2; i < argc; i++) {
+ if (parse_xsquota_item(argv[i], q.quota + i - 2))
+ goto err;
+ }
+
+ if (!strcmp(argv[1], "-g")) {
+ rc = libxl_xs_quota_global_set(ctx, &q);
+ } else {
+ uint32_t domid = find_domain(argv[1]);
+
+ rc = libxl_xs_quota_domain_set(ctx, domid, &q);
+ }
+
+ if (rc) {
+ fprintf(stderr, "Quota could not be set.\n");
+ rc = EXIT_FAILURE;
+ } else {
+ rc = EXIT_SUCCESS;
+ }
+
+ err:
+ libxl_xs_quota_list_dispose(&q);
+
+ return rc;
+}
--
2.53.0
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [PATCH v2 12/12] tools/xl: add support for xenstore quota setting via domain config
2026-03-20 15:01 [PATCH v2 00/12] tools: add support for per-domain xenstore quota Juergen Gross
` (10 preceding siblings ...)
2026-03-20 15:01 ` [PATCH v2 11/12] tools/xl: add xl commands for xenstore quota operations Juergen Gross
@ 2026-03-20 15:01 ` Juergen Gross
11 siblings, 0 replies; 20+ messages in thread
From: Juergen Gross @ 2026-03-20 15:01 UTC (permalink / raw)
To: xen-devel; +Cc: Juergen Gross, Anthony PERARD
Add a new "xenstore_quota" domain config parameter for setting the
Xenstore quota of a new domain via a list of <quota>=<val> items.
Signed-off-by: Juergen Gross <jgross@suse.com>
Reviewed-by: Anthony PERARD <anthony.perard@vates.tech>
---
V2:
- fix typo in commit message (Anthony Perard)
- use xcalloc() (Anthony Perard)
---
docs/man/xl.cfg.5.pod.in | 13 +++++++++++++
tools/xl/xl_parse.c | 19 ++++++++++++++++++-
2 files changed, 31 insertions(+), 1 deletion(-)
diff --git a/docs/man/xl.cfg.5.pod.in b/docs/man/xl.cfg.5.pod.in
index 27c455210b..3aac0bc4fb 100644
--- a/docs/man/xl.cfg.5.pod.in
+++ b/docs/man/xl.cfg.5.pod.in
@@ -748,6 +748,19 @@ via the B<xl info -x> command in dom0.
The default value is B<0xffffffff>, meaning that all possible Xenstore
features are visible by the guest.
+=item B<xenstore_quota=[ QUOTA_SPEC, QUOTA_SPEC, ...]>
+
+Specifies Xenstore quota values of the domain, overriding the default
+values of Xenstore.
+
+Each B<QUOTA_SPEC> is a B<quota-name>=B<value> specification. The supported
+B<quota-name> identifiers can be obtained by the B<xl xenstore-quota-get -g>
+command. B<value> is a non-negative integer.
+
+As per-domain Xenstore quota are an optional Xenstore feature, the
+B<xenstore_quota> config parameter may not be supported by all Xenstore
+implementations.
+
=back
=head2 Devices
diff --git a/tools/xl/xl_parse.c b/tools/xl/xl_parse.c
index 4b074fdb58..48c72dce9c 100644
--- a/tools/xl/xl_parse.c
+++ b/tools/xl/xl_parse.c
@@ -1354,7 +1354,7 @@ void parse_config_data(const char *config_source,
XLU_ConfigList *cpus, *vbds, *nics, *pcis, *cvfbs, *cpuids, *vtpms,
*usbctrls, *usbdevs, *p9devs, *vdispls, *pvcallsifs_devs;
XLU_ConfigList *channels, *ioports, *irqs, *iomem, *viridian, *dtdevs,
- *mca_caps, *smbios, *llc_colors;
+ *mca_caps, *smbios, *llc_colors, *xs_quota;
int num_ioports, num_irqs, num_iomem, num_cpus, num_viridian, num_mca_caps;
int num_smbios;
int pci_power_mgmt = 0;
@@ -1363,6 +1363,7 @@ void parse_config_data(const char *config_source,
int pci_seize = 0;
int i, e;
int num_llc_colors;
+ int num_xs_quota;
char *kernel_basename;
libxl_domain_create_info *c_info = &d_config->c_info;
@@ -1470,6 +1471,22 @@ void parse_config_data(const char *config_source,
if (!xlu_cfg_get_long (config, "xenstore_feature_mask", &l, 0))
b_info->xenstore_feature_mask = l;
+ if (!xlu_cfg_get_list(config, "xenstore_quota", &xs_quota, &num_xs_quota, 0)) {
+ b_info->xenstore_quota.num_quota = num_xs_quota;
+ b_info->xenstore_quota.quota = xcalloc(num_xs_quota, sizeof(* b_info->xenstore_quota.quota));
+
+ for (i = 0; i < num_xs_quota; i++) {
+ buf = xlu_cfg_get_listitem(xs_quota, i);
+ if (!buf) {
+ fprintf(stderr,
+ "xl: Can't get element %d in Xenstore quota list\n", i);
+ exit(1);
+ }
+ if (parse_xsquota_item(buf, b_info->xenstore_quota.quota + i))
+ exit(1);
+ }
+ }
+
libxl_domain_build_info_init_type(b_info, c_info->type);
if (b_info->type == LIBXL_DOMAIN_TYPE_PVH) {
--
2.53.0
^ permalink raw reply related [flat|nested] 20+ messages in thread
* Re: [PATCH v2 01/12] tools/libs/store: add get- and set-quota related functions
2026-03-20 15:01 ` [PATCH v2 01/12] tools/libs/store: add get- and set-quota related functions Juergen Gross
@ 2026-03-23 12:16 ` Anthony PERARD
0 siblings, 0 replies; 20+ messages in thread
From: Anthony PERARD @ 2026-03-23 12:16 UTC (permalink / raw)
To: Juergen Gross; +Cc: xen-devel, Julien Grall
On Fri, Mar 20, 2026 at 04:01:09PM +0100, Juergen Gross wrote:
> Add functions for getting and setting Xenstore quota to libxenstore:
>
> xs_get_quota_names(): get the names of the supported quota.
>
> xs_get_global_quota(): get the value of one global quota.
>
> xs_set_global_quota(): set the value of one global quota.
>
> xs_get_domain_quota(): get the value of one quota of a domain.
>
> xs_set_domain_quota(): set the value of one quota of a domain.
>
> Signed-off-by: Juergen Gross <jgross@suse.com>
Reviewed-by: Anthony PERARD <anthony.perard@vates.tech>
Thanks,
--
Anthony Perard | Vates XCP-ng Developer
XCP-ng & Xen Orchestra - Vates solutions
web: https://vates.tech
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH v2 02/12] tools/xenstored: add helper to parse domid
2026-03-20 15:01 ` [PATCH v2 02/12] tools/xenstored: add helper to parse domid Juergen Gross
@ 2026-03-23 12:30 ` Anthony PERARD
0 siblings, 0 replies; 20+ messages in thread
From: Anthony PERARD @ 2026-03-23 12:30 UTC (permalink / raw)
To: Juergen Gross; +Cc: xen-devel, Julien Grall
On Fri, Mar 20, 2026 at 04:01:10PM +0100, Juergen Gross wrote:
> Today a domid passed in by a command is parsed using atoi(). This
> will still "succeed" even with a domid like "x", resulting in "0" to
> be used instead.
>
> Use a common domid parser instead rejecting everything but integers
> in the range 0..65535 like specified in docs/misc/xenstore.txt.
>
> Signed-off-by: Juergen Gross <jgross@suse.com>
> ---
> V2:
> - new patch (kind of suggested by Anthony Perard)
Reviewed-by: Anthony PERARD <anthony.perard@vates.tech>
Thanks,
--
Anthony Perard | Vates XCP-ng Developer
XCP-ng & Xen Orchestra - Vates solutions
web: https://vates.tech
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH v2 05/12] tools/xenstored: add GLOBAL_QUOTA_DATA record for live update
2026-03-20 15:01 ` [PATCH v2 05/12] tools/xenstored: add GLOBAL_QUOTA_DATA record for live update Juergen Gross
@ 2026-03-23 12:50 ` Anthony PERARD
0 siblings, 0 replies; 20+ messages in thread
From: Anthony PERARD @ 2026-03-23 12:50 UTC (permalink / raw)
To: Juergen Gross; +Cc: xen-devel, Julien Grall
On Fri, Mar 20, 2026 at 04:01:13PM +0100, Juergen Gross wrote:
> Communicate the global quota settings via the GLOBAL_QUOTA_DATA
> record to the new Xenstore instance.
>
> This avoids to lose global quota settings done via xenstore-control.
>
> In theory it would be possible to drop any quota related command line
> parameters in the live update case, but they don't do any harm, as
> the record data is applied on top of the command line data.
>
> For soft-quota just prepend "soft-" to the quota name.
>
> Use sub-functions for building and analyzing the quota part of the
> migration stream, as they will be reused for per-domain quotas.
>
> Signed-off-by: Juergen Gross <jgross@suse.com>
> ---
> V2:
> - add macros for soft-quota name prefix and its length (Anthony Perard)
> - don't allow disabled quota in parse_quota_name() (Anthony Perard)
> - rename "len" to "rec_len" in dump_state_glb_quota() (Anthony Perard)
> - rename build_quota_data() parameter "name" to "names_buf" (Anthony Perard)
> - let get_quota_size() start with len 0 (Anthony Perard)
Reviewed-by: Anthony PERARD <anthony.perard@vates.tech>
Thanks,
--
Anthony Perard | Vates XCP-ng Developer
XCP-ng & Xen Orchestra - Vates solutions
web: https://vates.tech
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH v2 08/12] tools/xenstored: implement the GET/SET_QUOTA commands
2026-03-20 15:01 ` [PATCH v2 08/12] tools/xenstored: implement the GET/SET_QUOTA commands Juergen Gross
@ 2026-03-23 13:00 ` Anthony PERARD
0 siblings, 0 replies; 20+ messages in thread
From: Anthony PERARD @ 2026-03-23 13:00 UTC (permalink / raw)
To: Juergen Gross; +Cc: xen-devel, Julien Grall
On Fri, Mar 20, 2026 at 04:01:16PM +0100, Juergen Gross wrote:
> Add the implementation of the GET_QUOTA and SET_QUOTA wire commands.
>
> Signed-off-by: Juergen Gross <jgross@suse.com>
> ---
> V2:
> - refuse quota value Q_VAL_DISABLED (Anthony Perard)
> - use talloc_strdup() (Anthony Perard)
> - drop comments in domain.h (Anthony Perard)
Reviewed-by: Anthony PERARD <anthony.perard@vates.tech>
Thanks,
--
Anthony Perard | Vates XCP-ng Developer
XCP-ng & Xen Orchestra - Vates solutions
web: https://vates.tech
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH v2 09/12] tools/libxl: add functions for retrieving and setting xenstore quota
2026-03-20 15:01 ` [PATCH v2 09/12] tools/libxl: add functions for retrieving and setting xenstore quota Juergen Gross
@ 2026-03-23 13:19 ` Anthony PERARD
0 siblings, 0 replies; 20+ messages in thread
From: Anthony PERARD @ 2026-03-23 13:19 UTC (permalink / raw)
To: Juergen Gross; +Cc: xen-devel, Nick Rosbrook, George Dunlap
On Fri, Mar 20, 2026 at 04:01:17PM +0100, Juergen Gross wrote:
> Add some functions allowing to retrieve and set Xenstore quota (either
> global or domain specific).
>
> Signed-off-by: Juergen Gross <jgross@suse.com>
> Acked-by: Nick Rosbrook <enr0n@ubuntu.com> # golang stuff
> ---
> V2:
> - rename libxl functions to use "xs_quota" instead of "xsquota"
> (Anthony Perard)
> - rename the libxl_xs_quota_*_get() "q" parameter to "q_out"
> (Anthony Perard)
> - rename the struct xs_quota_set to xs_quota_list (Anthony Perard)
> - several style changes (Anthony Perard)
Reviewed-by: Anthony PERARD <anthony.perard@vates.tech>
Thanks,
--
Anthony Perard | Vates XCP-ng Developer
XCP-ng & Xen Orchestra - Vates solutions
web: https://vates.tech
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH v2 10/12] tools/libxl: add support for xenstore quota in domain_config
2026-03-20 15:01 ` [PATCH v2 10/12] tools/libxl: add support for xenstore quota in domain_config Juergen Gross
@ 2026-03-23 13:21 ` Anthony PERARD
0 siblings, 0 replies; 20+ messages in thread
From: Anthony PERARD @ 2026-03-23 13:21 UTC (permalink / raw)
To: Juergen Gross; +Cc: xen-devel, Nick Rosbrook, George Dunlap
On Fri, Mar 20, 2026 at 04:01:18PM +0100, Juergen Gross wrote:
> Add support for xenstore quota in the struct domain_config. Initially
> it will be used only for migration of a domain.
>
> Signed-off-by: Juergen Gross <jgross@suse.com>
> Acked-by: Nick Rosbrook <enr0n@ubuntu.com> # golang stuff
> ---
> V2:
> - use LOGED() for error logging (Anthony Perard)
> - mention additional struct member xenstore_quota in libxl.h
> (Anthony Perard)
Reviewed-by: Anthony PERARD <anthony.perard@vates.tech>
Thanks,
--
Anthony Perard | Vates XCP-ng Developer
XCP-ng & Xen Orchestra - Vates solutions
web: https://vates.tech
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH v2 11/12] tools/xl: add xl commands for xenstore quota operations
2026-03-20 15:01 ` [PATCH v2 11/12] tools/xl: add xl commands for xenstore quota operations Juergen Gross
@ 2026-03-23 14:08 ` Anthony PERARD
0 siblings, 0 replies; 20+ messages in thread
From: Anthony PERARD @ 2026-03-23 14:08 UTC (permalink / raw)
To: Juergen Gross; +Cc: xen-devel
On Fri, Mar 20, 2026 at 04:01:19PM +0100, Juergen Gross wrote:
> Add "xl xenstore-quota-get" and "xl xenstore-quota-set" commands for
> retrieving and setting global and per-domain Xenstore quota.
>
> Signed-off-by: Juergen Gross <jgross@suse.com>
> ---
> V2:
> - s/quota data/quotas/ in cmdtable (Anthony Perard)
> - test for quota value to fit into type (Anthony Perard)
> - use libxl_xs_quota_list_init() (Anthony Perard)
> - use xcalloc() (Anthony Perard)
Reviewed-by: Anthony PERARD <anthony.perard@vates.tech>
Thanks,
--
Anthony Perard | Vates XCP-ng Developer
XCP-ng & Xen Orchestra - Vates solutions
web: https://vates.tech
^ permalink raw reply [flat|nested] 20+ messages in thread
end of thread, other threads:[~2026-03-23 14:08 UTC | newest]
Thread overview: 20+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-20 15:01 [PATCH v2 00/12] tools: add support for per-domain xenstore quota Juergen Gross
2026-03-20 15:01 ` [PATCH v2 01/12] tools/libs/store: add get- and set-quota related functions Juergen Gross
2026-03-23 12:16 ` Anthony PERARD
2026-03-20 15:01 ` [PATCH v2 02/12] tools/xenstored: add helper to parse domid Juergen Gross
2026-03-23 12:30 ` Anthony PERARD
2026-03-20 15:01 ` [PATCH v2 03/12] tools/xenstored: add central quota check functions Juergen Gross
2026-03-20 15:01 ` [PATCH v2 04/12] tools/xenstored: rework hard_quotas and soft_quotas arrays Juergen Gross
2026-03-20 15:01 ` [PATCH v2 05/12] tools/xenstored: add GLOBAL_QUOTA_DATA record for live update Juergen Gross
2026-03-23 12:50 ` Anthony PERARD
2026-03-20 15:01 ` [PATCH v2 06/12] tools/xenstored: split acc[] array in struct domain Juergen Gross
2026-03-20 15:01 ` [PATCH v2 07/12] tools/xenstored: use per-domain quota settings Juergen Gross
2026-03-20 15:01 ` [PATCH v2 08/12] tools/xenstored: implement the GET/SET_QUOTA commands Juergen Gross
2026-03-23 13:00 ` Anthony PERARD
2026-03-20 15:01 ` [PATCH v2 09/12] tools/libxl: add functions for retrieving and setting xenstore quota Juergen Gross
2026-03-23 13:19 ` Anthony PERARD
2026-03-20 15:01 ` [PATCH v2 10/12] tools/libxl: add support for xenstore quota in domain_config Juergen Gross
2026-03-23 13:21 ` Anthony PERARD
2026-03-20 15:01 ` [PATCH v2 11/12] tools/xl: add xl commands for xenstore quota operations Juergen Gross
2026-03-23 14:08 ` Anthony PERARD
2026-03-20 15:01 ` [PATCH v2 12/12] tools/xl: add support for xenstore quota setting via domain config Juergen Gross
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.