Files
waydroid/tools/actions/container_manager.py
Alfred Neumayer b67b4415c1 container_manager: Keep schedtune cgroup around in case nesting is supported
Certain devices with Ubunutu Touch make use of the schedtune cgroup boosting mechanism,
allowing for better performance system-wide with very little tweaking.
Some devices have trouble using this cgroup in a regular GNU/Linux setting because
their kernels are not carrying the necessary kernel patch for enabling nesting. [1]

To please both those that want Waydroid to stop dropping their performance as well as
those having unpatched kernels, probe the capability to nest schedtune cgroups.

[1]: 6452429d1f
2023-09-07 15:46:00 +02:00

266 lines
8.8 KiB
Python

# Copyright 2021 Erfan Abdi
# SPDX-License-Identifier: GPL-3.0-or-later
from shutil import which
import logging
import os
import time
import glob
import signal
import sys
import uuid
import tools.config
from tools import helpers
from tools import services
import dbus
import dbus.service
import dbus.exceptions
from gi.repository import GLib
class DbusContainerManager(dbus.service.Object):
def __init__(self, looper, bus, object_path, args):
self.args = args
self.looper = looper
dbus.service.Object.__init__(self, bus, object_path)
@dbus.service.method("id.waydro.ContainerManager", in_signature='a{ss}', out_signature='', sender_keyword="sender", connection_keyword="conn")
def Start(self, session, sender, conn):
dbus_info = dbus.Interface(conn.get_object("org.freedesktop.DBus", "/org/freedesktop/DBus/Bus", False), "org.freedesktop.DBus")
uid = dbus_info.GetConnectionUnixUser(sender)
if str(uid) not in ["0", session["user_id"]]:
raise RuntimeError("Cannot start a session on behalf of another user")
pid = dbus_info.GetConnectionUnixProcessID(sender)
if str(uid) != "0" and str(pid) != session["pid"]:
raise RuntimeError("Invalid session pid")
do_start(self.args, session)
@dbus.service.method("id.waydro.ContainerManager", in_signature='b', out_signature='')
def Stop(self, quit_session):
stop(self.args, quit_session)
@dbus.service.method("id.waydro.ContainerManager", in_signature='', out_signature='')
def Freeze(self):
freeze(self.args)
@dbus.service.method("id.waydro.ContainerManager", in_signature='', out_signature='')
def Unfreeze(self):
unfreeze(self.args)
@dbus.service.method("id.waydro.ContainerManager", in_signature='', out_signature='a{ss}')
def GetSession(self):
try:
session = self.args.session
session["state"] = helpers.lxc.status(self.args)
return session
except AttributeError:
return {}
def service(args, looper):
dbus_obj = DbusContainerManager(looper, dbus.SystemBus(), '/ContainerManager', args)
looper.run()
def set_permissions(args, perm_list=None, mode="777"):
def chmod(path, mode):
if os.path.exists(path):
command = ["chmod", mode, "-R", path]
tools.helpers.run.user(args, command, check=False)
# Nodes list
if not perm_list:
perm_list = [
"/dev/ashmem",
# sw_sync for HWC
"/dev/sw_sync",
"/sys/kernel/debug/sync/sw_sync",
# Media
"/dev/Vcodec",
"/dev/MTK_SMI",
"/dev/mdp_sync",
"/dev/mtk_cmdq",
# Graphics
"/dev/dri",
"/dev/graphics",
"/dev/pvr_sync",
"/dev/ion",
]
# Framebuffers
perm_list.extend(glob.glob("/dev/fb*"))
# Videos
perm_list.extend(glob.glob("/dev/video*"))
for path in perm_list:
chmod(path, mode)
def start(args):
try:
name = dbus.service.BusName("id.waydro.Container", dbus.SystemBus(), do_not_queue=True)
except dbus.exceptions.NameExistsException:
logging.error("Container service is already running")
return
status = helpers.lxc.status(args)
if status == "STOPPED":
# Load binder and ashmem drivers
cfg = tools.config.load(args)
if cfg["waydroid"]["vendor_type"] == "MAINLINE":
if helpers.drivers.probeBinderDriver(args) != 0:
logging.error("Failed to load Binder driver")
helpers.drivers.probeAshmemDriver(args)
helpers.drivers.loadBinderNodes(args)
set_permissions(args, [
"/dev/" + args.BINDER_DRIVER,
"/dev/" + args.VNDBINDER_DRIVER,
"/dev/" + args.HWBINDER_DRIVER
], "666")
mainloop = GLib.MainLoop()
def sigint_handler(data):
stop(args)
mainloop.quit()
GLib.unix_signal_add(GLib.PRIORITY_HIGH, signal.SIGINT, sigint_handler, None)
GLib.unix_signal_add(GLib.PRIORITY_HIGH, signal.SIGTERM, sigint_handler, None)
service(args, mainloop)
else:
logging.error("WayDroid container is {}".format(status))
def do_start(args, session):
if "session" in args:
raise RuntimeError("Already tracking a session")
# Networking
command = [tools.config.tools_src +
"/data/scripts/waydroid-net.sh", "start"]
tools.helpers.run.user(args, command)
# Sensors
if which("waydroid-sensord"):
tools.helpers.run.user(
args, ["waydroid-sensord", "/dev/" + args.HWBINDER_DRIVER], output="background")
# Cgroup hacks
if which("start"):
command = ["start", "cgroup-lite"]
tools.helpers.run.user(args, command, check=False)
# Keep schedtune around in case nesting is supported
if os.path.ismount("/sys/fs/cgroup/schedtune"):
try:
os.mkdir("/sys/fs/cgroup/schedtune/probe0")
os.mkdir("/sys/fs/cgroup/schedtune/probe0/probe1")
except:
command = ["umount", "-l", "/sys/fs/cgroup/schedtune"]
tools.helpers.run.user(args, command, check=False)
finally:
if os.path.exists("/sys/fs/cgroup/schedtune/probe0/probe1"):
os.rmdir("/sys/fs/cgroup/schedtune/probe0/probe1")
if os.path.exists("/sys/fs/cgroup/schedtune/probe0"):
os.rmdir("/sys/fs/cgroup/schedtune/probe0")
#TODO: remove NFC hacks
if which("stop"):
command = ["stop", "nfcd"]
tools.helpers.run.user(args, command, check=False)
elif which("systemctl") and (tools.helpers.run.user(args, ["systemctl", "is-active", "-q", "nfcd"], check=False) == 0):
command = ["systemctl", "stop", "nfcd"]
tools.helpers.run.user(args, command, check=False)
# Set permissions
set_permissions(args)
# Create session-specific LXC config file
helpers.lxc.generate_session_lxc_config(args, session)
# Backwards compatibility
with open(tools.config.defaults["lxc"] + "/waydroid/config") as f:
if "config_session" not in f.read():
helpers.mount.bind(args, session["waydroid_data"],
tools.config.defaults["data"])
# Mount rootfs
cfg = tools.config.load(args)
helpers.images.mount_rootfs(args, cfg["waydroid"]["images_path"], session)
helpers.protocol.set_aidl_version(args)
helpers.lxc.start(args)
services.hardware_manager.start(args)
args.session = session
def stop(args, quit_session=True):
try:
services.hardware_manager.stop(args)
status = helpers.lxc.status(args)
if status != "STOPPED":
helpers.lxc.stop(args)
while helpers.lxc.status(args) != "STOPPED":
pass
# Networking
command = [tools.config.tools_src +
"/data/scripts/waydroid-net.sh", "stop"]
tools.helpers.run.user(args, command, check=False)
#TODO: remove NFC hacks
if which("start"):
command = ["start", "nfcd"]
tools.helpers.run.user(args, command, check=False)
elif which("systemctl") and (tools.helpers.run.user(args, ["systemctl", "is-enabled", "-q", "nfcd"], check=False) == 0):
command = ["systemctl", "start", "nfcd"]
tools.helpers.run.user(args, command, check=False)
# Sensors
if which("waydroid-sensord"):
command = ["pidof", "waydroid-sensord"]
pid = tools.helpers.run.user(args, command, check=False, output_return=True).strip()
if pid:
command = ["kill", "-9", pid]
tools.helpers.run.user(args, command, check=False)
# Umount rootfs
helpers.images.umount_rootfs(args)
# Backwards compatibility
try:
helpers.mount.umount_all(args, tools.config.defaults["data"])
except:
pass
if "session" in args:
if quit_session:
try:
os.kill(int(args.session["pid"]), signal.SIGUSR1)
except:
pass
del args.session
except:
pass
def restart(args):
status = helpers.lxc.status(args)
if status == "RUNNING":
helpers.lxc.stop(args)
helpers.lxc.start(args)
else:
logging.error("WayDroid container is {}".format(status))
def freeze(args):
status = helpers.lxc.status(args)
if status == "RUNNING":
helpers.lxc.freeze(args)
while helpers.lxc.status(args) == "RUNNING":
pass
else:
logging.error("WayDroid container is {}".format(status))
def unfreeze(args):
status = helpers.lxc.status(args)
if status == "FROZEN":
helpers.lxc.unfreeze(args)
while helpers.lxc.status(args) == "FROZEN":
pass