References: bsc#901488

# Commit 7e73a6e7f12ae080222c5d339799905de3443a6e
# Date 2014-12-11 17:14:07 +0100
# Author Jan Beulich <jbeulich@suse.com>
# Committer Jan Beulich <jbeulich@suse.com>
have architectures specify the number of PIRQs Dom0 gets

The current value of nr_static_irqs + 256 is often too small for larger
systems. Make it dependent on CPU count and number of IO-APIC pins on
x86, and (until it obtains PCI support) simply NR_IRQS on ARM.

Signed-off-by: Jan Beulich <jbeulich@suse.com>
Acked-by: David Vrabel <david.vrabel@citrix.com>
Acked-by: Ian Campbell <ian.campbell@citrix.com>

# Commit 75d28c917094b0e264874e92e8980b00a372b99f
# Date 2015-05-22 10:13:04 +0200
# Author Andrew Cooper <andrew.cooper3@citrix.com>
# Committer Jan Beulich <jbeulich@suse.com>
x86/irq: limit the maximum number of domain PIRQs

c/s 7e73a6e "have architectures specify the number of PIRQs a hardware domain
gets" increased the default number of pirqs for dom0, as 256 was found to be
too low in some cases.

However, it didn't account for the upper bound presented by the domains EOI
bitmap, registered with the PHYSDEVOP_pirq_eoi_gmfn_v* hypercall.

On a server with 240 cpus, Xen was observed to be attempting to clear the EOI
bit for dom0's pirq 0xb40f, which hit a pagefault.

Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>

Index: xen-4.4.4-testing/docs/misc/xen-command-line.markdown
===================================================================
--- xen-4.4.4-testing.orig/docs/misc/xen-command-line.markdown
+++ xen-4.4.4-testing/docs/misc/xen-command-line.markdown
@@ -540,13 +540,16 @@ effect the inverse meaning.
 ### extra\_guest\_irqs
 > `= [<domU number>][,<dom0 number>]`
 
-> Default: `32,256`
+> Default: `32,<variable>`
 
 Change the number of PIRQs available for guests.  The optional first number is
 common for all domUs, while the optional second number (preceded by a comma)
 is for dom0.  Changing the setting for domU has no impact on dom0 and vice
 versa.  For example to change dom0 without changing domU, use
-`extra_guest_irqs=,512`
+`extra_guest_irqs=,512`.  The default value for Dom0 is architecture
+dependent.
+Note that specifying zero as domU value means zero, while for dom0 it means
+to use the default.
 
 ### flask\_enabled
 > `= <integer>`
Index: xen-4.4.4-testing/xen/arch/x86/domain_build.c
===================================================================
--- xen-4.4.4-testing.orig/xen/arch/x86/domain_build.c
+++ xen-4.4.4-testing/xen/arch/x86/domain_build.c
@@ -100,7 +100,7 @@ static void __init parse_dom0_max_vcpus(
 }
 custom_param("dom0_max_vcpus", parse_dom0_max_vcpus);
 
-struct vcpu *__init alloc_dom0_vcpu0(void)
+unsigned int __init dom0_max_vcpus(void)
 {
     unsigned max_vcpus;
 
@@ -112,6 +112,13 @@ struct vcpu *__init alloc_dom0_vcpu0(voi
     if ( max_vcpus > MAX_VIRT_CPUS )
         max_vcpus = MAX_VIRT_CPUS;
 
+    return max_vcpus;
+}
+
+struct vcpu *__init alloc_dom0_vcpu0(void)
+{
+    unsigned int max_vcpus = dom0_max_vcpus();
+
     dom0->vcpu = xzalloc_array(struct vcpu *, max_vcpus);
     if ( !dom0->vcpu )
         return NULL;
Index: xen-4.4.4-testing/xen/arch/x86/io_apic.c
===================================================================
--- xen-4.4.4-testing.orig/xen/arch/x86/io_apic.c
+++ xen-4.4.4-testing/xen/arch/x86/io_apic.c
@@ -33,6 +33,7 @@
 #include <asm/smp.h>
 #include <asm/desc.h>
 #include <asm/msi.h>
+#include <asm/setup.h>
 #include <mach_apic.h>
 #include <io_ports.h>
 #include <public/physdev.h>
@@ -2613,3 +2614,17 @@ void __init init_ioapic_mappings(void)
            nr_irqs_gsi, nr_irqs - nr_irqs_gsi);
 }
 
