* [PATCH 01/11] tools/libs/store: add get- and set-quota related functions
2026-03-05 13:51 [PATCH 00/11] tools: add support for per-domain xenstore quota Juergen Gross
@ 2026-03-05 13:51 ` Juergen Gross
2026-03-13 14:23 ` Anthony PERARD
2026-03-05 13:51 ` [PATCH 02/11] tools/xenstored: add central quota check functions Juergen Gross
` (9 subsequent siblings)
10 siblings, 1 reply; 43+ messages in thread
From: Juergen Gross @ 2026-03-05 13:51 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>
---
tools/include/xenstore.h | 19 ++++++
tools/libs/store/Makefile | 2 +-
tools/libs/store/libxenstore.map | 8 +++
tools/libs/store/xs.c | 111 +++++++++++++++++++++++++++++++
4 files changed, 139 insertions(+), 1 deletion(-)
diff --git a/tools/include/xenstore.h b/tools/include/xenstore.h
index 423422dc50..6b661e5895 100644
--- a/tools/include/xenstore.h
+++ b/tools/include/xenstore.h
@@ -277,6 +277,25 @@ 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. */
+char **xs_get_quota_names(struct xs_handle *h, unsigned int *num);
+
+/* Get the value of one global quota. */
+bool xs_get_global_quota(struct xs_handle *h, char *quota,
+ unsigned int *value);
+
+/* Set the value of one global quota. */
+bool xs_set_global_quota(struct xs_handle *h, char *quota,
+ unsigned int value);
+
+/* Get the value of one domain quota. */
+bool xs_get_domain_quota(struct xs_handle *h, unsigned int domid,
+ char *quota, unsigned int *value);
+
+/* Set the value of one domain quota. */
+bool xs_set_domain_quota(struct xs_handle *h, unsigned int domid,
+ 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..dda37f7526 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));
}
+char **xs_get_quota_names(struct xs_handle *h, unsigned int *num)
+{
+ struct xsd_sockmsg msg = { .type = XS_GET_QUOTA };
+ struct iovec iov[1];
+ 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, 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 = 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, 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 = 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,
+ 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 = 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,
+ 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 = 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] 43+ messages in thread* Re: [PATCH 01/11] tools/libs/store: add get- and set-quota related functions
2026-03-05 13:51 ` [PATCH 01/11] tools/libs/store: add get- and set-quota related functions Juergen Gross
@ 2026-03-13 14:23 ` Anthony PERARD
2026-03-16 7:51 ` Jürgen Groß
0 siblings, 1 reply; 43+ messages in thread
From: Anthony PERARD @ 2026-03-13 14:23 UTC (permalink / raw)
To: Juergen Gross; +Cc: xen-devel, Julien Grall
On Thu, Mar 05, 2026 at 02:51:58PM +0100, Juergen Gross wrote:
> diff --git a/tools/include/xenstore.h b/tools/include/xenstore.h
> index 423422dc50..6b661e5895 100644
> --- a/tools/include/xenstore.h
> +++ b/tools/include/xenstore.h
> @@ -277,6 +277,25 @@ 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. */
> +char **xs_get_quota_names(struct xs_handle *h, unsigned int *num);
> +
> +/* Get the value of one global quota. */
> +bool xs_get_global_quota(struct xs_handle *h, char *quota,
> + unsigned int *value);
> +
> +/* Set the value of one global quota. */
> +bool xs_set_global_quota(struct xs_handle *h, char *quota,
> + unsigned int value);
> +
> +/* Get the value of one domain quota. */
> +bool xs_get_domain_quota(struct xs_handle *h, unsigned int domid,
> + char *quota, unsigned int *value);
> +
> +/* Set the value of one domain quota. */
> +bool xs_set_domain_quota(struct xs_handle *h, unsigned int domid,
> + char *quota, unsigned int value);
> +
Do you think all those new prototype could get a bit more descriptions?
Which parameter are actually output (and not input), what does it mean
to return false, do they set errno, is there something to do with the
return value of xs_get_quota_names?
For output arguments, libxl have a convention (well at least a mention
in the coding style) to suffix argument names with `_r` or `_out`.
For the strings, could we use `const char *` instead of non-const one?
> diff --git a/tools/libs/store/xs.c b/tools/libs/store/xs.c
> index 8f4b90a3cf..dda37f7526 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));
> }
>
> +char **xs_get_quota_names(struct xs_handle *h, unsigned int *num)
> +{
> + struct xsd_sockmsg msg = { .type = XS_GET_QUOTA };
> + struct iovec iov[1];
> + 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) {
If `c` is NULL, it's likely that this is the last iteration of the `for`
loop. But just in case, should we prevent the code from doing another
round and prevent `strchr(NULL, ' ')`? (Or just check that `c` is !NULL,
and let the loop finish set NULL for the remaining slot in `quota`)
> + *c = 0;
> + c++;
> + }
> + }
> +
> + return quota;
> +}
> +
> +bool xs_get_global_quota(struct xs_handle *h, 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 = 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, char *quota,
> + unsigned int value)
> +{
> + struct xsd_sockmsg msg = { .type = XS_SET_QUOTA };
> + char val_str[MAX_STRLEN(value)];
MAX_STRLEN doesn't have a great name, I wounder what is was :-). And
it's not about a maximum size of payload that could go on xs wire or
something, it's actually the maximum string size that can take a
numerical value, when converted to charaters.
The rest looks fine to me.
Thanks,
--
Anthony Perard | Vates XCP-ng Developer
XCP-ng & Xen Orchestra - Vates solutions
web: https://vates.tech
^ permalink raw reply [flat|nested] 43+ messages in thread* Re: [PATCH 01/11] tools/libs/store: add get- and set-quota related functions
2026-03-13 14:23 ` Anthony PERARD
@ 2026-03-16 7:51 ` Jürgen Groß
2026-03-19 13:31 ` Anthony PERARD
0 siblings, 1 reply; 43+ messages in thread
From: Jürgen Groß @ 2026-03-16 7:51 UTC (permalink / raw)
To: Anthony PERARD; +Cc: xen-devel, Julien Grall
[-- Attachment #1.1.1: Type: text/plain, Size: 4661 bytes --]
On 13.03.26 15:23, Anthony PERARD wrote:
> On Thu, Mar 05, 2026 at 02:51:58PM +0100, Juergen Gross wrote:
>> diff --git a/tools/include/xenstore.h b/tools/include/xenstore.h
>> index 423422dc50..6b661e5895 100644
>> --- a/tools/include/xenstore.h
>> +++ b/tools/include/xenstore.h
>> @@ -277,6 +277,25 @@ 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. */
>> +char **xs_get_quota_names(struct xs_handle *h, unsigned int *num);
>> +
>> +/* Get the value of one global quota. */
>> +bool xs_get_global_quota(struct xs_handle *h, char *quota,
>> + unsigned int *value);
>> +
>> +/* Set the value of one global quota. */
>> +bool xs_set_global_quota(struct xs_handle *h, char *quota,
>> + unsigned int value);
>> +
>> +/* Get the value of one domain quota. */
>> +bool xs_get_domain_quota(struct xs_handle *h, unsigned int domid,
>> + char *quota, unsigned int *value);
>> +
>> +/* Set the value of one domain quota. */
>> +bool xs_set_domain_quota(struct xs_handle *h, unsigned int domid,
>> + char *quota, unsigned int value);
>> +
>
> Do you think all those new prototype could get a bit more descriptions?
> Which parameter are actually output (and not input), what does it mean
> to return false, do they set errno, is there something to do with the
> return value of xs_get_quota_names?
Oh yes, of course.
> For output arguments, libxl have a convention (well at least a mention
> in the coding style) to suffix argument names with `_r` or `_out`.
Hmm, I don't think I'd like to change style now. This is not libxl after
all.
> For the strings, could we use `const char *` instead of non-const one?
Yes.
>
>> diff --git a/tools/libs/store/xs.c b/tools/libs/store/xs.c
>> index 8f4b90a3cf..dda37f7526 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));
>> }
>>
>> +char **xs_get_quota_names(struct xs_handle *h, unsigned int *num)
>> +{
>> + struct xsd_sockmsg msg = { .type = XS_GET_QUOTA };
>> + struct iovec iov[1];
>> + 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) {
>
> If `c` is NULL, it's likely that this is the last iteration of the `for`
> loop. But just in case, should we prevent the code from doing another
> round and prevent `strchr(NULL, ' ')`? (Or just check that `c` is !NULL,
> and let the loop finish set NULL for the remaining slot in `quota`)
Not sure this is really needed. *num is set just a few lines further up
using the same way to count the number of strings. Do we really need to do
consistency checks of intermediate results in such a short function?
>
>> + *c = 0;
>> + c++;
>> + }
>> + }
>> +
>> + return quota;
>> +}
>> +
>> +bool xs_get_global_quota(struct xs_handle *h, 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 = 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, char *quota,
>> + unsigned int value)
>> +{
>> + struct xsd_sockmsg msg = { .type = XS_SET_QUOTA };
>> + char val_str[MAX_STRLEN(value)];
>
> MAX_STRLEN doesn't have a great name, I wounder what is was :-). And
> it's not about a maximum size of payload that could go on xs wire or
> something, it's actually the maximum string size that can take a
> numerical value, when converted to charaters.
Unfortunately the MAX_STRLEN() macro is defined in a public header file.
I could define another macro with a different name doing the same and
use that here, but MAX_STRLEN() would still be there.
What is your preference?
Juergen
[-- Attachment #1.1.2: OpenPGP public key --]
[-- Type: application/pgp-keys, Size: 3743 bytes --]
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 495 bytes --]
^ permalink raw reply [flat|nested] 43+ messages in thread* Re: [PATCH 01/11] tools/libs/store: add get- and set-quota related functions
2026-03-16 7:51 ` Jürgen Groß
@ 2026-03-19 13:31 ` Anthony PERARD
0 siblings, 0 replies; 43+ messages in thread
From: Anthony PERARD @ 2026-03-19 13:31 UTC (permalink / raw)
To: Jürgen Groß; +Cc: xen-devel, Julien Grall
On Mon, Mar 16, 2026 at 08:51:21AM +0100, Jürgen Groß wrote:
> On 13.03.26 15:23, Anthony PERARD wrote:
> > On Thu, Mar 05, 2026 at 02:51:58PM +0100, Juergen Gross wrote:
> > > diff --git a/tools/libs/store/xs.c b/tools/libs/store/xs.c
> > > index 8f4b90a3cf..dda37f7526 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));
> > > }
> > > +char **xs_get_quota_names(struct xs_handle *h, unsigned int *num)
> > > +{
> > > + struct xsd_sockmsg msg = { .type = XS_GET_QUOTA };
> > > + struct iovec iov[1];
> > > + 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) {
> >
> > If `c` is NULL, it's likely that this is the last iteration of the `for`
> > loop. But just in case, should we prevent the code from doing another
> > round and prevent `strchr(NULL, ' ')`? (Or just check that `c` is !NULL,
> > and let the loop finish set NULL for the remaining slot in `quota`)
>
> Not sure this is really needed. *num is set just a few lines further up
> using the same way to count the number of strings. Do we really need to do
> consistency checks of intermediate results in such a short function?
I guess it's ok.
> >
> > > + *c = 0;
> > > + c++;
> > > + }
> > > + }
> > > +
> > > + return quota;
> > > +}
> > > +
> > > +bool xs_get_global_quota(struct xs_handle *h, 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 = 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, char *quota,
> > > + unsigned int value)
> > > +{
> > > + struct xsd_sockmsg msg = { .type = XS_SET_QUOTA };
> > > + char val_str[MAX_STRLEN(value)];
> >
> > MAX_STRLEN doesn't have a great name, I wounder what is was :-). And
> > it's not about a maximum size of payload that could go on xs wire or
> > something, it's actually the maximum string size that can take a
> > numerical value, when converted to charaters.
>
> Unfortunately the MAX_STRLEN() macro is defined in a public header file.
> I could define another macro with a different name doing the same and
> use that here, but MAX_STRLEN() would still be there.
>
> What is your preference?
Well, it's probably better to leave it like that. Having two different
macro names might be more confusing than useful. And I see that the
macro is only defined three time in the repo. Next time I'll stumble on
MAX_STRLEN, I'll know what it mean.
Cheers,,
--
Anthony Perard | Vates XCP-ng Developer
XCP-ng & Xen Orchestra - Vates solutions
web: https://vates.tech
^ permalink raw reply [flat|nested] 43+ messages in thread
* [PATCH 02/11] tools/xenstored: add central quota check functions
2026-03-05 13:51 [PATCH 00/11] tools: add support for per-domain xenstore quota Juergen Gross
2026-03-05 13:51 ` [PATCH 01/11] tools/libs/store: add get- and set-quota related functions Juergen Gross
@ 2026-03-05 13:51 ` Juergen Gross
2026-03-13 15:01 ` Anthony PERARD
2026-03-13 21:22 ` Jason Andryuk
2026-03-05 13:52 ` [PATCH 03/11] tools/xenstored: rework hard_quotas and soft_quotas arrays Juergen Gross
` (8 subsequent siblings)
10 siblings, 2 replies; 43+ messages in thread
From: Juergen Gross @ 2026-03-05 13:51 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().
Signed-off-by: Juergen Gross <jgross@suse.com>
---
tools/xenstored/core.c | 4 +-
tools/xenstored/domain.c | 74 +++++++++++++++++------------------
tools/xenstored/domain.h | 7 ++--
tools/xenstored/transaction.c | 2 +-
tools/xenstored/watch.c | 4 +-
5 files changed, 43 insertions(+), 48 deletions(-)
diff --git a/tools/xenstored/core.c b/tools/xenstored/core.c
index d509736c32..5a4bf3e302 100644
--- a/tools/xenstored/core.c
+++ b/tools/xenstored/core.c
@@ -1538,7 +1538,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_check_quota_add(conn->domain, ACC_NODES, 1)) {
ret = ENOSPC;
goto err;
}
@@ -2320,7 +2320,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 e453b3061f..1df9265ad5 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_check_quota_val(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_check_quota_add(struct domain *d, enum accitem what, int add)
+{
+ if (add < 0 || !d)
+ return false;
+
+ return domain_check_quota_val(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_check_quota_add(domain, ACC_OUTST, 0))
return false;
- if (domain->acc[ACC_MEM].val >= hard_quotas[ACC_MEM].val &&
- hard_quotas[ACC_MEM].val)
+ if (domain_check_quota_add(domain, ACC_MEM, 0))
return false;
}
@@ -904,16 +922,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_check_quota_add(d, ACC_NODES,
+ cd->acc[ACC_NODES]))
+ return ENOSPC;
}
}
@@ -1732,7 +1754,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_check_quota_val(conn->domain, what, val))
return true;
domain_acc_valid_max(conn->domain, what, val);
@@ -1752,21 +1774,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)
@@ -1781,7 +1791,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_check_quota_val(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",
@@ -1857,13 +1867,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);
@@ -1884,13 +1887,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..b229f6f4e0 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,8 @@ 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_check_quota_add(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..ccf93a1132 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_check_quota_add(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..36e4d33f22 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_check_quota_add(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] 43+ messages in thread* Re: [PATCH 02/11] tools/xenstored: add central quota check functions
2026-03-05 13:51 ` [PATCH 02/11] tools/xenstored: add central quota check functions Juergen Gross
@ 2026-03-13 15:01 ` Anthony PERARD
2026-03-16 7:53 ` Jürgen Groß
2026-03-13 21:22 ` Jason Andryuk
1 sibling, 1 reply; 43+ messages in thread
From: Anthony PERARD @ 2026-03-13 15:01 UTC (permalink / raw)
To: Juergen Gross; +Cc: xen-devel, Julien Grall
On Thu, Mar 05, 2026 at 02:51:59PM +0100, Juergen Gross wrote:
> 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().
This patch changes the return value of do_watch(), from E2BIG to ENOSPC,
but that's not mention.
> diff --git a/tools/xenstored/domain.c b/tools/xenstored/domain.c
> index e453b3061f..1df9265ad5 100644
> --- a/tools/xenstored/domain.c
> +++ b/tools/xenstored/domain.c
> @@ -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_check_quota_add(domain, ACC_OUTST, 0))
Is this change fixing a potential bug? As now we check if there's a
quota for OUTST.
> return false;
> - if (domain->acc[ACC_MEM].val >= hard_quotas[ACC_MEM].val &&
> - hard_quotas[ACC_MEM].val)
> + if (domain_check_quota_add(domain, ACC_MEM, 0))
> return false;
> }
>
> diff --git a/tools/xenstored/watch.c b/tools/xenstored/watch.c
> index b66a9f1a39..36e4d33f22 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_check_quota_add(conn->domain, ACC_WATCH, 1))
> + return ENOSPC;
Change of return value here not mentionned, even if it now aligned with
the value returned in other places.
>
> watch = add_watch(conn, vec[0], vec[1], relative, false);
> if (!watch)
Patch looks fine to me:
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] 43+ messages in thread* Re: [PATCH 02/11] tools/xenstored: add central quota check functions
2026-03-13 15:01 ` Anthony PERARD
@ 2026-03-16 7:53 ` Jürgen Groß
0 siblings, 0 replies; 43+ messages in thread
From: Jürgen Groß @ 2026-03-16 7:53 UTC (permalink / raw)
To: Anthony PERARD; +Cc: xen-devel, Julien Grall
[-- Attachment #1.1.1: Type: text/plain, Size: 2239 bytes --]
On 13.03.26 16:01, Anthony PERARD wrote:
> On Thu, Mar 05, 2026 at 02:51:59PM +0100, Juergen Gross wrote:
>> 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().
>
> This patch changes the return value of do_watch(), from E2BIG to ENOSPC,
> but that's not mention.
I can add a remark to the commit message.
>
>> diff --git a/tools/xenstored/domain.c b/tools/xenstored/domain.c
>> index e453b3061f..1df9265ad5 100644
>> --- a/tools/xenstored/domain.c
>> +++ b/tools/xenstored/domain.c
>> @@ -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_check_quota_add(domain, ACC_OUTST, 0))
>
> Is this change fixing a potential bug? As now we check if there's a
> quota for OUTST.
This is just changing how the quota is tested.
>
>> return false;
>> - if (domain->acc[ACC_MEM].val >= hard_quotas[ACC_MEM].val &&
>> - hard_quotas[ACC_MEM].val)
>> + if (domain_check_quota_add(domain, ACC_MEM, 0))
>> return false;
>> }
>>
>> diff --git a/tools/xenstored/watch.c b/tools/xenstored/watch.c
>> index b66a9f1a39..36e4d33f22 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_check_quota_add(conn->domain, ACC_WATCH, 1))
>> + return ENOSPC;
>
> Change of return value here not mentionned, even if it now aligned with
> the value returned in other places.
>
>>
>> watch = add_watch(conn, vec[0], vec[1], relative, false);
>> if (!watch)
>
> Patch looks fine to me:
> Reviewed-by: Anthony PERARD <anthony.perard@vates.tech>
Thanks
Juergen
[-- Attachment #1.1.2: OpenPGP public key --]
[-- Type: application/pgp-keys, Size: 3743 bytes --]
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 495 bytes --]
^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: [PATCH 02/11] tools/xenstored: add central quota check functions
2026-03-05 13:51 ` [PATCH 02/11] tools/xenstored: add central quota check functions Juergen Gross
2026-03-13 15:01 ` Anthony PERARD
@ 2026-03-13 21:22 ` Jason Andryuk
2026-03-16 8:18 ` Jürgen Groß
1 sibling, 1 reply; 43+ messages in thread
From: Jason Andryuk @ 2026-03-13 21:22 UTC (permalink / raw)
To: Juergen Gross, xen-devel; +Cc: Julien Grall, Anthony PERARD
On 2026-03-05 08:51, Juergen Gross wrote:
> 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().
>
> Signed-off-by: Juergen Gross <jgross@suse.com>
> diff --git a/tools/xenstored/domain.c b/tools/xenstored/domain.c
> index e453b3061f..1df9265ad5 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_check_quota_val(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;
Personally, I don't like the naming of *check* where the "good" return
is false. That seems backwards from what I expect. So I'd suggest
either flipping the return value or renaming. domain_quota_fail() or
something?
> +}
> +
> +bool domain_check_quota_add(struct domain *d, enum accitem what, int add)
> +{
> + if (add < 0 || !d)
> + return false;
> +
> + return domain_check_quota_val(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);
As an example, here "good" is true.
I see Anthony already gave an R-b, so just consider it as a suggestion.
Thanks,
Jason
^ permalink raw reply [flat|nested] 43+ messages in thread* Re: [PATCH 02/11] tools/xenstored: add central quota check functions
2026-03-13 21:22 ` Jason Andryuk
@ 2026-03-16 8:18 ` Jürgen Groß
0 siblings, 0 replies; 43+ messages in thread
From: Jürgen Groß @ 2026-03-16 8:18 UTC (permalink / raw)
To: Jason Andryuk, xen-devel; +Cc: Julien Grall, Anthony PERARD
[-- Attachment #1.1.1: Type: text/plain, Size: 1418 bytes --]
On 13.03.26 22:22, Jason Andryuk wrote:
> On 2026-03-05 08:51, Juergen Gross wrote:
>> 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().
>>
>> Signed-off-by: Juergen Gross <jgross@suse.com>
>
>> diff --git a/tools/xenstored/domain.c b/tools/xenstored/domain.c
>> index e453b3061f..1df9265ad5 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_check_quota_val(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;
>
> Personally, I don't like the naming of *check* where the "good" return is
> false. That seems backwards from what I expect. So I'd suggest either flipping
> the return value or renaming. domain_quota_fail() or something?
I'll go with domain_quota_exceeded().
Thanks,
Juergen
[-- Attachment #1.1.2: OpenPGP public key --]
[-- Type: application/pgp-keys, Size: 3743 bytes --]
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 491 bytes --]
^ permalink raw reply [flat|nested] 43+ messages in thread
* [PATCH 03/11] tools/xenstored: rework hard_quotas and soft_quotas arrays
2026-03-05 13:51 [PATCH 00/11] tools: add support for per-domain xenstore quota Juergen Gross
2026-03-05 13:51 ` [PATCH 01/11] tools/libs/store: add get- and set-quota related functions Juergen Gross
2026-03-05 13:51 ` [PATCH 02/11] tools/xenstored: add central quota check functions Juergen Gross
@ 2026-03-05 13:52 ` Juergen Gross
2026-03-13 16:09 ` Anthony PERARD
2026-03-05 13:52 ` [PATCH 04/11] tools/xenstored: add GLOBAL_QUOTA_DATA record for live update Juergen Gross
` (7 subsequent siblings)
10 siblings, 1 reply; 43+ messages in thread
From: Juergen Gross @ 2026-03-05 13:52 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>
---
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 5a4bf3e302..8a06b35808 100644
--- a/tools/xenstored/core.c
+++ b/tools/xenstored/core.c
@@ -2613,10 +2613,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;
@@ -2625,8 +2624,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;
}
}
@@ -2634,6 +2634,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",
@@ -2687,7 +2692,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;
@@ -2699,10 +2704,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;
@@ -2715,22 +2720,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 1df9265ad5..acdcaa769e 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_check_quota_val(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;
@@ -765,10 +770,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;
@@ -789,11 +794,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;
}
@@ -1600,12 +1604,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)
@@ -1742,7 +1746,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);
@@ -1802,21 +1806,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 b229f6f4e0..a6db358fdc 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] 43+ messages in thread* Re: [PATCH 03/11] tools/xenstored: rework hard_quotas and soft_quotas arrays
2026-03-05 13:52 ` [PATCH 03/11] tools/xenstored: rework hard_quotas and soft_quotas arrays Juergen Gross
@ 2026-03-13 16:09 ` Anthony PERARD
0 siblings, 0 replies; 43+ messages in thread
From: Anthony PERARD @ 2026-03-13 16:09 UTC (permalink / raw)
To: Juergen Gross; +Cc: xen-devel, Julien Grall
On Thu, Mar 05, 2026 at 02:52:00PM +0100, Juergen Gross wrote:
> 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>
Thanks,
--
Anthony Perard | Vates XCP-ng Developer
XCP-ng & Xen Orchestra - Vates solutions
web: https://vates.tech
^ permalink raw reply [flat|nested] 43+ messages in thread
* [PATCH 04/11] tools/xenstored: add GLOBAL_QUOTA_DATA record for live update
2026-03-05 13:51 [PATCH 00/11] tools: add support for per-domain xenstore quota Juergen Gross
` (2 preceding siblings ...)
2026-03-05 13:52 ` [PATCH 03/11] tools/xenstored: rework hard_quotas and soft_quotas arrays Juergen Gross
@ 2026-03-05 13:52 ` Juergen Gross
2026-03-13 17:08 ` Anthony PERARD
2026-03-05 13:52 ` [PATCH 05/11] tools/xenstored: split acc[] array in struct domain Juergen Gross
` (6 subsequent siblings)
10 siblings, 1 reply; 43+ messages in thread
From: Juergen Gross @ 2026-03-05 13:52 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>
---
tools/xenstored/domain.c | 123 +++++++++++++++++++++++++++++++++++++++
tools/xenstored/domain.h | 2 +
tools/xenstored/lu.c | 6 ++
3 files changed, 131 insertions(+)
diff --git a/tools/xenstored/domain.c b/tools/xenstored/domain.c
index acdcaa769e..694ae58973 100644
--- a/tools/xenstored/domain.c
+++ b/tools/xenstored/domain.c
@@ -1332,6 +1332,27 @@ 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-", 5)) {
+ *idx = Q_IDX_HARD;
+ } else {
+ *idx = Q_IDX_SOFT;
+ name += 5;
+ }
+ for (q = 0; q < ACC_N; q++) {
+ if (quota_adm[q].name && !strcmp(quota_adm[q].name, name)) {
+ *qidx = q;
+ return false;
+ }
+ }
+
+ return true;
+}
+
static int close_xgt_handle(void *_handle)
{
xengnttab_close(*(xengnttab_handle **)_handle);
@@ -2001,6 +2022,61 @@ void read_state_connection(const void *ctx, const void *state)
}
}
+static unsigned int get_quota_size(struct quota *quota, unsigned int *len)
+{
+ 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) {
+ 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) + 5 + 1;
+ }
+ }
+
+ return n;
+}
+
+static void build_quota_data(struct quota *quota, uint32_t *val, char *name)
+{
+ 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(name, quota_adm[q].name);
+ name += strlen(name) + 1;
+ }
+ if (quota[q].val[Q_IDX_SOFT] != Q_VAL_DISABLED) {
+ val[n++] = quota[q].val[Q_IDX_SOFT];
+ strcpy(name, "soft-");
+ strcpy(name + 5, quota_adm[q].name);
+ name += strlen(name) + 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;
@@ -2049,6 +2125,53 @@ 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 len = sizeof(*glb);
+ size_t ret;
+
+ n_quota = get_quota_size(quotas, &len);
+ len += n_quota * sizeof(glb->quota_val[0]);
+ len = ROUNDUP(len, 3);
+
+ record = talloc_size(NULL, len + sizeof(*head));
+ if (!record)
+ return "Dump global quota allocation error";
+
+ head = record;
+ head->type = XS_STATE_TYPE_GLB_QUOTA;
+ head->length = 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, 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 a6db358fdc..62ce3b3166 100644
--- a/tools/xenstored/domain.h
+++ b/tools/xenstored/domain.h
@@ -173,10 +173,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] 43+ messages in thread* Re: [PATCH 04/11] tools/xenstored: add GLOBAL_QUOTA_DATA record for live update
2026-03-05 13:52 ` [PATCH 04/11] tools/xenstored: add GLOBAL_QUOTA_DATA record for live update Juergen Gross
@ 2026-03-13 17:08 ` Anthony PERARD
2026-03-16 8:15 ` Jürgen Groß
0 siblings, 1 reply; 43+ messages in thread
From: Anthony PERARD @ 2026-03-13 17:08 UTC (permalink / raw)
To: Juergen Gross; +Cc: xen-devel, Julien Grall
On Thu, Mar 05, 2026 at 02:52:01PM +0100, Juergen Gross wrote:
> diff --git a/tools/xenstored/domain.c b/tools/xenstored/domain.c
> index acdcaa769e..694ae58973 100644
> --- a/tools/xenstored/domain.c
> +++ b/tools/xenstored/domain.c
> @@ -1332,6 +1332,27 @@ 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;
What do you think of using something like:
const char soft_prefix[] = "soft-";
const size_t soft_prefix_len = sizeof(soft_prefix) - 1;
to explain the `5`, here and in e.g. the function build_quota_data() ?
We used this in libxl in one place:
https://elixir.bootlin.com/xen/v4.21.0/source/tools/libs/light/libxl_qmp.c#L1288
But it's fine to leave it like that, as the '5's are close enought to
the prefix that we can guess easly enough.
> +
> + if (strncmp(name, "soft-", 5)) {
> + *idx = Q_IDX_HARD;
> + } else {
> + *idx = Q_IDX_SOFT;
> + name += 5;
> + }
> + for (q = 0; q < ACC_N; q++) {
> + if (quota_adm[q].name && !strcmp(quota_adm[q].name, name)) {
> + *qidx = q;
> + return false;
> + }
> + }
> +
> + return true;
> +}
> +
> static int close_xgt_handle(void *_handle)
> {
> xengnttab_close(*(xengnttab_handle **)_handle);
> @@ -2001,6 +2022,61 @@ void read_state_connection(const void *ctx, const void *state)
> }
> }
>
> +static unsigned int get_quota_size(struct quota *quota, unsigned int *len)
> +{
> + 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) {
> + 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) + 5 + 1;
The value 5 here isn't explained. A comment would be nice.
> + }
> + }
> +
> + return n;
> +}
> +
> +static void build_quota_data(struct quota *quota, uint32_t *val, char *name)
I guess we will need a leap of faith to trust that `val` is big enough,
after finding out that it's actually an output of multiple values, and
not an input of a single value.
And `name` seems to also be an output, and this is actually impossible
to guess from the prototype.
> +{
> + 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(name, quota_adm[q].name);
> + name += strlen(name) + 1;
> + }
> + if (quota[q].val[Q_IDX_SOFT] != Q_VAL_DISABLED) {
> + val[n++] = quota[q].val[Q_IDX_SOFT];
> + strcpy(name, "soft-");
> + strcpy(name + 5, quota_adm[q].name);
> + name += strlen(name) + 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;
So for `val`, we have a size. But, we don't have a size for `name`, are
we sure that it's safe to keep reading past `NUL` characters ? Is the
size of name available somewhere?
> + }
> +}
> +
> static int dump_state_domain(const void *k, void *v, void *arg)
> {
> struct domain *domain = v;
> @@ -2049,6 +2125,53 @@ 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 len = sizeof(*glb);
> + size_t ret;
> +
> + n_quota = get_quota_size(quotas, &len);
So, get_quota_size is actually an "add" the size to this variable, and
not "store" the size in this variable. That's not confusing at all.
> + len += n_quota * sizeof(glb->quota_val[0]);
> + len = ROUNDUP(len, 3);
> +
> + record = talloc_size(NULL, len + sizeof(*head));
> + if (!record)
> + return "Dump global quota allocation error";
> +
> + head = record;
> + head->type = XS_STATE_TYPE_GLB_QUOTA;
> + head->length = len;
> +
> + glb = (struct xs_state_glb_quota *)(head + 1);
> + glb->n_dom_quota = n_quota;
> + glb->n_glob_quota = 0;
Shouldn't `n_quota` be assigned to `n_glob_quota` instead? We don't have
per-domain quota yet, and only have global quota, right?
> +
> + build_quota_data(quotas, glb->quota_val,
> + (char *)(glb->quota_val + n_quota));
> +
> + ret = fwrite(record, 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);
> +}
> +
Thanks,
--
Anthony Perard | Vates XCP-ng Developer
XCP-ng & Xen Orchestra - Vates solutions
web: https://vates.tech
^ permalink raw reply [flat|nested] 43+ messages in thread* Re: [PATCH 04/11] tools/xenstored: add GLOBAL_QUOTA_DATA record for live update
2026-03-13 17:08 ` Anthony PERARD
@ 2026-03-16 8:15 ` Jürgen Groß
2026-03-18 12:16 ` Juergen Gross
2026-03-19 15:59 ` Anthony PERARD
0 siblings, 2 replies; 43+ messages in thread
From: Jürgen Groß @ 2026-03-16 8:15 UTC (permalink / raw)
To: Anthony PERARD; +Cc: xen-devel, Julien Grall
[-- Attachment #1.1.1: Type: text/plain, Size: 6391 bytes --]
On 13.03.26 18:08, Anthony PERARD wrote:
> On Thu, Mar 05, 2026 at 02:52:01PM +0100, Juergen Gross wrote:
>> diff --git a/tools/xenstored/domain.c b/tools/xenstored/domain.c
>> index acdcaa769e..694ae58973 100644
>> --- a/tools/xenstored/domain.c
>> +++ b/tools/xenstored/domain.c
>> @@ -1332,6 +1332,27 @@ 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;
>
> What do you think of using something like:
> const char soft_prefix[] = "soft-";
> const size_t soft_prefix_len = sizeof(soft_prefix) - 1;
> to explain the `5`, here and in e.g. the function build_quota_data() ?
> We used this in libxl in one place:
> https://elixir.bootlin.com/xen/v4.21.0/source/tools/libs/light/libxl_qmp.c#L1288
>
> But it's fine to leave it like that, as the '5's are close enought to
> the prefix that we can guess easly enough.
I can change it, but I'd prefer to use macros for that purpose.
>
>> +
>> + if (strncmp(name, "soft-", 5)) {
>> + *idx = Q_IDX_HARD;
>> + } else {
>> + *idx = Q_IDX_SOFT;
>> + name += 5;
>> + }
>> + for (q = 0; q < ACC_N; q++) {
>> + if (quota_adm[q].name && !strcmp(quota_adm[q].name, name)) {
>> + *qidx = q;
>> + return false;
>> + }
>> + }
>> +
>> + return true;
>> +}
>> +
>> static int close_xgt_handle(void *_handle)
>> {
>> xengnttab_close(*(xengnttab_handle **)_handle);
>> @@ -2001,6 +2022,61 @@ void read_state_connection(const void *ctx, const void *state)
>> }
>> }
>>
>> +static unsigned int get_quota_size(struct quota *quota, unsigned int *len)
>> +{
>> + 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) {
>> + 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) + 5 + 1;
>
> The value 5 here isn't explained. A comment would be nice.
Using the macro mentioned above will make it more descriptive.
>
>> + }
>> + }
>> +
>> + return n;
>> +}
>> +
>> +static void build_quota_data(struct quota *quota, uint32_t *val, char *name)
>
> I guess we will need a leap of faith to trust that `val` is big enough,
> after finding out that it's actually an output of multiple values, and
> not an input of a single value.
That's what get_quota_size() is calculating.
>
> And `name` seems to also be an output, and this is actually impossible
> to guess from the prototype.
True. What about names?
>
>> +{
>> + 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(name, quota_adm[q].name);
>> + name += strlen(name) + 1;
>> + }
>> + if (quota[q].val[Q_IDX_SOFT] != Q_VAL_DISABLED) {
>> + val[n++] = quota[q].val[Q_IDX_SOFT];
>> + strcpy(name, "soft-");
>> + strcpy(name + 5, quota_adm[q].name);
>> + name += strlen(name) + 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;
>
> So for `val`, we have a size. But, we don't have a size for `name`, are
> we sure that it's safe to keep reading past `NUL` characters ? Is the
> size of name available somewhere?
Yes. get_quota_size() calculated that as well.
>
>> + }
>> +}
>> +
>> static int dump_state_domain(const void *k, void *v, void *arg)
>> {
>> struct domain *domain = v;
>> @@ -2049,6 +2125,53 @@ 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 len = sizeof(*glb);
>> + size_t ret;
>> +
>> + n_quota = get_quota_size(quotas, &len);
>
> So, get_quota_size is actually an "add" the size to this variable, and
> not "store" the size in this variable. That's not confusing at all.
Would it be better if len is renamed to names_len (both here and the
parameter of get_quota_size())?
>
>> + len += n_quota * sizeof(glb->quota_val[0]);
>> + len = ROUNDUP(len, 3);
>> +
>> + record = talloc_size(NULL, len + sizeof(*head));
>> + if (!record)
>> + return "Dump global quota allocation error";
>> +
>> + head = record;
>> + head->type = XS_STATE_TYPE_GLB_QUOTA;
>> + head->length = len;
>> +
>> + glb = (struct xs_state_glb_quota *)(head + 1);
>> + glb->n_dom_quota = n_quota;
>> + glb->n_glob_quota = 0;
>
> Shouldn't `n_quota` be assigned to `n_glob_quota` instead? We don't have
> per-domain quota yet, and only have global quota, right?
We are applying all global quota values to the domains, so this is fine.
It isn't about where we store the quota (per domain or globally), but
how the quota values are used.
It would be possible to have e.g. "total_memory" or "total_nodes" quota
which would not apply to single domains, but to all of xenstore. Those
would need to be counted by n_glob_quota.
>
>> +
>> + build_quota_data(quotas, glb->quota_val,
>> + (char *)(glb->quota_val + n_quota));
>> +
>> + ret = fwrite(record, 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);
>> +}
>> +
Juergen
[-- Attachment #1.1.2: OpenPGP public key --]
[-- Type: application/pgp-keys, Size: 3743 bytes --]
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 495 bytes --]
^ permalink raw reply [flat|nested] 43+ messages in thread* Re: [PATCH 04/11] tools/xenstored: add GLOBAL_QUOTA_DATA record for live update
2026-03-16 8:15 ` Jürgen Groß
@ 2026-03-18 12:16 ` Juergen Gross
2026-03-19 16:15 ` Anthony PERARD
2026-03-19 15:59 ` Anthony PERARD
1 sibling, 1 reply; 43+ messages in thread
From: Juergen Gross @ 2026-03-18 12:16 UTC (permalink / raw)
To: Anthony PERARD; +Cc: xen-devel, Julien Grall
[-- Attachment #1.1.1: Type: text/plain, Size: 6034 bytes --]
On 16.03.26 09:15, Jürgen Groß wrote:
> On 13.03.26 18:08, Anthony PERARD wrote:
>> On Thu, Mar 05, 2026 at 02:52:01PM +0100, Juergen Gross wrote:
>>> diff --git a/tools/xenstored/domain.c b/tools/xenstored/domain.c
>>> index acdcaa769e..694ae58973 100644
>>> --- a/tools/xenstored/domain.c
>>> +++ b/tools/xenstored/domain.c
>>> @@ -1332,6 +1332,27 @@ 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;
>>
>> What do you think of using something like:
>> const char soft_prefix[] = "soft-";
>> const size_t soft_prefix_len = sizeof(soft_prefix) - 1;
>> to explain the `5`, here and in e.g. the function build_quota_data() ?
>> We used this in libxl in one place:
>> https://elixir.bootlin.com/xen/v4.21.0/source/tools/libs/light/
>> libxl_qmp.c#L1288
>>
>> But it's fine to leave it like that, as the '5's are close enought to
>> the prefix that we can guess easly enough.
>
> I can change it, but I'd prefer to use macros for that purpose.
>
>>
>>> +
>>> + if (strncmp(name, "soft-", 5)) {
>>> + *idx = Q_IDX_HARD;
>>> + } else {
>>> + *idx = Q_IDX_SOFT;
>>> + name += 5;
>>> + }
>>> + for (q = 0; q < ACC_N; q++) {
>>> + if (quota_adm[q].name && !strcmp(quota_adm[q].name, name)) {
>>> + *qidx = q;
>>> + return false;
>>> + }
>>> + }
>>> +
>>> + return true;
>>> +}
>>> +
>>> static int close_xgt_handle(void *_handle)
>>> {
>>> xengnttab_close(*(xengnttab_handle **)_handle);
>>> @@ -2001,6 +2022,61 @@ void read_state_connection(const void *ctx, const void
>>> *state)
>>> }
>>> }
>>> +static unsigned int get_quota_size(struct quota *quota, unsigned int *len)
>>> +{
>>> + 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) {
>>> + 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) + 5 + 1;
>>
>> The value 5 here isn't explained. A comment would be nice.
>
> Using the macro mentioned above will make it more descriptive.
>
>>
>>> + }
>>> + }
>>> +
>>> + return n;
>>> +}
>>> +
>>> +static void build_quota_data(struct quota *quota, uint32_t *val, char *name)
>>
>> I guess we will need a leap of faith to trust that `val` is big enough,
>> after finding out that it's actually an output of multiple values, and
>> not an input of a single value.
>
> That's what get_quota_size() is calculating.
>
>>
>> And `name` seems to also be an output, and this is actually impossible
>> to guess from the prototype.
>
> True. What about names?
I have chosen names_buf instead, making it more clear that this is an
output parameter for multiple names.
>
>>
>>> +{
>>> + 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(name, quota_adm[q].name);
>>> + name += strlen(name) + 1;
>>> + }
>>> + if (quota[q].val[Q_IDX_SOFT] != Q_VAL_DISABLED) {
>>> + val[n++] = quota[q].val[Q_IDX_SOFT];
>>> + strcpy(name, "soft-");
>>> + strcpy(name + 5, quota_adm[q].name);
>>> + name += strlen(name) + 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;
>>
>> So for `val`, we have a size. But, we don't have a size for `name`, are
>> we sure that it's safe to keep reading past `NUL` characters ? Is the
>> size of name available somewhere?
>
> Yes. get_quota_size() calculated that as well.
>
>>
>>> + }
>>> +}
>>> +
>>> static int dump_state_domain(const void *k, void *v, void *arg)
>>> {
>>> struct domain *domain = v;
>>> @@ -2049,6 +2125,53 @@ 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 len = sizeof(*glb);
>>> + size_t ret;
>>> +
>>> + n_quota = get_quota_size(quotas, &len);
>>
>> So, get_quota_size is actually an "add" the size to this variable, and
>> not "store" the size in this variable. That's not confusing at all.
>
> Would it be better if len is renamed to names_len (both here and the
> parameter of get_quota_size())?
In the end I have chosen to add a comment above get_quota_size() and
rename "len" to "rec_len" in dump_state_glb_quota().
Juergen
[-- Attachment #1.1.2: OpenPGP public key --]
[-- Type: application/pgp-keys, Size: 3743 bytes --]
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 495 bytes --]
^ permalink raw reply [flat|nested] 43+ messages in thread* Re: [PATCH 04/11] tools/xenstored: add GLOBAL_QUOTA_DATA record for live update
2026-03-18 12:16 ` Juergen Gross
@ 2026-03-19 16:15 ` Anthony PERARD
2026-03-19 16:31 ` Jürgen Groß
0 siblings, 1 reply; 43+ messages in thread
From: Anthony PERARD @ 2026-03-19 16:15 UTC (permalink / raw)
To: Juergen Gross; +Cc: xen-devel, Julien Grall
On Wed, Mar 18, 2026 at 01:16:30PM +0100, Juergen Gross wrote:
> On 16.03.26 09:15, Jürgen Groß wrote:
> > On 13.03.26 18:08, Anthony PERARD wrote:
> > > On Thu, Mar 05, 2026 at 02:52:01PM +0100, Juergen Gross wrote:
> > > > +static void build_quota_data(struct quota *quota, uint32_t *val, char *name)
> > >
> > > I guess we will need a leap of faith to trust that `val` is big enough,
> > > after finding out that it's actually an output of multiple values, and
> > > not an input of a single value.
> >
> > That's what get_quota_size() is calculating.
> >
> > >
> > > And `name` seems to also be an output, and this is actually impossible
> > > to guess from the prototype.
> >
> > True. What about names?
>
> I have chosen names_buf instead, making it more clear that this is an
> output parameter for multiple names.
Sounds good.
> > > > +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 len = sizeof(*glb);
> > > > + size_t ret;
> > > > +
> > > > + n_quota = get_quota_size(quotas, &len);
> > >
> > > So, get_quota_size is actually an "add" the size to this variable, and
> > > not "store" the size in this variable. That's not confusing at all.
> >
> > Would it be better if len is renamed to names_len (both here and the
> > parameter of get_quota_size())?
>
> In the end I have chosen to add a comment above get_quota_size() and
> rename "len" to "rec_len" in dump_state_glb_quota().
It would still be counter-intuitive if get_quota_size() returns different
values depending on the initial value of the second parameter. A comment
won't help. So I would still propose to add "sizeof(*glb)" to `len` or
`rec_len` after calling get_quota_size.
Cheers,
--
Anthony Perard | Vates XCP-ng Developer
XCP-ng & Xen Orchestra - Vates solutions
web: https://vates.tech
^ permalink raw reply [flat|nested] 43+ messages in thread* Re: [PATCH 04/11] tools/xenstored: add GLOBAL_QUOTA_DATA record for live update
2026-03-19 16:15 ` Anthony PERARD
@ 2026-03-19 16:31 ` Jürgen Groß
0 siblings, 0 replies; 43+ messages in thread
From: Jürgen Groß @ 2026-03-19 16:31 UTC (permalink / raw)
To: Anthony PERARD; +Cc: xen-devel, Julien Grall
[-- Attachment #1.1.1: Type: text/plain, Size: 1992 bytes --]
On 19.03.26 17:15, Anthony PERARD wrote:
> On Wed, Mar 18, 2026 at 01:16:30PM +0100, Juergen Gross wrote:
>> On 16.03.26 09:15, Jürgen Groß wrote:
>>> On 13.03.26 18:08, Anthony PERARD wrote:
>>>> On Thu, Mar 05, 2026 at 02:52:01PM +0100, Juergen Gross wrote:
>>>>> +static void build_quota_data(struct quota *quota, uint32_t *val, char *name)
>>>>
>>>> I guess we will need a leap of faith to trust that `val` is big enough,
>>>> after finding out that it's actually an output of multiple values, and
>>>> not an input of a single value.
>>>
>>> That's what get_quota_size() is calculating.
>>>
>>>>
>>>> And `name` seems to also be an output, and this is actually impossible
>>>> to guess from the prototype.
>>>
>>> True. What about names?
>>
>> I have chosen names_buf instead, making it more clear that this is an
>> output parameter for multiple names.
>
> Sounds good.
>
>>>>> +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 len = sizeof(*glb);
>>>>> + size_t ret;
>>>>> +
>>>>> + n_quota = get_quota_size(quotas, &len);
>>>>
>>>> So, get_quota_size is actually an "add" the size to this variable, and
>>>> not "store" the size in this variable. That's not confusing at all.
>>>
>>> Would it be better if len is renamed to names_len (both here and the
>>> parameter of get_quota_size())?
>>
>> In the end I have chosen to add a comment above get_quota_size() and
>> rename "len" to "rec_len" in dump_state_glb_quota().
>
> It would still be counter-intuitive if get_quota_size() returns different
> values depending on the initial value of the second parameter. A comment
> won't help. So I would still propose to add "sizeof(*glb)" to `len` or
> `rec_len` after calling get_quota_size.
Okay, will change that.
Juergen
[-- Attachment #1.1.2: OpenPGP public key --]
[-- Type: application/pgp-keys, Size: 3743 bytes --]
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 495 bytes --]
^ permalink raw reply [flat|nested] 43+ messages in thread
* Re: [PATCH 04/11] tools/xenstored: add GLOBAL_QUOTA_DATA record for live update
2026-03-16 8:15 ` Jürgen Groß
2026-03-18 12:16 ` Juergen Gross
@ 2026-03-19 15:59 ` Anthony PERARD
1 sibling, 0 replies; 43+ messages in thread
From: Anthony PERARD @ 2026-03-19 15:59 UTC (permalink / raw)
To: Jürgen Groß; +Cc: xen-devel, Julien Grall
On Mon, Mar 16, 2026 at 09:15:47AM +0100, Jürgen Groß wrote:
> On 13.03.26 18:08, Anthony PERARD wrote:
> > On Thu, Mar 05, 2026 at 02:52:01PM +0100, Juergen Gross wrote:
> > > diff --git a/tools/xenstored/domain.c b/tools/xenstored/domain.c
> > > index acdcaa769e..694ae58973 100644
> > > --- a/tools/xenstored/domain.c
> > > +++ b/tools/xenstored/domain.c
> > > @@ -1332,6 +1332,27 @@ 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;
> >
> > What do you think of using something like:
> > const char soft_prefix[] = "soft-";
> > const size_t soft_prefix_len = sizeof(soft_prefix) - 1;
> > to explain the `5`, here and in e.g. the function build_quota_data() ?
> > We used this in libxl in one place:
> > https://elixir.bootlin.com/xen/v4.21.0/source/tools/libs/light/libxl_qmp.c#L1288
> >
> > But it's fine to leave it like that, as the '5's are close enought to
> > the prefix that we can guess easly enough.
>
> I can change it, but I'd prefer to use macros for that purpose.
Sounds good.
> > > +static unsigned int get_quota_size(struct quota *quota, unsigned int *len)
> > > +{
> > > + 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) {
> > > + 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) + 5 + 1;
> >
> > The value 5 here isn't explained. A comment would be nice.
>
> Using the macro mentioned above will make it more descriptive.
Thanks.
> >
> > > + }
> > > + }
> > > +
> > > + return n;
> > > +}
> > > +
> > > +static void build_quota_data(struct quota *quota, uint32_t *val, char *name)
> >
> > I guess we will need a leap of faith to trust that `val` is big enough,
> > after finding out that it's actually an output of multiple values, and
> > not an input of a single value.
>
> That's what get_quota_size() is calculating.
Right. I'm probably just looking at function as been independent of the
rest of the program a bit too much.
> >
> > And `name` seems to also be an output, and this is actually impossible
> > to guess from the prototype.
>
> True. What about names?
`names` would be better here, indeed.
> >
> > > +{
> > > + 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(name, quota_adm[q].name);
> > > + name += strlen(name) + 1;
> > > + }
> > > + if (quota[q].val[Q_IDX_SOFT] != Q_VAL_DISABLED) {
> > > + val[n++] = quota[q].val[Q_IDX_SOFT];
> > > + strcpy(name, "soft-");
> > > + strcpy(name + 5, quota_adm[q].name);
> > > + name += strlen(name) + 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;
> >
> > So for `val`, we have a size. But, we don't have a size for `name`, are
> > we sure that it's safe to keep reading past `NUL` characters ? Is the
> > size of name available somewhere?
>
> Yes. get_quota_size() calculated that as well.
>
> >
> > > + }
> > > +}
> > > +
> > > static int dump_state_domain(const void *k, void *v, void *arg)
> > > {
> > > struct domain *domain = v;
> > > @@ -2049,6 +2125,53 @@ 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 len = sizeof(*glb);
> > > + size_t ret;
> > > +
> > > + n_quota = get_quota_size(quotas, &len);
> >
> > So, get_quota_size is actually an "add" the size to this variable, and
> > not "store" the size in this variable. That's not confusing at all.
>
> Would it be better if len is renamed to names_len (both here and the
> parameter of get_quota_size())?
Do you mean adding a new variable `names_len`? And having get_quota_size
set it to 0 before calculating the size?
I would be ok also with adding `sizeof(*glb)` to len after calling
get_quota_size(), but still need to have get_quota_size() start counting
from 0.
> >
> > > + len += n_quota * sizeof(glb->quota_val[0]);
> > > + len = ROUNDUP(len, 3);
> > > +
> > > + record = talloc_size(NULL, len + sizeof(*head));
> > > + if (!record)
> > > + return "Dump global quota allocation error";
> > > +
> > > + head = record;
> > > + head->type = XS_STATE_TYPE_GLB_QUOTA;
> > > + head->length = len;
> > > +
> > > + glb = (struct xs_state_glb_quota *)(head + 1);
> > > + glb->n_dom_quota = n_quota;
> > > + glb->n_glob_quota = 0;
> >
> > Shouldn't `n_quota` be assigned to `n_glob_quota` instead? We don't have
> > per-domain quota yet, and only have global quota, right?
>
> We are applying all global quota values to the domains, so this is fine.
>
> It isn't about where we store the quota (per domain or globally), but
> how the quota values are used.
>
> It would be possible to have e.g. "total_memory" or "total_nodes" quota
> which would not apply to single domains, but to all of xenstore. Those
> would need to be counted by n_glob_quota.
Right, I think I figured that out only after seen the patch creating a
different record for per-domain quota. (And seen that these two field
were not changed in later patches). But thank for the explanation, and
the possible way that n_glob_quota could be used.
Cheers,
--
Anthony Perard | Vates XCP-ng Developer
XCP-ng & Xen Orchestra - Vates solutions
web: https://vates.tech
^ permalink raw reply [flat|nested] 43+ messages in thread
* [PATCH 05/11] tools/xenstored: split acc[] array in struct domain
2026-03-05 13:51 [PATCH 00/11] tools: add support for per-domain xenstore quota Juergen Gross
` (3 preceding siblings ...)
2026-03-05 13:52 ` [PATCH 04/11] tools/xenstored: add GLOBAL_QUOTA_DATA record for live update Juergen Gross
@ 2026-03-05 13:52 ` Juergen Gross
2026-03-13 17:15 ` Anthony PERARD
2026-03-05 13:52 ` [PATCH 06/11] tools/xenstored: add infrastructure for per-domain quotas Juergen Gross
` (5 subsequent siblings)
10 siblings, 1 reply; 43+ messages in thread
From: Juergen Gross @ 2026-03-05 13:52 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>
---
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 694ae58973..9bd3ac7aca 100644
--- a/tools/xenstored/domain.c
+++ b/tools/xenstored/domain.c
@@ -140,10 +140,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;
@@ -410,7 +408,7 @@ bool domain_check_quota_add(struct domain *d, enum accitem what, int add)
if (add < 0 || !d)
return false;
- return domain_check_quota_val(d, what, d->acc[what].val + add);
+ return domain_check_quota_val(d, what, d->acc_val[what] + add);
}
static bool check_indexes(XENSTORE_RING_IDX cons, XENSTORE_RING_IDX prod)
@@ -582,7 +580,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)
@@ -590,7 +588,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,
@@ -774,7 +772,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;
}
@@ -1637,10 +1635,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
@@ -1651,7 +1649,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;
@@ -1710,10 +1708,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)
@@ -1757,7 +1755,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;
}
@@ -2193,7 +2191,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);
@@ -2248,7 +2246,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] 43+ messages in thread* [PATCH 06/11] tools/xenstored: add infrastructure for per-domain quotas
2026-03-05 13:51 [PATCH 00/11] tools: add support for per-domain xenstore quota Juergen Gross
` (4 preceding siblings ...)
2026-03-05 13:52 ` [PATCH 05/11] tools/xenstored: split acc[] array in struct domain Juergen Gross
@ 2026-03-05 13:52 ` Juergen Gross
2026-03-13 17:32 ` Anthony PERARD
2026-03-05 13:52 ` [PATCH 07/11] tools/xenstored: implement the GET/SET_QUOTA commands Juergen Gross
` (4 subsequent siblings)
10 siblings, 1 reply; 43+ messages in thread
From: Juergen Gross @ 2026-03-05 13:52 UTC (permalink / raw)
To: xen-devel; +Cc: Juergen Gross, Julien Grall, Anthony PERARD
Add the needed structures and helper functions for supporting quotas
per domain.
Signed-off-by: Juergen Gross <jgross@suse.com>
---
tools/xenstored/domain.c | 59 ++++++++++++++++++++++++++++++----------
1 file changed, 44 insertions(+), 15 deletions(-)
diff --git a/tools/xenstored/domain.c b/tools/xenstored/domain.c
index 9bd3ac7aca..8e52351695 100644
--- a/tools/xenstored/domain.c
+++ b/tools/xenstored/domain.c
@@ -389,6 +389,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];
}
@@ -397,6 +400,9 @@ static bool domain_check_quota_val(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;
@@ -809,6 +815,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) {
@@ -822,6 +829,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;
@@ -2079,25 +2091,38 @@ 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 len = sizeof(*sd);
+ size_t ret;
- if (lu_status->version > 1)
- sd.features = domain->features;
+ n_quota = get_quota_size(domain->acc, &len);
+ len += n_quota * sizeof(sd->quota_val[0]);
+ len = ROUNDUP(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, len + sizeof(*head));
+ if (!record)
return 1;
- return 0;
+ head = record;
+ head->type = XS_STATE_TYPE_DOMAIN;
+ head->length = 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, len + sizeof(*head), 1, fp);
+
+ talloc_free(record);
+
+ return (ret != 1 || dump_state_align(fp)) ? 1 : 0;
}
const char *dump_state_domains(FILE *fp)
@@ -2114,6 +2139,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)
@@ -2121,6 +2148,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] 43+ messages in thread* Re: [PATCH 06/11] tools/xenstored: add infrastructure for per-domain quotas
2026-03-05 13:52 ` [PATCH 06/11] tools/xenstored: add infrastructure for per-domain quotas Juergen Gross
@ 2026-03-13 17:32 ` Anthony PERARD
2026-03-16 8:17 ` Jürgen Groß
0 siblings, 1 reply; 43+ messages in thread
From: Anthony PERARD @ 2026-03-13 17:32 UTC (permalink / raw)
To: Juergen Gross; +Cc: xen-devel, Julien Grall
On Thu, Mar 05, 2026 at 02:52:03PM +0100, Juergen Gross wrote:
> Add the needed structures and helper functions for supporting quotas
> per domain.
I don't see any new struct or new helpers. What this patch do now is
updating the existing helpers to take per-domain quota into account. I
guess that comment was from an earlier iteration of the patch before it
got split into smaller ones, and easier to review.
>
> 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] 43+ messages in thread
* Re: [PATCH 06/11] tools/xenstored: add infrastructure for per-domain quotas
2026-03-13 17:32 ` Anthony PERARD
@ 2026-03-16 8:17 ` Jürgen Groß
0 siblings, 0 replies; 43+ messages in thread
From: Jürgen Groß @ 2026-03-16 8:17 UTC (permalink / raw)
To: Anthony PERARD; +Cc: xen-devel, Julien Grall
[-- Attachment #1.1.1: Type: text/plain, Size: 670 bytes --]
On 13.03.26 18:32, Anthony PERARD wrote:
> On Thu, Mar 05, 2026 at 02:52:03PM +0100, Juergen Gross wrote:
>> Add the needed structures and helper functions for supporting quotas
>> per domain.
>
> I don't see any new struct or new helpers. What this patch do now is
> updating the existing helpers to take per-domain quota into account. I
> guess that comment was from an earlier iteration of the patch before it
> got split into smaller ones, and easier to review.
Indeed.
Will change the commit message.
>
>>
>> Signed-off-by: Juergen Gross <jgross@suse.com>
>
> Reviewed-by: Anthony PERARD <anthony.perard@vates.tech>
Thanks,
Juergen
[-- Attachment #1.1.2: OpenPGP public key --]
[-- Type: application/pgp-keys, Size: 3743 bytes --]
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 495 bytes --]
^ permalink raw reply [flat|nested] 43+ messages in thread
* [PATCH 07/11] tools/xenstored: implement the GET/SET_QUOTA commands
2026-03-05 13:51 [PATCH 00/11] tools: add support for per-domain xenstore quota Juergen Gross
` (5 preceding siblings ...)
2026-03-05 13:52 ` [PATCH 06/11] tools/xenstored: add infrastructure for per-domain quotas Juergen Gross
@ 2026-03-05 13:52 ` Juergen Gross
2026-03-16 15:08 ` Anthony PERARD
2026-03-05 13:52 ` [PATCH 08/11] tools/libxl: add functions for retrieving and setting xenstore quota Juergen Gross
` (3 subsequent siblings)
10 siblings, 1 reply; 43+ messages in thread
From: Juergen Gross @ 2026-03-05 13:52 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>
---
tools/xenstored/core.c | 4 ++
tools/xenstored/domain.c | 106 +++++++++++++++++++++++++++++++++++++++
tools/xenstored/domain.h | 8 +++
3 files changed, 118 insertions(+)
diff --git a/tools/xenstored/core.c b/tools/xenstored/core.c
index 8a06b35808..e283d47184 100644
--- a/tools/xenstored/core.c
+++ b/tools/xenstored/core.c
@@ -2034,6 +2034,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 8e52351695..c0bc8a3eb7 100644
--- a/tools/xenstored/domain.c
+++ b/tools/xenstored/domain.c
@@ -1363,6 +1363,112 @@ 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_asprintf(ctx, "%s", "");
+ 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, "%ssoft-%s",
+ *resp ? " " : "", quota_adm[q].name);
+ if (!resp)
+ return ENOMEM;
+ }
+ }
+ } else {
+ if (n_pars == 1) {
+ quota = quotas;
+ name = vec[0];
+ } else {
+ domid = atoi(vec[0]);
+ 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 = atoi(vec[0]);
+ 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))
+ 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 62ce3b3166..6a06b0d1af 100644
--- a/tools/xenstored/domain.h
+++ b/tools/xenstored/domain.h
@@ -93,6 +93,14 @@ int do_get_feature(const void *ctx, struct connection *conn,
int do_set_feature(const void *ctx, struct connection *conn,
struct buffered_data *in);
+/* Get quota names or value */
+int do_get_quota(const void *ctx, struct connection *conn,
+ struct buffered_data *in);
+
+/* Set quota value */
+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] 43+ messages in thread* Re: [PATCH 07/11] tools/xenstored: implement the GET/SET_QUOTA commands
2026-03-05 13:52 ` [PATCH 07/11] tools/xenstored: implement the GET/SET_QUOTA commands Juergen Gross
@ 2026-03-16 15:08 ` Anthony PERARD
2026-03-16 15:27 ` Juergen Gross
0 siblings, 1 reply; 43+ messages in thread
From: Anthony PERARD @ 2026-03-16 15:08 UTC (permalink / raw)
To: Juergen Gross; +Cc: xen-devel, Julien Grall
On Thu, Mar 05, 2026 at 02:52:04PM +0100, Juergen Gross wrote:
> diff --git a/tools/xenstored/core.c b/tools/xenstored/core.c
> index 8a06b35808..e283d47184 100644
> --- a/tools/xenstored/core.c
> +++ b/tools/xenstored/core.c
> @@ -2034,6 +2034,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 8e52351695..c0bc8a3eb7 100644
> --- a/tools/xenstored/domain.c
> +++ b/tools/xenstored/domain.c
> @@ -1363,6 +1363,112 @@ 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_asprintf(ctx, "%s", "");
This could be written with talloc_strdup() instead, since there's no
formatting involve.
> + 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) {
Having set internally a value of Q_VAL_DISABLED, does it mean the named
quota is unsupported?
> + 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, "%ssoft-%s",
> + *resp ? " " : "", quota_adm[q].name);
> + if (!resp)
> + return ENOMEM;
> + }
> + }
> + } else {
> + if (n_pars == 1) {
> + quota = quotas;
> + name = vec[0];
> + } else {
> + domid = atoi(vec[0]);
Shall we check that vec[0] actually contain a plausible domid? (An
integer between 0..65535). Right now, this accept everything, and would
return 0 if there's not a single digit.
> + 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]);
Why do we return 4294967295 for disabled quota check when the spec say
to return "0" when a quota check is disabled? That is for quota names
that are supposed to be not supported (if we ask "GET_QUOTA" first).
> + 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]);
We should check that vec[1] is a valid quota value, and also not an
internal value. Otherwise, we can just have "-1" on the wire, and have
unexpected changes for example. Only "0" is documented as a quota been
disabled, "-1" or "4294967295" isn't.
> + } else {
> + domid = atoi(vec[0]);
> + 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))
> + 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 62ce3b3166..6a06b0d1af 100644
> --- a/tools/xenstored/domain.h
> +++ b/tools/xenstored/domain.h
> @@ -93,6 +93,14 @@ int do_get_feature(const void *ctx, struct connection *conn,
> int do_set_feature(const void *ctx, struct connection *conn,
> struct buffered_data *in);
>
> +/* Get quota names or value */
This could say "implement GET_QUOTA" or something instead. But a
comment here isn't going to give much value for internal functions.
> +int do_get_quota(const void *ctx, struct connection *conn,
> + struct buffered_data *in);
> +
> +/* Set quota value */
> +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);
Thanks,
--
Anthony Perard | Vates XCP-ng Developer
XCP-ng & Xen Orchestra - Vates solutions
web: https://vates.tech
^ permalink raw reply [flat|nested] 43+ messages in thread* Re: [PATCH 07/11] tools/xenstored: implement the GET/SET_QUOTA commands
2026-03-16 15:08 ` Anthony PERARD
@ 2026-03-16 15:27 ` Juergen Gross
2026-03-19 16:47 ` Anthony PERARD
0 siblings, 1 reply; 43+ messages in thread
From: Juergen Gross @ 2026-03-16 15:27 UTC (permalink / raw)
To: Anthony PERARD; +Cc: xen-devel, Julien Grall
[-- Attachment #1.1.1: Type: text/plain, Size: 6258 bytes --]
On 16.03.26 16:08, Anthony PERARD wrote:
> On Thu, Mar 05, 2026 at 02:52:04PM +0100, Juergen Gross wrote:
>> diff --git a/tools/xenstored/core.c b/tools/xenstored/core.c
>> index 8a06b35808..e283d47184 100644
>> --- a/tools/xenstored/core.c
>> +++ b/tools/xenstored/core.c
>> @@ -2034,6 +2034,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 8e52351695..c0bc8a3eb7 100644
>> --- a/tools/xenstored/domain.c
>> +++ b/tools/xenstored/domain.c
>> @@ -1363,6 +1363,112 @@ 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_asprintf(ctx, "%s", "");
>
> This could be written with talloc_strdup() instead, since there's no
> formatting involve.
Right.
>
>> + 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) {
>
> Having set internally a value of Q_VAL_DISABLED, does it mean the named
> quota is unsupported?
Yes. Right now all hard quota are supported and only one soft quota
is supported.
>
>> + 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, "%ssoft-%s",
>> + *resp ? " " : "", quota_adm[q].name);
>> + if (!resp)
>> + return ENOMEM;
>> + }
>> + }
>> + } else {
>> + if (n_pars == 1) {
>> + quota = quotas;
>> + name = vec[0];
>> + } else {
>> + domid = atoi(vec[0]);
>
> Shall we check that vec[0] actually contain a plausible domid? (An
> integer between 0..65535). Right now, this accept everything, and would
> return 0 if there's not a single digit.
I have followed the pattern used in other places where a domid is expected.
In the end nothing will really break.
Any integer not being a domid will result in ENOENT, while the case of not
a digit is a bug in privileged software (domids can be specified by dom0
only).
>
>> + 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]);
>
> Why do we return 4294967295 for disabled quota check when the spec say
> to return "0" when a quota check is disabled? That is for quota names
> that are supposed to be not supported (if we ask "GET_QUOTA" first).
parse_quota_name() should have returned true in this case, so EINVAL should
be returned.
Will fix that.
>
>> + 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]);
>
> We should check that vec[1] is a valid quota value, and also not an
> internal value. Otherwise, we can just have "-1" on the wire, and have
> unexpected changes for example. Only "0" is documented as a quota been
> disabled, "-1" or "4294967295" isn't.
Right, I'll check for val != Q_VAL_DISABLED.
>
>> + } else {
>> + domid = atoi(vec[0]);
>> + 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))
>> + 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 62ce3b3166..6a06b0d1af 100644
>> --- a/tools/xenstored/domain.h
>> +++ b/tools/xenstored/domain.h
>> @@ -93,6 +93,14 @@ int do_get_feature(const void *ctx, struct connection *conn,
>> int do_set_feature(const void *ctx, struct connection *conn,
>> struct buffered_data *in);
>>
>> +/* Get quota names or value */
>
> This could say "implement GET_QUOTA" or something instead. But a
> comment here isn't going to give much value for internal functions.
If nobody objects I'll drop the comment.
>
>> +int do_get_quota(const void *ctx, struct connection *conn,
>> + struct buffered_data *in);
>> +
>> +/* Set quota value */
>> +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);
Thanks,
Juergen
[-- Attachment #1.1.2: OpenPGP public key --]
[-- Type: application/pgp-keys, Size: 3743 bytes --]
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 495 bytes --]
^ permalink raw reply [flat|nested] 43+ messages in thread* Re: [PATCH 07/11] tools/xenstored: implement the GET/SET_QUOTA commands
2026-03-16 15:27 ` Juergen Gross
@ 2026-03-19 16:47 ` Anthony PERARD
2026-03-20 6:36 ` Jürgen Groß
0 siblings, 1 reply; 43+ messages in thread
From: Anthony PERARD @ 2026-03-19 16:47 UTC (permalink / raw)
To: Juergen Gross; +Cc: xen-devel, Julien Grall
On Mon, Mar 16, 2026 at 04:27:43PM +0100, Juergen Gross wrote:
> On 16.03.26 16:08, Anthony PERARD wrote:
> > On Thu, Mar 05, 2026 at 02:52:04PM +0100, Juergen Gross wrote:
> > > + if (n_pars == 1) {
> > > + quota = quotas;
> > > + name = vec[0];
> > > + } else {
> > > + domid = atoi(vec[0]);
> >
> > Shall we check that vec[0] actually contain a plausible domid? (An
> > integer between 0..65535). Right now, this accept everything, and would
> > return 0 if there's not a single digit.
>
> I have followed the pattern used in other places where a domid is expected.
>
> In the end nothing will really break.
On the daemon, no, not really.
> Any integer not being a domid will result in ENOENT, while the case of not
> a digit is a bug in privileged software (domids can be specified by dom0
> only).
It would be a bug, indeed, but xenstored can help telling exactly where
there's a bug, instead of ignoring it and carry-on. Well, just return
EINVAL when something other than a number is found. You do that for
quota, why not for domid as well?
That can also help when the daemon is replace by a different
implementation that actually do the checks. (well it would help finding
bug in the client earlier)
> > > + } else {
> > > + domid = atoi(vec[0]);
> > > + 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))
> > > + 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 62ce3b3166..6a06b0d1af 100644
> > > --- a/tools/xenstored/domain.h
> > > +++ b/tools/xenstored/domain.h
> > > @@ -93,6 +93,14 @@ int do_get_feature(const void *ctx, struct connection *conn,
> > > int do_set_feature(const void *ctx, struct connection *conn,
> > > struct buffered_data *in);
> > > +/* Get quota names or value */
> >
> > This could say "implement GET_QUOTA" or something instead. But a
> > comment here isn't going to give much value for internal functions.
>
> If nobody objects I'll drop the comment.
Sounds good.
Cheers,
--
Anthony Perard | Vates XCP-ng Developer
XCP-ng & Xen Orchestra - Vates solutions
web: https://vates.tech
^ permalink raw reply [flat|nested] 43+ messages in thread* Re: [PATCH 07/11] tools/xenstored: implement the GET/SET_QUOTA commands
2026-03-19 16:47 ` Anthony PERARD
@ 2026-03-20 6:36 ` Jürgen Groß
0 siblings, 0 replies; 43+ messages in thread
From: Jürgen Groß @ 2026-03-20 6:36 UTC (permalink / raw)
To: Anthony PERARD; +Cc: xen-devel, Julien Grall
[-- Attachment #1.1.1: Type: text/plain, Size: 1419 bytes --]
On 19.03.26 17:47, Anthony PERARD wrote:
> On Mon, Mar 16, 2026 at 04:27:43PM +0100, Juergen Gross wrote:
>> On 16.03.26 16:08, Anthony PERARD wrote:
>>> On Thu, Mar 05, 2026 at 02:52:04PM +0100, Juergen Gross wrote:
>>>> + if (n_pars == 1) {
>>>> + quota = quotas;
>>>> + name = vec[0];
>>>> + } else {
>>>> + domid = atoi(vec[0]);
>>>
>>> Shall we check that vec[0] actually contain a plausible domid? (An
>>> integer between 0..65535). Right now, this accept everything, and would
>>> return 0 if there's not a single digit.
>>
>> I have followed the pattern used in other places where a domid is expected.
>>
>> In the end nothing will really break.
>
> On the daemon, no, not really.
>
>> Any integer not being a domid will result in ENOENT, while the case of not
>> a digit is a bug in privileged software (domids can be specified by dom0
>> only).
>
> It would be a bug, indeed, but xenstored can help telling exactly where
> there's a bug, instead of ignoring it and carry-on. Well, just return
> EINVAL when something other than a number is found. You do that for
> quota, why not for domid as well?
>
> That can also help when the daemon is replace by a different
> implementation that actually do the checks. (well it would help finding
> bug in the client earlier)
I have added another patch introducing a domid parser and using it where
needed.
Juergen
[-- Attachment #1.1.2: OpenPGP public key --]
[-- Type: application/pgp-keys, Size: 3743 bytes --]
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 495 bytes --]
^ permalink raw reply [flat|nested] 43+ messages in thread
* [PATCH 08/11] tools/libxl: add functions for retrieving and setting xenstore quota
2026-03-05 13:51 [PATCH 00/11] tools: add support for per-domain xenstore quota Juergen Gross
` (6 preceding siblings ...)
2026-03-05 13:52 ` [PATCH 07/11] tools/xenstored: implement the GET/SET_QUOTA commands Juergen Gross
@ 2026-03-05 13:52 ` Juergen Gross
2026-03-10 13:58 ` Nick Rosbrook
2026-03-19 9:11 ` Anthony PERARD
2026-03-05 13:52 ` [PATCH 09/11] tools/libxl: add support for xenstore quota in domain_config Juergen Gross
` (2 subsequent siblings)
10 siblings, 2 replies; 43+ messages in thread
From: Juergen Gross @ 2026-03-05 13:52 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>
---
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 | 102 +++++++++++++++++++++++++++
6 files changed, 219 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..6c9af904c5 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
+ }
+
+// NewXsQuotaSet returns an instance of XsQuotaSet initialized with defaults.
+func NewXsQuotaSet() (*XsQuotaSet, error) {
+var (
+x XsQuotaSet
+xc C.libxl_xs_quota_set)
+
+C.libxl_xs_quota_set_init(&xc)
+defer C.libxl_xs_quota_set_dispose(&xc)
+
+if err := x.fromC(&xc); err != nil {
+return nil, err }
+
+return &x, nil}
+
+func (x *XsQuotaSet) fromC(xc *C.libxl_xs_quota_set) 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 *XsQuotaSet) toC(xc *C.libxl_xs_quota_set) (err error){defer func(){
+if err != nil{
+C.libxl_xs_quota_set_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..5393277190 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 XsQuotaSet struct {
+Quota []XsQuotaItem
+}
+
type DomainBuildInfo struct {
MaxVcpus int
AvailVcpus Bitmap
diff --git a/tools/include/libxl.h b/tools/include/libxl.h
index bc35e412da..a70d9d347f 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_xsquota_global_get()
+ * libxl_xsquota_global_set()
+ * libxl_xsquota_domain_get()
+ * libxl_xsquota_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_xsquota_global_get(libxl_ctx *ctx, libxl_xs_quota_set *q);
+int libxl_xsquota_global_set(libxl_ctx *ctx, libxl_xs_quota_set *q);
+int libxl_xsquota_domain_get(libxl_ctx *ctx, uint32_t domid,
+ libxl_xs_quota_set *q);
+int libxl_xsquota_domain_set(libxl_ctx *ctx, uint32_t domid,
+ libxl_xs_quota_set *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..c5ddc40f35 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_set = Struct("xs_quota_set", [
+ ("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..b9afa1c914
--- /dev/null
+++ b/tools/libs/light/libxl_xsquota.c
@@ -0,0 +1,102 @@
+/* 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_set *q,
+ bool (func)(struct xs_handle *h, unsigned int domid,
+ char *quota, unsigned int *value))
+{
+ char **names;
+ unsigned int num, i;
+ int rc = 0;
+ GC_INIT(ctx);
+
+ names = xs_get_quota_names(ctx->xsh, &num);
+ if (!names) {
+ /* Xenstore quota support is optional! */
+ if (errno != ENOSYS)
+ rc = ERROR_FAIL;
+ q->num_quota = 0;
+ goto out;
+ }
+
+ q->num_quota = num;
+ q->quota = libxl__calloc(NOGC, num, sizeof(*q->quota));
+ for (i = 0; i < num; i++) {
+ q->quota[i].name = libxl__strdup(NOGC, names[i]);
+ if (!func(ctx->xsh, domid, q->quota[i].name, &q->quota[i].val)) {
+ libxl_xs_quota_set_dispose(q);
+ rc = ERROR_FAIL;
+ break;
+ }
+ }
+
+ free(names);
+
+ out:
+ GC_FREE;
+ return rc;
+}
+
+static int set_quota(libxl_ctx *ctx, unsigned int domid, libxl_xs_quota_set *q,
+ bool (func)(struct xs_handle *h, unsigned int domid,
+ char *quota, unsigned int value))
+{
+ unsigned int i;
+ int rc = 0;
+ GC_INIT(ctx);
+
+ for (i = 0; i < q->num_quota; i++) {
+ if (!func(ctx->xsh, domid, q->quota[i].name, q->quota[i].val)) {
+ rc = ERROR_FAIL;
+ break;
+ }
+ }
+
+ GC_FREE;
+ return rc;
+}
+
+static bool get_global_quota(struct xs_handle *h, unsigned int domid,
+ char *quota, unsigned int *value)
+{
+ return xs_get_global_quota(h, quota, value);
+}
+
+int libxl_xsquota_global_get(libxl_ctx *ctx, libxl_xs_quota_set *q)
+{
+ return get_quota(ctx, 0, q, get_global_quota);
+}
+
+static bool set_global_quota(struct xs_handle *h, unsigned int domid,
+ char *quota, unsigned int value)
+{
+ return xs_set_global_quota(h, quota, value);
+}
+
+int libxl_xsquota_global_set(libxl_ctx *ctx, libxl_xs_quota_set *q)
+{
+ return set_quota(ctx, 0, q, set_global_quota);;
+}
+
+int libxl_xsquota_domain_get(libxl_ctx *ctx, uint32_t domid,
+ libxl_xs_quota_set *q)
+{
+ return get_quota(ctx, domid, q, xs_get_domain_quota);
+}
+
+int libxl_xsquota_domain_set(libxl_ctx *ctx, uint32_t domid,
+ libxl_xs_quota_set *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] 43+ messages in thread* Re: [PATCH 08/11] tools/libxl: add functions for retrieving and setting xenstore quota
2026-03-05 13:52 ` [PATCH 08/11] tools/libxl: add functions for retrieving and setting xenstore quota Juergen Gross
@ 2026-03-10 13:58 ` Nick Rosbrook
2026-03-19 9:11 ` Anthony PERARD
1 sibling, 0 replies; 43+ messages in thread
From: Nick Rosbrook @ 2026-03-10 13:58 UTC (permalink / raw)
To: Juergen Gross; +Cc: xen-devel, George Dunlap, Anthony PERARD
On Thu, Mar 5, 2026 at 8:52 AM Juergen Gross <jgross@suse.com> wrote:
>
> Add some functions allowing to retrieve and set Xenstore quota (either
> global or domain specific).
>
> Signed-off-by: Juergen Gross <jgross@suse.com>
> ---
> 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 | 102 +++++++++++++++++++++++++++
> 6 files changed, 219 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..6c9af904c5 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
> + }
> +
> +// NewXsQuotaSet returns an instance of XsQuotaSet initialized with defaults.
> +func NewXsQuotaSet() (*XsQuotaSet, error) {
> +var (
> +x XsQuotaSet
> +xc C.libxl_xs_quota_set)
> +
> +C.libxl_xs_quota_set_init(&xc)
> +defer C.libxl_xs_quota_set_dispose(&xc)
> +
> +if err := x.fromC(&xc); err != nil {
> +return nil, err }
> +
> +return &x, nil}
> +
> +func (x *XsQuotaSet) fromC(xc *C.libxl_xs_quota_set) 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 *XsQuotaSet) toC(xc *C.libxl_xs_quota_set) (err error){defer func(){
> +if err != nil{
> +C.libxl_xs_quota_set_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..5393277190 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 XsQuotaSet struct {
> +Quota []XsQuotaItem
> +}
> +
> type DomainBuildInfo struct {
> MaxVcpus int
> AvailVcpus Bitmap
For the golang stuff:
Acked-by: Nick Rosbrook <enr0n@ubuntu.com
^ permalink raw reply [flat|nested] 43+ messages in thread* Re: [PATCH 08/11] tools/libxl: add functions for retrieving and setting xenstore quota
2026-03-05 13:52 ` [PATCH 08/11] tools/libxl: add functions for retrieving and setting xenstore quota Juergen Gross
2026-03-10 13:58 ` Nick Rosbrook
@ 2026-03-19 9:11 ` Anthony PERARD
2026-03-19 11:00 ` Jürgen Groß
1 sibling, 1 reply; 43+ messages in thread
From: Anthony PERARD @ 2026-03-19 9:11 UTC (permalink / raw)
To: Juergen Gross; +Cc: xen-devel, Nick Rosbrook, George Dunlap
On Thu, Mar 05, 2026 at 02:52:05PM +0100, Juergen Gross wrote:
> diff --git a/tools/include/libxl.h b/tools/include/libxl.h
> index bc35e412da..a70d9d347f 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_xsquota_global_get()
> + * libxl_xsquota_global_set()
> + * libxl_xsquota_domain_get()
> + * libxl_xsquota_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_xsquota_global_get(libxl_ctx *ctx, libxl_xs_quota_set *q);
Could you rename the second arg as "q_r" or "q_out" ?
> +int libxl_xsquota_global_set(libxl_ctx *ctx, libxl_xs_quota_set *q);
> +int libxl_xsquota_domain_get(libxl_ctx *ctx, uint32_t domid,
> + libxl_xs_quota_set *q);
Same here.
> +int libxl_xsquota_domain_set(libxl_ctx *ctx, uint32_t domid,
> + libxl_xs_quota_set *q);
Could we prefix them all with "libxl_xs_quota_" ? I would rather that we
only use "xs_quota" or "xsquota".
> +
> #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..c5ddc40f35 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_set = Struct("xs_quota_set", [
Could you use "_list" as a suffix instead? It's a bit confusing to have
the word "set" as a double meaning, with a _set() function that takes a
set.
> + ("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..b9afa1c914
> --- /dev/null
> +++ b/tools/libs/light/libxl_xsquota.c
> @@ -0,0 +1,102 @@
> +/* 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_set *q,
> + bool (func)(struct xs_handle *h, unsigned int domid,
> + char *quota, unsigned int *value))
> +{
> + char **names;
> + unsigned int num, i;
> + int rc = 0;
We don't init `rc` variable in libxl function. Set `rc` to 0 just before
the "out" label.
> + GC_INIT(ctx);
> +
> + names = xs_get_quota_names(ctx->xsh, &num);
> + if (!names) {
> + /* Xenstore quota support is optional! */
> + if (errno != ENOSYS)
> + rc = ERROR_FAIL;
> + q->num_quota = 0;
It feels wrong to make changes to the output argument on error, if we
can avoid it. And here, I don't see any reason to change `q`.
> + goto out;
> + }
> +
Can you call libxl_xs_quota_set_init() first? As you call _dispose()
later.
> + q->num_quota = num;
> + q->quota = libxl__calloc(NOGC, num, sizeof(*q->quota));
> + for (i = 0; i < num; i++) {
> + q->quota[i].name = libxl__strdup(NOGC, names[i]);
> + if (!func(ctx->xsh, domid, q->quota[i].name, &q->quota[i].val)) {
Could you store the return value of `func()` in `ok`, and test `ok` in the
if instead?
> + libxl_xs_quota_set_dispose(q);
> + rc = ERROR_FAIL;
> + break;
This can be `goto out` once free(names) is moved to the out label.
> + }
> + }
> +
> + free(names);
Could you do that after the "out" label? And init `names` to NULL.
> +
> + out:
> + GC_FREE;
> + return rc;
> +}
> +
> +static int set_quota(libxl_ctx *ctx, unsigned int domid, libxl_xs_quota_set *q,
> + bool (func)(struct xs_handle *h, unsigned int domid,
> + char *quota, unsigned int value))
> +{
> + unsigned int i;
> + int rc = 0;
> + GC_INIT(ctx);
> +
> + for (i = 0; i < q->num_quota; i++) {
> + if (!func(ctx->xsh, domid, q->quota[i].name, q->quota[i].val)) {
> + rc = ERROR_FAIL;
> + break;
It would be better to write `goto out` instead.
> + }
> + }
> +
> + GC_FREE;
> + return rc;
> +}
Thanks,
--
| Vates
XCP-ng & Xen Orchestra - Vates solutions
web: https://vates.tech
^ permalink raw reply [flat|nested] 43+ messages in thread* Re: [PATCH 08/11] tools/libxl: add functions for retrieving and setting xenstore quota
2026-03-19 9:11 ` Anthony PERARD
@ 2026-03-19 11:00 ` Jürgen Groß
0 siblings, 0 replies; 43+ messages in thread
From: Jürgen Groß @ 2026-03-19 11:00 UTC (permalink / raw)
To: Anthony PERARD; +Cc: xen-devel, Nick Rosbrook, George Dunlap
[-- Attachment #1.1.1: Type: text/plain, Size: 6015 bytes --]
On 19.03.26 10:11, Anthony PERARD wrote:
> On Thu, Mar 05, 2026 at 02:52:05PM +0100, Juergen Gross wrote:
>> diff --git a/tools/include/libxl.h b/tools/include/libxl.h
>> index bc35e412da..a70d9d347f 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_xsquota_global_get()
>> + * libxl_xsquota_global_set()
>> + * libxl_xsquota_domain_get()
>> + * libxl_xsquota_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_xsquota_global_get(libxl_ctx *ctx, libxl_xs_quota_set *q);
>
> Could you rename the second arg as "q_r" or "q_out" ?
>
>> +int libxl_xsquota_global_set(libxl_ctx *ctx, libxl_xs_quota_set *q);
>> +int libxl_xsquota_domain_get(libxl_ctx *ctx, uint32_t domid,
>> + libxl_xs_quota_set *q);
>
> Same here.
>
>> +int libxl_xsquota_domain_set(libxl_ctx *ctx, uint32_t domid,
>> + libxl_xs_quota_set *q);
>
> Could we prefix them all with "libxl_xs_quota_" ? I would rather that we
> only use "xs_quota" or "xsquota".
>
>> +
>> #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..c5ddc40f35 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_set = Struct("xs_quota_set", [
>
> Could you use "_list" as a suffix instead? It's a bit confusing to have
> the word "set" as a double meaning, with a _set() function that takes a
> set.
>
>> + ("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..b9afa1c914
>> --- /dev/null
>> +++ b/tools/libs/light/libxl_xsquota.c
>> @@ -0,0 +1,102 @@
>> +/* 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_set *q,
>> + bool (func)(struct xs_handle *h, unsigned int domid,
>> + char *quota, unsigned int *value))
>> +{
>> + char **names;
>> + unsigned int num, i;
>> + int rc = 0;
>
> We don't init `rc` variable in libxl function. Set `rc` to 0 just before
> the "out" label.
>
>> + GC_INIT(ctx);
>> +
>> + names = xs_get_quota_names(ctx->xsh, &num);
>> + if (!names) {
>> + /* Xenstore quota support is optional! */
>> + if (errno != ENOSYS)
>> + rc = ERROR_FAIL;
>> + q->num_quota = 0;
>
> It feels wrong to make changes to the output argument on error, if we
> can avoid it. And here, I don't see any reason to change `q`.
>
>> + goto out;
>> + }
>> +
>
> Can you call libxl_xs_quota_set_init() first? As you call _dispose()
> later.
>
>> + q->num_quota = num;
>> + q->quota = libxl__calloc(NOGC, num, sizeof(*q->quota));
>> + for (i = 0; i < num; i++) {
>> + q->quota[i].name = libxl__strdup(NOGC, names[i]);
>> + if (!func(ctx->xsh, domid, q->quota[i].name, &q->quota[i].val)) {
>
> Could you store the return value of `func()` in `ok`, and test `ok` in the
> if instead?
>
>> + libxl_xs_quota_set_dispose(q);
>> + rc = ERROR_FAIL;
>> + break;
>
> This can be `goto out` once free(names) is moved to the out label.
>
>> + }
>> + }
>> +
>> + free(names);
>
> Could you do that after the "out" label? And init `names` to NULL.
>
>> +
>> + out:
>> + GC_FREE;
>> + return rc;
>> +}
>> +
>> +static int set_quota(libxl_ctx *ctx, unsigned int domid, libxl_xs_quota_set *q,
>> + bool (func)(struct xs_handle *h, unsigned int domid,
>> + char *quota, unsigned int value))
>> +{
>> + unsigned int i;
>> + int rc = 0;
>> + GC_INIT(ctx);
>> +
>> + for (i = 0; i < q->num_quota; i++) {
>> + if (!func(ctx->xsh, domid, q->quota[i].name, q->quota[i].val)) {
>> + rc = ERROR_FAIL;
>> + break;
>
> It would be better to write `goto out` instead.
>
>> + }
>> + }
>> +
>> + GC_FREE;
>> + return rc;
>> +}
To all your remarks: yes, will do.
Thanks,
Juergen
[-- Attachment #1.1.2: OpenPGP public key --]
[-- Type: application/pgp-keys, Size: 3743 bytes --]
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 495 bytes --]
^ permalink raw reply [flat|nested] 43+ messages in thread
* [PATCH 09/11] tools/libxl: add support for xenstore quota in domain_config
2026-03-05 13:51 [PATCH 00/11] tools: add support for per-domain xenstore quota Juergen Gross
` (7 preceding siblings ...)
2026-03-05 13:52 ` [PATCH 08/11] tools/libxl: add functions for retrieving and setting xenstore quota Juergen Gross
@ 2026-03-05 13:52 ` Juergen Gross
2026-03-10 13:57 ` Nick Rosbrook
2026-03-19 9:26 ` Anthony PERARD
2026-03-05 13:52 ` [PATCH 10/11] tools/xl: add xl commands for xenstore quota operations Juergen Gross
2026-03-05 13:52 ` [PATCH 11/11] tools/xl: add support for xenstore quota setting via domain config Juergen Gross
10 siblings, 2 replies; 43+ messages in thread
From: Juergen Gross @ 2026-03-05 13:52 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>
---
tools/golang/xenlight/helpers.gen.go | 6 ++++++
tools/golang/xenlight/types.gen.go | 1 +
tools/libs/light/libxl_dom.c | 8 ++++++++
tools/libs/light/libxl_domain.c | 10 ++++++++++
tools/libs/light/libxl_types.idl | 1 +
5 files changed, 26 insertions(+)
diff --git a/tools/golang/xenlight/helpers.gen.go b/tools/golang/xenlight/helpers.gen.go
index 6c9af904c5..416c2d3e07 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 5393277190..7e7292cf70 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 XsQuotaSet
}
type DomainBuildInfoTypeUnion interface {
diff --git a/tools/libs/light/libxl_dom.c b/tools/libs/light/libxl_dom.c
index 05ebc69534..1c0689d7ff 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_xsquota_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..86e43e7133 100644
--- a/tools/libs/light/libxl_domain.c
+++ b/tools/libs/light/libxl_domain.c
@@ -2533,6 +2533,16 @@ static void retrieve_domain_configuration_end(libxl__egc *egc,
}
}
+ /* Xenstore quota */
+ {
+ libxl_xs_quota_set_dispose(&d_config->b_info.xenstore_quota);
+ rc = libxl_xsquota_domain_get(CTX, domid, &d_config->b_info.xenstore_quota);
+ if (rc) {
+ LOGD(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 c5ddc40f35..ee8f89e462 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_set),
], dir=DIR_IN,
copy_deprecated_fn="libxl__domain_build_info_copy_deprecated",
--
2.53.0
^ permalink raw reply related [flat|nested] 43+ messages in thread* Re: [PATCH 09/11] tools/libxl: add support for xenstore quota in domain_config
2026-03-05 13:52 ` [PATCH 09/11] tools/libxl: add support for xenstore quota in domain_config Juergen Gross
@ 2026-03-10 13:57 ` Nick Rosbrook
2026-03-19 9:26 ` Anthony PERARD
1 sibling, 0 replies; 43+ messages in thread
From: Nick Rosbrook @ 2026-03-10 13:57 UTC (permalink / raw)
To: Juergen Gross; +Cc: xen-devel, George Dunlap, Anthony PERARD
On Thu, Mar 5, 2026 at 8:53 AM Juergen Gross <jgross@suse.com> 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>
> ---
> tools/golang/xenlight/helpers.gen.go | 6 ++++++
> tools/golang/xenlight/types.gen.go | 1 +
> tools/libs/light/libxl_dom.c | 8 ++++++++
> tools/libs/light/libxl_domain.c | 10 ++++++++++
> tools/libs/light/libxl_types.idl | 1 +
> 5 files changed, 26 insertions(+)
>
> diff --git a/tools/golang/xenlight/helpers.gen.go b/tools/golang/xenlight/helpers.gen.go
> index 6c9af904c5..416c2d3e07 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 5393277190..7e7292cf70 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 XsQuotaSet
> }
>
> type DomainBuildInfoTypeUnion interface {
For the golang stuff:
Acked-by: Nick Rosbrook <enr0n@ubuntu.com
^ permalink raw reply [flat|nested] 43+ messages in thread* Re: [PATCH 09/11] tools/libxl: add support for xenstore quota in domain_config
2026-03-05 13:52 ` [PATCH 09/11] tools/libxl: add support for xenstore quota in domain_config Juergen Gross
2026-03-10 13:57 ` Nick Rosbrook
@ 2026-03-19 9:26 ` Anthony PERARD
2026-03-19 11:01 ` Jürgen Groß
1 sibling, 1 reply; 43+ messages in thread
From: Anthony PERARD @ 2026-03-19 9:26 UTC (permalink / raw)
To: Juergen Gross; +Cc: xen-devel, Nick Rosbrook, George Dunlap
On Thu, Mar 05, 2026 at 02:52:06PM +0100, Juergen Gross wrote:
> diff --git a/tools/libs/light/libxl_domain.c b/tools/libs/light/libxl_domain.c
> index 5be47f687f..86e43e7133 100644
> --- a/tools/libs/light/libxl_domain.c
> +++ b/tools/libs/light/libxl_domain.c
> @@ -2533,6 +2533,16 @@ static void retrieve_domain_configuration_end(libxl__egc *egc,
> }
> }
>
> + /* Xenstore quota */
> + {
> + libxl_xs_quota_set_dispose(&d_config->b_info.xenstore_quota);
> + rc = libxl_xsquota_domain_get(CTX, domid, &d_config->b_info.xenstore_quota);
> + if (rc) {
> + LOGD(ERROR, domid, "Fail to get xenstore quota");
It would be useful to print "errno" with LOGED, right?
> + 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 c5ddc40f35..ee8f89e462 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_set),
This would deserve a new LIBXL_HAVE_ macro, or a mention about the new
field in the macro added in the previous patch.
Thanks,
--
Anthony Perard | Vates XCP-ng Developer
XCP-ng & Xen Orchestra - Vates solutions
web: https://vates.tech
^ permalink raw reply [flat|nested] 43+ messages in thread* Re: [PATCH 09/11] tools/libxl: add support for xenstore quota in domain_config
2026-03-19 9:26 ` Anthony PERARD
@ 2026-03-19 11:01 ` Jürgen Groß
0 siblings, 0 replies; 43+ messages in thread
From: Jürgen Groß @ 2026-03-19 11:01 UTC (permalink / raw)
To: Anthony PERARD; +Cc: xen-devel, Nick Rosbrook, George Dunlap
[-- Attachment #1.1.1: Type: text/plain, Size: 1659 bytes --]
On 19.03.26 10:26, Anthony PERARD wrote:
> On Thu, Mar 05, 2026 at 02:52:06PM +0100, Juergen Gross wrote:
>> diff --git a/tools/libs/light/libxl_domain.c b/tools/libs/light/libxl_domain.c
>> index 5be47f687f..86e43e7133 100644
>> --- a/tools/libs/light/libxl_domain.c
>> +++ b/tools/libs/light/libxl_domain.c
>> @@ -2533,6 +2533,16 @@ static void retrieve_domain_configuration_end(libxl__egc *egc,
>> }
>> }
>>
>> + /* Xenstore quota */
>> + {
>> + libxl_xs_quota_set_dispose(&d_config->b_info.xenstore_quota);
>> + rc = libxl_xsquota_domain_get(CTX, domid, &d_config->b_info.xenstore_quota);
>> + if (rc) {
>> + LOGD(ERROR, domid, "Fail to get xenstore quota");
>
> It would be useful to print "errno" with LOGED, right?
Okay.
>
>> + 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 c5ddc40f35..ee8f89e462 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_set),
>
> This would deserve a new LIBXL_HAVE_ macro, or a mention about the new
> field in the macro added in the previous patch.
I'll do the latter.
Thanks,
Juergen
[-- Attachment #1.1.2: OpenPGP public key --]
[-- Type: application/pgp-keys, Size: 3743 bytes --]
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 495 bytes --]
^ permalink raw reply [flat|nested] 43+ messages in thread
* [PATCH 10/11] tools/xl: add xl commands for xenstore quota operations
2026-03-05 13:51 [PATCH 00/11] tools: add support for per-domain xenstore quota Juergen Gross
` (8 preceding siblings ...)
2026-03-05 13:52 ` [PATCH 09/11] tools/libxl: add support for xenstore quota in domain_config Juergen Gross
@ 2026-03-05 13:52 ` Juergen Gross
2026-03-19 12:37 ` Anthony PERARD
2026-03-05 13:52 ` [PATCH 11/11] tools/xl: add support for xenstore quota setting via domain config Juergen Gross
10 siblings, 1 reply; 43+ messages in thread
From: Juergen Gross @ 2026-03-05 13:52 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>
---
tools/xl/Makefile | 1 +
tools/xl/xl.h | 2 +
tools/xl/xl_cmdtable.c | 10 +++++
tools/xl/xl_parse.c | 25 ++++++++++++
tools/xl/xl_parse.h | 1 +
tools/xl/xl_xsquota.c | 88 ++++++++++++++++++++++++++++++++++++++++++
6 files changed, 127 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..3de12b12ae 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 quota data",
+ "<Domain>|-g",
+ },
+ { "xenstore-quota-set",
+ &main_xsquota_set, 0, 1,
+ "Set global or domain specific Xenstore quota data",
+ "<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..934ad4eeef 100644
--- a/tools/xl/xl_parse.c
+++ b/tools/xl/xl_parse.c
@@ -1314,6 +1314,31 @@ out:
return ret;
}
+int parse_xsquota_item(const char *buf, struct libxl_xs_quota_item *item)
+{
+ const char *eq;
+ char *endptr;
+
+ 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;
+ item->val = strtoul(eq + 1, &endptr, 0);
+ if (errno || !eq[1] || *endptr) {
+ fprintf(stderr,
+ "Quota specification \"%s\" uses illegal value \"%s\".\n",
+ buf, eq);
+ return ERROR_INVAL;
+ }
+
+ 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..eaf19feac8
--- /dev/null
+++ b/tools/xl/xl_xsquota.c
@@ -0,0 +1,88 @@
+/* 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_set q;
+ unsigned int i;
+ int rc;
+
+ if (argc != 2) {
+ fprintf(stderr, "Domain or \"-g\" must be specified.\n");
+ return EXIT_FAILURE;
+ }
+
+ if (!strcmp(argv[1], "-g")) {
+ rc = libxl_xsquota_global_get(ctx, &q);
+ } else {
+ uint32_t domid = find_domain(argv[1]);
+
+ rc = libxl_xsquota_domain_get(ctx, domid, &q);
+ }
+
+ if (rc) {
+ 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_set_dispose(&q);
+
+ return EXIT_SUCCESS;
+}
+
+int main_xsquota_set(int argc, char **argv)
+{
+ unsigned int i;
+ libxl_xs_quota_set q;
+ int rc = EXIT_FAILURE;
+
+ if (argc < 3) {
+ fprintf(stderr, "Not enough parameters.\n");
+ return EXIT_FAILURE;
+ }
+
+ q.num_quota = argc - 2;
+ q.quota = calloc(q.num_quota, sizeof(*q.quota));
+ if (!q.quota) {
+ fprintf(stderr, "Memory allocation failure!\n");
+ goto err;
+ }
+
+ 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_xsquota_global_set(ctx, &q);
+ } else {
+ uint32_t domid = find_domain(argv[1]);
+
+ rc = libxl_xsquota_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_set_dispose(&q);
+
+ return rc;
+}
--
2.53.0
^ permalink raw reply related [flat|nested] 43+ messages in thread* Re: [PATCH 10/11] tools/xl: add xl commands for xenstore quota operations
2026-03-05 13:52 ` [PATCH 10/11] tools/xl: add xl commands for xenstore quota operations Juergen Gross
@ 2026-03-19 12:37 ` Anthony PERARD
2026-03-19 13:06 ` Jürgen Groß
0 siblings, 1 reply; 43+ messages in thread
From: Anthony PERARD @ 2026-03-19 12:37 UTC (permalink / raw)
To: Juergen Gross; +Cc: xen-devel
On Thu, Mar 05, 2026 at 02:52:07PM +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>
> ---
> tools/xl/Makefile | 1 +
> tools/xl/xl.h | 2 +
> tools/xl/xl_cmdtable.c | 10 +++++
> tools/xl/xl_parse.c | 25 ++++++++++++
> tools/xl/xl_parse.h | 1 +
> tools/xl/xl_xsquota.c | 88 ++++++++++++++++++++++++++++++++++++++++++
> 6 files changed, 127 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..3de12b12ae 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 quota data",
Maybe saying "quota values" instead of "quota data" would be slightly
better. Or maybe even "quotas" would be enough.
> + "<Domain>|-g",
> + },
> + { "xenstore-quota-set",
> + &main_xsquota_set, 0, 1,
> + "Set global or domain specific Xenstore quota data",
> + "<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..934ad4eeef 100644
> --- a/tools/xl/xl_parse.c
> +++ b/tools/xl/xl_parse.c
> @@ -1314,6 +1314,31 @@ out:
> return ret;
> }
>
> +int parse_xsquota_item(const char *buf, struct libxl_xs_quota_item *item)
> +{
> + const char *eq;
> + char *endptr;
> +
> + 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;
> + item->val = strtoul(eq + 1, &endptr, 0);
> + if (errno || !eq[1] || *endptr) {
I think we also need to check that the value returned by strtoul() can
actually be stored in `item->val`. It would be misleading to accept a
quota value and store a different one.
> + fprintf(stderr,
> + "Quota specification \"%s\" uses illegal value \"%s\".\n",
> + buf, eq);
> + return ERROR_INVAL;
> + }
> +
> + 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..eaf19feac8
> --- /dev/null
> +++ b/tools/xl/xl_xsquota.c
> @@ -0,0 +1,88 @@
> +/* 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_set q;
> + unsigned int i;
> + int rc;
You should call libxl_xs_quota_set_init(&q). (That would avoid a segv
later, when _disposed() is called.)
> +
> + if (argc != 2) {
> + fprintf(stderr, "Domain or \"-g\" must be specified.\n");
> + return EXIT_FAILURE;
> + }
> +
> + if (!strcmp(argv[1], "-g")) {
> + rc = libxl_xsquota_global_get(ctx, &q);
> + } else {
> + uint32_t domid = find_domain(argv[1]);
> +
> + rc = libxl_xsquota_domain_get(ctx, domid, &q);
> + }
> +
> + if (rc) {
> + 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_set_dispose(&q);
> +
> + return EXIT_SUCCESS;
> +}
> +
> +int main_xsquota_set(int argc, char **argv)
> +{
> + unsigned int i;
> + libxl_xs_quota_set q;
> + int rc = EXIT_FAILURE;
> +
> + if (argc < 3) {
> + fprintf(stderr, "Not enough parameters.\n");
I think you can call help("xenstore-quota-set") to provide the needed
info about what the parameters are.
> + return EXIT_FAILURE;
> + }
> +
> + q.num_quota = argc - 2;
> + q.quota = calloc(q.num_quota, sizeof(*q.quota));
> + if (!q.quota) {
> + fprintf(stderr, "Memory allocation failure!\n");
> + goto err;
> + }
There's `xcalloc() that can be use instead. It does check for memory
allocation failure.
(And that would avoid a segv in libxl_xs_quota_set_dispose() as
num_quota is set before quota is allocated.)
Thanks,
--
Anthony Perard | Vates XCP-ng Developer
XCP-ng & Xen Orchestra - Vates solutions
web: https://vates.tech
^ permalink raw reply [flat|nested] 43+ messages in thread* Re: [PATCH 10/11] tools/xl: add xl commands for xenstore quota operations
2026-03-19 12:37 ` Anthony PERARD
@ 2026-03-19 13:06 ` Jürgen Groß
0 siblings, 0 replies; 43+ messages in thread
From: Jürgen Groß @ 2026-03-19 13:06 UTC (permalink / raw)
To: Anthony PERARD; +Cc: xen-devel
[-- Attachment #1.1.1: Type: text/plain, Size: 6970 bytes --]
On 19.03.26 13:37, Anthony PERARD wrote:
> On Thu, Mar 05, 2026 at 02:52:07PM +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>
>> ---
>> tools/xl/Makefile | 1 +
>> tools/xl/xl.h | 2 +
>> tools/xl/xl_cmdtable.c | 10 +++++
>> tools/xl/xl_parse.c | 25 ++++++++++++
>> tools/xl/xl_parse.h | 1 +
>> tools/xl/xl_xsquota.c | 88 ++++++++++++++++++++++++++++++++++++++++++
>> 6 files changed, 127 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..3de12b12ae 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 quota data",
>
> Maybe saying "quota values" instead of "quota data" would be slightly
> better. Or maybe even "quotas" would be enough.
>
>> + "<Domain>|-g",
>> + },
>> + { "xenstore-quota-set",
>> + &main_xsquota_set, 0, 1,
>> + "Set global or domain specific Xenstore quota data",
>> + "<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..934ad4eeef 100644
>> --- a/tools/xl/xl_parse.c
>> +++ b/tools/xl/xl_parse.c
>> @@ -1314,6 +1314,31 @@ out:
>> return ret;
>> }
>>
>> +int parse_xsquota_item(const char *buf, struct libxl_xs_quota_item *item)
>> +{
>> + const char *eq;
>> + char *endptr;
>> +
>> + 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;
>> + item->val = strtoul(eq + 1, &endptr, 0);
>> + if (errno || !eq[1] || *endptr) {
>
> I think we also need to check that the value returned by strtoul() can
> actually be stored in `item->val`. It would be misleading to accept a
> quota value and store a different one.
>
>> + fprintf(stderr,
>> + "Quota specification \"%s\" uses illegal value \"%s\".\n",
>> + buf, eq);
>> + return ERROR_INVAL;
>> + }
>> +
>> + 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..eaf19feac8
>> --- /dev/null
>> +++ b/tools/xl/xl_xsquota.c
>> @@ -0,0 +1,88 @@
>> +/* 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_set q;
>> + unsigned int i;
>> + int rc;
>
> You should call libxl_xs_quota_set_init(&q). (That would avoid a segv
> later, when _disposed() is called.)
>
>> +
>> + if (argc != 2) {
>> + fprintf(stderr, "Domain or \"-g\" must be specified.\n");
>> + return EXIT_FAILURE;
>> + }
>> +
>> + if (!strcmp(argv[1], "-g")) {
>> + rc = libxl_xsquota_global_get(ctx, &q);
>> + } else {
>> + uint32_t domid = find_domain(argv[1]);
>> +
>> + rc = libxl_xsquota_domain_get(ctx, domid, &q);
>> + }
>> +
>> + if (rc) {
>> + 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_set_dispose(&q);
>> +
>> + return EXIT_SUCCESS;
>> +}
>> +
>> +int main_xsquota_set(int argc, char **argv)
>> +{
>> + unsigned int i;
>> + libxl_xs_quota_set q;
>> + int rc = EXIT_FAILURE;
>> +
>> + if (argc < 3) {
>> + fprintf(stderr, "Not enough parameters.\n");
>
> I think you can call help("xenstore-quota-set") to provide the needed
> info about what the parameters are.
>
>> + return EXIT_FAILURE;
>> + }
>> +
>> + q.num_quota = argc - 2;
>> + q.quota = calloc(q.num_quota, sizeof(*q.quota));
>> + if (!q.quota) {
>> + fprintf(stderr, "Memory allocation failure!\n");
>> + goto err;
>> + }
>
> There's `xcalloc() that can be use instead. It does check for memory
> allocation failure.
>
> (And that would avoid a segv in libxl_xs_quota_set_dispose() as
> num_quota is set before quota is allocated.)
I'm fine with all your suggestions.
Thanks,
Juergen
[-- Attachment #1.1.2: OpenPGP public key --]
[-- Type: application/pgp-keys, Size: 3743 bytes --]
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 495 bytes --]
^ permalink raw reply [flat|nested] 43+ messages in thread
* [PATCH 11/11] tools/xl: add support for xenstore quota setting via domain config
2026-03-05 13:51 [PATCH 00/11] tools: add support for per-domain xenstore quota Juergen Gross
` (9 preceding siblings ...)
2026-03-05 13:52 ` [PATCH 10/11] tools/xl: add xl commands for xenstore quota operations Juergen Gross
@ 2026-03-05 13:52 ` Juergen Gross
2026-03-19 13:06 ` Anthony PERARD
10 siblings, 1 reply; 43+ messages in thread
From: Juergen Gross @ 2026-03-05 13:52 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>
---
docs/man/xl.cfg.5.pod.in | 13 +++++++++++++
tools/xl/xl_parse.c | 23 ++++++++++++++++++++++-
2 files changed, 35 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 934ad4eeef..06a5b60736 100644
--- a/tools/xl/xl_parse.c
+++ b/tools/xl/xl_parse.c
@@ -1351,7 +1351,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;
@@ -1360,6 +1360,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;
@@ -1467,6 +1468,26 @@ 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 = calloc(num_xs_quota, sizeof(* b_info->xenstore_quota.quota));
+ if (b_info->xenstore_quota.quota == NULL) {
+ fprintf(stderr, "unable to allocate memory for xenstore_quota\n");
+ exit(-1);
+ }
+
+ 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] 43+ messages in thread* Re: [PATCH 11/11] tools/xl: add support for xenstore quota setting via domain config
2026-03-05 13:52 ` [PATCH 11/11] tools/xl: add support for xenstore quota setting via domain config Juergen Gross
@ 2026-03-19 13:06 ` Anthony PERARD
2026-03-19 13:11 ` Jürgen Groß
0 siblings, 1 reply; 43+ messages in thread
From: Anthony PERARD @ 2026-03-19 13:06 UTC (permalink / raw)
To: Juergen Gross; +Cc: xen-devel
On Thu, Mar 05, 2026 at 02:52:08PM +0100, Juergen Gross wrote:
> Add a new "xenstore-quota" domain config parameter for setting the
You mean `xenstore_quota` ^ here.
> Xenstore quota of a new domain via a list of <quota>=<val> items.
>
> Signed-off-by: Juergen Gross <jgross@suse.com>
> ---
> diff --git a/tools/xl/xl_parse.c b/tools/xl/xl_parse.c
> index 934ad4eeef..06a5b60736 100644
> --- a/tools/xl/xl_parse.c
> +++ b/tools/xl/xl_parse.c
> @@ -1467,6 +1468,26 @@ 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 = calloc(num_xs_quota, sizeof(* b_info->xenstore_quota.quota));
You can call xcalloc() instead, and then not need to check for
allocation failure.
And with the two changes: 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] 43+ messages in thread* Re: [PATCH 11/11] tools/xl: add support for xenstore quota setting via domain config
2026-03-19 13:06 ` Anthony PERARD
@ 2026-03-19 13:11 ` Jürgen Groß
0 siblings, 0 replies; 43+ messages in thread
From: Jürgen Groß @ 2026-03-19 13:11 UTC (permalink / raw)
To: Anthony PERARD; +Cc: xen-devel
[-- Attachment #1.1.1: Type: text/plain, Size: 1186 bytes --]
On 19.03.26 14:06, Anthony PERARD wrote:
> On Thu, Mar 05, 2026 at 02:52:08PM +0100, Juergen Gross wrote:
>> Add a new "xenstore-quota" domain config parameter for setting the
>
> You mean `xenstore_quota` ^ here.
>
>> Xenstore quota of a new domain via a list of <quota>=<val> items.
>>
>> Signed-off-by: Juergen Gross <jgross@suse.com>
>> ---
>> diff --git a/tools/xl/xl_parse.c b/tools/xl/xl_parse.c
>> index 934ad4eeef..06a5b60736 100644
>> --- a/tools/xl/xl_parse.c
>> +++ b/tools/xl/xl_parse.c
>> @@ -1467,6 +1468,26 @@ 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 = calloc(num_xs_quota, sizeof(* b_info->xenstore_quota.quota));
>
> You can call xcalloc() instead, and then not need to check for
> allocation failure.
>
> And with the two changes: Reviewed-by: Anthony PERARD <anthony.perard@vates.tech>
Thanks,
Juergen
[-- Attachment #1.1.2: OpenPGP public key --]
[-- Type: application/pgp-keys, Size: 3743 bytes --]
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 495 bytes --]
^ permalink raw reply [flat|nested] 43+ messages in thread