From: Denis Mukhin <dmukhin@ford.com>
Embedded deployments of Xen do not need to have support for more than dozen of
domains.
Introduce build-time configuration option to limit the number of domains during
run-time.
Also, move DOMID_FIRST_RESERVED compile-time check from Arm to common code.
Suggested-by: Julien Grall <julien@xen.org>
Signed-off-by: Denis Mukhin <dmukhin@ford.com>
---
This is an RFC.
Changes since v7:
- moved DOMID_FIRST_RESERVED compile-time checks to common code
- changed description for MAX_DOMID Kconfig option
---
 xen/arch/arm/tee/ffa.c              |  3 +--
 xen/arch/x86/cpu/mcheck/mce.c       |  2 +-
 xen/arch/x86/cpu/vpmu.c             |  2 +-
 xen/common/Kconfig                  |  7 +++++++
 xen/common/domain.c                 | 21 ++++++++++++---------
 xen/common/sched/core.c             |  4 ++--
 xen/drivers/passthrough/vtd/iommu.c |  2 +-
 xen/include/public/domctl.h         |  2 +-
 8 files changed, 26 insertions(+), 17 deletions(-)
diff --git a/xen/arch/arm/tee/ffa.c b/xen/arch/arm/tee/ffa.c
index 3bbdd7168a..7417ce6bed 100644
--- a/xen/arch/arm/tee/ffa.c
+++ b/xen/arch/arm/tee/ffa.c
@@ -331,10 +331,9 @@ static int ffa_domain_init(struct domain *d)
      * reserved for the hypervisor and we only support secure endpoints using
      * FF-A IDs with BIT 15 set to 1 so make sure those are not used by Xen.
      */
-    BUILD_BUG_ON(DOMID_FIRST_RESERVED >= UINT16_MAX);
     BUILD_BUG_ON((DOMID_MASK & BIT(15, U)) != 0);
 
-    if ( d->domain_id >= DOMID_FIRST_RESERVED )
+    if ( d->domain_id >= CONFIG_MAX_DOMID )
         return -ERANGE;
 
     ctx = xzalloc(struct ffa_ctx);
