#!/usr/bin/python3 # # pmqos-static.py: Simple daemon for setting static PM QoS values. It is a part # of 'tuned' and it should not be called manually. # # Copyright (C) 2011 Red Hat, Inc. # Authors: Jan Vcelak # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # from __future__ import print_function import os import signal import struct import sys import time # Used interface is described in Linux kernel documentation: # Documentation/power/pm_qos_interface.txt ALLOWED_INTERFACES = [ "cpu_dma_latency", "network_latency", "network_throughput" ] PIDFILE = "/var/run/tuned/pmqos-static.pid" def do_fork(): pid = os.fork() if pid > 0: sys.exit(0) def close_fds(): f = open('/dev/null', 'w+') os.dup2(f.fileno(), sys.stdin.fileno()) os.dup2(f.fileno(), sys.stdout.fileno()) os.dup2(f.fileno(), sys.stderr.fileno()) def write_pidfile(): with os.fdopen(os.open(PIDFILE, os.O_CREAT | os.O_TRUNC | os.O_WRONLY, 0o644), "w") as f: f.write("%d" % os.getpid()) def daemonize(): do_fork() os.chdir("/") os.setsid() os.umask(0) do_fork() close_fds() def set_pmqos(name, value): filename = "/dev/%s" % name bin_value = struct.pack("i", int(value)) try: fd = os.open(filename, os.O_WRONLY) except OSError: print("Cannot open (%s)." % filename, file=sys.stderr) return None os.write(fd, bin_value) return fd def sleep_forever(): while True: time.sleep(86400) def sigterm_handler(signum, frame): global pmqos_fds if type(pmqos_fds) is list: for fd in pmqos_fds: os.close(fd) sys.exit(0) def run_daemon(options): try: daemonize() write_pidfile() signal.signal(signal.SIGTERM, sigterm_handler) except Exception as e: print("Cannot daemonize (%s)." % e, file=sys.stderr) return False global pmqos_fds pmqos_fds = [] for (name, value) in list(options.items()): try: new_fd = set_pmqos(name, value) if new_fd is not None: pmqos_fds.append(new_fd) except: # we are daemonized pass if len(pmqos_fds) > 0: sleep_forever() else: return False def kill_daemon(force = False): try: with open(PIDFILE, "r") as pidfile: daemon_pid = int(pidfile.read()) except IOError as e: if not force: print("Cannot open PID file (%s)." % e, file=sys.stderr) return False try: os.kill(daemon_pid, signal.SIGTERM) except OSError as e: if not force: print("Cannot terminate the daemon (%s)." % e, file=sys.stderr) return False try: os.unlink(PIDFILE) except OSError as e: if not force: print("Cannot delete the PID file (%s)." % e, file=sys.stderr) return False return True if __name__ == "__main__": disable = False options = {} for option in sys.argv[1:]: if option == "disable": disable = True break try: (name, value) = option.split("=") except ValueError: name = option value = None if name in ALLOWED_INTERFACES and len(value) > 0: options[name] = value else: print("Invalid option (%s)." % option, file=sys.stderr) if disable: sys.exit(0 if kill_daemon() else 1) if len(options) == 0: print("No options set. Not starting.", file=sys.stderr) sys.exit(1) kill_daemon(True) run_daemon(options) sys.exit(1)