1
0
Fork 0

scheduler: Improve logging

Debug messages are now logged when setting some task's parameter
fails because the task vanished. Previously errors were logged.

Log messages (for kernel threads and zombie processes) or warnings
(otherwise) are now logged when setting a task's affinity fails because
its affinity is fixed. Previously either errors or nothing was logged,
depending on the specific case.

Resolves: rhbz#1474961

Signed-off-by: Ondřej Lysoněk <olysonek@redhat.com>
This commit is contained in:
Ondřej Lysoněk 2017-08-29 13:47:13 +02:00
parent ff6f69f604
commit f9f5b073a5
2 changed files with 77 additions and 18 deletions

View file

@ -14,6 +14,7 @@ import tuned.consts as consts
import procfs
import schedutils
from tuned.utils.commands import commands
import errno
log = tuned.logs.get()
@ -27,8 +28,6 @@ class SchedulerPlugin(base.Plugin):
_dict_sched2param = {"SCHED_FIFO":"f", "SCHED_BATCH":"b", "SCHED_RR":"r",
"SCHED_OTHER":"o", "SCHED_IDLE":"i"}
PF_NO_SETAFFINITY = 0x04000000
def __init__(self, monitor_repository, storage_factory, hardware_inventory, device_matcher, device_matcher_udev, plugin_instance_factory, global_cfg, variables):
super(self.__class__, self).__init__(monitor_repository, storage_factory, hardware_inventory, device_matcher, device_matcher_udev, plugin_instance_factory, global_cfg, variables)
self._has_dynamic_options = True
@ -121,9 +120,20 @@ class SchedulerPlugin(base.Plugin):
else:
return None
def _pid_exists(self, pid):
try:
p = procfs.pidstat(pid)
return True
except:
return False
def _get_rt(self, pid):
(rc, out) = self._cmd.execute(["chrt", "-p", str(pid)])
(rc, out, err_msg) = self._cmd.execute(["chrt", "-p", str(pid)], return_err = True)
if rc != 0:
if self._pid_exists(pid):
log.error(err_msg)
else:
log.debug("Unable to read scheduling parameters for PID %s, the task vanished." % pid)
return None
vals = out.split("\n", 1)
if len(vals) > 1:
@ -136,8 +146,13 @@ class SchedulerPlugin(base.Plugin):
return (sched, prio)
def _get_affinity(self, pid, no_error = False):
(rc, out) = self._cmd.execute(["taskset", "-p", str(pid)], no_errors = [1] if no_error else [])
(rc, out, err_msg) = self._cmd.execute(["taskset", "-p", str(pid)], no_errors = [1] if no_error else [], return_err = True)
if rc != 0:
if rc != 1 or not no_error:
if self._pid_exists(pid):
log.error(err_msg)
else:
log.debug("Unable to read affinity for PID %s, the task vanished." % pid)
return None
v = self._parse_val(out.split("\n", 1)[0])
log.debug("read affinity '%s' for pid '%s'" % (v, pid))
@ -165,13 +180,54 @@ class SchedulerPlugin(base.Plugin):
else:
schedl = []
log.debug("setting scheduler priority to '%s' for PID '%s'" % (prio, pid))
self._cmd.execute(["chrt"] + schedl + ["-p", str(prio), str(pid)], no_errors = [1] if no_error else [])
(ret, out, err_msg) = self._cmd.execute(["chrt"] + schedl + ["-p", str(prio), str(pid)], no_errors = [1] if no_error else [], return_err = True)
if ret == 0 or (ret == 1 and no_error):
return
if self._pid_exists(pid):
log.error(err_msg)
else:
log.debug("Unable to set scheduling parameters for PID %s, the task vanished." % pid)
# Return codes:
# 0 - Affinity is fixed
# 1 - Affinity is changeable
# -1 - Task vanished
# -2 - Error
def _affinity_changeable(self, pid, process = None):
try:
if process is None:
process = procfs.process(pid)
if process["stat"].is_bound_to_cpu():
if process["stat"]["state"] == "Z":
log.debug("Affinity of zombie task with PID %s cannot be changed, the task's affinity mask is fixed." % pid)
elif len(process["cmdline"]) == 0:
log.debug("Affinity of kernel thread with PID %s cannot be changed, the task's affinity mask is fixed." % pid)
else:
log.warn("Affinity of task with PID %s cannot be changed, the task's affinity mask is fixed." % pid)
return 0
else:
return 1
except IOError as e:
if e[0] == errno.ENOENT or e[0] == errno.ESRCH:
log.debug("Unable to set affinity for PID %s, the task vanished." % pid)
return -1
else:
log.error("Failed to get task info for PID %s: %s" % (pid, str(e)))
return -2
except (OSError, AttributeError, KeyError) as e:
log.error("Failed to get task info for PID %s: %s" % (pid, str(e)))
return -2
def _set_affinity(self, pid, affinity, no_error = False):
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)], no_errors = [1] if no_error else [])
(ret, out, err_msg) = self._cmd.execute(["taskset", "-p", str(affinity), str(pid)], no_errors = [1] if no_error else [], return_err = True)
if ret == 0 or (ret == 1 and no_error):
return
res = self._affinity_changeable(pid)
if res == 1 or res == -2:
log.error(err_msg)
#tune process and store previous values
def _tune_process(self, instance, pid, cmd, sched, prio, affinity, no_error = False):
@ -289,13 +345,6 @@ class SchedulerPlugin(base.Plugin):
if enabling and value is not None:
self._ps_blacklist = "|".join(map(lambda v: "(%s)" % v, re.split(r"(?<!\\);", str(value))))
def _affinity_changeable(self, p):
try:
flags = p["stat"]["flags"]
except (IOError, OSError, AttributeError, KeyError):
return False
return not bool(int(flags) & self.PF_NO_SETAFFINITY)
# TODO: merge with _get_affinity
def _get_affinity2(self, pid):
try:
@ -303,6 +352,7 @@ class SchedulerPlugin(base.Plugin):
# Workaround for old python-schedutils which incorrectly raised error
except (SystemError, OSError) as e:
if e[0] == 3:
log.debug("Unable to read affinity for PID %s, the task vanished." % pid)
return None
log.error("unable to get affinity for PID '%s': %s" % (str(pid), e))
return None
@ -314,6 +364,7 @@ class SchedulerPlugin(base.Plugin):
# Workaround for old python-schedutils which incorrectly raised error
except (SystemError, OSError) as e:
if e[0] == 3:
log.debug("Unable to set affinity for PID %s, the task vanished." % pid)
return False
log.error("unable to set affinity '%s' for PID '%s': %s" % (str(affinity), str(pid), e))
return False
@ -329,7 +380,7 @@ class SchedulerPlugin(base.Plugin):
def _set_all_obj_affinity(self, objs, affinity, threads = False, intersect = False):
_affinity = affinity
for obj in objs:
if not self._affinity_changeable(objs[obj]):
if self._affinity_changeable(obj, process = objs[obj]) != 1:
continue
prev_affinity = self._get_affinity2(obj)
if prev_affinity is None:

