Daniel Bristot de Oliveira
2015-03-18 15:22:02 UTC
Currently it is possible to set the CPU affinity and sched tunables for
threads running on a system. However, tuna does not permit to start a
new application with these parameters set.
This patch adds the ability to start a new process with its affinity
and sched tunable set before it starts to run.
To do so, one parameter was added to tuna's command line. This
parameter is the Action -r, --run=:
-r, --run="COMMAND"
Run the COMMAND. If arguments are passed, the entire com‐
mand line must be provided inside "quotes". Modifiers -c
and -p can be used to set the affinity and scheduler tun‐
ables of the given COMMAND. The arg[0] (i.e. the command)
will be set in THREAD-LIST. Likewise the -t, the COMMAND
accepts the prefix + and - as wildcards in order to be
appended or removed from THREAD-LIST, respectively.
The option -r will fork a new process, set the sched tunables and
affinity, and execute the new application's binary.
Tuna will wait for the new process to return, and then continue
its execution. That means that it is possible to execute many Actions
after the creation of a new process, including the start of many process
in a single command line.
Example of use:
[***@kiron tuna]# tuna -p fifo:1 -t bash -c 1,2 -r +httpd -P
thread ctxt_switches
pid SCHED_ rtpri affinity voluntary nonvoluntary cmd
10550 OTHER 0 0,1,2,3 300 81 bash
12898 OTHER 0 0,1,2,3 30 6 bash
12930 OTHER 0 0,1,2,3 246 27 bash
13022 FIFO 1 1,2 1 0 httpd
In this example, the schedule policy:prio fifo:1 is saved for future
Actions, "bash" is set in Thread list, cpus 1,2 are set in the cpulist,
the command "httpd" is appended to the Thread list and is started.
Finally, the threads in Thread list are printed.
Signed-off-by: Daniel Bristot de Oliveira <***@bristot.me>
---
tuna-cmd.py | 98 ++++++++++++++++++++++++++++++++++++++++++++----------------
tuna/tuna.py | 59 ++++++++++++++++++++++++++++--------
2 files changed, 119 insertions(+), 38 deletions(-)
diff --git a/tuna-cmd.py b/tuna-cmd.py
index f85b629..c90be47 100755
--- a/tuna-cmd.py
+++ b/tuna-cmd.py
@@ -69,6 +69,8 @@ def usage():
print fmt % ('-Q, --show_irqs', _('Show IRQ list'))
print fmt % ('-q, --irqs=' + _('IRQ-LIST'), _('%(irqlist)s affected by commands') %
{"irqlist": _('IRQ-LIST')})
+ print fmt % ('-r, --run=' + _('COMMAND'), _('fork a new process and run the %(command)s') % \
+ {"command": _('COMMAND')})
print fmt % ('-s, --save=' + _('FILENAME'), _('Save kthreads sched tunables to %(filename)s') % \
{"filename": _('FILENAME')})
print fmt % ('-S, --sockets=' +
@@ -375,8 +377,8 @@ def thread_mapper(s):
return [ int(s), ]
except:
pass
- if not ps:
- ps = procfs.pidstats()
+
+ ps = procfs.pidstats()
try:
return ps.find_by_regex(re.compile(fnmatch.translate(s)))
@@ -450,13 +452,14 @@ def main():
i18n_init()
try:
- short = "a:c:CfgGhiIKlmNp:PQq:s:S:t:UvWx"
+ short = "a:c:CfgGhiIKlmNp:PQq:r:s:S:t:UvWx"
long = ["cpus=", "affect_children", "filter", "gui", "help",
"isolate", "include", "no_kthreads", "move", "nohz_full",
"show_sockets", "priority=", "show_threads",
"show_irqs", "irqs=",
"save=", "sockets=", "threads=", "no_uthreads",
- "version", "what_is", "spread","cgroup","config_file_apply=","config_file_list="]
+ "version", "what_is", "spread","cgroup","config_file_apply=","config_file_list=",
+ "run=" ]
if have_inet_diag:
short += "n"
long.append("show_sockets")
@@ -473,11 +476,14 @@ def main():
cpu_list = None
irq_list = None
irq_list_str = None
+ rtprio = None
+ policy = None
thread_list = []
thread_list_str = None
filter = False
affect_children = False
show_sockets = False
+ p_waiting_action = False
for o, a in opts:
if o in ("-h", "--help"):
@@ -502,20 +508,25 @@ def main():
elif o in ("-G", "--cgroup"):
cgroups = True
elif o in ("-t", "--threads"):
- (op, a) = pick_op(a)
- op_list = reduce(lambda i, j: i + j,
- map(thread_mapper, a.split(",")))
- op_list = list(set(op_list))
- thread_list = do_list_op(op, thread_list, op_list)
- # Check if a process name was especified and no
- # threads was found, which would result in an empty
- # thread list, i.e. we would print all the threads
- # in the system when we should print nothing.
- if not op_list and type(a) == type(''):
- thread_list_str = do_list_op(op, thread_list_str,
- a.split(","))
- if not op:
- irq_list = None
+ # The -t - will reset thread list
+ if a == '-':
+ thread_list = []
+ thread_list_str = ''
+ else:
+ (op, a) = pick_op(a)
+ op_list = reduce(lambda i, j: i + j,
+ map(thread_mapper, a.split(",")))
+ op_list = list(set(op_list))
+ thread_list = do_list_op(op, thread_list, op_list)
+ # Check if a process name was especified and no
+ # threads was found, which would result in an empty
+ # thread list, i.e. we would print all the threads
+ # in the system when we should print nothing.
+ if not op_list and type(a) == type(''):
+ thread_list_str = do_list_op(op, thread_list_str,
+ a.split(","))
+ if not op:
+ irq_list = None
elif o in ("-f", "--filter"):
filter = True
elif o in ("-g", "--gui"):
@@ -531,14 +542,17 @@ def main():
sys.exit(2)
tuna.include_cpus(cpu_list, get_nr_cpus())
elif o in ("-p", "--priority"):
+ # Save policy and rtprio for future Actions (e.g. --run).
+ (policy, rtprio) = tuna.get_policy_and_rtprio(a)
if not thread_list:
- print ("tuna: %s " % o) + _("requires a thread list!")
- sys.exit(2)
- try:
- tuna.threads_set_priority(thread_list, a, affect_children)
- except (SystemError, OSError) as err: # (3, 'No such process') old python-schedutils incorrectly raised SystemError
- print "tuna: %s" % err
- sys.exit(2)
+ # For backward compatibility
+ p_waiting_action = True
+ else:
+ try:
+ tuna.threads_set_priority(thread_list, a, affect_children)
+ except (SystemError, OSError) as err: # (3, 'No such process') old python-schedutils incorrectly raised SystemError
+ print "tuna: %s" % err
+ sys.exit(2)
elif o in ("-P", "--show_threads"):
# If the user specified process names that weren't
# resolved to pids, don't show all threads.
@@ -629,6 +643,40 @@ def main():
sys.exit(2)
for tid in thread_list:
thread_help(tid)
+ elif o in ("-r", "--run"):
+ # If -p is set, it will be consumed. So, no backward compatible
+ # error handling action must be taken.
+ p_waiting_action = False
+
+ # pick_op() before run the command: to remove the prefix
+ # + or - from command line.
+ (op, a) = pick_op(a)
+
+ # In order to include the new process, it must run
+ # the command first, and then get the list of pids,
+ tuna.run_command(a, policy, rtprio, cpu_list)
+
+ op_list = reduce(lambda i, j: i + j,
+ map(thread_mapper, a.split(",")))
+ op_list = list(set(op_list))
+ thread_list = do_list_op(op, thread_list, op_list)
+
+ # Check if a process name was especified and no
+ # threads was found, which would result in an empty
+ # thread list, i.e. we would print all the threads
+ # in the system when we should print nothing.
+ if not op_list and type(a) == type(''):
+ thread_list_str = do_list_op(op, thread_list_str,
+ a.split(","))
+ if not op:
+ irq_list = None
+
+ # For backward compatibility: when -p used to be only an Action, it
+ # used to exit(2) if no action was taken (i.e. if no threads_list
+ # was set).
+ if p_waiting_action:
+ print ("tuna: -p ") + _("requires a thread list!")
+ sys.exit(2)
if run_gui:
try:
diff --git a/tuna/tuna.py b/tuna/tuna.py
index 0063c1d..1465a7c 100755
--- a/tuna/tuna.py
+++ b/tuna/tuna.py
@@ -1,7 +1,7 @@
# -*- python -*-
# -*- coding: utf-8 -*-
-import copy, ethtool, os, procfs, re, schedutils
+import copy, ethtool, os, procfs, re, schedutils, sys, shlex
import help, fnmatch
try:
@@ -455,6 +455,22 @@ def get_irq_affinity_text(irqs, irq):
# needs root prio to read /proc/irq/<NUM>/smp_affinity
return ""
+def get_policy_and_rtprio(parm):
+ parms = parm.split(":")
+ rtprio = 0
+ policy = None
+ if parms[0].upper() in ["OTHER", "BATCH", "IDLE", "FIFO", "RR"]:
+ policy = schedutils.schedfromstr("SCHED_%s" % parms[0].upper())
+ if len(parms) > 1:
+ rtprio = int(parms[1])
+ elif parms[0].upper() in ["FIFO", "RR"]:
+ rtprio = 1
+ elif parms[0].isdigit():
+ rtprio = int(parms[0])
+ else:
+ raise ValueError
+ return (policy, rtprio)
+
def thread_filtered(tid, cpus_filtered, show_kthreads, show_uthreads):
if cpus_filtered:
try:
@@ -489,18 +505,9 @@ def thread_set_priority(tid, policy, rtprio):
schedutils.set_scheduler(tid, policy, rtprio)
def threads_set_priority(tids, parm, affect_children = False):
- parms = parm.split(":")
- rtprio = 0
- policy = None
- if parms[0].upper() in ["OTHER", "BATCH", "IDLE", "FIFO", "RR"]:
- policy = schedutils.schedfromstr("SCHED_%s" % parms[0].upper())
- if len(parms) > 1:
- rtprio = int(parms[1])
- elif parms[0].upper() in ["FIFO", "RR"]:
- rtprio = 1
- elif parms[0].isdigit():
- rtprio = int(parms[0])
- else:
+ try:
+ (policy, rtprio) = get_policy_and_rtprio(parm)
+ except ValueError:
print "tuna: " + _("\"%s\" is unsupported priority value!") % parms[0]
return
@@ -559,6 +566,32 @@ def get_kthread_sched_tunings(proc = None):
return kthreads
+def run_command(cmd, policy, rtprio, cpu_list):
+ newpid = os.fork()
+ if newpid == 0:
+ cmd_list = shlex.split(cmd)
+ pid = os.getpid()
+ if rtprio:
+ try:
+ thread_set_priority(pid, policy, rtprio)
+ except (SystemError, OSError) as err:
+ print "tuna: %s" % err
+ sys.exit(2)
+ if cpu_list:
+ try:
+ schedutils.set_affinity(pid, cpu_list)
+ except (SystemError, OSError) as err:
+ print "tuna: %s" % err
+ sys.exit(2)
+
+ try:
+ os.execvp(cmd_list[0], cmd_list)
+ except (SystemError, OSError) as err:
+ print "tuna: %s" % err
+ sys.exit(2)
+ else:
+ os.waitpid(newpid, 0);
+
def generate_rtgroups(filename, kthreads, nr_cpus):
f = file(filename, "w")
f.write('''# Generated by tuna
threads running on a system. However, tuna does not permit to start a
new application with these parameters set.
This patch adds the ability to start a new process with its affinity
and sched tunable set before it starts to run.
To do so, one parameter was added to tuna's command line. This
parameter is the Action -r, --run=:
-r, --run="COMMAND"
Run the COMMAND. If arguments are passed, the entire com‐
mand line must be provided inside "quotes". Modifiers -c
and -p can be used to set the affinity and scheduler tun‐
ables of the given COMMAND. The arg[0] (i.e. the command)
will be set in THREAD-LIST. Likewise the -t, the COMMAND
accepts the prefix + and - as wildcards in order to be
appended or removed from THREAD-LIST, respectively.
The option -r will fork a new process, set the sched tunables and
affinity, and execute the new application's binary.
Tuna will wait for the new process to return, and then continue
its execution. That means that it is possible to execute many Actions
after the creation of a new process, including the start of many process
in a single command line.
Example of use:
[***@kiron tuna]# tuna -p fifo:1 -t bash -c 1,2 -r +httpd -P
thread ctxt_switches
pid SCHED_ rtpri affinity voluntary nonvoluntary cmd
10550 OTHER 0 0,1,2,3 300 81 bash
12898 OTHER 0 0,1,2,3 30 6 bash
12930 OTHER 0 0,1,2,3 246 27 bash
13022 FIFO 1 1,2 1 0 httpd
In this example, the schedule policy:prio fifo:1 is saved for future
Actions, "bash" is set in Thread list, cpus 1,2 are set in the cpulist,
the command "httpd" is appended to the Thread list and is started.
Finally, the threads in Thread list are printed.
Signed-off-by: Daniel Bristot de Oliveira <***@bristot.me>
---
tuna-cmd.py | 98 ++++++++++++++++++++++++++++++++++++++++++++----------------
tuna/tuna.py | 59 ++++++++++++++++++++++++++++--------
2 files changed, 119 insertions(+), 38 deletions(-)
diff --git a/tuna-cmd.py b/tuna-cmd.py
index f85b629..c90be47 100755
--- a/tuna-cmd.py
+++ b/tuna-cmd.py
@@ -69,6 +69,8 @@ def usage():
print fmt % ('-Q, --show_irqs', _('Show IRQ list'))
print fmt % ('-q, --irqs=' + _('IRQ-LIST'), _('%(irqlist)s affected by commands') %
{"irqlist": _('IRQ-LIST')})
+ print fmt % ('-r, --run=' + _('COMMAND'), _('fork a new process and run the %(command)s') % \
+ {"command": _('COMMAND')})
print fmt % ('-s, --save=' + _('FILENAME'), _('Save kthreads sched tunables to %(filename)s') % \
{"filename": _('FILENAME')})
print fmt % ('-S, --sockets=' +
@@ -375,8 +377,8 @@ def thread_mapper(s):
return [ int(s), ]
except:
pass
- if not ps:
- ps = procfs.pidstats()
+
+ ps = procfs.pidstats()
try:
return ps.find_by_regex(re.compile(fnmatch.translate(s)))
@@ -450,13 +452,14 @@ def main():
i18n_init()
try:
- short = "a:c:CfgGhiIKlmNp:PQq:s:S:t:UvWx"
+ short = "a:c:CfgGhiIKlmNp:PQq:r:s:S:t:UvWx"
long = ["cpus=", "affect_children", "filter", "gui", "help",
"isolate", "include", "no_kthreads", "move", "nohz_full",
"show_sockets", "priority=", "show_threads",
"show_irqs", "irqs=",
"save=", "sockets=", "threads=", "no_uthreads",
- "version", "what_is", "spread","cgroup","config_file_apply=","config_file_list="]
+ "version", "what_is", "spread","cgroup","config_file_apply=","config_file_list=",
+ "run=" ]
if have_inet_diag:
short += "n"
long.append("show_sockets")
@@ -473,11 +476,14 @@ def main():
cpu_list = None
irq_list = None
irq_list_str = None
+ rtprio = None
+ policy = None
thread_list = []
thread_list_str = None
filter = False
affect_children = False
show_sockets = False
+ p_waiting_action = False
for o, a in opts:
if o in ("-h", "--help"):
@@ -502,20 +508,25 @@ def main():
elif o in ("-G", "--cgroup"):
cgroups = True
elif o in ("-t", "--threads"):
- (op, a) = pick_op(a)
- op_list = reduce(lambda i, j: i + j,
- map(thread_mapper, a.split(",")))
- op_list = list(set(op_list))
- thread_list = do_list_op(op, thread_list, op_list)
- # Check if a process name was especified and no
- # threads was found, which would result in an empty
- # thread list, i.e. we would print all the threads
- # in the system when we should print nothing.
- if not op_list and type(a) == type(''):
- thread_list_str = do_list_op(op, thread_list_str,
- a.split(","))
- if not op:
- irq_list = None
+ # The -t - will reset thread list
+ if a == '-':
+ thread_list = []
+ thread_list_str = ''
+ else:
+ (op, a) = pick_op(a)
+ op_list = reduce(lambda i, j: i + j,
+ map(thread_mapper, a.split(",")))
+ op_list = list(set(op_list))
+ thread_list = do_list_op(op, thread_list, op_list)
+ # Check if a process name was especified and no
+ # threads was found, which would result in an empty
+ # thread list, i.e. we would print all the threads
+ # in the system when we should print nothing.
+ if not op_list and type(a) == type(''):
+ thread_list_str = do_list_op(op, thread_list_str,
+ a.split(","))
+ if not op:
+ irq_list = None
elif o in ("-f", "--filter"):
filter = True
elif o in ("-g", "--gui"):
@@ -531,14 +542,17 @@ def main():
sys.exit(2)
tuna.include_cpus(cpu_list, get_nr_cpus())
elif o in ("-p", "--priority"):
+ # Save policy and rtprio for future Actions (e.g. --run).
+ (policy, rtprio) = tuna.get_policy_and_rtprio(a)
if not thread_list:
- print ("tuna: %s " % o) + _("requires a thread list!")
- sys.exit(2)
- try:
- tuna.threads_set_priority(thread_list, a, affect_children)
- except (SystemError, OSError) as err: # (3, 'No such process') old python-schedutils incorrectly raised SystemError
- print "tuna: %s" % err
- sys.exit(2)
+ # For backward compatibility
+ p_waiting_action = True
+ else:
+ try:
+ tuna.threads_set_priority(thread_list, a, affect_children)
+ except (SystemError, OSError) as err: # (3, 'No such process') old python-schedutils incorrectly raised SystemError
+ print "tuna: %s" % err
+ sys.exit(2)
elif o in ("-P", "--show_threads"):
# If the user specified process names that weren't
# resolved to pids, don't show all threads.
@@ -629,6 +643,40 @@ def main():
sys.exit(2)
for tid in thread_list:
thread_help(tid)
+ elif o in ("-r", "--run"):
+ # If -p is set, it will be consumed. So, no backward compatible
+ # error handling action must be taken.
+ p_waiting_action = False
+
+ # pick_op() before run the command: to remove the prefix
+ # + or - from command line.
+ (op, a) = pick_op(a)
+
+ # In order to include the new process, it must run
+ # the command first, and then get the list of pids,
+ tuna.run_command(a, policy, rtprio, cpu_list)
+
+ op_list = reduce(lambda i, j: i + j,
+ map(thread_mapper, a.split(",")))
+ op_list = list(set(op_list))
+ thread_list = do_list_op(op, thread_list, op_list)
+
+ # Check if a process name was especified and no
+ # threads was found, which would result in an empty
+ # thread list, i.e. we would print all the threads
+ # in the system when we should print nothing.
+ if not op_list and type(a) == type(''):
+ thread_list_str = do_list_op(op, thread_list_str,
+ a.split(","))
+ if not op:
+ irq_list = None
+
+ # For backward compatibility: when -p used to be only an Action, it
+ # used to exit(2) if no action was taken (i.e. if no threads_list
+ # was set).
+ if p_waiting_action:
+ print ("tuna: -p ") + _("requires a thread list!")
+ sys.exit(2)
if run_gui:
try:
diff --git a/tuna/tuna.py b/tuna/tuna.py
index 0063c1d..1465a7c 100755
--- a/tuna/tuna.py
+++ b/tuna/tuna.py
@@ -1,7 +1,7 @@
# -*- python -*-
# -*- coding: utf-8 -*-
-import copy, ethtool, os, procfs, re, schedutils
+import copy, ethtool, os, procfs, re, schedutils, sys, shlex
import help, fnmatch
try:
@@ -455,6 +455,22 @@ def get_irq_affinity_text(irqs, irq):
# needs root prio to read /proc/irq/<NUM>/smp_affinity
return ""
+def get_policy_and_rtprio(parm):
+ parms = parm.split(":")
+ rtprio = 0
+ policy = None
+ if parms[0].upper() in ["OTHER", "BATCH", "IDLE", "FIFO", "RR"]:
+ policy = schedutils.schedfromstr("SCHED_%s" % parms[0].upper())
+ if len(parms) > 1:
+ rtprio = int(parms[1])
+ elif parms[0].upper() in ["FIFO", "RR"]:
+ rtprio = 1
+ elif parms[0].isdigit():
+ rtprio = int(parms[0])
+ else:
+ raise ValueError
+ return (policy, rtprio)
+
def thread_filtered(tid, cpus_filtered, show_kthreads, show_uthreads):
if cpus_filtered:
try:
@@ -489,18 +505,9 @@ def thread_set_priority(tid, policy, rtprio):
schedutils.set_scheduler(tid, policy, rtprio)
def threads_set_priority(tids, parm, affect_children = False):
- parms = parm.split(":")
- rtprio = 0
- policy = None
- if parms[0].upper() in ["OTHER", "BATCH", "IDLE", "FIFO", "RR"]:
- policy = schedutils.schedfromstr("SCHED_%s" % parms[0].upper())
- if len(parms) > 1:
- rtprio = int(parms[1])
- elif parms[0].upper() in ["FIFO", "RR"]:
- rtprio = 1
- elif parms[0].isdigit():
- rtprio = int(parms[0])
- else:
+ try:
+ (policy, rtprio) = get_policy_and_rtprio(parm)
+ except ValueError:
print "tuna: " + _("\"%s\" is unsupported priority value!") % parms[0]
return
@@ -559,6 +566,32 @@ def get_kthread_sched_tunings(proc = None):
return kthreads
+def run_command(cmd, policy, rtprio, cpu_list):
+ newpid = os.fork()
+ if newpid == 0:
+ cmd_list = shlex.split(cmd)
+ pid = os.getpid()
+ if rtprio:
+ try:
+ thread_set_priority(pid, policy, rtprio)
+ except (SystemError, OSError) as err:
+ print "tuna: %s" % err
+ sys.exit(2)
+ if cpu_list:
+ try:
+ schedutils.set_affinity(pid, cpu_list)
+ except (SystemError, OSError) as err:
+ print "tuna: %s" % err
+ sys.exit(2)
+
+ try:
+ os.execvp(cmd_list[0], cmd_list)
+ except (SystemError, OSError) as err:
+ print "tuna: %s" % err
+ sys.exit(2)
+ else:
+ os.waitpid(newpid, 0);
+
def generate_rtgroups(filename, kthreads, nr_cpus):
f = file(filename, "w")
f.write('''# Generated by tuna
--
1.9.3
1.9.3