1
0
Fork 0

plugins: added scheduler plugin

Currently it is substitution for the rtctl tool,
but there are small differences in the syntax.

Format for the rules is:

group.GROUPNAME=RULE_PRIO:SCHED:PRIO:AFFINITY:REGEX

where RULE_PRIO defines tuned priority of the
rule. Rules are sorted according to priority.
This is needed for inheritence to be able
to reorder previously defined rule. For same
RULE_PRIO, rules should be processed in the order
there were defined (but this is Python interpreter
dependant). To disable inherited rule for GROUPNAME
use:

group.GROUPNAME=

SCHED must be one of:
 'f' for FIFO,
 'b' for batch,
 'r' for round robin,
 'o' for other,
 '*' means not to change.

AFFINITY is a hex number, see taskset(1) for details
about number of CPUs. The '*' means not to change.

REGEX is Python regex. It must match against output of

  ps -eo cmd

Any given process name may match more than one group.
Default priority and scheduling policy are taken from
the last matching rege.

You need to write 'group.' prefix before your
group name. This is because there may be added
other parameters to the plugin later.

Example of usage:

[scheduler]
group.kthreads=0:*:1:*:\[.*\]$
group.watchdog=0:f:99:*:\[watchdog.*\]

Resolves: rhbz#1100826

Signed-off-by: Jaroslav Škarvada <jskarvad@redhat.com>
This commit is contained in:
Jaroslav Škarvada 2014-10-01 00:41:03 +02:00
parent 71a0b2d523
commit bf42cb9ba3
2 changed files with 171 additions and 0 deletions

View file

@ -12,6 +12,7 @@ Requires(preun): systemd
Requires(postun): systemd
Requires: python-decorator, dbus-python, pygobject3-base, python-pyudev
Requires: virt-what, python-configobj, ethtool, gawk, kernel-tools, hdparm
Requires: util-linux
%description
The tuned package contains a daemon that tunes system settings dynamically.

View file

@ -0,0 +1,170 @@
import base
from decorators import *
import tuned.logs
import re
from subprocess import *
from tuned.utils.commands import commands
log = tuned.logs.get()
class SchedulerPlugin(base.Plugin):
"""
Plugin for tuning of scheduler. Currently it can control scheduling
priorities of system threads (it is substitution for the rtctl tool).
"""
def __init__(self, *args, **kwargs):
super(self.__class__, self).__init__(*args, **kwargs)
self._has_dynamic_options = True
self._cmd = commands()
def _scheduler_storage_key(self, instance):
return "%s/options" % instance.name
def _instance_init(self, instance):
instance._has_dynamic_tuning = False
instance._has_static_tuning = True
# FIXME: do we want to do this here?
# recover original values in case of crash
instance._scheduler_original = self._storage.get(self._scheduler_storage_key(instance), {})
if len(instance._scheduler_original) > 0:
log.info("recovering scheduling settings from previous run")
self._instance_unapply_static(instance)
instance._scheduler_original = {}
self._storage.unset(self._scheduler_storage_key(instance))
instance._scheduler = instance.options
def _instance_cleanup(self, instance):
pass
# self._storage.unset(self._scheduler_storage_key(instance))
def get_processes(self):
(rc, out) = self._cmd.execute(["ps", "-eopid,cmd", "--no-headers"])
if rc != 0 or len(out) <= 0:
return None
return dict(map(lambda (pid, cmd): (pid.lstrip(), cmd.lstrip()),
filter(lambda i: len(i) == 2, map(lambda s: s.split(None, 1), out.split("\n")))))
def _sched2abbrev(sched):
if (sched == "SCHED_OTHER"):
return
elif (sched == "SCHED_FIFO"):
return
elif (sched == "SCHED_RR"):
return
elif (sched == "SCHED_BATCH"):
return
elif (sched == "SCHED_IDLE"):
return
else:
return None
def _parse_val(self, val):
v = val.split(":", 1)
if len(v) == 2:
return v[1].strip()
else:
return None
def _get_rt(self, pid):
(rc, out) = self._cmd.execute(["chrt", "-p", str(pid)])
if rc != 0:
return None
vals = out.split("\n", 1)
if len(vals) > 1:
sched = self._parse_val(vals[0])
prio = self._parse_val(vals[1])
else:
sched = None
prio = None
log.debug("read scheduler policy '%s' and priority '%s' for pid '%s'" % (sched, prio, pid))
return (sched, prio)
def _get_affinity(self, pid):
(rc, out) = self._cmd.execute(["taskset", "-p", str(pid)])
if rc != 0:
return None
v = self._parse_val(out.split("\n", 1)[0])
log.debug("read affinity '%s' for pid '%s'" % (v, pid))
return v
def _schedcfg2param(self, sched):
if sched == "f":
return "-f"
elif sched == "b":
return "-b"
elif sched == "r":
return "-r"
elif sched == "o":
return "-o"
else:
return ""
def _sched2param(self, sched):
if sched == "SCHED_FIFO":
return "-f"
elif sched == "SCHED_BATCH":
return "-b"
elif sched == "SCHED_RR":
return "-r"
elif sched == "SCHED_OTHER":
return "-o"
elif sched == "SCHED_IDLE":
return "-i"
else:
return ""
def _set_rt(self, pid, sched, prio):
if pid is None or prio is None:
return
if sched is not None and len(sched) > 0:
schedl = [sched]
log.debug("setting scheduler policy to '%s' for PID '%s'" % (sched, pid))
else:
schedl = []
log.debug("setting scheduler priority to '%s' for PID '%s'" % (prio, pid))
self._cmd.execute(["chrt"] + schedl + ["-p", str(prio), str(pid)])
def _set_affinity(self, pid, affinity):
if pid is None or affinity is None:
return
log.debug("setting affinity to '%s' for PID '%s'" % (affinity, pid))
self._cmd.execute(["taskset", "-p", str(affinity), str(pid)])
def _instance_apply_static(self, instance):
ps = self.get_processes()
if ps is None:
log.error("error applying tuning, cannot get information about running processes")
return
sched_cfg = map(lambda (option, value): (option, value.split(":", 4)), instance._scheduler.items())
buf = filter(lambda (option, vals): re.match(r"group\.", option) and len(vals) == 5, sched_cfg)
sched_cfg = sorted(buf, key=lambda (option, vals): vals[0])
sched_all = dict()
for option, vals in sched_cfg:
try:
r = re.compile(vals[4])
except re.error as e:
log.error("error compiling regular expression: '%s'" % str(vals[4]))
continue
processes = filter(lambda (pid, cmd): re.search(r, cmd) is not None, ps.items())
sched = dict(map(lambda (pid, cmd): (pid, (cmd, option, vals[1], vals[2], vals[3], vals[4])), processes))
sched_all.update(sched)
for pid, vals in sched_all.items():
(sched, prio) = self._get_rt(pid)
affinity = self._get_affinity(pid)
if affinity is not None and sched is not None and prio is not None:
instance._scheduler_original[pid] = (vals[0], sched, prio, affinity)
self._set_rt(pid, self._schedcfg2param(vals[2]), vals[3])
if vals[4] != "*":
self._set_affinity(pid, vals[4])
self._storage.set("options", instance._scheduler_original)
def _instance_unapply_static(self, instance, profile_switch = False):
ps = self.get_processes()
for pid, vals in instance._scheduler_original.iteritems():
# if command line for the pid didn't change, it's very probably the same process
if ps[pid] == vals[0]:
self._set_rt(pid, self._sched2param(vals[1]), vals[2])
self._set_affinity(pid, vals[3])