blob: ef636c235fb81a49bb3f1b2fb92fc06f8dfe952c [file] [log] [blame]
#
# Copyright 2009 - 2013 Clark Williams <williams@redhat.com>
# Copyright 2012 - 2013 David Sommerseth <davids@redhat.com>
# Copyright 2014 - 2017 Clark Williams <williams@redhat.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
# For the avoidance of doubt the "preferred form" of this code is one which
# is in an open unpatent encumbered format. Where cryptographic key signing
# forms part of the process of creating an executable the information
# including keys needed to generate an equivalently functional executable
# are deemed to be part of the source code.
#
import sys, os, os.path, glob, subprocess
from signal import SIGTERM
from rteval.modules import rtevalRuntimeError
from rteval.modules.loads import CommandLineLoad
from rteval.Log import Log
from rteval.misc import expand_cpulist
from rteval.systopology import SysTopology
kernel_prefix="linux-4.9"
class KBuildJob(object):
'''Class to manage a build job bound to a particular node'''
def __init__(self, node, kdir, logger=None):
self.kdir = kdir
self.jobid = None
self.node = node
self.logger = logger
self.builddir = os.path.dirname(kdir)
self.objdir = "%s/node%d" % (self.builddir, int(node))
if not os.path.isdir(self.objdir):
os.mkdir(self.objdir)
if os.path.exists('/usr/bin/numactl'):
self.binder = 'numactl --cpunodebind %d' % int(self.node)
else:
self.binder = 'taskset -c %s' % str(self.node)
self.jobs = self.calc_jobs_per_cpu() * len(self.node)
self.log(Log.DEBUG, "node %d: jobs == %d" % (int(node), self.jobs))
self.runcmd = "%s make O=%s -C %s -j%d bzImage modules" % (self.binder, self.objdir, self.kdir, self.jobs)
self.cleancmd = "%s make O=%s -C %s clean allmodconfig" % (self.binder, self.objdir, self.kdir)
self.log(Log.DEBUG, "node%d kcompile command: %s" % (int(node), self.runcmd))
def __str__(self):
return self.runcmd
def log(self, logtype, msg):
if self.logger:
self.logger.log(logtype, "[kcompile node%d] %s" % (int(self.node), msg))
def calc_jobs_per_cpu(self):
mult = 2
self.log(Log.DEBUG, "calulating jobs for node %d" % int(self.node))
# get memory total in gigabytes
mem = int(self.node.meminfo['MemTotal']) / 1024.0 / 1024.0 / 1024.0
# ratio of gigabytes to #cores
ratio = float(mem) / float(len(self.node))
if ratio < 1.0:
ratio = 1.0
if ratio < 1.0 or ratio > 2.0:
mult = 1
self.log(Log.DEBUG, "memory/cores ratio on node %d: %f" % (int(self.node), ratio))
self.log(Log.DEBUG, "returning jobs/core value of: %d" % int(ratio) * mult)
return int(int(ratio) * int(mult))
def clean(self, sin=None, sout=None, serr=None):
self.log(Log.DEBUG, "cleaning objdir %s" % self.objdir)
subprocess.call(self.cleancmd, shell=True,
stdin=sin, stdout=sout, stderr=serr)
def run(self, sin=None, sout=None, serr=None):
self.log(Log.INFO, "starting workload on node %d" % int(self.node))
self.log(Log.DEBUG, "running on node %d: %s" % (int(self.node), self.runcmd))
self.jobid = subprocess.Popen(self.runcmd, shell=True,
stdin=sin, stdout=sout, stderr=serr)
def isrunning(self):
if self.jobid == None:
return False
return (self.jobid.poll() == None)
def stop(self):
if not self.jobid:
return True
return self.jobid.terminate()
class Kcompile(CommandLineLoad):
def __init__(self, config, logger):
self.buildjobs = {}
self.config = config
self.topology = SysTopology()
CommandLineLoad.__init__(self, "kcompile", config, logger)
self.logger = logger
def _WorkloadSetup(self):
# find our source tarball
if self._cfg.has_key('tarball'):
tarfile = os.path.join(self.srcdir, self._cfg.tarfile)
if not os.path.exists(tarfile):
raise rtevalRuntimeError(self, " tarfile %s does not exist!" % tarfile)
self.source = tarfile
else:
tarfiles = glob.glob(os.path.join(self.srcdir, "%s*" % kernel_prefix))
if len(tarfiles):
self.source = tarfiles[0]
else:
raise rtevalRuntimeError(self, " no kernel tarballs found in %s" % self.srcdir)
# check for existing directory
kdir=None
names=os.listdir(self.builddir)
for d in names:
if d.startswith(kernel_prefix):
kdir=d
break
if kdir == None:
self._log(Log.DEBUG, "unpacking kernel tarball")
tarargs = ['tar', '-C', self.builddir, '-x']
if self.source.endswith(".bz2"):
tarargs.append("-j")
elif self.source.endswith(".gz"):
tarargs.append("-z")
tarargs.append("-f")
tarargs.append(self.source)
try:
subprocess.call(tarargs)
except:
self._log(Log.DEBUG, "untar'ing kernel self.source failed!")
sys.exit(-1)
names = os.listdir(self.builddir)
for d in names:
self._log(Log.DEBUG, "checking %s" % d)
if d.startswith(kernel_prefix):
kdir=d
break
if kdir == None:
raise rtevalRuntimeError(self, "Can't find kernel directory!")
self.mydir = os.path.join(self.builddir, kdir)
self._log(Log.DEBUG, "mydir = %s" % self.mydir)
self._log(Log.DEBUG, "systopology: %s" % self.topology)
self.jobs = len(self.topology)
self.args = []
for n in self.topology:
self._log(Log.DEBUG, "Configuring build job for node %d" % int(n))
self.buildjobs[n] = KBuildJob(n, self.mydir, self.logger)
self.args.append(str(self.buildjobs[n])+";")
def _WorkloadBuild(self):
null = os.open("/dev/null", os.O_RDWR)
if self._logging:
out = self.open_logfile("kcompile-build.stdout")
err = self.open_logfile("kcompile-build.stderr")
else:
out = err = null
# clean up any damage from previous runs
try:
ret = subprocess.call(["make", "-C", self.mydir, "mrproper"],
stdin=null, stdout=out, stderr=err)
if ret:
raise rtevalRuntimeError(self, "kcompile setup failed: %d" % ret)
except KeyboardInterrupt, m:
self._log(Log.DEBUG, "keyboard interrupt, aborting")
return
self._log(Log.DEBUG, "ready to run")
if self._logging:
os.close(out)
os.close(err)
# clean up object dirs and make sure each has a config file
for n in self.topology:
self.buildjobs[n].clean(sin=null,sout=null,serr=null)
os.close(null)
self._setReady()
def _WorkloadPrepare(self):
self.__nullfd = os.open("/dev/null", os.O_RDWR)
if self._logging:
self.__outfd = self.open_logfile("kcompile.stdout")
self.__errfd = self.open_logfile("kcompile.stderr")
else:
self.__outfd = self.__errfd = self.__nullfd
if self._cfg.has_key('cpulist') and self._cfg.cpulist:
cpulist = self._cfg.cpulist
self.num_cpus = len(expand_cpulist(cpulist))
else:
cpulist = ""
def _WorkloadTask(self):
for n in self.topology:
if not self.buildjobs[n]:
raise RuntimeError, "Build job not set up for node %d" % int(n)
if self.buildjobs[n].jobid is None or self.buildjobs[n].jobid.poll() is not None:
self._log(Log.INFO, "Starting load on node %d" % n)
self.buildjobs[n].run(self.__nullfd, self.__outfd, self.__errfd)
def WorkloadAlive(self):
# if any of the jobs has stopped, return False
for n in self.topology:
if self.buildjobs[n].jobid.poll() is not None:
return False
return True
def _WorkloadCleanup(self):
self._log(Log.DEBUG, "out of stopevent loop")
for n in self.buildjobs:
if self.buildjobs[n].jobid.poll() == None:
self._log(Log.DEBUG, "stopping job on node %d" % int(n))
self.buildjobs[n].jobid.terminate()
self.buildjobs[n].jobid.wait()
del self.buildjobs[n].jobid
os.close(self.__nullfd)
del self.__nullfd
if self._logging:
os.close(self.__outfd)
del self.__outfd
os.close(self.__errfd)
del self.__errfd
self._setFinished()
def ModuleParameters():
return {"source": {"descr": "Source tar ball",
"default": "linux-4.9.tar.xz",
"metavar": "TARBALL"},
"jobspercore": {"descr": "Number of working threads per core",
"default": 2,
"metavar": "NUM"},
}
def create(config, logger):
return Kcompile(config, logger)