X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=coresched.py;h=0802ad8ca7716b9d893f2fe0768d5ba988d6d60a;hb=b5f4c526ed94508bc4507f4ed04c87c0a1afa76e;hp=cc0282b051029b03a0a0585d8d63614a5ec49949;hpb=b96a2e5fd3601536d4e1af2701ceee57ad032ee9;p=nodemanager.git diff --git a/coresched.py b/coresched.py index cc0282b..0802ad8 100644 --- a/coresched.py +++ b/coresched.py @@ -21,8 +21,32 @@ class CoreSched: One core is always left unreserved for system slices. """ - def __init__(self): + def __init__(self, cgroup_var_name="cpuset.cpus", slice_attr_name="cpu_cores"): self.cpus = [] + self.cgroup_var_name = cgroup_var_name + self.slice_attr_name = slice_attr_name + + def get_cgroup_var(self, name): + """ decode cpuset.cpus or cpuset.mems into a list of units that can + be reserved. + """ + + data = open("/dev/cgroup/" + name).readline().strip() + + units = [] + + # cpuset.cpus could be something as arbitrary as: + # 0,1,2-3,4,5-6 + # deal with commas and ranges + for part in data.split(","): + unitRange = part.split("-") + if len(unitRange) == 1: + unitRange = (unitRange[0], unitRange[0]) + for i in range(int(unitRange[0]), int(unitRange[1])+1): + if not i in units: + units.append(i) + + return units def get_cpus(self): """ return a list of available cpu identifiers: [0,1,2,3...] @@ -33,20 +57,9 @@ class CoreSched: if self.cpus!=[]: return self.cpus - cpuset_cpus = open("/dev/cgroup/cpuset.cpus").readline().strip() + self.cpus = self.get_cgroup_var(self.cgroup_var_name) - # cpuset.cpus could be something as arbitrary as: - # 0,1,2-3,4,5-6 - # deal with commas and ranges - for part in cpuset_cpus.split(","): - cpuRange = part.split("-") - if len(cpuRange) == 1: - cpuRange = (cpuRange[0], cpuRange[0]) - for i in range(int(cpuRange[0]), int(cpuRange[1])+1): - if not i in self.cpus: - self.cpus.append(i) - - return self.cpus + return self.cpus def get_cgroups (self): """ return a list of cgroups @@ -60,36 +73,75 @@ class CoreSched: cgroups.append(filename) return cgroups + def decodeCoreSpec (self, cores): + """ Decode the value of the core attribute. It's a number, followed by + an optional letter "b" to indicate besteffort cores should also + be supplied. + """ + bestEffort = False + + if cores.endswith("b"): + cores = cores[:-1] + bestEffort = True + + try: + cores = int(cores) + except ValueError: + cores = 0 + + return (cores, bestEffort) + def adjustCores (self, slivers): """ slivers is a dict of {sliver_name: rec} rec is a dict of attributes rec['_rspec'] is the effective rspec """ - logger.log("CoreSched: adjusting cores") - cpus = self.get_cpus()[:] + logger.log("CoreSched (" + self.cgroup_var_name + "): available units: " + str(cpus)) + reservations = {} + # allocate the cores to the slivers that have them reserved for name, rec in slivers.iteritems(): rspec = rec["_rspec"] - cores = rspec.get("cpu_cores", 0) + cores = rspec.get(self.slice_attr_name, 0) + (cores, bestEffort) = self.decodeCoreSpec(cores) + while (cores>0): # one cpu core reserved for best effort and system slices if len(cpus)<=1: - logger.log("CoreSched: ran out of cpu cores while scheduling: " + name) + logger.log("CoreSched: ran out of units while scheduling sliver " + name) else: cpu = cpus.pop() - logger.log("CoreSched: allocating cpu " + str(cpu) + " to slice " + name) + logger.log("CoreSched: allocating unit " + str(cpu) + " to slice " + name) reservations[name] = reservations.get(name,[]) + [cpu] cores = cores-1 # the leftovers go to everyone else - logger.log("CoreSched: allocating cpus " + str(cpus) + " to _default") + logger.log("CoreSched: allocating unit " + str(cpus) + " to _default") reservations["_default"] = cpus[:] + # now check and see if any of our slices had the besteffort flag + # set + for name, rec in slivers.iteritems(): + rspec = rec["_rspec"] + cores = rspec.get(self.slice_attr_name, 0) + (cores, bestEffort) = self.decodeCoreSpec(cores) + + # if the bestEffort flag isn't set then we have nothing to do + if not bestEffort: + continue + + # note that if a reservation is [], then we don't need to add + # bestEffort cores to it, since it is bestEffort by default. + + if reservations.get(name,[]) != []: + reservations[name] = reservations[name] + reservations["_default"] + logger.log("CoreSched: adding besteffort units to " + name + ". new units = " + str(reservations[name])) + self.reserveCores(reservations) def reserveCores (self, reservations): @@ -109,17 +161,20 @@ class CoreSched: self.reserveDefault(default) for cgroup in self.get_cgroups(): - cpus = reservations.get(cgroup, default) - - logger.log("CoreSched: reserving " + cgroup + " " + str(cpus)) + if cgroup in reservations: + cpus = reservations[cgroup] + logger.log("CoreSched: reserving " + self.cgroup_var_name + " on " + cgroup + ": " + str(cpus)) + else: + # no log message for default; too much verbosity in the common case + cpus = default - file("/dev/cgroup/" + cgroup + "/cpuset.cpus", "w").write( self.listToRange(cpus) + "\n" ) + file("/dev/cgroup/" + cgroup + "/" + self.cgroup_var_name, "w").write( self.listToRange(cpus) + "\n" ) def reserveDefault (self, cpus): if not os.path.exists("/etc/vservers/.defaults/cgroup"): os.makedirs("/etc/vservers/.defaults/cgroup") - file("/etc/vservers/.defaults/cgroup/cpuset.cpus", "w").write( self.listToRange(cpus) + "\n" ) + file("/etc/vservers/.defaults/cgroup/" + self.cgroup_var_name, "w").write( self.listToRange(cpus) + "\n" ) def listToRange (self, list): """ take a list of items [1,2,3,5,...] and return it as a range: "1-3,5"