Blame SOURCES/CLI-start-a-process-from-tuna.patch

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