1
0
Fork 0
This commit is contained in:
Peter Jung 2026-05-16 08:26:55 -04:00 committed by GitHub
commit 8cc88a9c8f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 220 additions and 0 deletions

View file

@ -0,0 +1,112 @@
import tempfile
import unittest
try:
from unittest.mock import Mock
from unittest.mock import call
from unittest.mock import patch
except ImportError:
from mock import Mock
from mock import call
from mock import patch
from tuned.monitors.repository import Repository
from tuned.plugins.plugin_amd_x3d import AMDX3DPlugin
from tuned.plugins.plugin_amd_x3d import _find_x3d_paths
from tuned.utils.commands import commands
import tuned.plugins as plugins
import tuned.profiles as profiles
from tuned import storage
class AMDX3DPluginTestCase(unittest.TestCase):
def setUp(self):
self._storage_file = tempfile.NamedTemporaryFile()
plugin_instance_factory = plugins.instance.Factory()
storage_provider = storage.PickleProvider(self._storage_file.name)
storage_factory = storage.Factory(storage_provider)
self._plugin = AMDX3DPlugin(
Repository(),
storage_factory,
Mock(),
Mock(),
Mock(),
plugin_instance_factory,
None,
profiles.variables.Variables(),
)
self._plugin._cmd = commands()
self._plugin._cmd.read_file = Mock()
self._plugin._cmd.write_to_file = Mock()
def tearDown(self):
self._storage_file.close()
def _create_instance(self, mode="cache"):
instance = self._plugin.create_instance(
"amd_x3d",
0,
"",
None,
"",
"",
{"mode": mode},
)
self._plugin.initialize_instance(instance)
return instance
def test_find_x3d_paths_sorted(self):
with patch("tuned.plugins.plugin_amd_x3d.glob.glob",
return_value=["/sys/devices/b", "/sys/devices/a"]):
self.assertEqual(_find_x3d_paths(),
["/sys/devices/a", "/sys/devices/b"])
def test_apply_verify_and_unapply_mode(self):
paths = [
"/sys/bus/platform/drivers/amd_x3d_vcache/AMDI0001:00/amd_x3d_mode",
"/sys/bus/platform/drivers/amd_x3d_vcache/AMDI0002:00/amd_x3d_mode",
]
instance = self._create_instance(mode="cache")
with patch.object(self._plugin, "_x3d_paths", return_value=paths):
self._plugin._cmd.read_file.return_value = "frequency\n"
instance.apply_tuning()
self.assertEqual(
self._plugin._storage_get(instance, self._plugin._commands["mode"]),
"frequency",
)
self._plugin._cmd.write_to_file.assert_has_calls([
call(paths[0], "cache", no_error=False),
call(paths[1], "cache", no_error=False),
])
self._plugin._cmd.read_file.reset_mock()
self._plugin._cmd.write_to_file.reset_mock()
self._plugin._cmd.read_file.return_value = "frequency [cache]\n"
self.assertTrue(instance.verify_tuning(False))
self._plugin._cmd.write_to_file.assert_not_called()
self._plugin._cmd.read_file.reset_mock()
self._plugin._cmd.write_to_file.reset_mock()
instance.unapply_tuning()
self._plugin._cmd.write_to_file.assert_has_calls([
call(paths[0], "frequency", no_error=False),
call(paths[1], "frequency", no_error=False),
])
self.assertIsNone(
self._plugin._storage_get(instance, self._plugin._commands["mode"])
)
def test_apply_without_supported_device_is_noop(self):
instance = self._create_instance(mode="cache")
with patch.object(self._plugin, "_x3d_paths", return_value=[]):
instance.apply_tuning()
self._plugin._cmd.write_to_file.assert_not_called()
self.assertIsNone(
self._plugin._storage_get(instance, self._plugin._commands["mode"])
)

View file

@ -0,0 +1,108 @@
import glob
import errno
from . import base
from .decorators import *
import tuned.logs
log = tuned.logs.get()
_X3D_MODE_GLOB = "/sys/bus/platform/drivers/amd_x3d_vcache/*/amd_x3d_mode"
_VALID_MODES = frozenset(["cache", "frequency"])
def _find_x3d_paths():
"""Return discovered amd_x3d_mode sysfs paths in a stable order."""
return sorted(glob.glob(_X3D_MODE_GLOB))
class AMDX3DPlugin(base.Plugin):
"""
Controls the AMD 3D V-Cache scheduling mode on dual-CCD processors
such as Ryzen 9 7950X3D, 7900X3D, 9950X3D, and 9900X3D processors.
The [option]`mode` option configures the `amd_x3d_vcache` kernel
driver. The driver exposes a sysfs knob that biases the scheduler
towards one CCD or the other:
* `cache`
+
Prefer the CCD with 3D V-Cache. This is useful for games and other
cache-sensitive workloads.
* `frequency`
+
Prefer the non-X3D CCD, which can usually boost higher. This is
useful for throughput-oriented compute workloads and is the kernel
default.
The plugin discovers the sysfs path using a glob because the ACPI
device name can vary across boards. On systems without the
`amd_x3d_vcache` driver or without a supported dual-CCD X3D CPU, the
plugin does nothing.
.Prefer the X3D CCD for games
====
----
[amd_x3d]
mode=cache
----
====
.Prefer the higher-frequency CCD for compute workloads
====
----
[amd_x3d]
mode=frequency
----
====
"""
@classmethod
def _get_config_options(cls):
return {
"mode": None,
}
def _instance_init(self, instance):
instance._has_static_tuning = True
instance._has_dynamic_tuning = False
def _instance_cleanup(self, instance):
pass
def _x3d_paths(self):
return _find_x3d_paths()
@command_set("mode")
def _set_mode(self, value, instance, sim, remove):
if value not in _VALID_MODES:
if not sim:
log.warning("amd_x3d: invalid mode '%s', expected one of: %s"
% (value, ", ".join(sorted(_VALID_MODES))))
return None
paths = self._x3d_paths()
if not paths:
if not sim:
log.debug("amd_x3d: no AMD 3D V-Cache device found, skipping")
return None
if not sim:
for path in paths:
log.info("amd_x3d: setting mode to '%s' on %s" % (value, path))
self._cmd.write_to_file(path, value,
no_error=[errno.ENOENT] if remove else False)
return value
@command_get("mode")
def _get_mode(self, instance):
paths = self._x3d_paths()
if not paths:
return None
# All CCD pairs share the same mode; read from the first found path.
data = self._cmd.read_file(paths[0]).strip()
if not data:
return None
return self._cmd.get_active_option(data)