diff --git a/docs/man/xmdomain.cfg.pod.5 b/docs/man/xmdomain.cfg.pod.5 index 4b9a35a446..df868543b1 100644 --- a/docs/man/xmdomain.cfg.pod.5 +++ b/docs/man/xmdomain.cfg.pod.5 @@ -156,6 +156,16 @@ Specifies which CPU the domain should be started on, where 0 specifies the first cpu, 1 the second, and so on. This defaults to -1, which means Xen is free to pick which CPU to start on. +=item B + +Specifies a list of CPUs on which the domains' VCPUs are allowed to +execute upon. The syntax supports ranges (0-3), and negation, ^1. +For instance: + + cpus = "0-3,5,^1" + +Will result in CPUs 0, 2, 3, 5 being available for use by the domain. + =item B Extra information to append to the end of the kernel parameter line. diff --git a/tools/examples/xmexample.vmx b/tools/examples/xmexample.vmx index e3d4d44ba9..8b694e32fe 100644 --- a/tools/examples/xmexample.vmx +++ b/tools/examples/xmexample.vmx @@ -30,8 +30,10 @@ name = "ExampleVMXDomain" # the number of cpus guest platform has, default=1 vcpus=1 -# Which CPU to start domain on? -#cpu = -1 # leave to Xen to pick +# List of which CPUS this domain is allowed to use, default Xen picks +#cpus = "" # leave to Xen to pick +#cpus = "0" # all vcpus run on CPU0 +#cpus = "0-3,5,^1" # run on cpus 0,2,3,5 # Optionally define mac and/or bridge for the network interfaces. # Random MACs are assigned if not given. diff --git a/tools/examples/xmexample.vti b/tools/examples/xmexample.vti index 540543a991..5722eb0699 100644 --- a/tools/examples/xmexample.vti +++ b/tools/examples/xmexample.vti @@ -23,8 +23,10 @@ memory = 256 # A name for your domain. All domains must have different names. name = "ExampleVMXDomain" -# Which CPU to start domain on? -#cpu = -1 # leave to Xen to pick +# List of which CPUS this domain is allowed to use, default Xen picks +#cpus = "" # leave to Xen to pick +#cpus = "0" # all vcpus run on CPU0 +#cpus = "0-3,5,^1" # run on cpus 0,2,3,5 # Disable vif for now nics=0 diff --git a/tools/examples/xmexample1 b/tools/examples/xmexample1 index cd73c7c3fa..4e7fb4d72f 100644 --- a/tools/examples/xmexample1 +++ b/tools/examples/xmexample1 @@ -22,8 +22,10 @@ memory = 64 # A name for your domain. All domains must have different names. name = "ExampleDomain" -# Which CPU to start domain on? -#cpu = -1 # leave to Xen to pick +# List of which CPUS this domain is allowed to use, default Xen picks +#cpus = "" # leave to Xen to pick +#cpus = "0" # all vcpus run on CPU0 +#cpus = "0-3,5,^1" # run on cpus 0,2,3,5 # Number of Virtual CPUS to use, default is 1 #vcpus = 1 diff --git a/tools/examples/xmexample2 b/tools/examples/xmexample2 index ba89eef7eb..6a562a9609 100644 --- a/tools/examples/xmexample2 +++ b/tools/examples/xmexample2 @@ -51,9 +51,11 @@ memory = 64 # so we use the vmid to create a name. name = "VM%d" % vmid -# Which CPU to start domain on? -#cpu = -1 # leave to Xen to pick -cpu = vmid # set based on vmid (mod number of CPUs) +# List of which CPUS this domain is allowed to use, default Xen picks +#cpus = "" # leave to Xen to pick +#cpus = "0" # all vcpus run on CPU0 +#cpus = "0-3,5,^1" # run on cpus 0,2,3,5 +#cpus = "%s" % vmid # set based on vmid (mod number of CPUs) # Number of Virtual CPUS to use, default is 1 #vcpus = 1 diff --git a/tools/examples/xmexample3 b/tools/examples/xmexample3 index 45211c7129..cdb993a2ff 100644 --- a/tools/examples/xmexample3 +++ b/tools/examples/xmexample3 @@ -51,9 +51,11 @@ memory = 64 # so we use the vmid to create a name. name = "VM%d" % vmid -# Which CPU to start domain on? -#cpu = -1 # leave to Xen to pick -cpu = vmid # set based on vmid (mod number of CPUs) +# List of which CPUS this domain is allowed to use, default Xen picks +#cpus = "" # leave to Xen to pick +#cpus = "0" # all vcpus run on CPU0 +#cpus = "0-3,5,^1" # run on cpus 0,2,3,5 +cpus = "%s" % vmid # set based on vmid (mod number of CPUs) #---------------------------------------------------------------------------- # Define network interfaces. diff --git a/tools/python/xen/xend/XendDomainInfo.py b/tools/python/xen/xend/XendDomainInfo.py index 0a2ebe52aa..9c9d23b436 100644 --- a/tools/python/xen/xend/XendDomainInfo.py +++ b/tools/python/xen/xend/XendDomainInfo.py @@ -286,6 +286,7 @@ def parseConfig(config): result[e[0]] = get_cfg(e[0], e[1]) result['cpu'] = get_cfg('cpu', int) + result['cpus'] = get_cfg('cpus', str) result['image'] = get_cfg('image') try: @@ -299,6 +300,43 @@ def parseConfig(config): 'Invalid configuration setting: vcpus = %s: %s' % (sxp.child_value(result['image'], 'vcpus', 1), str(exn))) + try: + # support legacy config files with 'cpu' parameter + # NB: prepending to list to support previous behavior + # where 'cpu' parameter pinned VCPU0. + if result['cpu']: + if result['cpus']: + result['cpus'] = "%s,%s" % (str(result['cpu']), result['cpus']) + else: + result['cpus'] = str(result['cpu']) + + # convert 'cpus' string to list of ints + # 'cpus' supports a list of ranges (0-3), seperated by + # commas, and negation, (^1). + # Precedence is settled by order of the string: + # "0-3,^1" -> [0,2,3] + # "0-3,^1,1" -> [0,1,2,3] + if result['cpus']: + cpus = [] + for c in result['cpus'].split(','): + if c.find('-') != -1: + (x,y) = c.split('-') + for i in range(int(x),int(y)+1): + cpus.append(int(i)) + else: + # remove this element from the list + if c[0] == '^': + cpus = [x for x in cpus if x != int(c[1])] + else: + cpus.append(int(c)) + + result['cpus'] = cpus + + except ValueError, exn: + raise VmError( + 'Invalid configuration setting: cpus = %s: %s' % + (result['cpus'], exn)) + result['backend'] = [] for c in sxp.children(config, 'backend'): result['backend'].append(sxp.name(sxp.child0(c))) @@ -486,6 +524,7 @@ class XendDomainInfo: defaultInfo('on_reboot', lambda: "restart") defaultInfo('on_crash', lambda: "restart") defaultInfo('cpu', lambda: None) + defaultInfo('cpus', lambda: []) defaultInfo('cpu_weight', lambda: 1.0) # some domains don't have a config file (e.g. dom0 ) @@ -1130,9 +1169,15 @@ class XendDomainInfo: xc.domain_setmaxmem(self.domid, m) xc.domain_memory_increase_reservation(self.domid, m, 0, 0) - cpu = self.info['cpu'] - if cpu is not None and cpu != -1: - xc.domain_pincpu(self.domid, 0, 1 << cpu) + # repin domain vcpus if a restricted cpus list is provided + # this is done prior to memory allocation to aide in memory + # distribution for NUMA systems. + cpus = self.info['cpus'] + if cpus is not None and len(cpus) > 0: + for v in range(0, self.info['max_vcpu_id']+1): + # pincpu takes a list of ints + cpu = [ int( cpus[v % len(cpus)] ) ] + xc.domain_pincpu(self.domid, v, cpu) self.createChannels() diff --git a/tools/python/xen/xm/create.py b/tools/python/xen/xm/create.py index 211474dc7e..7577d2b637 100644 --- a/tools/python/xen/xm/create.py +++ b/tools/python/xen/xm/create.py @@ -154,7 +154,11 @@ gopts.var('maxmem', val='MEMORY', gopts.var('cpu', val='CPU', fn=set_int, default=None, - use="CPU to run the domain on.") + use="CPU to run the VCPU0 on.") + +gopts.var('cpus', val='CPUS', + fn=set_int, default=None, + use="CPUS to run the domain on.") gopts.var('lapic', val='LAPIC', fn=set_int, default=0, @@ -572,6 +576,8 @@ def make_config(vals): if vals.cpu is not None: config.append(['cpu', vals.cpu]) + if vals.cpus is not None: + config.append(['cpus', vals.cpus]) if vals.cpu_weight is not None: config.append(['cpu_weight', vals.cpu_weight]) if vals.blkif: diff --git a/tools/python/xen/xm/main.py b/tools/python/xen/xm/main.py index 83c1bc4794..0053ac9aad 100644 --- a/tools/python/xen/xm/main.py +++ b/tools/python/xen/xm/main.py @@ -459,7 +459,9 @@ def xm_vcpu_list(args): for x in server.xend_node()[1:]: if len(x) > 1 and x[0] == 'nr_cpus': nr_cpus = int(x[1]) - cpumap = filter(lambda x: x < nr_cpus, cpumap) + # normalize cpumap by modulus nr_cpus, and drop duplicates + cpumap = dict.fromkeys( + map(lambda x: x % nr_cpus, cpumap)).keys() if len(cpumap) == nr_cpus: return "any cpu" break