diff --git a/xen/arch/x86/cpu/mcheck/mce.c b/xen/arch/x86/cpu/mcheck/mce.c
index 1c348e557d..ee8ddd33b0 100644
--- a/xen/arch/x86/cpu/mcheck/mce.c
+++ b/xen/arch/x86/cpu/mcheck/mce.c
@@ -1493,7 +1493,7 @@ long do_mca(XEN_GUEST_HANDLE_PARAM(xen_mc_t) u_xen_mc)
             d = rcu_lock_domain_by_any_id(mc_msrinject->mcinj_domid);
             if ( d == NULL )
             {
-                if ( mc_msrinject->mcinj_domid >= DOMID_FIRST_RESERVED )
+                if ( mc_msrinject->mcinj_domid >= CONFIG_MAX_DOMID )
                     return x86_mcerr("do_mca inject: incompatible flag "
                                      "MC_MSRINJ_F_GPADDR with domain %d",
                                      -EINVAL, domid);
diff --git a/xen/arch/x86/cpu/vpmu.c b/xen/arch/x86/cpu/vpmu.c
index c28192ea26..67d423e088 100644
--- a/xen/arch/x86/cpu/vpmu.c
+++ b/xen/arch/x86/cpu/vpmu.c
@@ -174,7 +174,7 @@ void vpmu_do_interrupt(void)
      * in XENPMU_MODE_ALL, for everyone.
      */
     if ( (vpmu_mode & XENPMU_MODE_ALL) ||
-         (sampled->domain->domain_id >= DOMID_FIRST_RESERVED) )
+         (sampled->domain->domain_id >= CONFIG_MAX_DOMID) )
     {
         sampling = choose_hwdom_vcpu();
         if ( !sampling )
diff --git a/xen/common/Kconfig b/xen/common/Kconfig
index 6d43be2e6e..66b91840f2 100644
--- a/xen/common/Kconfig
+++ b/xen/common/Kconfig
@@ -576,4 +576,11 @@ config BUDDY_ALLOCATOR_SIZE
 	  Amount of memory reserved for the buddy allocator to serve Xen heap,
 	  working alongside the colored one.
 
+config MAX_DOMID
+	int "Maximum number of user domains"
+	range 1 32752
+	default 32752
+	help
+	  Specifies the maximum number of domains a user can create.
+
 endmenu
diff --git a/xen/common/domain.c b/xen/common/domain.c
index 01a65cb35d..204b71d096 100644
--- a/xen/common/domain.c
+++ b/xen/common/domain.c
@@ -68,7 +68,7 @@ struct domain *domain_list;
 
 /* Non-system domain ID allocator. */
 static DEFINE_SPINLOCK(domid_lock);
-static DECLARE_BITMAP(domid_bitmap, DOMID_FIRST_RESERVED);
+static DECLARE_BITMAP(domid_bitmap, CONFIG_MAX_DOMID);
 static domid_t domid_last;
 
 /*
@@ -155,7 +155,7 @@ int domain_init_states(void)
     ASSERT(rw_is_write_locked_by_me(¤t->domain->event_lock));
 
     dom_state_changed = xvzalloc_array(unsigned long,
-                                       BITS_TO_LONGS(DOMID_FIRST_RESERVED));
+                                       BITS_TO_LONGS(CONFIG_MAX_DOMID));
     if ( !dom_state_changed )
         return -ENOMEM;
 
@@ -235,7 +235,7 @@ int get_domain_state(struct xen_domctl_get_domain_state *info, struct domain *d,
     while ( dom_state_changed )
     {
         dom = find_first_bit(dom_state_changed, DOMID_MASK + 1);
-        if ( dom >= DOMID_FIRST_RESERVED )
+        if ( dom >= CONFIG_MAX_DOMID )
             break;
         if ( test_and_clear_bit(dom, dom_state_changed) )
         {
@@ -824,7 +824,7 @@ struct domain *domain_create(domid_t domid,
     /* Sort out our idea of is_hardware_domain(). */
     if ( (flags & CDF_hardware) || domid == hardware_domid )
     {
-        if ( hardware_domid < 0 || hardware_domid >= DOMID_FIRST_RESERVED )
+        if ( hardware_domid < 0 || hardware_domid >= CONFIG_MAX_DOMID )
             panic("The value of hardware_dom must be a valid domain ID\n");
 
         /* late_hwdom is only allowed for dom0. */
@@ -2414,9 +2414,12 @@ domid_t get_initial_domain_id(void)
 
 domid_t domid_alloc(domid_t domid)
 {
+    BUILD_BUG_ON(DOMID_FIRST_RESERVED >= UINT16_MAX);
+    BUILD_BUG_ON(DOMID_FIRST_RESERVED < CONFIG_MAX_DOMID);
+
     spin_lock(&domid_lock);
 
-    if ( domid < DOMID_FIRST_RESERVED )
+    if ( domid < CONFIG_MAX_DOMID )
     {
         if ( __test_and_set_bit(domid, domid_bitmap) )
             domid = DOMID_INVALID;
@@ -2426,13 +2429,13 @@ domid_t domid_alloc(domid_t domid)
         bool reserved = __test_and_set_bit(get_initial_domain_id(),
                                            domid_bitmap);
 
-        domid = find_next_zero_bit(domid_bitmap, DOMID_FIRST_RESERVED,
+        domid = find_next_zero_bit(domid_bitmap, CONFIG_MAX_DOMID,
                                    domid_last);
 
-        if ( domid == DOMID_FIRST_RESERVED )
-            domid = find_next_zero_bit(domid_bitmap, DOMID_FIRST_RESERVED, 0);
+        if ( domid == CONFIG_MAX_DOMID )
+            domid = find_next_zero_bit(domid_bitmap, CONFIG_MAX_DOMID, 0);
 
-        if ( domid == DOMID_FIRST_RESERVED )
+        if ( domid == CONFIG_MAX_DOMID )
         {
             domid = DOMID_INVALID;
         }
diff --git a/xen/common/sched/core.c b/xen/common/sched/core.c
index 9043414290..f1bfb6f6a2 100644
--- a/xen/common/sched/core.c
+++ b/xen/common/sched/core.c
@@ -867,7 +867,7 @@ int sched_init_domain(struct domain *d, unsigned int poolid)
     int ret;
 
     ASSERT(d->cpupool == NULL);
-    ASSERT(d->domain_id < DOMID_FIRST_RESERVED);
+    ASSERT(d->domain_id < CONFIG_MAX_DOMID);
 
     if ( (ret = cpupool_add_domain(d, poolid)) )
         return ret;
@@ -891,7 +891,7 @@ int sched_init_domain(struct domain *d, unsigned int poolid)
 
 void sched_destroy_domain(struct domain *d)
 {
-    ASSERT(d->domain_id < DOMID_FIRST_RESERVED);
+    ASSERT(d->domain_id < CONFIG_MAX_DOMID);
 
     if ( d->cpupool )
     {
diff --git a/xen/drivers/passthrough/vtd/iommu.c b/xen/drivers/passthrough/vtd/iommu.c
index c55f02c97e..5df85ca629 100644
--- a/xen/drivers/passthrough/vtd/iommu.c
+++ b/xen/drivers/passthrough/vtd/iommu.c
@@ -1509,7 +1509,7 @@ int domain_context_mapping_one(
 
         prev_did = context_domain_id(lctxt);
         domid = did_to_domain_id(iommu, prev_did);
-        if ( domid < DOMID_FIRST_RESERVED )
+        if ( domid < CONFIG_MAX_DOMID )
             prev_dom = rcu_lock_domain_by_id(domid);
         else if ( pdev ? domid == pdev->arch.pseudo_domid : domid > DOMID_MASK )
             prev_dom = rcu_lock_domain(dom_io);
diff --git a/xen/include/public/domctl.h b/xen/include/public/domctl.h
index 5b2063eed9..0c14c30c1b 100644
--- a/xen/include/public/domctl.h
+++ b/xen/include/public/domctl.h
@@ -36,7 +36,7 @@
 
 /*
  * NB. xen_domctl.domain is an IN/OUT parameter for this operation.
- * If it is specified as an invalid value (0 or >= DOMID_FIRST_RESERVED),
+ * If it is specified as an invalid value (0 or >= CONFIG_MAX_DOMID),
  * an id is auto-allocated and returned.
  */
 /* XEN_DOMCTL_createdomain */
-- 
2.34.1On 21.05.2025 02:00, dmkhn@proton.me wrote: > --- a/xen/arch/arm/tee/ffa.c > +++ b/xen/arch/arm/tee/ffa.c > @@ -331,10 +331,9 @@ static int ffa_domain_init(struct domain *d) > * reserved for the hypervisor and we only support secure endpoints using > * FF-A IDs with BIT 15 set to 1 so make sure those are not used by Xen. > */ > - BUILD_BUG_ON(DOMID_FIRST_RESERVED >= UINT16_MAX); Why's this being moved to common code? It certainly may have a purpose here (which I'm simply unaware of); I don't see what purpose it has in common code. > --- a/xen/common/Kconfig > +++ b/xen/common/Kconfig > @@ -576,4 +576,11 @@ config BUDDY_ALLOCATOR_SIZE > Amount of memory reserved for the buddy allocator to serve Xen heap, > working alongside the colored one. > > +config MAX_DOMID > + int "Maximum number of user domains" > + range 1 32752 > + default 32752 > + help > + Specifies the maximum number of domains a user can create. My prior comment remains: The description and help needs to be accurate, in order to not cause any confusion. In a true dom0less environment I'm not sure the "user" can create any domains (post boot, that is). And when there is Dom0 (or late hwdom), the number specified already isn't the number of domains one can create (again, post boot, which is how I understand "user domains"). If someone picked 1 as the value here, it's unclear to me how late hwdom or dom0less would work in the first place. > --- a/xen/include/public/domctl.h > +++ b/xen/include/public/domctl.h > @@ -36,7 +36,7 @@ > > /* > * NB. xen_domctl.domain is an IN/OUT parameter for this operation. > - * If it is specified as an invalid value (0 or >= DOMID_FIRST_RESERVED), > + * If it is specified as an invalid value (0 or >= CONFIG_MAX_DOMID), In the public interface I question the relevance of any implementation details of the hypervisor. Jan
On Wed, May 21, 2025 at 09:31:34AM +0200, Jan Beulich wrote:
> On 21.05.2025 02:00, dmkhn@proton.me wrote:
> > --- a/xen/arch/arm/tee/ffa.c
> > +++ b/xen/arch/arm/tee/ffa.c
> > @@ -331,10 +331,9 @@ static int ffa_domain_init(struct domain *d)
> >       * reserved for the hypervisor and we only support secure endpoints using
> >       * FF-A IDs with BIT 15 set to 1 so make sure those are not used by Xen.
> >       */
> > -    BUILD_BUG_ON(DOMID_FIRST_RESERVED >= UINT16_MAX);
> 
> Why's this being moved to common code? It certainly may have a purpose here
> (which I'm simply unaware of); I don't see what purpose it has in common
> code.
My understanding having DOMID_FIRST_RESERVED compile-time checks in one place
is good for testability: the check in question also applies to x86.
I will drop that hunk.
> 
> > --- a/xen/common/Kconfig
> > +++ b/xen/common/Kconfig
> > @@ -576,4 +576,11 @@ config BUDDY_ALLOCATOR_SIZE
> >  	  Amount of memory reserved for the buddy allocator to serve Xen heap,
> >  	  working alongside the colored one.
> >
> > +config MAX_DOMID
> > +	int "Maximum number of user domains"
> > +	range 1 32752
> > +	default 32752
> > +	help
> > +	  Specifies the maximum number of domains a user can create.
> 
> My prior comment remains: The description and help needs to be accurate, in
> order to not cause any confusion. In a true dom0less environment I'm not
> sure the "user" can create any domains (post boot, that is). And when there
> is Dom0 (or late hwdom), the number specified already isn't the number of
> domains one can create (again, post boot, which is how I understand "user
> domains"). If someone picked 1 as the value here, it's unclear to me how
> late hwdom or dom0less would work in the first place.
Do you think something like the following will be more accurate?
    config MAX_DOMID
       int "Maximum number of domains"
       range 1 32752
       default 32752
       help
         Specifies the maximum number of domains: dom0 or late hwdom,
         predefined domains, post-boot domains, excluding Xen system domains
         (domid >= DOMID_FIRST_RESERVED).
> 
> > --- a/xen/include/public/domctl.h
> > +++ b/xen/include/public/domctl.h
> > @@ -36,7 +36,7 @@
> >
> >  /*
> >   * NB. xen_domctl.domain is an IN/OUT parameter for this operation.
> > - * If it is specified as an invalid value (0 or >= DOMID_FIRST_RESERVED),
> > + * If it is specified as an invalid value (0 or >= CONFIG_MAX_DOMID),
> 
> In the public interface I question the relevance of any implementation
> details of the hypervisor.
Ack, will revert.
> 
> Jan
> 
                
            On 22.05.2025 02:09, dmkhn@proton.me wrote: > On Wed, May 21, 2025 at 09:31:34AM +0200, Jan Beulich wrote: >> On 21.05.2025 02:00, dmkhn@proton.me wrote: >>> --- a/xen/arch/arm/tee/ffa.c >>> +++ b/xen/arch/arm/tee/ffa.c >>> @@ -331,10 +331,9 @@ static int ffa_domain_init(struct domain *d) >>> * reserved for the hypervisor and we only support secure endpoints using >>> * FF-A IDs with BIT 15 set to 1 so make sure those are not used by Xen. >>> */ >>> - BUILD_BUG_ON(DOMID_FIRST_RESERVED >= UINT16_MAX); >> >> Why's this being moved to common code? It certainly may have a purpose here >> (which I'm simply unaware of); I don't see what purpose it has in common >> code. > > My understanding having DOMID_FIRST_RESERVED compile-time checks in one place > is good for testability: the check in question also applies to x86. > > I will drop that hunk. And also the other one, unless you can explain what exactly you're checking. The connection between DOMID_FIRST_RESERVED and UINT16_MAX is at best indirect, through domid_t. Yet if domid_t was widened (possible in principle, but breaking the ABI) that check would end up wrong without the compiler noticing (unless DOMID_FIRST_RESERVED was also bumped, which however is an independent thing). >>> --- a/xen/common/Kconfig >>> +++ b/xen/common/Kconfig >>> @@ -576,4 +576,11 @@ config BUDDY_ALLOCATOR_SIZE >>> Amount of memory reserved for the buddy allocator to serve Xen heap, >>> working alongside the colored one. >>> >>> +config MAX_DOMID >>> + int "Maximum number of user domains" >>> + range 1 32752 >>> + default 32752 >>> + help >>> + Specifies the maximum number of domains a user can create. >> >> My prior comment remains: The description and help needs to be accurate, in >> order to not cause any confusion. In a true dom0less environment I'm not >> sure the "user" can create any domains (post boot, that is). And when there >> is Dom0 (or late hwdom), the number specified already isn't the number of >> domains one can create (again, post boot, which is how I understand "user >> domains"). If someone picked 1 as the value here, it's unclear to me how >> late hwdom or dom0less would work in the first place. > > Do you think something like the following will be more accurate? > > config MAX_DOMID > int "Maximum number of domains" > range 1 32752 > default 32752 > help > Specifies the maximum number of domains: dom0 or late hwdom, > predefined domains, post-boot domains, excluding Xen system domains > (domid >= DOMID_FIRST_RESERVED). Especially the mention of DOMID_FIRST_RESERVED is too much of an implementation detail here, imo. Beyond that - maybe, but I'm not overly happy this way either. As an aside - MAX_DOMID and "Maximum number of domains" are conflicting with one another, too: Do you mean "maximum ID" or "maximum number of"? The two are different by 1. Jan
On Thu, May 22, 2025 at 09:01:51AM +0200, Jan Beulich wrote:
> On 22.05.2025 02:09, dmkhn@proton.me wrote:
> > On Wed, May 21, 2025 at 09:31:34AM +0200, Jan Beulich wrote:
> >> On 21.05.2025 02:00, dmkhn@proton.me wrote:
> >>> --- a/xen/arch/arm/tee/ffa.c
> >>> +++ b/xen/arch/arm/tee/ffa.c
> >>> @@ -331,10 +331,9 @@ static int ffa_domain_init(struct domain *d)
> >>>       * reserved for the hypervisor and we only support secure endpoints using
> >>>       * FF-A IDs with BIT 15 set to 1 so make sure those are not used by Xen.
> >>>       */
> >>> -    BUILD_BUG_ON(DOMID_FIRST_RESERVED >= UINT16_MAX);
> >>
> >> Why's this being moved to common code? It certainly may have a purpose here
> >> (which I'm simply unaware of); I don't see what purpose it has in common
> >> code.
> >
> > My understanding having DOMID_FIRST_RESERVED compile-time checks in one place
> > is good for testability: the check in question also applies to x86.
> >
> > I will drop that hunk.
> 
> And also the other one, unless you can explain what exactly you're checking.
> The connection between DOMID_FIRST_RESERVED and UINT16_MAX is at best
> indirect, through domid_t. Yet if domid_t was widened (possible in principle,
> but breaking the ABI) that check would end up wrong without the compiler
> noticing (unless DOMID_FIRST_RESERVED was also bumped, which however is an
> independent thing).
diff --git a/xen/include/public/xen.h b/xen/include/public/xen.h
index 82b9c05a76..452d9f63dc 100644
--- a/xen/include/public/xen.h
+++ b/xen/include/public/xen.h
@@ -572,7 +572,7 @@ DEFINE_XEN_GUEST_HANDLE(mmuext_op_t);
 #endif
 
 /* Domain ids >= DOMID_FIRST_RESERVED cannot be used for ordinary domains. */
-#define DOMID_FIRST_RESERVED xen_mk_uint(0x7FF0)
+#define DOMID_FIRST_RESERVED xen_mk_uint(0xFFFF7FF0)
 
 /* DOMID_SELF is used in certain contexts to refer to oneself. */
 #define DOMID_SELF           xen_mk_uint(0x7FF0)
The above patch ^^ pretty much passes the compilation on x86.
The compile time check in question currently exists for Arm only but could
catch such mistake when the limit is bumped (if any), it is easy to
spot.
> 
> >>> --- a/xen/common/Kconfig
> >>> +++ b/xen/common/Kconfig
> >>> @@ -576,4 +576,11 @@ config BUDDY_ALLOCATOR_SIZE
> >>>  	  Amount of memory reserved for the buddy allocator to serve Xen heap,
> >>>  	  working alongside the colored one.
> >>>
> >>> +config MAX_DOMID
> >>> +	int "Maximum number of user domains"
> >>> +	range 1 32752
> >>> +	default 32752
> >>> +	help
> >>> +	  Specifies the maximum number of domains a user can create.
> >>
> >> My prior comment remains: The description and help needs to be accurate, in
> >> order to not cause any confusion. In a true dom0less environment I'm not
> >> sure the "user" can create any domains (post boot, that is). And when there
> >> is Dom0 (or late hwdom), the number specified already isn't the number of
> >> domains one can create (again, post boot, which is how I understand "user
> >> domains"). If someone picked 1 as the value here, it's unclear to me how
> >> late hwdom or dom0less would work in the first place.
> >
> > Do you think something like the following will be more accurate?
> >
> >     config MAX_DOMID
> >        int "Maximum number of domains"
> >        range 1 32752
> >        default 32752
> >        help
> >          Specifies the maximum number of domains: dom0 or late hwdom,
> >          predefined domains, post-boot domains, excluding Xen system domains
> >          (domid >= DOMID_FIRST_RESERVED).
> 
> Especially the mention of DOMID_FIRST_RESERVED is too much of an implementation
> detail here, imo. Beyond that - maybe, but I'm not overly happy this way either.
Will the following description will be satisfactory?
config MAX_DOMID
       int "Maximum domain ID"
       range 1 32752
       default 32752
       help
         Specifies the maximum domain ID (dom0 or late hwdom, predefined
         domains, post-boot domains, excluding Xen system domains).
> 
> As an aside - MAX_DOMID and "Maximum number of domains" are conflicting
> with one another, too: Do you mean "maximum ID" or "maximum number of"? The two
> are different by 1.
That would be "maximum ID", thank you.
> 
> Jan
                
            On 22.05.2025 20:08, dmkhn@proton.me wrote: > On Thu, May 22, 2025 at 09:01:51AM +0200, Jan Beulich wrote: >> On 22.05.2025 02:09, dmkhn@proton.me wrote: >>> On Wed, May 21, 2025 at 09:31:34AM +0200, Jan Beulich wrote: >>>> On 21.05.2025 02:00, dmkhn@proton.me wrote: >>>>> --- a/xen/common/Kconfig >>>>> +++ b/xen/common/Kconfig >>>>> @@ -576,4 +576,11 @@ config BUDDY_ALLOCATOR_SIZE >>>>> Amount of memory reserved for the buddy allocator to serve Xen heap, >>>>> working alongside the colored one. >>>>> >>>>> +config MAX_DOMID >>>>> + int "Maximum number of user domains" >>>>> + range 1 32752 >>>>> + default 32752 >>>>> + help >>>>> + Specifies the maximum number of domains a user can create. >>>> >>>> My prior comment remains: The description and help needs to be accurate, in >>>> order to not cause any confusion. In a true dom0less environment I'm not >>>> sure the "user" can create any domains (post boot, that is). And when there >>>> is Dom0 (or late hwdom), the number specified already isn't the number of >>>> domains one can create (again, post boot, which is how I understand "user >>>> domains"). If someone picked 1 as the value here, it's unclear to me how >>>> late hwdom or dom0less would work in the first place. >>> >>> Do you think something like the following will be more accurate? >>> >>> config MAX_DOMID >>> int "Maximum number of domains" >>> range 1 32752 >>> default 32752 >>> help >>> Specifies the maximum number of domains: dom0 or late hwdom, >>> predefined domains, post-boot domains, excluding Xen system domains >>> (domid >= DOMID_FIRST_RESERVED). >> >> Especially the mention of DOMID_FIRST_RESERVED is too much of an implementation >> detail here, imo. Beyond that - maybe, but I'm not overly happy this way either. > > Will the following description will be satisfactory? > > config MAX_DOMID > int "Maximum domain ID" > range 1 32752 > default 32752 > help > Specifies the maximum domain ID (dom0 or late hwdom, predefined > domains, post-boot domains, excluding Xen system domains). This is better, yes, yet ... >> As an aside - MAX_DOMID and "Maximum number of domains" are conflicting >> with one another, too: Do you mean "maximum ID" or "maximum number of"? The two >> are different by 1. > > That would be "maximum ID", thank you. ... imo asking for "maximum" of something is perhaps a little odd. Asking for "number of" is the more usual thing imo. Yet as before - I remain unconvinced we need such a Kconfig setting in the first place. Jan
© 2016 - 2025 Red Hat, Inc.