View file

@ -200,7 +200,7 @@ class commands:
# "no_errors" can be list of return codes not treated as errors, if 0 is in no_errors, it means any error
# returns (retcode, out), where retcode is exit code of the executed process or -errno if
# OSError or IOError exception happened
def execute(self, args, shell = False, cwd = None, no_errors = []):
def execute(self, args, shell = False, cwd = None, no_errors = [], return_err = False):
retcode = 0
if self._environment is None:
self._environment = os.environ.copy()
@ -208,6 +208,7 @@ class commands:
self._debug("Executing %s." % str(args))
out = ""
err_msg = None
try:
proc = Popen(args, stdout = PIPE, stderr = PIPE, env = self._environment, shell = shell, cwd = cwd, close_fds = True)
out, err = proc.communicate()
@ -217,12 +218,19 @@ class commands:
err_out = err[:-1]
if len(err_out) == 0:
err_out = out[:-1]
self._error("Executing %s error: %s" % (args[0], err_out))
err_msg = "Executing %s error: %s" % (args[0], err_out)
if not return_err:
self._error(err_msg)
except (OSError, IOError) as e:
retcode = -e.errno if e.errno is not None else -1
if not abs(retcode) in no_errors and not 0 in no_errors:
self._error("Executing %s error: %s" % (args[0], e))
return retcode, out
err_msg = "Executing %s error: %s" % (args[0], e)
if not return_err:
self._error(err_msg)
if return_err:
return retcode, out, err_msg
else:
return retcode, out
# Helper for parsing kernel options like:
# [always] never