+unsigned int arch_dom0_irqs(void)
+{
+    unsigned int n = min_t(unsigned int, fls(num_present_cpus()),
+                           dom0_max_vcpus());
+
+    n = min(nr_irqs_gsi + n * NR_DYNAMIC_VECTORS, nr_irqs);
+
+    /* Bounded by the domain pirq eoi bitmap gfn. */
+    n = min_t(unsigned int, n, PAGE_SIZE * BITS_PER_BYTE);
+
+    printk("Dom0 has maximum %u PIRQs\n", n);
+
+    return n;
+}
Index: xen-4.4.4-testing/xen/arch/x86/irq.c
===================================================================
--- xen-4.4.4-testing.orig/xen/arch/x86/irq.c
+++ xen-4.4.4-testing/xen/arch/x86/irq.c
@@ -1070,13 +1070,19 @@ bool_t cpu_has_pending_apic_eoi(void)
 static inline void set_pirq_eoi(struct domain *d, unsigned int irq)
 {
     if ( is_pv_domain(d) && d->arch.pv_domain.pirq_eoi_map )
+    {
+        ASSERT(irq < PAGE_SIZE * BITS_PER_BYTE);
         set_bit(irq, d->arch.pv_domain.pirq_eoi_map);
+    }
 }
 
 static inline void clear_pirq_eoi(struct domain *d, unsigned int irq)
 {
     if ( is_pv_domain(d) && d->arch.pv_domain.pirq_eoi_map )
+    {
+        ASSERT(irq < PAGE_SIZE * BITS_PER_BYTE);
         clear_bit(irq, d->arch.pv_domain.pirq_eoi_map);
+    }
 }
 
 static void set_eoi_ready(void *data);
Index: xen-4.4.4-testing/xen/common/domain.c
===================================================================
--- xen-4.4.4-testing.orig/xen/common/domain.c
+++ xen-4.4.4-testing/xen/common/domain.c
@@ -183,7 +183,7 @@ struct vcpu *alloc_vcpu(
     return v;
 }
 
-static unsigned int __read_mostly extra_dom0_irqs = 256;
+static unsigned int __read_mostly extra_dom0_irqs;
 static unsigned int __read_mostly extra_domU_irqs = 32;
 static void __init parse_extra_guest_irqs(const char *s)
 {
@@ -270,7 +270,8 @@ struct domain *domain_create(
         if ( domid )
             d->nr_pirqs = nr_static_irqs + extra_domU_irqs;
         else
-            d->nr_pirqs = nr_static_irqs + extra_dom0_irqs;
+            d->nr_pirqs = extra_dom0_irqs ? nr_static_irqs + extra_dom0_irqs
+                                          : arch_dom0_irqs();
         if ( d->nr_pirqs > nr_irqs )
             d->nr_pirqs = nr_irqs;
 
Index: xen-4.4.4-testing/xen/include/asm-arm/irq.h
===================================================================
--- xen-4.4.4-testing.orig/xen/include/asm-arm/irq.h
+++ xen-4.4.4-testing/xen/include/asm-arm/irq.h
@@ -21,10 +21,10 @@ struct irq_cfg {
 
 #define NR_LOCAL_IRQS	32
 #define NR_IRQS		1024
-#define nr_irqs NR_IRQS
 
 #define nr_irqs NR_IRQS
 #define nr_static_irqs NR_IRQS
+#define arch_dom0_irqs() NR_IRQS
 
 struct irq_desc;
 struct irqaction;
Index: xen-4.4.4-testing/xen/include/asm-x86/setup.h
===================================================================
--- xen-4.4.4-testing.orig/xen/include/asm-x86/setup.h
+++ xen-4.4.4-testing/xen/include/asm-x86/setup.h
@@ -36,6 +36,8 @@ int construct_dom0(
 unsigned long initial_images_nrpages(void);
 void discard_initial_images(void);
 
+unsigned int dom0_max_vcpus(void);
+
 int xen_in_range(unsigned long mfn);
 
 void microcode_grab_module(
Index: xen-4.4.4-testing/xen/include/xen/irq.h
===================================================================
--- xen-4.4.4-testing.orig/xen/include/xen/irq.h
+++ xen-4.4.4-testing/xen/include/xen/irq.h
@@ -149,4 +149,8 @@ static inline void set_native_irq_info(u
 
 unsigned int set_desc_affinity(struct irq_desc *, const cpumask_t *);
 
+#ifndef arch_dom0_irqs
+unsigned int arch_dom0_irqs(void);
+#endif
+
 #endif /* __XEN_IRQ_H__ */
