| # -*- python -*- |
| # -*- coding: utf-8 -*- |
| |
| import copy, ethtool, os, procfs, re, schedutils |
| import help, fnmatch |
| from procfs import utilist |
| |
| try: |
| set |
| except NameError: |
| from sets import Set as set |
| |
| try: |
| fntable |
| except NameError: |
| fntable = [] |
| |
| def kthread_help(key): |
| if '/' in key: |
| key = key[:key.rfind('/')+1] |
| return help.KTHREAD_HELP.get(key, " ") |
| |
| def proc_sys_help(key): |
| if not len(fntable): |
| regMatch = ['[', '*', '?'] |
| for value in help.PROC_SYS_HELP: |
| for char in regMatch: |
| if char in value: |
| fntable.append(value) |
| temp = help.PROC_SYS_HELP.get(key, "") |
| if len(temp): |
| return key + ":\n" + temp |
| else: |
| for value in fntable: |
| if fnmatch.fnmatch(key, value): |
| return key + ":\n" + help.PROC_SYS_HELP.get(value, "") |
| return key |
| |
| def kthread_help_plain_text(pid, cmdline): |
| cmdline = cmdline.split(' ')[0] |
| params = {'pid':pid, 'cmdline':cmdline} |
| |
| if iskthread(pid): |
| title = _("Kernel Thread %(pid)d (%(cmdline)s):") % params |
| help = kthread_help(cmdline) |
| else: |
| title = _("User Thread %(pid)d (%(cmdline)s):") % params |
| help = title |
| |
| return help, title |
| |
| def iskthread(pid): |
| # FIXME: we should leave to the callers to handle all the exceptions, |
| # in this function, so that they know that the thread vanished and |
| # can act accordingly, removing entries from tree views, etc |
| try: |
| f = file("/proc/%d/smaps" % pid) |
| except IOError: |
| # Thread has vanished |
| return True |
| |
| line = f.readline() |
| f.close() |
| if line: |
| return False |
| # Zombies also doesn't have smaps entries, so check the |
| # state: |
| try: |
| p = procfs.pidstat(pid) |
| except: |
| return True |
| |
| if p["state"] == 'Z': |
| return False |
| return True |
| |
| def irq_thread_number(cmd): |
| if cmd[:4] == "irq/": |
| return cmd[4:cmd.find('-')] |
| elif cmd[:4] == "IRQ-": |
| return cmd[4:] |
| else: |
| raise LookupError |
| |
| def is_irq_thread(cmd): |
| return cmd[:4] in ("IRQ-", "irq/") |
| |
| def threaded_irq_re(irq): |
| return re.compile("(irq/%s-.+|IRQ-%s)" % (irq, irq)) |
| |
| # FIXME: Move to python-linux-procfs |
| def has_threaded_irqs(ps): |
| irq_re = re.compile("(irq/[0-9]+-.+|IRQ-[0-9]+)") |
| return len(ps.find_by_regex(irq_re)) > 0 |
| |
| def set_irq_affinity_filename(filename, bitmasklist): |
| pathname="/proc/irq/%s" % filename |
| f = file(pathname, "w") |
| text = ",".join(map(lambda a: "%x" % a, bitmasklist)) |
| f.write("%s\n" % text) |
| try: |
| f.close() |
| except IOError: |
| # This happens with IRQ 0, for instance |
| return False |
| return True |
| |
| def set_irq_affinity(irq, bitmasklist): |
| return set_irq_affinity_filename("%d/smp_affinity" % irq, bitmasklist) |
| |
| def cpustring_to_list(cpustr): |
| """Convert a string of numbers to an integer list. |
| |
| Given a string of comma-separated numbers and number ranges, |
| return a simple sorted list of the integers it represents. |
| |
| This function will throw exceptions for badly-formatted strings. |
| |
| Returns a list of integers.""" |
| |
| fields = cpustr.strip().split(",") |
| cpu_list = [] |
| for field in fields: |
| ends = [ int(a, 0) for a in field.split("-") ] |
| if len(ends) > 2: |
| raise "Syntax error" |
| if len(ends) == 2: |
| cpu_list += range(ends[0], ends[1] + 1) |
| else: |
| cpu_list += [ends[0]] |
| return list(set(cpu_list)) |
| |
| def list_to_cpustring(l): |
| """Convert a list of integers into a range string. |
| |
| Consecutive values will be collapsed into ranges. |
| |
| This should not throw any exceptions as long as the list is all |
| positive integers. |
| |
| Returns a string.""" |
| |
| l = list(set(l)) |
| strings = [] |
| inrange = False |
| prev = -2 |
| while len(l): |
| i = l.pop(0) |
| if i - 1 == prev: |
| while len(l): |
| j = l.pop(0) |
| if j - 1 != i: |
| l.insert(0, j) |
| break; |
| i = j |
| t = strings.pop() |
| if int(t) + 1 == i: |
| strings.append("%s,%u" % (t, i)) |
| else: |
| strings.append("%s-%u" % (t, i)) |
| else: |
| strings.append("%u" % i) |
| prev = i |
| return ",".join(strings) |
| |
| # FIXME: move to python-linux-procfs |
| def is_hardirq_handler(self, pid): |
| PF_HARDIRQ = 0x08000000 |
| try: |
| return int(self.processes[pid]["stat"]["flags"]) & \ |
| PF_HARDIRQ and True or False |
| except: |
| return False |
| |
| # FIXME: move to python-linux-procfs |
| def cannot_set_affinity(self, pid): |
| PF_NO_SETAFFINITY = 0x04000000 |
| try: |
| return int(self.processes[pid]["stat"]["flags"]) & \ |
| PF_NO_SETAFFINITY and True or False |
| except: |
| return True |
| |
| def move_threads_to_cpu(cpus, pid_list, set_affinity_warning = None, |
| spread = False): |
| changed = False |
| |
| ps = procfs.pidstats() |
| cpu_idx = 0 |
| nr_cpus = len(cpus) |
| new_affinity = cpus |
| last_cpu = max(cpus) + 1 |
| for pid in pid_list: |
| if spread: |
| new_affinity = [cpus[cpu_idx]] |
| cpu_idx += 1 |
| if cpu_idx == nr_cpus: |
| cpu_idx = 0 |
| |
| try: |
| try: |
| curr_affinity = schedutils.get_affinity(pid) |
| except (SystemError, OSError) as e: # (3, 'No such process') old python-schedutils incorrectly raised SystemError |
| if e[0] == 3: # 'No such process' |
| continue |
| curr_affinity = None |
| raise e |
| if set(curr_affinity) != set(new_affinity): |
| try: |
| schedutils.set_affinity(pid, new_affinity) |
| curr_affinity = schedutils.get_affinity(pid) |
| except (SystemError, OSError) as e: # (3, 'No such process') old python-schedutils incorrectly raised SystemError |
| if e[0] == 3: # 'No such process' |
| continue |
| curr_affinity == None |
| raise e |
| if set(curr_affinity) == set(new_affinity): |
| changed = True |
| if is_hardirq_handler(ps, pid): |
| try: |
| irq = int(ps[pid]["stat"]["comm"][4:]) |
| bitmasklist = procfs.hexbitmask(new_affinity, last_cpu) |
| set_irq_affinity(irq, bitmasklist) |
| except: |
| pass |
| elif set_affinity_warning: |
| set_affinity_warning(pid, new_affinity) |
| else: |
| print "move_threads_to_cpu: %s " % \ |
| (_("could not change %(pid)d affinity to %(new_affinity)s") % \ |
| {'pid':pid, 'new_affinity':new_affinity}) |
| |
| # See if this is the thread group leader |
| if not ps.has_key(pid): |
| continue |
| |
| threads = procfs.pidstats("/proc/%d/task" % pid) |
| for tid in threads.keys(): |
| try: |
| curr_affinity = schedutils.get_affinity(tid) |
| except (SystemError, OSError) as e: # (3, 'No such process') old python-schedutils incorrectly raised SystemError |
| if e[0] == 3: |
| continue |
| raise e |
| if set(curr_affinity) != set(new_affinity): |
| try: |
| schedutils.set_affinity(tid, new_affinity) |
| curr_affinity = schedutils.get_affinity(tid) |
| except (SystemError, OSError) as e: # (3, 'No such process') old python-schedutils incorrectly raised SystemError |
| if e[0] == 3: |
| continue |
| raise e |
| if set(curr_affinity) == set(new_affinity): |
| changed = True |
| elif set_affinity_warning: |
| set_affinity_warning(tid, new_affinity) |
| else: |
| print "move_threads_to_cpu: %s " % \ |
| (_("could not change %(pid)d affinity to %(new_affinity)s") % \ |
| {'pid':pid, 'new_affinity':new_affinity}) |
| except (SystemError, OSError) as e: # (3, 'No such process') old python-schedutils incorrectly raised SystemError |
| if e[0] == 3: |
| # process died |
| continue |
| raise e |
| return changed |
| |
| def move_irqs_to_cpu(cpus, irq_list, spread = False): |
| changed = 0 |
| unprocessed = [] |
| |
| cpu_idx = 0 |
| nr_cpus = len(cpus) |
| new_affinity = cpus |
| last_cpu = max(cpus) + 1 |
| irqs = None |
| ps = procfs.pidstats() |
| for i in irq_list: |
| try: |
| irq = int(i) |
| except: |
| if not irqs: |
| irqs = procfs.interrupts() |
| irq = irqs.find_by_user(i) |
| if not irq: |
| unprocessed.append(i) |
| continue |
| try: |
| irq = int(irq) |
| except: |
| unprocessed.append(i) |
| continue |
| |
| if spread: |
| new_affinity = [cpus[cpu_idx]] |
| cpu_idx += 1 |
| if cpu_idx == nr_cpus: |
| cpu_idx = 0 |
| |
| bitmasklist = procfs.hexbitmask(new_affinity, last_cpu) |
| set_irq_affinity(irq, bitmasklist) |
| changed += 1 |
| pid = ps.find_by_name("IRQ-%d" % irq) |
| if pid: |
| pid = int(pid[0]) |
| try: |
| schedutils.set_affinity(pid, new_affinity) |
| except (SystemError, OSError) as e: # (3, 'No such process') old python-schedutils incorrectly raised SystemError |
| if e[0] == 3: |
| unprocessed.append(i) |
| changed -= 1 |
| continue |
| raise e |
| |
| return (changed, unprocessed) |
| |
| def affinity_remove_cpus(affinity, cpus, nr_cpus): |
| # If the cpu being isolated was the only one in the current affinity |
| affinity = list(set(affinity) - set(cpus)) |
| if not affinity: |
| affinity = range(nr_cpus) |
| affinity = list(set(affinity) - set(cpus)) |
| return affinity |
| |
| # Shound be moved to python_linux_procfs.interrupts, shared with interrupts.parse_affinity, etc. |
| def parse_irq_affinity_filename(filename, nr_cpus): |
| f = file("/proc/irq/%s" % filename) |
| line = f.readline() |
| f.close() |
| return utilist.bitmasklist(line, nr_cpus) |
| |
| |
| def isolate_cpus(cpus, nr_cpus): |
| ps = procfs.pidstats() |
| ps.reload_threads() |
| previous_pid_affinities = {} |
| for pid in ps.keys(): |
| if cannot_set_affinity(ps, pid): |
| continue |
| try: |
| affinity = schedutils.get_affinity(pid) |
| except (SystemError, OSError) as e: # (3, 'No such process') old python-schedutils incorrectly raised SystemError |
| if e[0] == 3: |
| continue |
| raise e |
| if set(affinity).intersection(set(cpus)): |
| previous_pid_affinities[pid] = copy.copy(affinity) |
| affinity = affinity_remove_cpus(affinity, cpus, nr_cpus) |
| try: |
| schedutils.set_affinity(pid, affinity) |
| except (SystemError, OSError) as e: # (3, 'No such process') old python-schedutils incorrectly raised SystemError |
| if e[0] == 3: |
| continue |
| raise e |
| |
| if not ps[pid].has_key("threads"): |
| continue |
| threads = ps[pid]["threads"] |
| for tid in threads.keys(): |
| if cannot_set_affinity(ps, tid): |
| continue |
| try: |
| affinity = schedutils.get_affinity(tid) |
| except (SystemError, OSError) as e: # (3, 'No such process') old python-schedutils incorrectly raised SystemError |
| if e[0] == 3: |
| continue |
| raise e |
| if set(affinity).intersection(set(cpus)): |
| previous_pid_affinities[tid] = copy.copy(affinity) |
| affinity = affinity_remove_cpus(affinity, cpus, nr_cpus) |
| try: |
| schedutils.set_affinity(tid, affinity) |
| except (SystemError, OSError) as e: # (3, 'No such process') old python-schedutils incorrectly raised SystemError |
| if e[0] == 3: |
| continue |
| raise e |
| |
| del ps |
| |
| # Now isolate it from IRQs too |
| irqs = procfs.interrupts() |
| previous_irq_affinities = {} |
| for irq in irqs.keys(): |
| # LOC, NMI, TLB, etc |
| if not irqs[irq].has_key("affinity"): |
| continue |
| affinity = irqs[irq]["affinity"] |
| if set(affinity).intersection(set(cpus)): |
| previous_irq_affinities[irq] = copy.copy(affinity) |
| affinity = affinity_remove_cpus(affinity, cpus, nr_cpus) |
| set_irq_affinity(int(irq), |
| procfs.hexbitmask(affinity, |
| nr_cpus)) |
| |
| affinity = parse_irq_affinity_filename("default_smp_affinity", nr_cpus) |
| affinity = affinity_remove_cpus(affinity, cpus, nr_cpus) |
| set_irq_affinity_filename("default_smp_affinity", procfs.hexbitmask(affinity, nr_cpus)) |
| |
| return (previous_pid_affinities, previous_irq_affinities) |
| |
| def include_cpus(cpus, nr_cpus): |
| ps = procfs.pidstats() |
| ps.reload_threads() |
| previous_pid_affinities = {} |
| for pid in ps.keys(): |
| if cannot_set_affinity(ps, pid): |
| continue |
| try: |
| affinity = schedutils.get_affinity(pid) |
| except (SystemError, OSError) as e: # (3, 'No such process') old python-schedutils incorrectly raised SystemError |
| if e[0] == 3: |
| continue |
| raise e |
| if set(affinity).intersection(set(cpus)) != set(cpus): |
| previous_pid_affinities[pid] = copy.copy(affinity) |
| affinity = list(set(affinity + cpus)) |
| try: |
| schedutils.set_affinity(pid, affinity) |
| except (SystemError, OSError) as e: # (3, 'No such process') old python-schedutils incorrectly raised SystemError |
| if e[0] == 3: |
| continue |
| raise e |
| |
| if not ps[pid].has_key("threads"): |
| continue |
| threads = ps[pid]["threads"] |
| for tid in threads.keys(): |
| if cannot_set_affinity(ps, tid): |
| continue |
| try: |
| affinity = schedutils.get_affinity(tid) |
| except (SystemError, OSError) as e: # (3, 'No such process') old python-schedutils incorrectly raised SystemError |
| if e[0] == 3: |
| continue |
| raise e |
| if set(affinity).intersection(set(cpus)) != set(cpus): |
| previous_pid_affinities[tid] = copy.copy(affinity) |
| affinity = list(set(affinity + cpus)) |
| try: |
| schedutils.set_affinity(tid, affinity) |
| except (SystemError, OSError) as e: # (3, 'No such process') old python-schedutils incorrectly raised SystemError |
| if e[0] == 3: |
| continue |
| raise e |
| |
| del ps |
| |
| # Now include it in IRQs too |
| irqs = procfs.interrupts() |
| previous_irq_affinities = {} |
| for irq in irqs.keys(): |
| # LOC, NMI, TLB, etc |
| if not irqs[irq].has_key("affinity"): |
| continue |
| affinity = irqs[irq]["affinity"] |
| if set(affinity).intersection(set(cpus)) != set(cpus): |
| previous_irq_affinities[irq] = copy.copy(affinity) |
| affinity = list(set(affinity + cpus)) |
| set_irq_affinity(int(irq), |
| procfs.hexbitmask(affinity, nr_cpus)) |
| |
| affinity = parse_irq_affinity_filename("default_smp_affinity", nr_cpus) |
| affinity = list(set(affinity + cpus)) |
| set_irq_affinity_filename("default_smp_affinity", procfs.hexbitmask(affinity, nr_cpus)) |
| |
| return (previous_pid_affinities, previous_irq_affinities) |
| |
| def get_irq_users(irqs, irq, nics = None): |
| if not nics: |
| nics = ethtool.get_active_devices() |
| users = irqs[irq]["users"] |
| for u in users: |
| if u in nics: |
| try: |
| users[users.index(u)] = "%s(%s)" % (u, ethtool.get_module(u)) |
| except IOError: |
| # Old kernel, doesn't implement ETHTOOL_GDRVINFO |
| pass |
| return users |
| |
| def get_irq_affinity_text(irqs, irq): |
| affinity_list = irqs[irq]["affinity"] |
| try: |
| return list_to_cpustring(affinity_list) |
| except: |
| # needs root prio to read /proc/irq/<NUM>/smp_affinity |
| return "" |
| |
| def thread_filtered(tid, cpus_filtered, show_kthreads, show_uthreads): |
| if cpus_filtered: |
| try: |
| affinity = schedutils.get_affinity(tid) |
| except (SystemError, OSError) as e: # (3, 'No such process') old python-schedutils incorrectly raised SystemError |
| if e[0] == 3: |
| return False |
| raise e |
| |
| if set(cpus_filtered + affinity) == set(cpus_filtered): |
| return True |
| |
| if not (show_kthreads and show_uthreads): |
| kthread = iskthread(tid) |
| if ((not show_kthreads) and kthread) or \ |
| ((not show_uthreads) and not kthread): |
| return True |
| |
| return False |
| |
| def irq_filtered(irq, irqs, cpus_filtered, is_root): |
| if cpus_filtered and is_root: |
| affinity = irqs[irq]["affinity"] |
| if set(cpus_filtered + affinity) == set(cpus_filtered): |
| return True |
| |
| return False |
| |
| def thread_set_priority(tid, policy, rtprio): |
| if not policy and policy != 0: |
| policy = schedutils.get_scheduler(tid) |
| 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: |
| print "tuna: " + _("\"%s\" is unsupported priority value!") % parms[0] |
| return |
| |
| for tid in tids: |
| try: |
| thread_set_priority(tid, policy, rtprio) |
| except (SystemError, OSError) as e: # (3, 'No such process') old python-schedutils incorrectly raised SystemError |
| if e[0] == 3: |
| continue |
| raise e |
| if affect_children: |
| for child in [int (a) for a in os.listdir("/proc/%d/task" % tid)]: |
| if child != tid: |
| try: |
| thread_set_priority(child, policy, rtprio) |
| except (SystemError, OSError) as e: # (3, 'No such process') old python-schedutils incorrectly raised SystemError |
| if e[0] == 3: |
| continue |
| raise e |
| |
| class sched_tunings: |
| def __init__(self, name, pid, policy, rtprio, affinity, percpu): |
| self.name = name |
| self.pid = pid |
| self.policy = policy |
| self.rtprio = int(rtprio) |
| self.affinity = affinity |
| self.percpu = percpu |
| |
| def get_kthread_sched_tunings(proc = None): |
| if not proc: |
| proc = procfs.pidstats() |
| |
| kthreads = {} |
| for pid in proc.keys(): |
| name = proc[pid]["stat"]["comm"] |
| # Trying to set the priority of the migration threads will |
| # fail, at least on 3.6.0-rc1 and doesn't make sense anyway |
| # and this function is only used to save those priorities |
| # to reset them using tools like rtctl, skip those to |
| # avoid sched_setscheduler/chrt to fail |
| if iskthread(pid) and not name.startswith("migration/"): |
| rtprio = int(proc[pid]["stat"]["rt_priority"]) |
| try: |
| policy = schedutils.get_scheduler(pid) |
| affinity = schedutils.get_affinity(pid) |
| except (SystemError, OSError) as e: # (3, 'No such process') old python-schedutils incorrectly raised SystemError |
| if e[0] == 3: |
| continue |
| raise e |
| percpu = iskthread(pid) and \ |
| proc.is_bound_to_cpu(pid) |
| kthreads[name] = sched_tunings(name, pid, policy, |
| rtprio, affinity, |
| percpu) |
| |
| return kthreads |
| |
| def generate_rtgroups(filename, kthreads, nr_cpus): |
| f = file(filename, "w") |
| f.write('''# Generated by tuna |
| # |
| # Use it with rtctl: |
| # |
| # rtctl --file %s reset |
| # |
| # Please use 'man rtctl' for more operations |
| # |
| # Associate processes into named groups with default priority and |
| # scheduling policy. |
| # |
| # Format is: <groupname>:<sched>:<prio>:<regex> |
| # |
| # groupname must start at beginning of line. |
| # sched must be one of: 'f' (fifo) |
| # 'b' (batch) |
| # 'r' (round-robin) |
| # 'o' (other) |
| # '*' (leave alone) |
| # regex is an awk regex |
| # |
| # The regex is matched against process names as printed by "ps -eo cmd". |
| |
| ''' % filename) |
| f.write("kthreads:*:1:*:\[.*\]$\n\n") |
| |
| per_cpu_kthreads = [] |
| names = kthreads.keys() |
| names.sort() |
| for name in names: |
| kt = kthreads[name] |
| try: |
| idx = name.index("/") |
| common = name[:idx] |
| if common in per_cpu_kthreads: |
| continue |
| per_cpu_kthreads.append(common) |
| name = common |
| if common[:5] == "sirq-": |
| common = "(sirq|softirq)" + common[4:] |
| elif common[:8] == "softirq-": |
| common = "(sirq|softirq)" + common[7:] |
| name = "s" + name[4:] |
| regex = common + "\/.*" |
| except: |
| idx = 0 |
| regex = name |
| pass |
| if kt.percpu or idx != 0 or name == "posix_cpu_timer": |
| # Don't mess with workqueues, etc |
| # posix_cpu_timer is too long and doesn't |
| # have PF_THREAD_BOUND in its per process |
| # flags... |
| mask = "*" |
| else: |
| mask = ",".join([hex(a) for a in \ |
| procfs.hexbitmask(kt.affinity, nr_cpus)]) |
| f.write("%s:%c:%d:%s:\[%s\]$\n" % (name, |
| schedutils.schedstr(kt.policy)[6].lower(), |
| kt.rtprio, mask, regex)) |
| f.close() |
| |
| |
| def nohz_full_list(): |
| return [ int(cpu) for cpu in procfs.cmdline().options["nohz_full"].split(",") ] |