mirror of
https://github.com/FeralInteractive/gamemode.git
synced 2025-06-06 15:47:20 +02:00
Compare commits
146 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
af07e169d5 | ||
![]() |
6e5c950141 | ||
![]() |
499af4c7bb | ||
![]() |
5f691c3171 | ||
![]() |
f5fbdcf014 | ||
![]() |
7c49d6e1db | ||
![]() |
d84c5ded1c | ||
![]() |
2d71be6a94 | ||
![]() |
1e3e55c5d8 | ||
![]() |
e75f2c3942 | ||
![]() |
81d26c79b4 | ||
![]() |
5b3b4ae638 | ||
![]() |
1d9c1e6a72 | ||
![]() |
2eecd513ac | ||
![]() |
086b3fe5b4 | ||
![]() |
2f69f7c023 | ||
![]() |
c54d6d4243 | ||
![]() |
715a1389b7 | ||
![]() |
c0516059ed | ||
![]() |
a875008d8e | ||
![]() |
ec10c591ff | ||
![]() |
fef538ba92 | ||
![]() |
a2fe0108b5 | ||
![]() |
c854772369 | ||
![]() |
7d55ef856b | ||
![]() |
1c293d7818 | ||
![]() |
9646f2bd93 | ||
![]() |
8e0a71a0bc | ||
![]() |
3fa41891cf | ||
![]() |
8cea4c2418 | ||
![]() |
4a82094c98 | ||
![]() |
5180d89e66 | ||
![]() |
e82e9dafd0 | ||
![]() |
bb40374067 | ||
![]() |
c3b9d4ce8b | ||
![]() |
070216ad81 | ||
![]() |
6b03bc9354 | ||
![]() |
5867058d19 | ||
![]() |
49dc7ee49b | ||
![]() |
41191dc607 | ||
![]() |
22001c56b1 | ||
![]() |
088639e8b2 | ||
![]() |
9e21ac3924 | ||
![]() |
e2e8e981a2 | ||
![]() |
7389d5ed1d | ||
![]() |
162374c026 | ||
![]() |
4700089325 | ||
![]() |
c7a4572d73 | ||
![]() |
7381d93c13 | ||
![]() |
97e588a5ad | ||
![]() |
9a7ee00bbf | ||
![]() |
b2bd7e5919 | ||
![]() |
97cee92d94 | ||
![]() |
b9e8448afe | ||
![]() |
6c197f9b70 | ||
![]() |
485a7f920a | ||
![]() |
f48c58f34c | ||
![]() |
fad889db45 | ||
![]() |
775c93001c | ||
![]() |
160bf91665 | ||
![]() |
138ad384e3 | ||
![]() |
9614ceca9b | ||
![]() |
865ffb2588 | ||
![]() |
b83fb8f83e | ||
![]() |
e882505881 | ||
![]() |
88a15aba86 | ||
![]() |
9cb119be62 | ||
![]() |
fd226e46ba | ||
![]() |
91eb57574c | ||
![]() |
25a99af4a1 | ||
![]() |
740a82a761 | ||
![]() |
173a8594b4 | ||
![]() |
81dc3f95e5 | ||
![]() |
912d4bdc19 | ||
![]() |
85b9abd654 | ||
![]() |
2f7075b9c6 | ||
![]() |
2e26331d97 | ||
![]() |
303a5a9734 | ||
![]() |
a9042199c6 | ||
![]() |
3cabf9859d | ||
![]() |
51ee251efb | ||
![]() |
ba1c593d0e | ||
![]() |
7d00c1f0a4 | ||
![]() |
c070604a22 | ||
![]() |
dc2f7bbcd0 | ||
![]() |
437a129900 | ||
![]() |
4997adef8d | ||
![]() |
1b10b679cd | ||
![]() |
495a659895 | ||
![]() |
2dbd565340 | ||
![]() |
01024927d2 | ||
![]() |
32a0b5b636 | ||
![]() |
520dc85ac3 | ||
![]() |
82206534dc | ||
![]() |
1f28880509 | ||
![]() |
8cfc345312 | ||
![]() |
7cf59587ce | ||
![]() |
f5882d5158 | ||
![]() |
06f01938a9 | ||
![]() |
71f4b875ce | ||
![]() |
b103bfdd60 | ||
![]() |
4cffdef805 | ||
![]() |
3f969bbf1f | ||
![]() |
1ca2daf47f | ||
![]() |
179d5432e4 | ||
![]() |
4934191b19 | ||
![]() |
55b799e3df | ||
![]() |
337f1b8a8e | ||
![]() |
898ab01924 | ||
![]() |
1e24067430 | ||
![]() |
e34e9c5a43 | ||
![]() |
aee9703872 | ||
![]() |
4dc99dff76 | ||
![]() |
5fff74598a | ||
![]() |
c3d638d7ce | ||
![]() |
86591acdf2 | ||
![]() |
618466005e | ||
![]() |
f5a715e124 | ||
![]() |
f498f2a20a | ||
![]() |
9b44fc6def | ||
![]() |
b58b32072a | ||
![]() |
f50e7fffe7 | ||
![]() |
fac33baa09 | ||
![]() |
e7dcf8792d | ||
![]() |
db1a0e3ace | ||
![]() |
01b849a518 | ||
![]() |
b8fa857b35 | ||
![]() |
23493e5cc3 | ||
![]() |
c96f7379b4 | ||
![]() |
898feb9c52 | ||
![]() |
ab06ba419e | ||
![]() |
6c60565f33 | ||
![]() |
5e366bd55d | ||
![]() |
126e67fb6b | ||
![]() |
4000a32584 | ||
![]() |
b11d2912e2 | ||
![]() |
0344e7c311 | ||
![]() |
993857d0e8 | ||
![]() |
5fd0065df6 | ||
![]() |
6592c2229c | ||
![]() |
05a4c0c152 | ||
![]() |
fb7062bd9c | ||
![]() |
49a0ef8c0b | ||
![]() |
cfa8b9a2c4 | ||
![]() |
62659edd2b | ||
![]() |
9037b730f6 |
@ -19,7 +19,6 @@ AlwaysBreakTemplateDeclarations: false
|
||||
BinPackArguments: false
|
||||
BinPackParameters: true
|
||||
BreakBeforeBinaryOperators: None
|
||||
BreakBeforeBinaryOperators: None
|
||||
BreakBeforeBraces: Linux
|
||||
BreakBeforeTernaryOperators: true
|
||||
BreakConstructorInitializersBeforeComma: false
|
||||
@ -39,16 +38,14 @@ SortIncludes: true
|
||||
# IncludeBlocksStyle changed to IncludeBlocks, between xenial and disco, so we can't use it for consistency
|
||||
# IncludeBlocks: Regroup
|
||||
IncludeCategories:
|
||||
- Regex: '^"gamemode.h"'
|
||||
- Regex: '^<linux\/'
|
||||
Priority: 0
|
||||
- Regex: '^"build-'
|
||||
Priority: 1
|
||||
- Regex: '^"common-'
|
||||
Priority: 2
|
||||
- Regex: '^"gamemode-'
|
||||
Priority: 3
|
||||
- Regex: '^<'
|
||||
Priority: 4
|
||||
Priority: 1
|
||||
- Regex: '^"gamemode.h"'
|
||||
Priority: 2
|
||||
- Regex: '^"'
|
||||
Priority: 3
|
||||
# IncludeIsMainRegex: (project doesn't use a main includes that can add other includes via regex)
|
||||
IndentCaseLabels: false
|
||||
IndentWidth: 4
|
||||
|
41
.github/workflows/build-and-test.yml
vendored
Normal file
41
.github/workflows/build-and-test.yml
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
name: Build and test
|
||||
on: [push, pull_request]
|
||||
permissions:
|
||||
contents: read
|
||||
actions: write
|
||||
|
||||
jobs:
|
||||
build-and-test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo DEBIAN_FRONTEND=noninteractive apt-get install -y build-essential meson appstream clang clang-format clang-tools libdbus-1-dev libinih-dev libsystemd-dev systemd-dev
|
||||
- name: Check format
|
||||
env:
|
||||
CI: "true"
|
||||
run: |
|
||||
./scripts/format-check.sh
|
||||
- name: Build and install
|
||||
env:
|
||||
CI: "true"
|
||||
run: |
|
||||
./bootstrap.sh -Dwith-examples=true
|
||||
- name: Tests
|
||||
run: |
|
||||
meson test -C builddir
|
||||
- name: Simulate game
|
||||
run: |
|
||||
dbus-run-session -- gamemode-simulate-game
|
||||
- name: Static analyser check
|
||||
run: |
|
||||
./scripts/static-analyser-check.sh
|
||||
- name: Upload logs
|
||||
if: ${{ always() }}
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: logs
|
||||
path: |
|
||||
builddir/meson-logs/
|
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
/builddir
|
||||
/subprojects/packagecache/
|
||||
/subprojects/inih-r*
|
25
.travis.yml
25
.travis.yml
@ -1,25 +0,0 @@
|
||||
os: linux
|
||||
dist: focal
|
||||
language: c
|
||||
compiler: gcc
|
||||
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- appstream
|
||||
- clang
|
||||
- clang-format
|
||||
- libinih-dev
|
||||
- libdbus-1-dev
|
||||
- libsystemd-dev
|
||||
- meson
|
||||
artifacts:
|
||||
paths:
|
||||
- $(git ls-files -o | tr "\n" ":")
|
||||
|
||||
script:
|
||||
- ./scripts/format-check.sh
|
||||
- ./bootstrap.sh -Dwith-examples=true
|
||||
- meson test -C builddir
|
||||
- dbus-run-session -- gamemode-simulate-game
|
||||
- ./scripts/static-analyser-check.sh
|
89
CHANGELOG.md
89
CHANGELOG.md
@ -1,6 +1,93 @@
|
||||
## 1.8.2
|
||||
|
||||
### Changes
|
||||
|
||||
* Fix idle inhibitor closing bus connection too early (#466)
|
||||
* Fix hybrid CPU core pinning (#455)
|
||||
* Fix unreadable process maps in gamemodelist (#463)
|
||||
* Fixed crash if dbus is not accesible (#458)
|
||||
* Various bugfixes and improvements to documentation
|
||||
|
||||
### Contributors
|
||||
|
||||
* @notpeelz
|
||||
* @patatahooligan
|
||||
* Reilly Brogan @ReillyBrogan
|
||||
* Alexandru Ionut Tripon @Trial97
|
||||
* Kostadin @kostadinsh
|
||||
* Daniel Martinez @Calandracas606
|
||||
|
||||
[View the full list of contributors](https://github.com/FeralInteractive/gamemode/graphs/contributors?from=2023-12-13&to=2024-08-19&type=c)
|
||||
|
||||
## 1.8.1
|
||||
|
||||
### Changes
|
||||
|
||||
* Fix polkit parse error (#449)
|
||||
|
||||
### Contributors
|
||||
|
||||
* @Vam-Jam
|
||||
|
||||
[View the full list of contributors](https://github.com/FeralInteractive/gamemode/graphs/contributors?from=2022-07-22&to=2023-12-12&type=c)
|
||||
|
||||
## 1.8
|
||||
|
||||
### Changes
|
||||
|
||||
* Add CPU core pinning and parking capability (#416)
|
||||
* Allow disabling the Linux kernel split lock mitigation (#446)
|
||||
* Fix building when pidfd_open is available (Fixes build with glibc 2.36) (#379)
|
||||
* Unify privileged group configuration between pam, systemd, & polkit (#375)
|
||||
* Various other bugfixes and improved default configuration
|
||||
|
||||
### Contributors
|
||||
|
||||
* Henrik Holst @HenrikHolst
|
||||
* Kira Bruneau @kira-bruneau
|
||||
* James Le Cuirot @chewi
|
||||
* Hugo Locurcio @Calinou
|
||||
* Zoltán Nyikos @nyz93
|
||||
* @ashuntu
|
||||
* @szymon-gniado
|
||||
|
||||
[View the full list of contributors](https://github.com/FeralInteractive/gamemode/graphs/contributors?from=2022-07-22&to=2023-12-06&type=c)
|
||||
|
||||
## 1.7
|
||||
|
||||
### Changes
|
||||
|
||||
* Added new utility: `gamemodelist` (#346)
|
||||
* Run executables from `PATH` instead of `/usr/bin` (#323)
|
||||
* Add a trivial `gamemode.conf` file, which creates the gamemode group (#339)
|
||||
* Various minor bugfixes and updates to documentation
|
||||
|
||||
### Contributors
|
||||
|
||||
* Sam Gleske @samrocketman
|
||||
* Kira Bruneau @kira-bruneau
|
||||
* Stephan Lachnit @stephanlachnit
|
||||
* Emil Velikov @evelikov-work
|
||||
|
||||
[View the full list of contributors](https://github.com/FeralInteractive/gamemode/graphs/contributors?from=2021-02-19&to=2022-07-21&type=c)
|
||||
|
||||
## 1.6.1
|
||||
|
||||
### Changes
|
||||
|
||||
* Use inih r53
|
||||
* Packaging changes for Arch
|
||||
* Minor metainfo improvements
|
||||
|
||||
### Contributors
|
||||
|
||||
* Stephan Lachnit @stephanlachnit
|
||||
* Alberto Oporto Ames @otreblan
|
||||
|
||||
## 1.6
|
||||
|
||||
### Changes
|
||||
|
||||
* Created new manpages for `gamemoderun` and the example, now called `gamemode-simulate-game`
|
||||
* Add ability to change lib directory of `gamemoderun`
|
||||
* Add option to use `elogind`
|
||||
@ -41,7 +128,7 @@ Minor changes for Debian and Ubuntu packaging:
|
||||
|
||||
* Alex Smith @aejsmith
|
||||
* Christian Kellner @gicmo
|
||||
* Jason Ekstrand @jekstrand
|
||||
* Faith Ekstrand @gfxstrand
|
||||
|
||||
## 1.4
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
Copyright (c) 2017-2020, Feral Interactive
|
||||
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
|
98
README.md
98
README.md
@ -10,16 +10,17 @@ Currently GameMode includes support for optimisations including:
|
||||
* Kernel scheduler (`SCHED_ISO`)
|
||||
* Screensaver inhibiting
|
||||
* GPU performance mode (NVIDIA and AMD), GPU overclocking (NVIDIA)
|
||||
* CPU core pinning or parking
|
||||
* Custom scripts
|
||||
|
||||
GameMode packages are available for Ubuntu, Debian, Solus, the AUR, Gentoo, Fedora, OpenSUSE, Mageia and possibly more.
|
||||
GameMode packages are available for Ubuntu, Debian, Solus, Arch, Gentoo, Fedora, OpenSUSE, Mageia and possibly more.
|
||||
|
||||
Issues with GameMode should be reported here in the issues section, and not reported to Feral directly.
|
||||
|
||||
---
|
||||
## Requesting GameMode
|
||||
|
||||
For games/launchers which integrate GameMode support (see list later on), simply running the game will automatically activate GameMode.
|
||||
For games/launchers which integrate GameMode support, simply running the game will automatically activate GameMode.
|
||||
|
||||
For others, you must manually request GameMode when running the game. This can be done by launching the game through `gamemoderun`:
|
||||
```bash
|
||||
@ -41,61 +42,78 @@ LD_PRELOAD="$LD_PRELOAD:/usr/\$LIB/libgamemodeauto.so.0"
|
||||
|
||||
The daemon is configured with a `gamemode.ini` file. [example/gamemode.ini](https://github.com/FeralInteractive/gamemode/blob/master/example/gamemode.ini) is an example of what this file would look like, with explanations for all the variables.
|
||||
|
||||
Config files are loaded and merged from the following directories, in order:
|
||||
1. `/usr/share/gamemode/`
|
||||
2. `/etc/`
|
||||
3. `$XDG_CONFIG_HOME` or `$HOME/.config/`
|
||||
4. `$PWD`
|
||||
Configuration files are loaded and merged from the following directories, from highest to lowest priority:
|
||||
|
||||
1. `$PWD` ("unsafe" - **`[gpu]` settings take no effect in this file**)
|
||||
2. `$XDG_CONFIG_HOME` or `$HOME/.config/` ("unsafe" - **`[gpu]` settings take no effect in this file**)
|
||||
3. `/etc/`
|
||||
4. `/usr/share/gamemode/`
|
||||
|
||||
---
|
||||
## Note for Hybrid GPU users
|
||||
|
||||
It's not possible to integrate commands like optirun automatically inside GameMode, since the GameMode request is made once the game has already started. However it is possible to use a hybrid GPU wrapper like optirun by starting the game with `gamemoderun`.
|
||||
|
||||
You can do this by setting the environment variable `GAMEMODERUNEXEC` to your wrapper's launch command, so for example `GAMEMODERUNEXEC=optirun`, `GAMEMODERUNEXEC="env DRI_PRIME=1"`, or `GAMEMODERUNEXEC="env __NV_PRIME_RENDER_OFFLOAD=1 env __GLX_VENDOR_LIBRARY_NAME=nvidia env __VK_LAYER_NV_optimus=NVIDIA_only"`. This environment variable can be set globally (e.g. in /etc/environment), so that the same prefix command does not have to be duplicated everywhere you want to use `gamemoderun`.
|
||||
You can do this by setting the environment variable `GAMEMODERUNEXEC` to your wrapper's launch command, so for example `GAMEMODERUNEXEC=optirun`, `GAMEMODERUNEXEC="env DRI_PRIME=1"`, or `GAMEMODERUNEXEC="env __NV_PRIME_RENDER_OFFLOAD=1 __GLX_VENDOR_LIBRARY_NAME=nvidia __VK_LAYER_NV_optimus=NVIDIA_only"`. This environment variable can be set globally (e.g. in /etc/environment), so that the same prefix command does not have to be duplicated everywhere you want to use `gamemoderun`.
|
||||
|
||||
GameMode will not be injected to the wrapper.
|
||||
|
||||
---
|
||||
## Apps with GameMode integration
|
||||
|
||||
### Games
|
||||
The following games are known to integrate GameMode support (meaning they don't require any additional configuration to activate GameMode while running):
|
||||
* DiRT 4
|
||||
* Rise of the Tomb Raider
|
||||
* Shadow of the Tomb Raider
|
||||
* Total War Saga: Thrones of Britannia
|
||||
* Total War: Three Kingdoms
|
||||
* Total War: WARHAMMER II
|
||||
|
||||
### Others
|
||||
Other apps which can integrate with GameMode include:
|
||||
* [ATLauncher](https://atlauncher.com/downloads) Minecraft launcher
|
||||
* GNOME Shell ([via extension](https://github.com/gicmo/gamemode-extension)) - indicates when GameMode is active in the top panel.
|
||||
* Lutris - Enables GameMode for all games by default if available (must have both 32- and 64-bit GameMode libraries installed), configurable in preferences.
|
||||
|
||||
---
|
||||
## Development [](https://travis-ci.org/FeralInteractive/gamemode)
|
||||
## Development [](https://github.com/FeralInteractive/gamemode/actions/workflows/build-and-test.yml)
|
||||
|
||||
The design of GameMode has a clear-cut abstraction between the host daemon and library (`gamemoded` and `libgamemode`), and the client loaders (`libgamemodeauto` and `gamemode_client.h`) that allows for safe use without worrying about whether the daemon is installed or running. This design also means that while the host library currently relies on `systemd` for exchanging messages with the daemon, it's entirely possible to implement other internals that still work with the same clients.
|
||||
|
||||
See repository subdirectories for information on each component.
|
||||
|
||||
### Install Dependencies
|
||||
GameMode depends on `meson` for building and `systemd` for internal communication. This repo contains a `bootstrap.sh` script to allow for quick install to the user bus, but check `meson_options.txt` for custom settings.
|
||||
GameMode depends on `meson` for building and `systemd` for internal communication. This repo contains a `bootstrap.sh` script to allow for quick install to the user bus, but check `meson_options.txt` for custom settings. These instructions all assume that you
|
||||
already have a C development environment (gcc or clang, libc-devel, etc) installed.
|
||||
|
||||
#### Ubuntu/Debian (you may also need `dbus-user-session`)
|
||||
#### Ubuntu/Debian
|
||||
Note: Debian 13 and Ubuntu 25.04 (and later) need to install `systemd-dev` in addition to the dependencies below.
|
||||
```bash
|
||||
apt install meson libsystemd-dev pkg-config ninja-build git libdbus-1-dev libinih-dev
|
||||
apt update && apt install meson libsystemd-dev pkg-config ninja-build git dbus-user-session libdbus-1-dev libinih-dev build-essential
|
||||
```
|
||||
|
||||
On Debian 12 and Ubuntu 22 (and earlier), you'll need to install `python3` and `python3-venv` packages to install the latest meson version from `pip`.
|
||||
|
||||
```bash
|
||||
python3 -m venv .venv
|
||||
source .venv/bin/activate
|
||||
pip install meson
|
||||
```
|
||||
|
||||
Later you can deactivate the virtual environment and remove it.
|
||||
|
||||
```bash
|
||||
deactivate
|
||||
rm -rf .venv
|
||||
```
|
||||
|
||||
#### Arch
|
||||
```bash
|
||||
pacman -S meson systemd git dbus
|
||||
pacman -S meson systemd git dbus libinih gcc pkgconf
|
||||
```
|
||||
|
||||
#### RHEL 10 and variants
|
||||
Note: Older versions of RHEL (and variants) cannot build gamemode due to not exposing libdbus-1 to pkg-config.
|
||||
(also - don't try and play games on RHEL, come on)
|
||||
|
||||
You must have [EPEL](https://docs.fedoraproject.org/en-US/epel/getting-started/) enabled to install all dependencies.
|
||||
```bash
|
||||
dnf install meson systemd-devel pkg-config git dbus-devel inih-devel
|
||||
```
|
||||
|
||||
#### Fedora
|
||||
```bash
|
||||
dnf install meson systemd-devel pkg-config git dbus-devel
|
||||
dnf install meson systemd-devel pkg-config git dbus-devel inih-devel
|
||||
```
|
||||
|
||||
#### OpenSUSE Leap/Tumbleweed
|
||||
```bash
|
||||
zypper install meson systemd-devel git dbus-1-devel libgcc_s1 libstdc++-devel libinih-devel
|
||||
```
|
||||
|
||||
#### Gentoo
|
||||
Gentoo has an ebuild which builds a stable release from sources. It will also pull in all the dependencies so you can work on the source code.
|
||||
```bash
|
||||
@ -106,15 +124,25 @@ You can also install using the latest sources from git:
|
||||
ACCEPT_KEYWORDS="**" emerge --ask ~games-util/gamemode-9999
|
||||
```
|
||||
|
||||
#### Nix
|
||||
Similar to Gentoo, nixOS already has a package for gamemode, so we can use that to setup an environment:
|
||||
```bash
|
||||
nix-shell -p pkgs.gamemode.buildInputs pkgs.gamemode.nativeBuildInputs
|
||||
```
|
||||
|
||||
### Build and Install GameMode
|
||||
Then clone, build and install a release version of GameMode at 1.6:
|
||||
Then clone, build and install a release version of GameMode at 1.8.2:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/FeralInteractive/gamemode.git
|
||||
cd gamemode
|
||||
git checkout 1.6 # omit to build the master branch
|
||||
git checkout 1.8.2 # omit to build the master branch
|
||||
./bootstrap.sh
|
||||
```
|
||||
To test GameMode installed and will run correctly:
|
||||
```bash
|
||||
gamemoded -t
|
||||
```
|
||||
|
||||
To uninstall:
|
||||
```bash
|
||||
@ -136,7 +164,7 @@ See the [contributors](https://github.com/FeralInteractive/gamemode/graphs/contr
|
||||
---
|
||||
## License
|
||||
|
||||
Copyright © 2017-2020 Feral Interactive
|
||||
Copyright © 2017-2025 Feral Interactive and the GameMode contributors
|
||||
|
||||
GameMode is available under the terms of the BSD 3-Clause License (Revised)
|
||||
|
||||
|
22
bootstrap.sh
22
bootstrap.sh
@ -14,9 +14,9 @@ if [ ! -f "/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor" ]; then
|
||||
echo "This probably means that you have disabled processor scheduling features in your BIOS. See README.md (or GitHub issue #44) for more information."
|
||||
echo "This means GameMode's CPU governor control feature will not work (other features will still work)."
|
||||
|
||||
if [ "$TRAVIS" != "true" ]; then
|
||||
if [ "$CI" != "true" ]; then
|
||||
# Allow to continue the install, as gamemode has other useful features
|
||||
read -p "Would you like to continue anyway [Y/N]? " -r
|
||||
read -p "Would you like to continue anyway [y/N]? " -r
|
||||
[[ $REPLY =~ ^[Yy]$ ]]
|
||||
fi
|
||||
fi
|
||||
@ -26,12 +26,12 @@ fi
|
||||
|
||||
# Echo the rest so it's obvious
|
||||
set -x
|
||||
meson builddir --prefix=$prefix --buildtype debugoptimized -Dwith-systemd-user-unit-dir=/etc/systemd/user "$@"
|
||||
meson setup builddir --prefix=$prefix --buildtype debugoptimized -Dwith-systemd-user-unit-dir=/etc/systemd/user "$@"
|
||||
ninja -C builddir
|
||||
|
||||
# Verify user wants to install
|
||||
set +x
|
||||
if [ "$TRAVIS" != "true" ]; then
|
||||
if [ "$CI" != "true" ]; then
|
||||
read -p "Install to $prefix? [y/N] " -r
|
||||
[[ $REPLY =~ ^[Yy]$ ]]
|
||||
fi
|
||||
@ -39,10 +39,12 @@ set -x
|
||||
|
||||
sudo ninja install -C builddir
|
||||
|
||||
# Restart polkit so we don't get pop-ups whenever we pkexec
|
||||
if systemctl list-unit-files |grep -q polkit.service; then
|
||||
sudo systemctl try-restart polkit
|
||||
fi
|
||||
if [ "$CI" != "true" ]; then
|
||||
# Restart polkit so we don't get pop-ups whenever we pkexec
|
||||
if systemctl list-unit-files | grep -q polkit.service; then
|
||||
sudo systemctl try-restart polkit
|
||||
fi
|
||||
|
||||
# Reload systemd configuration so that it picks up the new service.
|
||||
systemctl --user daemon-reload
|
||||
# Reload systemd configuration so that it picks up the new service.
|
||||
systemctl --user daemon-reload
|
||||
fi
|
||||
|
71
common/common-cpu.c
Normal file
71
common/common-cpu.c
Normal file
@ -0,0 +1,71 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of Feral Interactive nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#include "common-cpu.h"
|
||||
#include "common-logging.h"
|
||||
|
||||
char *parse_cpulist(char *cpulist, long *from, long *to)
|
||||
{
|
||||
if (!cpulist || *cpulist == '\0')
|
||||
return NULL;
|
||||
|
||||
char *endp;
|
||||
*from = strtol(cpulist, &endp, 10);
|
||||
|
||||
if (endp == cpulist)
|
||||
return NULL;
|
||||
|
||||
if (*endp == '\0' || *endp == ',') {
|
||||
*to = *from;
|
||||
|
||||
if (*endp == '\0')
|
||||
return endp;
|
||||
|
||||
return endp + 1;
|
||||
}
|
||||
|
||||
if (*endp != '-')
|
||||
return NULL;
|
||||
|
||||
cpulist = endp + 1;
|
||||
*to = strtol(cpulist, &endp, 10);
|
||||
|
||||
if (endp == cpulist)
|
||||
return NULL;
|
||||
|
||||
if (*to < *from)
|
||||
return NULL;
|
||||
|
||||
if (*endp == '\0')
|
||||
return endp;
|
||||
|
||||
return endp + 1;
|
||||
}
|
51
common/common-cpu.h
Normal file
51
common/common-cpu.h
Normal file
@ -0,0 +1,51 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of Feral Interactive nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <sched.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define IS_CPU_PARK 0
|
||||
#define IS_CPU_PIN 1
|
||||
|
||||
/* Storage for CPU info*/
|
||||
struct GameModeCPUInfo {
|
||||
size_t num_cpu;
|
||||
int park_or_pin;
|
||||
cpu_set_t *online;
|
||||
cpu_set_t *to_keep;
|
||||
};
|
||||
|
||||
/* parses a list of cpu cores in the format "a,b-c,d-e,f" */
|
||||
char *parse_cpulist(char *cpulist, long *from, long *to);
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2017-2019, Feral Interactive
|
||||
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
@ -140,7 +140,7 @@ int run_external_process(const char *const *exec_args, char buffer[EXTERNAL_BUFF
|
||||
* bindings that these objects are completely constant.
|
||||
* http://pubs.opengroup.org/onlinepubs/9699919799/functions/exec.html
|
||||
*/
|
||||
if (execv(exec_args[0], (char *const *)exec_args) != 0) {
|
||||
if (execvp(exec_args[0], (char *const *)exec_args) != 0) {
|
||||
LOG_ERROR("Failed to execute external process: %s %s\n", exec_args[0], strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2017-2019, Feral Interactive
|
||||
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2017-2019, Feral Interactive
|
||||
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
@ -122,12 +122,9 @@ const char *get_gov_state(void)
|
||||
long length = ftell(f);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
|
||||
if (length == -1)
|
||||
{
|
||||
if (length == -1) {
|
||||
LOG_ERROR("Failed to seek file %s\n", gov);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
char contents[length];
|
||||
|
||||
if (fread(contents, 1, (size_t)length, f) > 0) {
|
||||
@ -135,8 +132,10 @@ const char *get_gov_state(void)
|
||||
strtok(contents, "\n");
|
||||
if (strlen(governor) > 0 && strncmp(governor, contents, 64) != 0) {
|
||||
/* Don't handle the mixed case, this shouldn't ever happen
|
||||
* But it is a clear sign we shouldn't carry on */
|
||||
LOG_ERROR("Governors malformed: got \"%s\", expected \"%s\"", contents, governor);
|
||||
* But it is a clear sign we shouldn't carry on */
|
||||
LOG_ERROR("Governors malformed: got \"%s\", expected \"%s\"",
|
||||
contents,
|
||||
governor);
|
||||
fclose(f);
|
||||
return "malformed";
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2017-2019, Feral Interactive
|
||||
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2017-2019, Feral Interactive
|
||||
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2017-2019, Feral Interactive
|
||||
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2017-2019, Feral Interactive
|
||||
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
|
||||
Copyright (c) 2019, Red Hat
|
||||
All rights reserved.
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2017-2019, Feral Interactive
|
||||
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2017-2019, Feral Interactive
|
||||
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2017-2019, Feral Interactive
|
||||
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2017-2019, Feral Interactive
|
||||
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
|
||||
Copyright (c) 2019, Red Hat
|
||||
All rights reserved.
|
||||
|
||||
@ -58,6 +58,8 @@ static int pidfd_open(pid_t pid, unsigned int flags)
|
||||
{
|
||||
return (int)syscall(__NR_pidfd_open, pid, flags);
|
||||
}
|
||||
#else
|
||||
#include <sys/pidfd.h>
|
||||
#endif
|
||||
|
||||
/* pidfd functions */
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2017-2019, Feral Interactive
|
||||
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
|
||||
Copyright (c) 2019, Red Hat
|
||||
All rights reserved.
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2017-2019, Feral Interactive
|
||||
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
|
||||
Copyright (c) 2019, Intel Corporation
|
||||
All rights reserved.
|
||||
|
||||
@ -35,10 +35,10 @@ POSSIBILITY OF SUCH DAMAGE.
|
||||
#include "common-power.h"
|
||||
#include "common-logging.h"
|
||||
|
||||
#include <linux/limits.h>
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <glob.h>
|
||||
#include <linux/limits.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2017-2019, Feral Interactive
|
||||
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
|
86
common/common-profile.c
Normal file
86
common/common-profile.c
Normal file
@ -0,0 +1,86 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2025, the GameMode contributors
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of Feral Interactive nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include "common-profile.h"
|
||||
#include "common-logging.h"
|
||||
|
||||
/**
|
||||
* Path for platform profile
|
||||
*/
|
||||
const char *profile_path = "/sys/firmware/acpi/platform_profile";
|
||||
|
||||
/**
|
||||
* Check if platform profile file exists
|
||||
*/
|
||||
int profile_exists(void)
|
||||
{
|
||||
return !access(profile_path, F_OK);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current platform profile state
|
||||
*/
|
||||
const char *get_profile_state(void)
|
||||
{
|
||||
/* Persistent profile state */
|
||||
static char profile[64] = { 0 };
|
||||
memset(profile, 0, sizeof(profile));
|
||||
|
||||
FILE *f = fopen(profile_path, "r");
|
||||
if (!f) {
|
||||
LOG_ERROR("Failed to open file for read %s\n", profile_path);
|
||||
return "none";
|
||||
}
|
||||
|
||||
/* Grab the file length */
|
||||
fseek(f, 0, SEEK_END);
|
||||
long length = ftell(f);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
|
||||
if (length == -1) {
|
||||
LOG_ERROR("Failed to seek file %s\n", profile_path);
|
||||
} else {
|
||||
char contents[length + 1];
|
||||
|
||||
if (fread(contents, 1, (size_t)length, f) > 0) {
|
||||
strtok(contents, "\n");
|
||||
strncpy(profile, contents, sizeof(profile) - 1);
|
||||
} else {
|
||||
LOG_ERROR("Failed to read contents of %s\n", profile_path);
|
||||
}
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
|
||||
return profile;
|
||||
}
|
50
common/common-profile.h
Normal file
50
common/common-profile.h
Normal file
@ -0,0 +1,50 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2025, the GameMode contributors
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of Feral Interactive nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <linux/limits.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/**
|
||||
* Path for platform profile
|
||||
*/
|
||||
extern const char *profile_path;
|
||||
|
||||
/**
|
||||
* Check if platform profile file exists
|
||||
*/
|
||||
int profile_exists(void);
|
||||
|
||||
/**
|
||||
* Get the current platform profile state
|
||||
*/
|
||||
const char *get_profile_state(void);
|
64
common/common-splitlock.c
Normal file
64
common/common-splitlock.c
Normal file
@ -0,0 +1,64 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2025, the GameMode contributors
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of Feral Interactive nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include "common-splitlock.h"
|
||||
#include "common-logging.h"
|
||||
|
||||
/**
|
||||
* Path for the split lock mitigation state
|
||||
*/
|
||||
const char *splitlock_path = "/proc/sys/kernel/split_lock_mitigate";
|
||||
|
||||
/**
|
||||
* Return the current split lock mitigation state
|
||||
*/
|
||||
long get_splitlock_state(void)
|
||||
{
|
||||
FILE *f = fopen(splitlock_path, "r");
|
||||
if (!f) {
|
||||
LOG_ERROR("Failed to open file for read %s\n", splitlock_path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
char contents[41] = { 0 };
|
||||
long value = -1;
|
||||
|
||||
if (fread(contents, 1, sizeof contents - 1, f) > 0) {
|
||||
value = strtol(contents, NULL, 10);
|
||||
} else {
|
||||
LOG_ERROR("Failed to read contents of %s\n", splitlock_path);
|
||||
}
|
||||
fclose(f);
|
||||
|
||||
return value;
|
||||
}
|
44
common/common-splitlock.h
Normal file
44
common/common-splitlock.h
Normal file
@ -0,0 +1,44 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2025, the GameMode contributors
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of Feral Interactive nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <linux/limits.h>
|
||||
|
||||
/**
|
||||
* Path for the split lock mitagation state
|
||||
*/
|
||||
extern const char *splitlock_path;
|
||||
|
||||
/**
|
||||
* Get the current split lock mitigation state
|
||||
*/
|
||||
long get_splitlock_state(void);
|
@ -2,9 +2,12 @@
|
||||
common_sources = [
|
||||
'common-logging.c',
|
||||
'common-governors.c',
|
||||
'common-profile.c',
|
||||
'common-splitlock.c',
|
||||
'common-external.c',
|
||||
'common-helpers.c',
|
||||
'common-gpu.c',
|
||||
'common-cpu.c',
|
||||
'common-pidfds.c',
|
||||
'common-power.c',
|
||||
]
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2017-2019, Feral Interactive
|
||||
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
@ -35,10 +35,13 @@ POSSIBILITY OF SUCH DAMAGE.
|
||||
#include "common-helpers.h"
|
||||
#include "common-logging.h"
|
||||
|
||||
#include "build-config.h"
|
||||
|
||||
/* Ben Hoyt's inih library */
|
||||
#include <ini.h>
|
||||
|
||||
#include <dirent.h>
|
||||
#include <libgen.h>
|
||||
#include <math.h>
|
||||
#include <pthread.h>
|
||||
#include <pwd.h>
|
||||
@ -85,6 +88,9 @@ struct GameModeConfig {
|
||||
char defaultgov[CONFIG_VALUE_MAX];
|
||||
char desiredgov[CONFIG_VALUE_MAX];
|
||||
|
||||
char defaultprof[CONFIG_VALUE_MAX];
|
||||
char desiredprof[CONFIG_VALUE_MAX];
|
||||
|
||||
char igpu_desiredgov[CONFIG_VALUE_MAX];
|
||||
float igpu_power_threshold;
|
||||
|
||||
@ -95,6 +101,8 @@ struct GameModeConfig {
|
||||
|
||||
long inhibit_screensaver;
|
||||
|
||||
long disable_splitlock;
|
||||
|
||||
long reaper_frequency;
|
||||
|
||||
char apply_gpu_optimisations[CONFIG_VALUE_MAX];
|
||||
@ -104,6 +112,9 @@ struct GameModeConfig {
|
||||
long nv_powermizer_mode;
|
||||
char amd_performance_level[CONFIG_VALUE_MAX];
|
||||
|
||||
char cpu_park_cores[CONFIG_VALUE_MAX];
|
||||
char cpu_pin_cores[CONFIG_VALUE_MAX];
|
||||
|
||||
long require_supervisor;
|
||||
char supervisor_whitelist[CONFIG_LIST_MAX][CONFIG_VALUE_MAX];
|
||||
char supervisor_blacklist[CONFIG_LIST_MAX][CONFIG_VALUE_MAX];
|
||||
@ -257,6 +268,10 @@ static int inih_handler(void *user, const char *section, const char *name, const
|
||||
valid = get_string_value(value, self->values.defaultgov);
|
||||
} else if (strcmp(name, "desiredgov") == 0) {
|
||||
valid = get_string_value(value, self->values.desiredgov);
|
||||
} else if (strcmp(name, "defaultprof") == 0) {
|
||||
valid = get_string_value(value, self->values.defaultprof);
|
||||
} else if (strcmp(name, "desiredprof") == 0) {
|
||||
valid = get_string_value(value, self->values.desiredprof);
|
||||
} else if (strcmp(name, "igpu_desiredgov") == 0) {
|
||||
valid = get_string_value(value, self->values.igpu_desiredgov);
|
||||
} else if (strcmp(name, "igpu_power_threshold") == 0) {
|
||||
@ -269,6 +284,8 @@ static int inih_handler(void *user, const char *section, const char *name, const
|
||||
valid = get_string_value(value, self->values.ioprio);
|
||||
} else if (strcmp(name, "inhibit_screensaver") == 0) {
|
||||
valid = get_long_value(name, value, &self->values.inhibit_screensaver);
|
||||
} else if (strcmp(name, "disable_splitlock") == 0) {
|
||||
valid = get_long_value(name, value, &self->values.disable_splitlock);
|
||||
}
|
||||
} else if (strcmp(section, "gpu") == 0) {
|
||||
/* Protect the user - don't allow these config options from unsafe config locations */
|
||||
@ -277,9 +294,7 @@ static int inih_handler(void *user, const char *section, const char *name, const
|
||||
"The [gpu] config section is not configurable from unsafe config files! Option %s "
|
||||
"will be ignored!\n",
|
||||
name);
|
||||
LOG_ERROR(
|
||||
"Consider moving this option to /etc/gamemode.ini or "
|
||||
"/usr/share/gamemode/gamemode.ini\n");
|
||||
LOG_ERROR("Consider moving this option to /etc/gamemode.ini\n");
|
||||
}
|
||||
|
||||
/* GPU subsection */
|
||||
@ -296,6 +311,12 @@ static int inih_handler(void *user, const char *section, const char *name, const
|
||||
} else if (strcmp(name, "amd_performance_level") == 0) {
|
||||
valid = get_string_value(value, self->values.amd_performance_level);
|
||||
}
|
||||
} else if (strcmp(section, "cpu") == 0) {
|
||||
if (strcmp(name, "park_cores") == 0) {
|
||||
valid = get_string_value(value, self->values.cpu_park_cores);
|
||||
} else if (strcmp(name, "pin_cores") == 0) {
|
||||
valid = get_string_value(value, self->values.cpu_pin_cores);
|
||||
}
|
||||
} else if (strcmp(section, "supervisor") == 0) {
|
||||
/* Supervisor subsection */
|
||||
if (strcmp(name, "supervisor_whitelist") == 0) {
|
||||
@ -362,8 +383,9 @@ static void load_config_files(GameModeConfig *self)
|
||||
/* Set some non-zero defaults */
|
||||
self->values.igpu_power_threshold = DEFAULT_IGPU_POWER_THRESHOLD;
|
||||
self->values.inhibit_screensaver = 1; /* Defaults to on */
|
||||
self->values.disable_splitlock = 1; /* Defaults to on */
|
||||
self->values.reaper_frequency = DEFAULT_REAPER_FREQ;
|
||||
self->values.gpu_device = -1; /* 0 is a valid device ID so use -1 to indicate no value */
|
||||
self->values.gpu_device = 0;
|
||||
self->values.nv_powermizer_mode = -1;
|
||||
self->values.nv_core_clock_mhz_offset = -1;
|
||||
self->values.nv_mem_clock_mhz_offset = -1;
|
||||
@ -378,7 +400,7 @@ static void load_config_files(GameModeConfig *self)
|
||||
bool protected;
|
||||
};
|
||||
struct ConfigLocation locations[CONFIG_NUM_LOCATIONS] = {
|
||||
{ "/usr/share/gamemode", true }, /* shipped default config */
|
||||
{ SYSCONFDIR, true }, /* shipped default config */
|
||||
{ "/etc", true }, /* administrator config */
|
||||
{ config_location_home, false }, /* $XDG_CONFIG_HOME or $HOME/.config/ */
|
||||
{ config_location_local, false } /* local data eg. $PWD */
|
||||
@ -631,6 +653,16 @@ bool config_get_inhibit_screensaver(GameModeConfig *self)
|
||||
return val == 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Gets the disable splitlock setting
|
||||
*/
|
||||
bool config_get_disable_splitlock(GameModeConfig *self)
|
||||
{
|
||||
long val;
|
||||
memcpy_locked_config(self, &val, &self->values.disable_splitlock, sizeof(long));
|
||||
return val == 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get a set of scripts to call when gamemode starts
|
||||
*/
|
||||
@ -666,13 +698,29 @@ void config_get_default_governor(GameModeConfig *self, char governor[CONFIG_VALU
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the chosen desired governor
|
||||
* Get the chosen desired platform profile
|
||||
*/
|
||||
void config_get_desired_governor(GameModeConfig *self, char governor[CONFIG_VALUE_MAX])
|
||||
{
|
||||
memcpy_locked_config(self, governor, self->values.desiredgov, sizeof(self->values.desiredgov));
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the chosen default platform profile
|
||||
*/
|
||||
void config_get_default_profile(GameModeConfig *self, char profile[CONFIG_VALUE_MAX])
|
||||
{
|
||||
memcpy_locked_config(self, profile, self->values.defaultprof, sizeof(self->values.defaultprof));
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the chosen desired governor
|
||||
*/
|
||||
void config_get_desired_profile(GameModeConfig *self, char profile[CONFIG_VALUE_MAX])
|
||||
{
|
||||
memcpy_locked_config(self, profile, self->values.desiredprof, sizeof(self->values.desiredprof));
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the chosen iGPU desired governor
|
||||
*/
|
||||
@ -794,6 +842,25 @@ void config_get_amd_performance_level(GameModeConfig *self, char value[CONFIG_VA
|
||||
*/
|
||||
DEFINE_CONFIG_GET(require_supervisor)
|
||||
|
||||
/*
|
||||
* Get various config info for cpu optimisations
|
||||
*/
|
||||
void config_get_cpu_park_cores(GameModeConfig *self, char value[CONFIG_VALUE_MAX])
|
||||
{
|
||||
memcpy_locked_config(self,
|
||||
value,
|
||||
&self->values.cpu_park_cores,
|
||||
sizeof(self->values.cpu_park_cores));
|
||||
}
|
||||
|
||||
void config_get_cpu_pin_cores(GameModeConfig *self, char value[CONFIG_VALUE_MAX])
|
||||
{
|
||||
memcpy_locked_config(self,
|
||||
value,
|
||||
&self->values.cpu_pin_cores,
|
||||
sizeof(self->values.cpu_pin_cores));
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks if the supervisor is whitelisted
|
||||
*/
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2017-2019, Feral Interactive
|
||||
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
@ -102,11 +102,14 @@ bool config_get_inhibit_screensaver(GameModeConfig *self);
|
||||
long config_get_script_timeout(GameModeConfig *self);
|
||||
void config_get_default_governor(GameModeConfig *self, char governor[CONFIG_VALUE_MAX]);
|
||||
void config_get_desired_governor(GameModeConfig *self, char governor[CONFIG_VALUE_MAX]);
|
||||
void config_get_default_profile(GameModeConfig *self, char profile[CONFIG_VALUE_MAX]);
|
||||
void config_get_desired_profile(GameModeConfig *self, char profile[CONFIG_VALUE_MAX]);
|
||||
void config_get_igpu_desired_governor(GameModeConfig *self, char governor[CONFIG_VALUE_MAX]);
|
||||
float config_get_igpu_power_threshold(GameModeConfig *self);
|
||||
void config_get_soft_realtime(GameModeConfig *self, char softrealtime[CONFIG_VALUE_MAX]);
|
||||
long config_get_renice_value(GameModeConfig *self);
|
||||
long config_get_ioprio_value(GameModeConfig *self);
|
||||
bool config_get_disable_splitlock(GameModeConfig *self);
|
||||
|
||||
/*
|
||||
* Get various config info for gpu optimisations
|
||||
@ -118,6 +121,12 @@ long config_get_nv_mem_clock_mhz_offset(GameModeConfig *self);
|
||||
long config_get_nv_powermizer_mode(GameModeConfig *self);
|
||||
void config_get_amd_performance_level(GameModeConfig *self, char value[CONFIG_VALUE_MAX]);
|
||||
|
||||
/*
|
||||
* Get various config info for cpu optimisations
|
||||
*/
|
||||
void config_get_cpu_park_cores(GameModeConfig *self, char value[CONFIG_VALUE_MAX]);
|
||||
void config_get_cpu_pin_cores(GameModeConfig *self, char value[CONFIG_VALUE_MAX]);
|
||||
|
||||
/**
|
||||
* Functions to get supervisor config permissions
|
||||
*/
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2017-2019, Feral Interactive
|
||||
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
@ -36,6 +36,8 @@ POSSIBILITY OF SUCH DAMAGE.
|
||||
#include "common-helpers.h"
|
||||
#include "common-logging.h"
|
||||
#include "common-power.h"
|
||||
#include "common-profile.h"
|
||||
#include "common-splitlock.h"
|
||||
|
||||
#include "gamemode.h"
|
||||
#include "gamemode-config.h"
|
||||
@ -45,6 +47,7 @@ POSSIBILITY OF SUCH DAMAGE.
|
||||
#include <assert.h>
|
||||
#include <fcntl.h>
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
#include <stdatomic.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/time.h>
|
||||
@ -70,6 +73,11 @@ enum GameModeGovernor {
|
||||
GAME_MODE_GOVERNOR_IGPU_DESIRED,
|
||||
};
|
||||
|
||||
enum GameModeProfile {
|
||||
GAME_MODE_PROFILE_DEFAULT,
|
||||
GAME_MODE_PROFILE_DESIRED,
|
||||
};
|
||||
|
||||
struct GameModeContext {
|
||||
pthread_rwlock_t rwlock; /**<Guard access to the client list */
|
||||
_Atomic int refcount; /**<Allow cycling the game mode */
|
||||
@ -81,13 +89,22 @@ struct GameModeContext {
|
||||
|
||||
enum GameModeGovernor current_govenor;
|
||||
|
||||
char initial_profile[64];
|
||||
enum GameModeProfile current_profile;
|
||||
|
||||
struct GameModeGPUInfo *stored_gpu; /**<Stored GPU info for the current GPU */
|
||||
struct GameModeGPUInfo *target_gpu; /**<Target GPU info for the current GPU */
|
||||
|
||||
struct GameModeCPUInfo *cpu; /**<Stored CPU info for the current CPU */
|
||||
|
||||
GameModeIdleInhibitor *idle_inhibitor;
|
||||
|
||||
bool igpu_optimization_enabled;
|
||||
uint32_t last_cpu_energy_uj;
|
||||
uint32_t last_igpu_energy_uj;
|
||||
|
||||
long initial_split_lock_mitigate;
|
||||
|
||||
/* Reaper control */
|
||||
struct {
|
||||
pthread_t thread;
|
||||
@ -111,6 +128,7 @@ static void game_mode_context_enter(GameModeContext *self);
|
||||
static void game_mode_context_leave(GameModeContext *self);
|
||||
static char *game_mode_context_find_exe(pid_t pid);
|
||||
static void game_mode_execute_scripts(char scripts[CONFIG_LIST_MAX][CONFIG_VALUE_MAX], int timeout);
|
||||
static int game_mode_disable_splitlock(GameModeContext *self, bool disable);
|
||||
|
||||
static void start_reaper_thread(GameModeContext *self)
|
||||
{
|
||||
@ -145,6 +163,11 @@ void game_mode_context_init(GameModeContext *self)
|
||||
game_mode_initialise_gpu(self->config, &self->stored_gpu);
|
||||
game_mode_initialise_gpu(self->config, &self->target_gpu);
|
||||
|
||||
/* Initialise the current CPU info */
|
||||
game_mode_initialise_cpu(self->config, &self->cpu);
|
||||
|
||||
self->initial_split_lock_mitigate = -1;
|
||||
|
||||
pthread_rwlock_init(&self->rwlock, NULL);
|
||||
|
||||
/* Get the reaper thread going */
|
||||
@ -187,31 +210,72 @@ void game_mode_context_destroy(GameModeContext *self)
|
||||
game_mode_free_gpu(&self->stored_gpu);
|
||||
game_mode_free_gpu(&self->target_gpu);
|
||||
|
||||
/* Destroy the cpu object */
|
||||
game_mode_free_cpu(&self->cpu);
|
||||
|
||||
/* Destroy the config object */
|
||||
config_destroy(self->config);
|
||||
|
||||
pthread_rwlock_destroy(&self->rwlock);
|
||||
}
|
||||
|
||||
static void game_mode_store_splitlock(GameModeContext *self)
|
||||
{
|
||||
long initial_state = get_splitlock_state();
|
||||
self->initial_split_lock_mitigate = initial_state;
|
||||
LOG_MSG("split lock mitigation was initially set to [%ld]\n", initial_state);
|
||||
}
|
||||
|
||||
static int game_mode_disable_splitlock(GameModeContext *self, bool disable)
|
||||
{
|
||||
if (!config_get_disable_splitlock(self->config))
|
||||
return 0;
|
||||
|
||||
long value_num = self->initial_split_lock_mitigate;
|
||||
char value_str[40];
|
||||
|
||||
if (disable && value_num == 0)
|
||||
return 0;
|
||||
|
||||
if (value_num == -1)
|
||||
return 0;
|
||||
|
||||
sprintf(value_str, "%ld", value_num);
|
||||
|
||||
const char *const exec_args[] = {
|
||||
"pkexec", LIBEXECDIR "/procsysctl", "split_lock_mitigate", value_str, NULL,
|
||||
};
|
||||
|
||||
LOG_MSG("Requesting update of split_lock_mitigate to %s\n", value_str);
|
||||
int ret = run_external_process(exec_args, NULL, -1);
|
||||
if (ret != 0) {
|
||||
LOG_ERROR("Failed to update split_lock_mitigate\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void game_mode_store_governor(GameModeContext *self)
|
||||
{
|
||||
if (self->current_govenor != GAME_MODE_GOVERNOR_DEFAULT)
|
||||
return;
|
||||
|
||||
const char *initial_state = get_gov_state();
|
||||
if (initial_state == NULL)
|
||||
return;
|
||||
|
||||
strncpy(self->initial_cpu_mode, initial_state, sizeof(self->initial_cpu_mode) - 1);
|
||||
self->initial_cpu_mode[sizeof(self->initial_cpu_mode) - 1] = '\0';
|
||||
LOG_MSG("governor was initially set to [%s]\n", initial_state);
|
||||
}
|
||||
|
||||
static int game_mode_set_governor(GameModeContext *self, enum GameModeGovernor gov)
|
||||
{
|
||||
if (self->current_govenor == gov) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (self->current_govenor == GAME_MODE_GOVERNOR_DEFAULT) {
|
||||
/* Read the initial governor state so we can revert it correctly */
|
||||
const char *initial_state = get_gov_state();
|
||||
if (initial_state == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* store the initial cpu governor mode */
|
||||
strncpy(self->initial_cpu_mode, initial_state, sizeof(self->initial_cpu_mode) - 1);
|
||||
self->initial_cpu_mode[sizeof(self->initial_cpu_mode) - 1] = '\0';
|
||||
LOG_MSG("governor was initially set to [%s]\n", initial_state);
|
||||
}
|
||||
|
||||
char *gov_str = NULL;
|
||||
char gov_config_str[CONFIG_VALUE_MAX] = { 0 };
|
||||
switch (gov) {
|
||||
@ -235,7 +299,7 @@ static int game_mode_set_governor(GameModeContext *self, enum GameModeGovernor g
|
||||
}
|
||||
|
||||
const char *const exec_args[] = {
|
||||
"/usr/bin/pkexec", LIBEXECDIR "/cpugovctl", "set", gov_str, NULL,
|
||||
"pkexec", LIBEXECDIR "/cpugovctl", "set", gov_str, NULL,
|
||||
};
|
||||
|
||||
LOG_MSG("Requesting update of governor policy to %s\n", gov_str);
|
||||
@ -251,6 +315,65 @@ static int game_mode_set_governor(GameModeContext *self, enum GameModeGovernor g
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void game_mode_store_profile(GameModeContext *self)
|
||||
{
|
||||
if (!profile_exists() || self->current_profile != GAME_MODE_PROFILE_DEFAULT)
|
||||
return;
|
||||
|
||||
const char *initial_state = get_profile_state();
|
||||
if (initial_state == NULL)
|
||||
return;
|
||||
|
||||
strncpy(self->initial_profile, initial_state, sizeof(self->initial_profile) - 1);
|
||||
self->initial_profile[sizeof(self->initial_profile) - 1] = '\0';
|
||||
LOG_MSG("platform profile was initially set to [%s]\n", initial_state);
|
||||
}
|
||||
|
||||
static int game_mode_set_profile(GameModeContext *self, enum GameModeProfile prof)
|
||||
{
|
||||
if (self->current_profile == prof) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!profile_exists()) {
|
||||
LOG_MSG("Setting platform profile unsupported; skipping\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *prof_str = NULL;
|
||||
char prof_config_str[CONFIG_VALUE_MAX] = { 0 };
|
||||
switch (prof) {
|
||||
case GAME_MODE_PROFILE_DEFAULT:
|
||||
config_get_default_profile(self->config, prof_config_str);
|
||||
prof_str = prof_config_str[0] != '\0' ? prof_config_str : self->initial_profile;
|
||||
break;
|
||||
|
||||
case GAME_MODE_PROFILE_DESIRED:
|
||||
config_get_desired_profile(self->config, prof_config_str);
|
||||
prof_str = prof_config_str[0] != '\0' ? prof_config_str : "performance";
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(!"Invalid platform profile requested");
|
||||
}
|
||||
|
||||
const char *const exec_args[] = {
|
||||
"pkexec", LIBEXECDIR "/platprofctl", "set", prof_str, NULL,
|
||||
};
|
||||
|
||||
LOG_MSG("Requesting update of platform profile to %s\n", prof_str);
|
||||
int ret = run_external_process(exec_args, NULL, -1);
|
||||
if (ret != 0) {
|
||||
LOG_ERROR("Failed to update platform profile\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Update the current govenor only if we succeed at setting govenors. */
|
||||
self->current_profile = prof;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void game_mode_enable_igpu_optimization(GameModeContext *self)
|
||||
{
|
||||
float threshold = config_get_igpu_power_threshold(self->config);
|
||||
@ -338,6 +461,15 @@ unlock:
|
||||
pthread_rwlock_unlock(&self->rwlock);
|
||||
}
|
||||
|
||||
static void game_mode_context_store_defaults(GameModeContext *self)
|
||||
{
|
||||
game_mode_store_profile(self);
|
||||
|
||||
game_mode_store_governor(self);
|
||||
|
||||
game_mode_store_splitlock(self);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pivot into game mode.
|
||||
*
|
||||
@ -349,6 +481,14 @@ static void game_mode_context_enter(GameModeContext *self)
|
||||
LOG_MSG("Entering Game Mode...\n");
|
||||
sd_notifyf(0, "STATUS=%sGameMode is now active.%s\n", "\x1B[1;32m", "\x1B[0m");
|
||||
|
||||
/* Store the default value for everything before anything changes. */
|
||||
game_mode_context_store_defaults(self);
|
||||
|
||||
/* Set the profile before anything else since it can restrict things
|
||||
* like the governor.
|
||||
*/
|
||||
game_mode_set_profile(self, GAME_MODE_PROFILE_DESIRED);
|
||||
|
||||
if (game_mode_set_governor(self, GAME_MODE_GOVERNOR_DESIRED) == 0) {
|
||||
/* We just switched to a non-default governor. Enable the iGPU
|
||||
* optimization.
|
||||
@ -357,13 +497,19 @@ static void game_mode_context_enter(GameModeContext *self)
|
||||
}
|
||||
|
||||
/* Inhibit the screensaver */
|
||||
if (config_get_inhibit_screensaver(self->config))
|
||||
game_mode_inhibit_screensaver(true);
|
||||
if (config_get_inhibit_screensaver(self->config)) {
|
||||
game_mode_destroy_idle_inhibitor(self->idle_inhibitor);
|
||||
self->idle_inhibitor = game_mode_create_idle_inhibitor();
|
||||
}
|
||||
|
||||
game_mode_disable_splitlock(self, true);
|
||||
|
||||
/* Apply GPU optimisations by first getting the current values, and then setting the target */
|
||||
game_mode_get_gpu(self->stored_gpu);
|
||||
game_mode_apply_gpu(self->target_gpu);
|
||||
|
||||
game_mode_park_cpu(self->cpu);
|
||||
|
||||
/* Run custom scripts last - ensures the above are applied first and these scripts can react to
|
||||
* them if needed */
|
||||
char scripts[CONFIG_LIST_MAX][CONFIG_VALUE_MAX];
|
||||
@ -384,12 +530,23 @@ static void game_mode_context_leave(GameModeContext *self)
|
||||
LOG_MSG("Leaving Game Mode...\n");
|
||||
sd_notifyf(0, "STATUS=%sGameMode is currently deactivated.%s\n", "\x1B[1;36m", "\x1B[0m");
|
||||
|
||||
/* Restore profile before anything else since it can restrict things
|
||||
* like the governor.
|
||||
*/
|
||||
game_mode_set_profile(self, GAME_MODE_PROFILE_DEFAULT);
|
||||
|
||||
/* Remove GPU optimisations */
|
||||
game_mode_apply_gpu(self->stored_gpu);
|
||||
|
||||
game_mode_unpark_cpu(self->cpu);
|
||||
|
||||
/* UnInhibit the screensaver */
|
||||
if (config_get_inhibit_screensaver(self->config))
|
||||
game_mode_inhibit_screensaver(false);
|
||||
if (config_get_inhibit_screensaver(self->config)) {
|
||||
game_mode_destroy_idle_inhibitor(self->idle_inhibitor);
|
||||
self->idle_inhibitor = NULL;
|
||||
}
|
||||
|
||||
game_mode_disable_splitlock(self, false);
|
||||
|
||||
game_mode_set_governor(self, GAME_MODE_GOVERNOR_DEFAULT);
|
||||
|
||||
@ -521,6 +678,9 @@ static int game_mode_apply_client_optimisations(GameModeContext *self, pid_t cli
|
||||
/* Apply scheduler policies */
|
||||
game_mode_apply_scheduling(self, client);
|
||||
|
||||
/* Apply core pinning */
|
||||
game_mode_apply_core_pinning(self->cpu, client, false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -633,6 +793,8 @@ static int game_mode_remove_client_optimisations(GameModeContext *self, pid_t cl
|
||||
/* Restore the renice value for the process, expecting it to be our config value */
|
||||
game_mode_apply_renice(self, client, (int)config_get_renice_value(self->config));
|
||||
|
||||
/* Restore the process affinity to all online cores */
|
||||
game_mode_undo_core_pinning(self->cpu, client);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -870,6 +1032,16 @@ uint64_t game_mode_client_get_timestamp(GameModeClient *client)
|
||||
return (uint64_t)client->timestamp;
|
||||
}
|
||||
|
||||
static void game_mode_reapply_core_pinning_internal(GameModeContext *self)
|
||||
{
|
||||
pthread_rwlock_wrlock(&self->rwlock);
|
||||
if (game_mode_context_num_clients(self)) {
|
||||
for (GameModeClient *cl = self->client; cl; cl = cl->next)
|
||||
game_mode_apply_core_pinning(self->cpu, cl->pid, true);
|
||||
}
|
||||
pthread_rwlock_unlock(&self->rwlock);
|
||||
}
|
||||
|
||||
/* Internal refresh config function (assumes no contention with reaper thread) */
|
||||
static void game_mode_reload_config_internal(GameModeContext *self)
|
||||
{
|
||||
@ -888,6 +1060,7 @@ static void game_mode_reload_config_internal(GameModeContext *self)
|
||||
|
||||
/* Reload the config */
|
||||
config_reload(self->config);
|
||||
game_mode_reconfig_cpu(self->config, &self->cpu);
|
||||
|
||||
/* Re-apply all current optimisations */
|
||||
if (game_mode_context_num_clients(self)) {
|
||||
@ -933,6 +1106,9 @@ static void *game_mode_context_reaper(void *userdata)
|
||||
/* Expire remaining entries */
|
||||
game_mode_context_auto_expire(self);
|
||||
|
||||
/* Re apply the thread affinity mask (aka core pinning) */
|
||||
game_mode_reapply_core_pinning_internal(self);
|
||||
|
||||
/* Check if we should be reloading the config, and do so if needed */
|
||||
if (config_needs_reload(self->config)) {
|
||||
LOG_MSG("Detected config file changes\n");
|
||||
|
540
daemon/gamemode-cpu.c
Normal file
540
daemon/gamemode-cpu.c
Normal file
@ -0,0 +1,540 @@
|
||||
|
||||
/*
|
||||
|
||||
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of Feral Interactive nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <linux/limits.h>
|
||||
#include <dirent.h>
|
||||
#include <sched.h>
|
||||
|
||||
#include "common-cpu.h"
|
||||
#include "common-external.h"
|
||||
#include "common-helpers.h"
|
||||
#include "common-logging.h"
|
||||
|
||||
#include "gamemode.h"
|
||||
#include "gamemode-config.h"
|
||||
|
||||
#include "build-config.h"
|
||||
|
||||
static int read_small_file(char *path, char **buf, size_t *buflen)
|
||||
{
|
||||
FILE *f = fopen(path, "r");
|
||||
|
||||
if (!f) {
|
||||
LOG_ERROR("Couldn't open file at %s : %s\n", path, strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
|
||||
ssize_t nread = getline(buf, buflen, f);
|
||||
|
||||
if (nread == -1) {
|
||||
LOG_ERROR("Couldn't read file at %s : %s\n", path, strerror(errno));
|
||||
fclose(f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
|
||||
while (nread > 0 && ((*buf)[nread - 1] == '\n' || (*buf)[nread - 1] == '\r'))
|
||||
nread--;
|
||||
|
||||
(*buf)[nread] = '\0';
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void set_online_from_list(char *cpulist, GameModeCPUInfo *info)
|
||||
{
|
||||
long from, to;
|
||||
while ((cpulist = parse_cpulist(cpulist, &from, &to))) {
|
||||
for (long cpu = from; cpu < to + 1; cpu++) {
|
||||
CPU_SET_S((size_t)cpu, CPU_ALLOC_SIZE(info->num_cpu), info->online);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int check_pe_cores(char **buf, size_t *buflen, GameModeCPUInfo *info)
|
||||
{
|
||||
if (!read_small_file("/sys/devices/cpu_core/cpus", buf, buflen))
|
||||
return 0;
|
||||
|
||||
LOG_MSG("found kernel support for checking P/E-cores\n");
|
||||
|
||||
long from, to;
|
||||
char *list = *buf;
|
||||
while ((list = parse_cpulist(list, &from, &to))) {
|
||||
for (long cpu = from; cpu < to + 1; cpu++) {
|
||||
CPU_SET_S((size_t)cpu, CPU_ALLOC_SIZE(info->num_cpu), info->to_keep);
|
||||
}
|
||||
}
|
||||
|
||||
if (CPU_EQUAL_S(CPU_ALLOC_SIZE(info->num_cpu), info->online, info->to_keep) ||
|
||||
CPU_COUNT_S(CPU_ALLOC_SIZE(info->num_cpu), info->to_keep) == 0)
|
||||
LOG_MSG("kernel did not indicate that this was an P/E-cores system\n");
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int walk_sysfs(char *cpulist, char **buf, size_t *buflen, GameModeCPUInfo *info)
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
unsigned long long max_cache = 0, max_freq = 0;
|
||||
long from, to;
|
||||
|
||||
cpu_set_t *freq_cores = CPU_ALLOC(info->num_cpu);
|
||||
|
||||
char *list = cpulist;
|
||||
while ((list = parse_cpulist(list, &from, &to))) {
|
||||
for (long cpu = from; cpu < to + 1; cpu++) {
|
||||
/* check for L3 cache non-uniformity among the cores */
|
||||
int ret =
|
||||
snprintf(path, PATH_MAX, "/sys/devices/system/cpu/cpu%ld/cache/index3/size", cpu);
|
||||
|
||||
if (ret > 0 && ret < PATH_MAX) {
|
||||
if (read_small_file(path, buf, buflen)) {
|
||||
char *endp;
|
||||
unsigned long long cache_size = strtoull(*buf, &endp, 10);
|
||||
|
||||
if (*endp == 'K') {
|
||||
cache_size *= 1024;
|
||||
} else if (*endp == 'M') {
|
||||
cache_size *= 1024 * 1024;
|
||||
} else if (*endp == 'G') {
|
||||
cache_size *= 1024 * 1024 * 1024;
|
||||
} else if (*endp != '\0') {
|
||||
LOG_MSG("cpu L3 cache size (%s) on core #%ld is silly\n", *buf, cpu);
|
||||
cache_size = 0;
|
||||
}
|
||||
|
||||
if (cache_size > max_cache) {
|
||||
max_cache = cache_size;
|
||||
CPU_ZERO_S(CPU_ALLOC_SIZE(info->num_cpu), info->to_keep);
|
||||
}
|
||||
|
||||
if (cache_size == max_cache)
|
||||
CPU_SET_S((size_t)cpu, CPU_ALLOC_SIZE(info->num_cpu), info->to_keep);
|
||||
}
|
||||
}
|
||||
|
||||
/* check for frequency non-uniformity among the cores */
|
||||
ret = snprintf(path,
|
||||
PATH_MAX,
|
||||
"/sys/devices/system/cpu/cpu%ld/cpufreq/cpuinfo_max_freq",
|
||||
cpu);
|
||||
|
||||
if (ret > 0 && ret < PATH_MAX) {
|
||||
if (read_small_file(path, buf, buflen)) {
|
||||
unsigned long long freq = strtoull(*buf, NULL, 10);
|
||||
unsigned long long cutoff = (freq * 10) / 100;
|
||||
|
||||
if (freq > max_freq) {
|
||||
if (max_freq < freq - cutoff)
|
||||
CPU_ZERO_S(CPU_ALLOC_SIZE(info->num_cpu), freq_cores);
|
||||
|
||||
max_freq = freq;
|
||||
}
|
||||
|
||||
if (freq + cutoff >= max_freq)
|
||||
CPU_SET_S((size_t)cpu, CPU_ALLOC_SIZE(info->num_cpu), freq_cores);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (CPU_EQUAL_S(CPU_ALLOC_SIZE(info->num_cpu), info->online, info->to_keep) ||
|
||||
CPU_COUNT_S(CPU_ALLOC_SIZE(info->num_cpu), info->to_keep) == 0) {
|
||||
LOG_MSG("cpu L3 cache was uniform, this is not a x3D with multiple chiplets\n");
|
||||
|
||||
CPU_FREE(info->to_keep);
|
||||
info->to_keep = freq_cores;
|
||||
|
||||
if (CPU_EQUAL_S(CPU_ALLOC_SIZE(info->num_cpu), info->online, info->to_keep) ||
|
||||
CPU_COUNT_S(CPU_ALLOC_SIZE(info->num_cpu), info->to_keep) == 0)
|
||||
LOG_MSG("cpu frequency was uniform, this is not a big.LITTLE type of system\n");
|
||||
} else {
|
||||
CPU_FREE(freq_cores);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int walk_string(char *cpulist, char *config_cpulist, GameModeCPUInfo *info)
|
||||
{
|
||||
long from, to;
|
||||
|
||||
char *list = cpulist;
|
||||
while ((list = parse_cpulist(list, &from, &to))) {
|
||||
for (long cpu = from; cpu < to + 1; cpu++) {
|
||||
CPU_SET_S((size_t)cpu, CPU_ALLOC_SIZE(info->num_cpu), info->online);
|
||||
|
||||
if (info->park_or_pin == IS_CPU_PARK)
|
||||
CPU_SET_S((size_t)cpu, CPU_ALLOC_SIZE(info->num_cpu), info->to_keep);
|
||||
}
|
||||
}
|
||||
|
||||
list = config_cpulist;
|
||||
while ((list = parse_cpulist(list, &from, &to))) {
|
||||
for (long cpu = from; cpu < to + 1; cpu++) {
|
||||
if (CPU_ISSET_S((size_t)cpu, CPU_ALLOC_SIZE(info->num_cpu), info->online)) {
|
||||
if (info->park_or_pin == IS_CPU_PARK)
|
||||
CPU_CLR_S((size_t)cpu, CPU_ALLOC_SIZE(info->num_cpu), info->to_keep);
|
||||
else
|
||||
CPU_SET_S((size_t)cpu, CPU_ALLOC_SIZE(info->num_cpu), info->to_keep);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void game_mode_reconfig_cpu(GameModeConfig *config, GameModeCPUInfo **info)
|
||||
{
|
||||
game_mode_unpark_cpu(*info);
|
||||
game_mode_free_cpu(info);
|
||||
game_mode_initialise_cpu(config, info);
|
||||
}
|
||||
|
||||
int game_mode_initialise_cpu(GameModeConfig *config, GameModeCPUInfo **info)
|
||||
{
|
||||
/* Verify input, this is programmer error */
|
||||
if (!info || *info)
|
||||
FATAL_ERROR("Invalid GameModeCPUInfo passed to %s", __func__);
|
||||
|
||||
/* Early out if we have this feature turned off */
|
||||
char park_cores[CONFIG_VALUE_MAX];
|
||||
char pin_cores[CONFIG_VALUE_MAX];
|
||||
config_get_cpu_park_cores(config, park_cores);
|
||||
config_get_cpu_pin_cores(config, pin_cores);
|
||||
|
||||
int park_or_pin = -1;
|
||||
|
||||
if (pin_cores[0] != '\0') {
|
||||
if (strcasecmp(pin_cores, "no") == 0 || strcasecmp(pin_cores, "false") == 0 ||
|
||||
strcmp(pin_cores, "0") == 0) {
|
||||
park_or_pin = -2;
|
||||
} else if (strcasecmp(pin_cores, "yes") == 0 || strcasecmp(pin_cores, "true") == 0 ||
|
||||
strcmp(pin_cores, "1") == 0) {
|
||||
pin_cores[0] = '\0';
|
||||
park_or_pin = IS_CPU_PIN;
|
||||
} else {
|
||||
park_or_pin = IS_CPU_PIN;
|
||||
}
|
||||
}
|
||||
|
||||
if (park_or_pin != IS_CPU_PIN && park_cores[0] != '\0') {
|
||||
if (strcasecmp(park_cores, "no") == 0 || strcasecmp(park_cores, "false") == 0 ||
|
||||
strcmp(park_cores, "0") == 0) {
|
||||
if (park_or_pin == -2)
|
||||
return 0;
|
||||
|
||||
park_or_pin = -1;
|
||||
} else if (strcasecmp(park_cores, "yes") == 0 || strcasecmp(park_cores, "true") == 0 ||
|
||||
strcmp(park_cores, "1") == 0) {
|
||||
park_cores[0] = '\0';
|
||||
park_or_pin = IS_CPU_PARK;
|
||||
} else {
|
||||
park_or_pin = IS_CPU_PARK;
|
||||
}
|
||||
}
|
||||
|
||||
/* always default to pin */
|
||||
if (park_or_pin != IS_CPU_PARK)
|
||||
park_or_pin = IS_CPU_PIN;
|
||||
|
||||
char *buf = NULL, *buf2 = NULL;
|
||||
size_t buflen = 0, buf2len = 0;
|
||||
|
||||
/* first we find which cores are online, this also helps us to determine the max
|
||||
* cpu core number that we need to allocate the cpulist later */
|
||||
if (!read_small_file("/sys/devices/system/cpu/online", &buf, &buflen))
|
||||
goto error_exit;
|
||||
|
||||
long from, to, max = 0;
|
||||
char *s = buf;
|
||||
while ((s = parse_cpulist(s, &from, &to))) {
|
||||
if (to > max)
|
||||
max = to;
|
||||
}
|
||||
|
||||
/* either parsing failed or we have only a single core, in either case
|
||||
* we cannot optimize anyway */
|
||||
if (max == 0)
|
||||
goto early_exit;
|
||||
|
||||
GameModeCPUInfo *new_info = malloc(sizeof(GameModeCPUInfo));
|
||||
memset(new_info, 0, sizeof(GameModeCPUInfo));
|
||||
|
||||
new_info->num_cpu = (size_t)(max + 1);
|
||||
new_info->park_or_pin = park_or_pin;
|
||||
new_info->online = CPU_ALLOC(new_info->num_cpu);
|
||||
new_info->to_keep = CPU_ALLOC(new_info->num_cpu);
|
||||
|
||||
CPU_ZERO_S(CPU_ALLOC_SIZE(new_info->num_cpu), new_info->online);
|
||||
CPU_ZERO_S(CPU_ALLOC_SIZE(new_info->num_cpu), new_info->to_keep);
|
||||
|
||||
if (park_or_pin == IS_CPU_PARK && park_cores[0] != '\0') {
|
||||
if (!walk_string(buf, park_cores, new_info))
|
||||
goto error_exit;
|
||||
} else if (park_or_pin == IS_CPU_PIN && pin_cores[0] != '\0') {
|
||||
if (!walk_string(buf, pin_cores, new_info))
|
||||
goto error_exit;
|
||||
} else {
|
||||
set_online_from_list(buf, new_info);
|
||||
|
||||
if (!check_pe_cores(&buf2, &buf2len, new_info)) {
|
||||
if (!walk_sysfs(buf, &buf2, &buf2len, new_info))
|
||||
goto error_exit;
|
||||
}
|
||||
}
|
||||
|
||||
if (park_or_pin == IS_CPU_PARK &&
|
||||
CPU_EQUAL_S(CPU_ALLOC_SIZE(new_info->num_cpu), new_info->online, new_info->to_keep)) {
|
||||
game_mode_free_cpu(&new_info);
|
||||
LOG_MSG("I can find no reason to perform core parking on this system!\n");
|
||||
goto error_exit;
|
||||
}
|
||||
|
||||
if (CPU_COUNT_S(CPU_ALLOC_SIZE(new_info->num_cpu), new_info->to_keep) == 0) {
|
||||
game_mode_free_cpu(&new_info);
|
||||
LOG_MSG("I can find no reason to perform core pinning on this system!\n");
|
||||
goto error_exit;
|
||||
}
|
||||
|
||||
if (CPU_COUNT_S(CPU_ALLOC_SIZE(new_info->num_cpu), new_info->to_keep) < 4) {
|
||||
game_mode_free_cpu(&new_info);
|
||||
LOG_MSG(
|
||||
"logic or config would result in less than 4 active cores, will not apply cpu core "
|
||||
"parking/pinning!\n");
|
||||
goto error_exit;
|
||||
}
|
||||
|
||||
*info = new_info;
|
||||
|
||||
early_exit:
|
||||
free(buf);
|
||||
free(buf2);
|
||||
return 0;
|
||||
|
||||
error_exit:
|
||||
free(buf);
|
||||
free(buf2);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int log_state(char *cpulist, int *pos, const long first, const long last)
|
||||
{
|
||||
int ret;
|
||||
if (*pos != 0) {
|
||||
ret = snprintf(cpulist + *pos, ARG_MAX - (size_t)*pos, ",");
|
||||
|
||||
if (ret < 0 || (size_t)ret >= (ARG_MAX - (size_t)*pos)) {
|
||||
LOG_ERROR("snprintf failed, will not apply cpu core parking!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
*pos += ret;
|
||||
}
|
||||
|
||||
if (first == last)
|
||||
ret = snprintf(cpulist + *pos, ARG_MAX - (size_t)*pos, "%ld", first);
|
||||
else
|
||||
ret = snprintf(cpulist + *pos, ARG_MAX - (size_t)*pos, "%ld-%ld", first, last);
|
||||
|
||||
if (ret < 0 || (size_t)ret >= (ARG_MAX - (size_t)*pos)) {
|
||||
LOG_ERROR("snprintf failed, will not apply cpu core parking!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
*pos += ret;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int game_mode_park_cpu(const GameModeCPUInfo *info)
|
||||
{
|
||||
if (!info || info->park_or_pin == IS_CPU_PIN)
|
||||
return 0;
|
||||
|
||||
long first = -1, last = -1;
|
||||
|
||||
char cpulist[ARG_MAX];
|
||||
int pos = 0;
|
||||
|
||||
for (long cpu = 0; cpu < (long)(info->num_cpu); cpu++) {
|
||||
if (CPU_ISSET_S((size_t)cpu, CPU_ALLOC_SIZE(info->num_cpu), info->online) &&
|
||||
!CPU_ISSET_S((size_t)cpu, CPU_ALLOC_SIZE(info->num_cpu), info->to_keep)) {
|
||||
if (first == -1) {
|
||||
first = cpu;
|
||||
last = cpu;
|
||||
} else if (last + 1 == cpu) {
|
||||
last = cpu;
|
||||
} else {
|
||||
if (!log_state(cpulist, &pos, first, last))
|
||||
return 0;
|
||||
|
||||
first = cpu;
|
||||
last = cpu;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (first != -1)
|
||||
log_state(cpulist, &pos, first, last);
|
||||
|
||||
const char *const exec_args[] = {
|
||||
"pkexec", LIBEXECDIR "/cpucorectl", "offline", cpulist, NULL,
|
||||
};
|
||||
|
||||
LOG_MSG("Requesting parking of cores %s\n", cpulist);
|
||||
int ret = run_external_process(exec_args, NULL, -1);
|
||||
if (ret != 0) {
|
||||
LOG_ERROR("Failed to park cpu cores\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int game_mode_unpark_cpu(const GameModeCPUInfo *info)
|
||||
{
|
||||
if (!info || info->park_or_pin == IS_CPU_PIN)
|
||||
return 0;
|
||||
|
||||
long first = -1, last = -1;
|
||||
|
||||
char cpulist[ARG_MAX];
|
||||
int pos = 0;
|
||||
|
||||
for (long cpu = 0; cpu < (long)(info->num_cpu); cpu++) {
|
||||
if (CPU_ISSET_S((size_t)cpu, CPU_ALLOC_SIZE(info->num_cpu), info->online) &&
|
||||
!CPU_ISSET_S((size_t)cpu, CPU_ALLOC_SIZE(info->num_cpu), info->to_keep)) {
|
||||
if (first == -1) {
|
||||
first = cpu;
|
||||
last = cpu;
|
||||
} else if (last + 1 == cpu) {
|
||||
last = cpu;
|
||||
} else {
|
||||
if (!log_state(cpulist, &pos, first, last))
|
||||
return 0;
|
||||
|
||||
first = cpu;
|
||||
last = cpu;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (first != -1)
|
||||
log_state(cpulist, &pos, first, last);
|
||||
|
||||
const char *const exec_args[] = {
|
||||
"pkexec", LIBEXECDIR "/cpucorectl", "online", cpulist, NULL,
|
||||
};
|
||||
|
||||
LOG_MSG("Requesting unparking of cores %s\n", cpulist);
|
||||
int ret = run_external_process(exec_args, NULL, -1);
|
||||
if (ret != 0) {
|
||||
LOG_ERROR("Failed to unpark cpu cores\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void apply_affinity_mask(pid_t pid, size_t cpusetsize, const cpu_set_t *mask,
|
||||
const bool be_silent)
|
||||
{
|
||||
char buffer[PATH_MAX];
|
||||
char *proc_path = NULL;
|
||||
DIR *proc_dir = NULL;
|
||||
|
||||
if (!(proc_path = buffered_snprintf(buffer, "/proc/%d/task", pid))) {
|
||||
if (!be_silent) {
|
||||
LOG_ERROR("Unable to find executable for PID %d: %s\n", pid, strerror(errno));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(proc_dir = opendir(proc_path))) {
|
||||
if (!be_silent) {
|
||||
LOG_ERROR("Unable to find executable for PID %d: %s\n", pid, strerror(errno));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
struct dirent *entry;
|
||||
while ((entry = readdir(proc_dir))) {
|
||||
if (entry->d_name[0] == '.')
|
||||
continue;
|
||||
|
||||
int tid = atoi(entry->d_name);
|
||||
|
||||
if (sched_setaffinity(tid, cpusetsize, mask) != 0 && !be_silent)
|
||||
LOG_ERROR("Failed to pin thread %d: %s\n", tid, strerror(errno));
|
||||
}
|
||||
|
||||
closedir(proc_dir);
|
||||
}
|
||||
|
||||
void game_mode_apply_core_pinning(const GameModeCPUInfo *info, const pid_t client,
|
||||
const bool be_silent)
|
||||
{
|
||||
if (!info || info->park_or_pin == IS_CPU_PARK)
|
||||
return;
|
||||
|
||||
if (!be_silent)
|
||||
LOG_MSG("Pinning process...\n");
|
||||
|
||||
apply_affinity_mask(client, CPU_ALLOC_SIZE(info->num_cpu), info->to_keep, be_silent);
|
||||
}
|
||||
|
||||
void game_mode_undo_core_pinning(const GameModeCPUInfo *info, const pid_t client)
|
||||
{
|
||||
if (!info || info->park_or_pin == IS_CPU_PARK)
|
||||
return;
|
||||
|
||||
LOG_MSG("Pinning process back to all online cores...\n");
|
||||
apply_affinity_mask(client, CPU_ALLOC_SIZE(info->num_cpu), info->online, false);
|
||||
}
|
||||
|
||||
void game_mode_free_cpu(GameModeCPUInfo **info)
|
||||
{
|
||||
if ((*info)) {
|
||||
CPU_FREE((*info)->online);
|
||||
(*info)->online = NULL;
|
||||
|
||||
CPU_FREE((*info)->to_keep);
|
||||
(*info)->to_keep = NULL;
|
||||
|
||||
free(*info);
|
||||
*info = NULL;
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2017-2019, Feral Interactive
|
||||
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
@ -708,81 +708,106 @@ void game_mode_context_loop(GameModeContext *context)
|
||||
}
|
||||
}
|
||||
|
||||
struct GameModeIdleInhibitor {
|
||||
sd_bus *bus;
|
||||
unsigned int cookie;
|
||||
};
|
||||
|
||||
/**
|
||||
* Attempts to inhibit the screensaver
|
||||
* Uses the "org.freedesktop.ScreenSaver" interface
|
||||
*/
|
||||
static unsigned int screensaver_inhibit_cookie = 0;
|
||||
int game_mode_inhibit_screensaver(bool inhibit)
|
||||
GameModeIdleInhibitor *game_mode_create_idle_inhibitor(void)
|
||||
{
|
||||
const char *service = "org.freedesktop.ScreenSaver";
|
||||
const char *object_path = "/org/freedesktop/ScreenSaver";
|
||||
const char *interface = "org.freedesktop.ScreenSaver";
|
||||
const char *function = inhibit ? "Inhibit" : "UnInhibit";
|
||||
|
||||
sd_bus_message *msg = NULL;
|
||||
sd_bus *bus_local = NULL;
|
||||
sd_bus_error err;
|
||||
memset(&err, 0, sizeof(sd_bus_error));
|
||||
|
||||
int result = -1;
|
||||
sd_bus_error err = SD_BUS_ERROR_NULL;
|
||||
|
||||
// Open the user bus
|
||||
int ret = sd_bus_open_user(&bus_local);
|
||||
if (ret < 0) {
|
||||
LOG_ERROR("Could not connect to user bus: %s\n", strerror(-ret));
|
||||
return -1;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (inhibit) {
|
||||
ret = sd_bus_call_method(bus_local,
|
||||
service,
|
||||
object_path,
|
||||
interface,
|
||||
function,
|
||||
&err,
|
||||
&msg,
|
||||
"ss",
|
||||
"com.feralinteractive.GameMode",
|
||||
"GameMode Activated");
|
||||
} else {
|
||||
ret = sd_bus_call_method(bus_local,
|
||||
service,
|
||||
object_path,
|
||||
interface,
|
||||
function,
|
||||
&err,
|
||||
&msg,
|
||||
"u",
|
||||
screensaver_inhibit_cookie);
|
||||
}
|
||||
ret = sd_bus_call_method(bus_local,
|
||||
"org.freedesktop.ScreenSaver",
|
||||
"/org/freedesktop/ScreenSaver",
|
||||
"org.freedesktop.ScreenSaver",
|
||||
"Inhibit",
|
||||
&err,
|
||||
&msg,
|
||||
"ss",
|
||||
"com.feralinteractive.GameMode",
|
||||
"GameMode Activated");
|
||||
|
||||
if (ret < 0) {
|
||||
LOG_ERROR(
|
||||
"Could not call %s on %s: %s\n"
|
||||
"Failed to call Inhibit on org.freedesktop.ScreenSaver: %s\n"
|
||||
"\t%s\n"
|
||||
"\t%s\n",
|
||||
function,
|
||||
service,
|
||||
strerror(-ret),
|
||||
err.name,
|
||||
err.message);
|
||||
} else if (inhibit) {
|
||||
// Read the reply
|
||||
ret = sd_bus_message_read(msg, "u", &screensaver_inhibit_cookie);
|
||||
if (ret < 0) {
|
||||
LOG_ERROR("Failure to parse response from %s on %s: %s\n",
|
||||
function,
|
||||
service,
|
||||
strerror(-ret));
|
||||
} else {
|
||||
result = 0;
|
||||
}
|
||||
} else {
|
||||
result = 0;
|
||||
sd_bus_close(bus_local);
|
||||
sd_bus_unrefp(&bus_local);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
sd_bus_unref(bus_local);
|
||||
// Read the reply
|
||||
unsigned int cookie = 0;
|
||||
ret = sd_bus_message_read(msg, "u", &cookie);
|
||||
if (ret < 0) {
|
||||
LOG_ERROR("Invalid response from Inhibit on org.freedesktop.ScreenSaver: %s\n",
|
||||
strerror(-ret));
|
||||
sd_bus_close(bus_local);
|
||||
sd_bus_unrefp(&bus_local);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return result;
|
||||
GameModeIdleInhibitor *inhibitor = malloc(sizeof(GameModeIdleInhibitor));
|
||||
if (inhibitor == NULL) {
|
||||
sd_bus_close(bus_local);
|
||||
sd_bus_unrefp(&bus_local);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
inhibitor->bus = bus_local;
|
||||
inhibitor->cookie = cookie;
|
||||
|
||||
return inhibitor;
|
||||
}
|
||||
|
||||
void game_mode_destroy_idle_inhibitor(GameModeIdleInhibitor *inhibitor)
|
||||
{
|
||||
sd_bus_message *msg = NULL;
|
||||
sd_bus_error err = SD_BUS_ERROR_NULL;
|
||||
|
||||
if (inhibitor == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
int ret = sd_bus_call_method(inhibitor->bus,
|
||||
"org.freedesktop.ScreenSaver",
|
||||
"/org/freedesktop/ScreenSaver",
|
||||
"org.freedesktop.ScreenSaver",
|
||||
"UnInhibit",
|
||||
&err,
|
||||
&msg,
|
||||
"u",
|
||||
inhibitor->cookie);
|
||||
|
||||
if (ret < 0) {
|
||||
LOG_ERROR(
|
||||
"Failed to call UnInhibit on org.freedesktop.ScreenSaver: %s\n"
|
||||
"\t%s\n"
|
||||
"\t%s\n",
|
||||
strerror(-ret),
|
||||
err.name,
|
||||
err.message);
|
||||
}
|
||||
|
||||
sd_bus_close(inhibitor->bus);
|
||||
sd_bus_unrefp(&inhibitor->bus);
|
||||
free(inhibitor);
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
|
||||
/*
|
||||
|
||||
Copyright (c) 2017-2019, Feral Interactive
|
||||
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
@ -167,7 +167,7 @@ int game_mode_apply_gpu(const GameModeGPUInfo *info)
|
||||
|
||||
// Set up our command line to pass to gpuclockctl
|
||||
const char *const exec_args[] = {
|
||||
"/usr/bin/pkexec",
|
||||
"pkexec",
|
||||
LIBEXECDIR "/gpuclockctl",
|
||||
device,
|
||||
"set",
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2017-2019, Feral Interactive
|
||||
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
@ -60,7 +60,7 @@ POSSIBILITY OF SUCH DAMAGE.
|
||||
#endif
|
||||
|
||||
#ifndef IOPRIO_PRIO_DATA
|
||||
#define IOPRIO_PRIO_DATA(mask) ((mask)&IOPRIO_PRIO_MASK)
|
||||
#define IOPRIO_PRIO_DATA(mask) ((mask) & IOPRIO_PRIO_MASK)
|
||||
#endif
|
||||
|
||||
#ifndef IOPRIO_PRIO_VALUE
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2017-2019, Feral Interactive
|
||||
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2017-2019, Feral Interactive
|
||||
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
@ -36,6 +36,7 @@ POSSIBILITY OF SUCH DAMAGE.
|
||||
#include "common-gpu.h"
|
||||
#include "common-helpers.h"
|
||||
#include "common-logging.h"
|
||||
#include "common-profile.h"
|
||||
|
||||
#include "gamemode.h"
|
||||
#include "gamemode-config.h"
|
||||
@ -268,8 +269,7 @@ static int run_gamemoderun_and_reaper_tests(struct GameModeConfig *config)
|
||||
/* Close stdout, we don't care if sh prints anything */
|
||||
fclose(stdout);
|
||||
/* Preload into sh and then kill it */
|
||||
if (execl("/usr/bin/gamemoderun", "/usr/bin/gamemoderun", "sleep", "5", (char *)NULL) ==
|
||||
-1) {
|
||||
if (execlp("gamemoderun", "gamemoderun", "sleep", "5", (char *)NULL) == -1) {
|
||||
LOG_ERROR("failed to launch gamemoderun with execl: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
@ -358,6 +358,62 @@ static int run_cpu_governor_tests(struct GameModeConfig *config)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Check the platform profile setting works */
|
||||
static int run_platform_profile_tests(struct GameModeConfig *config)
|
||||
{
|
||||
if (!profile_exists())
|
||||
return 1;
|
||||
|
||||
/* get the two config parameters we care about */
|
||||
char desiredprof[CONFIG_VALUE_MAX] = { 0 };
|
||||
config_get_desired_profile(config, desiredprof);
|
||||
|
||||
if (desiredprof[0] == '\0')
|
||||
strcpy(desiredprof, "performance");
|
||||
|
||||
char defaultprof[CONFIG_VALUE_MAX] = { 0 };
|
||||
config_get_default_profile(config, defaultprof);
|
||||
|
||||
if (defaultprof[0] == '\0') {
|
||||
const char *currentprof = get_profile_state();
|
||||
if (currentprof) {
|
||||
strncpy(defaultprof, currentprof, CONFIG_VALUE_MAX - 1);
|
||||
} else {
|
||||
LOG_ERROR(
|
||||
"Could not get current platform profile state, this indicates an error! See rest "
|
||||
"of log.\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Start gamemode */
|
||||
gamemode_request_start();
|
||||
|
||||
/* Verify the platform profile is the desired one */
|
||||
const char *currentprof = get_profile_state();
|
||||
if (strncmp(currentprof, desiredprof, CONFIG_VALUE_MAX) != 0) {
|
||||
LOG_ERROR("Platform profile was not set to %s (was actually %s)!\n",
|
||||
desiredprof,
|
||||
currentprof);
|
||||
gamemode_request_end();
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* End gamemode */
|
||||
gamemode_request_end();
|
||||
|
||||
/* Verify the platform profile has been set back */
|
||||
currentprof = get_profile_state();
|
||||
if (strncmp(currentprof, defaultprof, CONFIG_VALUE_MAX) != 0) {
|
||||
LOG_ERROR("Platform profile was not set back to %s (was actually %s)!\n",
|
||||
defaultprof,
|
||||
currentprof);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int run_custom_scripts_tests(struct GameModeConfig *config)
|
||||
{
|
||||
int scriptstatus = 0;
|
||||
@ -802,11 +858,32 @@ static int game_mode_run_feature_tests(struct GameModeConfig *config)
|
||||
LOG_MSG("::: Passed\n");
|
||||
else {
|
||||
LOG_MSG("::: Failed!\n");
|
||||
LOG_MSG(" -- You may need to add your user to the gamemode group:");
|
||||
LOG_MSG(" -- $ sudo usermod -aG gamemode $(whoami)");
|
||||
// Consider the CPU governor feature required
|
||||
status = -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Does the platform profile get set properly? */
|
||||
{
|
||||
LOG_MSG("::: Verifying platform profile setting\n");
|
||||
|
||||
int profstatus = run_platform_profile_tests(config);
|
||||
|
||||
if (profstatus == 1)
|
||||
LOG_MSG("::: Passed (platform profile not supported)\n");
|
||||
else if (profstatus == 0)
|
||||
LOG_MSG("::: Passed\n");
|
||||
else {
|
||||
LOG_MSG("::: Failed!\n");
|
||||
LOG_MSG(" -- You may need to add your user to the gamemode group:");
|
||||
LOG_MSG(" -- $ sudo usermod -aG gamemode $(whoami)");
|
||||
// If available, setting the platform profile should work
|
||||
status = -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Do custom scripts run? */
|
||||
{
|
||||
LOG_MSG("::: Verifying Scripts\n");
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2017-2019, Feral Interactive
|
||||
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2017-2019, Feral Interactive
|
||||
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
@ -203,10 +203,25 @@ void game_mode_free_gpu(GameModeGPUInfo **info);
|
||||
int game_mode_apply_gpu(const GameModeGPUInfo *info);
|
||||
int game_mode_get_gpu(GameModeGPUInfo *info);
|
||||
|
||||
/** gamemode-cpu.c
|
||||
* Provides internal functions to apply optimisations to cpus
|
||||
*/
|
||||
typedef struct GameModeCPUInfo GameModeCPUInfo;
|
||||
int game_mode_initialise_cpu(GameModeConfig *config, GameModeCPUInfo **info);
|
||||
void game_mode_free_cpu(GameModeCPUInfo **info);
|
||||
void game_mode_reconfig_cpu(GameModeConfig *config, GameModeCPUInfo **info);
|
||||
int game_mode_park_cpu(const GameModeCPUInfo *info);
|
||||
int game_mode_unpark_cpu(const GameModeCPUInfo *info);
|
||||
void game_mode_apply_core_pinning(const GameModeCPUInfo *info, const pid_t client,
|
||||
const bool be_silent);
|
||||
void game_mode_undo_core_pinning(const GameModeCPUInfo *info, const pid_t client);
|
||||
|
||||
/** gamemode-dbus.c
|
||||
* Provides an API interface for using dbus
|
||||
*/
|
||||
typedef struct GameModeIdleInhibitor GameModeIdleInhibitor;
|
||||
void game_mode_context_loop(GameModeContext *context) __attribute__((noreturn));
|
||||
int game_mode_inhibit_screensaver(bool inhibit);
|
||||
GameModeIdleInhibitor *game_mode_create_idle_inhibitor(void);
|
||||
void game_mode_destroy_idle_inhibitor(GameModeIdleInhibitor *inhibitor);
|
||||
void game_mode_client_registered(pid_t);
|
||||
void game_mode_client_unregistered(pid_t);
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2017-2019, Feral Interactive
|
||||
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
@ -133,12 +133,9 @@ static void daemonize(const char *name)
|
||||
int devnull_r = open("/dev/null", O_RDONLY);
|
||||
int devnull_w = open("/dev/null", O_WRONLY);
|
||||
|
||||
if (devnull_r == -1 || devnull_w == -1)
|
||||
{
|
||||
if (devnull_r == -1 || devnull_w == -1) {
|
||||
LOG_ERROR("Failed to redirect standard input and output to /dev/null\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
dup2(devnull_r, STDIN_FILENO);
|
||||
dup2(devnull_w, STDOUT_FILENO);
|
||||
dup2(devnull_w, STDERR_FILENO);
|
||||
|
@ -7,6 +7,7 @@ daemon_sources = [
|
||||
'gamemode-wine.c',
|
||||
'gamemode-tests.c',
|
||||
'gamemode-gpu.c',
|
||||
'gamemode-cpu.c',
|
||||
'gamemode-dbus.c',
|
||||
'gamemode-config.c',
|
||||
]
|
||||
|
@ -1 +0,0 @@
|
||||
@@LIMITSGROUP@ - nice -10
|
@ -1,38 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE policyconfig PUBLIC
|
||||
"-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN"
|
||||
"http://www.freedesktop.org/standards/PolicyKit/1.0/policyconfig.dtd">
|
||||
<policyconfig>
|
||||
|
||||
<!--
|
||||
Copyright (c) 2017-2019, Feral Interactive
|
||||
All rights reserved.
|
||||
-->
|
||||
|
||||
<vendor>Feral GameMode Activation</vendor>
|
||||
<vendor_url>http://www.feralinteractive.com</vendor_url>
|
||||
|
||||
<action id="com.feralinteractive.GameMode.governor-helper">
|
||||
<description>Modify the CPU governor</description>
|
||||
<message>Authentication is required to modify the CPU governor</message>
|
||||
<defaults>
|
||||
<allow_any>no</allow_any>
|
||||
<allow_inactive>no</allow_inactive>
|
||||
<allow_active>yes</allow_active>
|
||||
</defaults>
|
||||
<annotate key="org.freedesktop.policykit.exec.path">@LIBEXECDIR@/cpugovctl</annotate>
|
||||
</action>
|
||||
|
||||
<action id="com.feralinteractive.GameMode.gpu-helper">
|
||||
<description>Modify the GPU clock states</description>
|
||||
<message>Authentication is required to modify the GPU clock states</message>
|
||||
<defaults>
|
||||
<allow_any>no</allow_any>
|
||||
<allow_inactive>no</allow_inactive>
|
||||
<allow_active>yes</allow_active>
|
||||
</defaults>
|
||||
<annotate key="org.freedesktop.policykit.exec.path">@LIBEXECDIR@/gpuclockctl</annotate>
|
||||
<annotate key="org.freedesktop.policykit.exec.allow_gui">true</annotate>
|
||||
</action>
|
||||
|
||||
</policyconfig>
|
@ -73,7 +73,7 @@ Or, distribute \fBlibgamemodeauto.so.0\fR and either link with \fB\-lgamemodeaut
|
||||
\fBgamemoded\fR will load and merge \fBgamemode.ini\fR config files from these directories in the following order:
|
||||
|
||||
.RS 4
|
||||
/usr/share/gamemode/
|
||||
@SYSCONFDIR@/
|
||||
.RE
|
||||
.RS 4
|
||||
/etc/
|
||||
|
32
data/gamemodelist
Executable file
32
data/gamemodelist
Executable file
@ -0,0 +1,32 @@
|
||||
#!/bin/bash
|
||||
# Created by Sam Gleske
|
||||
# Created Sat Jan 1 16:56:54 EST 2022
|
||||
# MIT License - https://github.com/samrocketman/home
|
||||
|
||||
# DESCRIPTION
|
||||
# Find all running processes which have loaded Feral Interactive gamemode
|
||||
# via libgamemodeauto.so. This script will not detect processes which load
|
||||
# gamemode without libgamemodeauto.so.
|
||||
|
||||
# DEVELOPMENT ENVIRONMENT
|
||||
# Ubuntu 18.04.6 LTS
|
||||
# Linux 5.4.0-91-generic x86_64
|
||||
# GNU bash, version 4.4.20(1)-release (x86_64-pc-linux-gnu)
|
||||
# find (GNU findutils) 4.7.0-git
|
||||
# GNU Awk 4.1.4, API: 1.1 (GNU MPFR 4.0.1, GNU MP 6.1.2)
|
||||
# xargs (GNU findutils) 4.7.0-git
|
||||
# ps from procps-ng 3.3.12
|
||||
|
||||
if [ -z "${USER:-}" ]; then
|
||||
echo '$USER variable not defined.' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -d /proc ]; then
|
||||
echo 'ERROR: /proc filesystem missing. We do not appear to be running on Linux.' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
find /proc -maxdepth 2 -type f -user "${USER}" -readable -name maps -exec \
|
||||
awk -- 'BEGINFILE { if (ERRNO) nextfile } $0 ~ /libgamemodeauto\.so\.0/ {pid=FILENAME; gsub("[^0-9]", "", pid); print pid;nextfile}' {} + \
|
||||
| xargs | xargs -I{} -- ps -o pid,ppid,user,ni,psr,comm --pid '{}'
|
68
data/gamemodelist.1.in
Normal file
68
data/gamemodelist.1.in
Normal file
@ -0,0 +1,68 @@
|
||||
.\" Manpage for gamemodelist.
|
||||
.\" Contact linux-contact@feralinteractive.com to correct errors or typos.
|
||||
.TH gamemodelist 1 "4 May 2020" "@GAMEMODE_VERSION@" "gamemodelist man page"
|
||||
.SH NAME
|
||||
gamemodelist \- search for processes running with gamemode
|
||||
.SH SYNOPSIS
|
||||
\fBgamemodelist\fR
|
||||
.SH DESCRIPTION
|
||||
\fBgamemodelist\fR will search the runtime of all processes running which started \fBGameMode\fR via \fBlibgamemodeauto.so\fR and print them with
|
||||
.BR ps (1)
|
||||
command output. This helper script makes it easy to find which processes are utilizing \fBGameMode\fR via \fBlibgamemodeauto.so\fR when troubleshooting potential game issues.
|
||||
|
||||
.SH USAGE
|
||||
\fBlibgamemodeauto.so.0\fR will be found in the shared object maps of running processes utilizing \fBGameMode\fR. Run the following command to print processes loaded with \fBlibgamemodeauto.so.0\fR. \fBGameMode\fR can be started other ways but this script will only detect processes utilizing \fBGameMode\fR via \fBlibgamemodeauto.so\fR.
|
||||
|
||||
.RS 4
|
||||
gamemodelist
|
||||
.RE
|
||||
|
||||
.SH OUTPUT
|
||||
The output is a process table from
|
||||
.BR ps (1)
|
||||
command.
|
||||
|
||||
.RS 4
|
||||
PID PPID USER NI PSR COMMAND
|
||||
.RE
|
||||
|
||||
Where each of these fields are defined in
|
||||
.BR ps (1)
|
||||
manual. For your convenience here's a definition for each field.
|
||||
|
||||
.RS 4
|
||||
.TS
|
||||
l lw(3i).
|
||||
\fBCOLUMN DESCRIPTION\fR
|
||||
PID Process ID
|
||||
PPID Parent process ID
|
||||
USER User name owning the process.
|
||||
NI T{
|
||||
Nice value. This ranges from 19 (nicest) to \-20 (not nice to others),
|
||||
See
|
||||
.IR nice (1).
|
||||
T}
|
||||
PSR T{
|
||||
Processor that process is currently assigned to. Useful when setting process affinity using
|
||||
.IR taskset (1)
|
||||
utility.
|
||||
T}
|
||||
COMMAND Command name (only the executable name).
|
||||
.TE
|
||||
.RE
|
||||
|
||||
.SH SEE ALSO
|
||||
.BR gamemodrun (1),
|
||||
.BR nice (1),
|
||||
.BR ps (1),
|
||||
.BR taskset (1).
|
||||
|
||||
.SH ABOUT
|
||||
GameMode source can be found at \fIhttps://github.com/FeralInteractive/gamemode.git\fR
|
||||
|
||||
.SH AUTHOR
|
||||
.BR gamemodelist
|
||||
was authored by Sam Gleske (https://github.com/samrocketman/)
|
||||
|
||||
.BR GameMode
|
||||
was authored by Feral Interactive (linux-contact@feralinteractive.com)
|
@ -1,11 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<component type="console-application">
|
||||
<component>
|
||||
<id>io.github.feralinteractive.gamemode</id>
|
||||
|
||||
<name>gamemode</name>
|
||||
<summary>daemon that allows games to request a set of optimizations be temporarily applied</summary>
|
||||
<developer_name>Feral Interactive</developer_name>
|
||||
|
||||
<!-- <icon type="stock">io.github.feralinteractive.gamemode</icon> -->
|
||||
|
||||
<metadata_license>FSFAP</metadata_license>
|
||||
<project_license>BSD-3-Clause</project_license>
|
||||
|
||||
@ -29,12 +31,11 @@
|
||||
</ul>
|
||||
</description>
|
||||
|
||||
<content_rating type="oars-1.1" />
|
||||
<categories>
|
||||
<category>Utility</category>
|
||||
<category>Game</category>
|
||||
</categories>
|
||||
|
||||
<provides>
|
||||
<binary>gamemoderun</binary>
|
||||
</provides>
|
||||
|
||||
<url type="homepage">https://feralinteractive.github.io/gamemode</url>
|
||||
</component>
|
||||
|
@ -1,46 +1,100 @@
|
||||
data_conf = configuration_data()
|
||||
data_conf.set('BINDIR', path_bindir)
|
||||
data_conf.set('LIBEXECDIR', path_libexecdir)
|
||||
data_conf.set('SYSCONFDIR', path_sysconfdir)
|
||||
data_conf.set('GAMEMODE_PREFIX', path_prefix)
|
||||
data_conf.set('GAMEMODE_VERSION', meson.project_version())
|
||||
data_conf.set('GAMEMODE_PRIVILEGED_GROUP', with_privileged_group)
|
||||
|
||||
# Pull in the example config
|
||||
config_example = run_command(
|
||||
'cat',
|
||||
join_paths(meson.source_root(), 'example', 'gamemode.ini')
|
||||
join_paths(meson.project_source_root(), 'example', 'gamemode.ini'),
|
||||
check: true,
|
||||
).stdout().strip()
|
||||
data_conf.set('GAMEMODE_EXAMPLE_CONFIG', config_example)
|
||||
|
||||
if sd_bus_provider == 'systemd'
|
||||
# Install systemd user unit
|
||||
configure_file(
|
||||
input: 'gamemoded.service.in',
|
||||
output: 'gamemoded.service',
|
||||
configuration: data_conf,
|
||||
install_dir: path_systemd_unit_dir,
|
||||
)
|
||||
if with_systemd_unit
|
||||
# Install systemd user unit
|
||||
configure_file(
|
||||
input: 'systemd/user/gamemoded.service.in',
|
||||
output: 'gamemoded.service',
|
||||
configuration: data_conf,
|
||||
install_dir: path_systemd_unit_dir,
|
||||
)
|
||||
endif
|
||||
if with_systemd_group
|
||||
# Install the sysusers.d file
|
||||
configure_file(
|
||||
input: 'systemd/sysusers.d/gamemode.conf.in',
|
||||
output: 'gamemode.conf',
|
||||
configuration: data_conf,
|
||||
install_dir: path_systemd_group_dir,
|
||||
)
|
||||
endif
|
||||
endif
|
||||
|
||||
if with_pam_renicing
|
||||
# Install the limits.d configuration file
|
||||
configure_file(
|
||||
input: 'pam_limits/10-gamemode.conf.in',
|
||||
output: '10-gamemode.conf',
|
||||
configuration: data_conf,
|
||||
install_dir: path_pam_limits_dir,
|
||||
)
|
||||
endif
|
||||
|
||||
# Install the D-BUS service file
|
||||
configure_file(
|
||||
input: 'com.feralinteractive.GameMode.service.in',
|
||||
input: 'dbus/com.feralinteractive.GameMode.service.in',
|
||||
output: 'com.feralinteractive.GameMode.service',
|
||||
configuration: data_conf,
|
||||
install_dir: path_dbus_service_dir,
|
||||
)
|
||||
|
||||
|
||||
# Install the Polkit action file in all cases
|
||||
configure_file(
|
||||
input: 'com.feralinteractive.GameMode.policy.in',
|
||||
output: 'com.feralinteractive.GameMode.policy',
|
||||
configuration: data_conf,
|
||||
install_dir: path_polkit_action_dir,
|
||||
)
|
||||
# Install the Polkit action & rule files for the privileged gamemode group
|
||||
if with_privileged_group != ''
|
||||
configure_file(
|
||||
input: 'polkit/actions/com.feralinteractive.GameMode.policy.in',
|
||||
output: 'com.feralinteractive.GameMode.policy',
|
||||
configuration: data_conf,
|
||||
install_dir: path_polkit_action_dir,
|
||||
)
|
||||
configure_file(
|
||||
input: 'polkit/rules.d/gamemode.rules.in',
|
||||
output: 'gamemode.rules',
|
||||
configuration: data_conf,
|
||||
install_dir: path_polkit_rule_dir,
|
||||
)
|
||||
endif
|
||||
|
||||
# Install the helper run script
|
||||
# Install the helper run script and man page
|
||||
if get_option('default_library') == 'static'
|
||||
warning('gamemoderun will not be installed as a shared libgamemodeauto library is required')
|
||||
else
|
||||
install_data(
|
||||
files('gamemoderun'),
|
||||
install_dir: path_bindir,
|
||||
install_mode: 'rwxr-xr-x',
|
||||
)
|
||||
|
||||
gamemoderun_manpage = configure_file(
|
||||
input: files('gamemoderun.1.in'),
|
||||
output: 'gamemoderun.1',
|
||||
configuration: data_conf,
|
||||
)
|
||||
|
||||
install_man(
|
||||
gamemoderun_manpage,
|
||||
install_dir: join_paths(path_mandir, 'man1')
|
||||
)
|
||||
endif
|
||||
|
||||
# Install script to find processes with gamemode lib in runtime
|
||||
install_data(
|
||||
files('gamemoderun'),
|
||||
files('gamemodelist'),
|
||||
install_dir: path_bindir,
|
||||
install_mode: 'rwxr-xr-x',
|
||||
)
|
||||
@ -57,14 +111,14 @@ install_man(
|
||||
install_dir: join_paths(path_mandir, 'man8')
|
||||
)
|
||||
|
||||
gamemoderun_manpage = configure_file(
|
||||
input: files('gamemoderun.1.in'),
|
||||
output: 'gamemoderun.1',
|
||||
gamemodelist_manpage = configure_file(
|
||||
input: files('gamemodelist.1.in'),
|
||||
output: 'gamemodelist.1',
|
||||
configuration: data_conf,
|
||||
)
|
||||
|
||||
install_man(
|
||||
gamemoderun_manpage,
|
||||
gamemodelist_manpage,
|
||||
install_dir: join_paths(path_mandir, 'man1')
|
||||
)
|
||||
|
||||
|
1
data/pam_limits/10-gamemode.conf.in
Normal file
1
data/pam_limits/10-gamemode.conf.in
Normal file
@ -0,0 +1 @@
|
||||
@@GAMEMODE_PRIVILEGED_GROUP@ - nice -10
|
73
data/polkit/actions/com.feralinteractive.GameMode.policy.in
Normal file
73
data/polkit/actions/com.feralinteractive.GameMode.policy.in
Normal file
@ -0,0 +1,73 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE policyconfig PUBLIC
|
||||
"-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN"
|
||||
"http://www.freedesktop.org/standards/PolicyKit/1.0/policyconfig.dtd">
|
||||
<policyconfig>
|
||||
|
||||
<!--
|
||||
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
|
||||
All rights reserved.
|
||||
-->
|
||||
|
||||
<vendor>Feral GameMode Activation</vendor>
|
||||
<vendor_url>http://www.feralinteractive.com</vendor_url>
|
||||
|
||||
<action id="com.feralinteractive.GameMode.governor-helper">
|
||||
<description>Modify the CPU governor</description>
|
||||
<message>Authentication is required to modify the CPU governor</message>
|
||||
<defaults>
|
||||
<allow_any>no</allow_any>
|
||||
<allow_inactive>no</allow_inactive>
|
||||
<allow_active>no</allow_active>
|
||||
</defaults>
|
||||
<annotate key="org.freedesktop.policykit.exec.path">@LIBEXECDIR@/cpugovctl</annotate>
|
||||
</action>
|
||||
|
||||
<action id="com.feralinteractive.GameMode.gpu-helper">
|
||||
<description>Modify the GPU clock states</description>
|
||||
<message>Authentication is required to modify the GPU clock states</message>
|
||||
<defaults>
|
||||
<allow_any>no</allow_any>
|
||||
<allow_inactive>no</allow_inactive>
|
||||
<allow_active>no</allow_active>
|
||||
</defaults>
|
||||
<annotate key="org.freedesktop.policykit.exec.path">@LIBEXECDIR@/gpuclockctl</annotate>
|
||||
<annotate key="org.freedesktop.policykit.exec.allow_gui">true</annotate>
|
||||
</action>
|
||||
|
||||
<action id="com.feralinteractive.GameMode.cpu-helper">
|
||||
<description>Modify the CPU core states</description>
|
||||
<message>Authentication is required to modify the CPU core states</message>
|
||||
<defaults>
|
||||
<allow_any>no</allow_any>
|
||||
<allow_inactive>no</allow_inactive>
|
||||
<allow_active>no</allow_active>
|
||||
</defaults>
|
||||
<annotate key="org.freedesktop.policykit.exec.path">@LIBEXECDIR@/cpucorectl</annotate>
|
||||
<annotate key="org.freedesktop.policykit.exec.allow_gui">true</annotate>
|
||||
</action>
|
||||
|
||||
<action id="com.feralinteractive.GameMode.procsys-helper">
|
||||
<description>Modify the /proc/sys values</description>
|
||||
<message>Authentication is required to modify the /proc/sys/ values</message>
|
||||
<defaults>
|
||||
<allow_any>no</allow_any>
|
||||
<allow_inactive>no</allow_inactive>
|
||||
<allow_active>no</allow_active>
|
||||
</defaults>
|
||||
<annotate key="org.freedesktop.policykit.exec.path">@LIBEXECDIR@/procsysctl</annotate>
|
||||
<annotate key="org.freedesktop.policykit.exec.allow_gui">true</annotate>
|
||||
</action>
|
||||
|
||||
<action id="com.feralinteractive.GameMode.profile-helper">
|
||||
<description>Modify the platform profile</description>
|
||||
<message>Authentication is required to modify platform profile</message>
|
||||
<defaults>
|
||||
<allow_any>no</allow_any>
|
||||
<allow_inactive>no</allow_inactive>
|
||||
<allow_active>no</allow_active>
|
||||
</defaults>
|
||||
<annotate key="org.freedesktop.policykit.exec.path">@LIBEXECDIR@/platprofctl</annotate>
|
||||
<annotate key="org.freedesktop.policykit.exec.allow_gui">true</annotate>
|
||||
</action>
|
||||
</policyconfig>
|
15
data/polkit/rules.d/gamemode.rules.in
Normal file
15
data/polkit/rules.d/gamemode.rules.in
Normal file
@ -0,0 +1,15 @@
|
||||
/*
|
||||
* Allow users in privileged gamemode group to run cpugovctl &
|
||||
* gpuclockctl without authentication
|
||||
*/
|
||||
polkit.addRule(function (action, subject) {
|
||||
if ((action.id == "com.feralinteractive.GameMode.governor-helper" ||
|
||||
action.id == "com.feralinteractive.GameMode.gpu-helper" ||
|
||||
action.id == "com.feralinteractive.GameMode.cpu-helper" ||
|
||||
action.id == "com.feralinteractive.GameMode.procsys-helper" ||
|
||||
action.id == "com.feralinteractive.GameMode.profile-helper") &&
|
||||
subject.isInGroup("@GAMEMODE_PRIVILEGED_GROUP@"))
|
||||
{
|
||||
return polkit.Result.YES;
|
||||
}
|
||||
});
|
1
data/systemd/sysusers.d/gamemode.conf.in
Normal file
1
data/systemd/sysusers.d/gamemode.conf.in
Normal file
@ -0,0 +1 @@
|
||||
g @GAMEMODE_PRIVILEGED_GROUP@ - -
|
@ -7,6 +7,11 @@ desiredgov=performance
|
||||
; The default governor is used when leaving GameMode instead of restoring the original value
|
||||
;defaultgov=powersave
|
||||
|
||||
; The desired platform profile is used when entering GameMode instead of "performance"
|
||||
desiredprof=performance
|
||||
; The default platform profile is used when leaving GameMode instead of restoring the original value
|
||||
;defaultgov=low-power
|
||||
|
||||
; The iGPU desired governor is used when the integrated GPU is under heavy load
|
||||
igpu_desiredgov=powersave
|
||||
; Threshold to use to decide when the integrated GPU is under heavy load.
|
||||
@ -23,6 +28,8 @@ softrealtime=off
|
||||
|
||||
; GameMode can renice game processes. You can put any value between 0 and 20 here, the value
|
||||
; will be negated and applied as a nice value (0 means no change). Defaults to 0.
|
||||
; To use this feature, the user must be added to the gamemode group (and then rebooted):
|
||||
; sudo usermod -aG gamemode $(whoami)
|
||||
renice=0
|
||||
|
||||
; By default, GameMode adjusts the iopriority of clients to BE/0, you can put any value
|
||||
@ -35,6 +42,10 @@ ioprio=0
|
||||
; Defaults to 1
|
||||
inhibit_screensaver=1
|
||||
|
||||
; Sets whether gamemode will disable split lock mitigation when active
|
||||
; Defaults to 1
|
||||
disable_splitlock=1
|
||||
|
||||
[filter]
|
||||
; If "whitelist" entry has a value(s)
|
||||
; gamemode will reject anything not in the whitelist
|
||||
@ -75,6 +86,17 @@ inhibit_screensaver=1
|
||||
; This corresponds to power_dpm_force_performance_level, "manual" is not supported for now
|
||||
;amd_performance_level=high
|
||||
|
||||
[cpu]
|
||||
; Parking or Pinning can be enabled with either "yes", "true" or "1" and disabled with "no", "false" or "0".
|
||||
; Either can also be set to a specific list of cores to park or pin, comma separated list where "-" denotes
|
||||
; a range. E.g "park_cores=1,8-15" would park cores 1 and 8 to 15.
|
||||
; The default is uncommented is to disable parking but enable pinning. If either is enabled the code will
|
||||
; currently only properly autodetect Ryzen 7900x3d, 7950x3d and Intel CPU:s with E- and P-cores.
|
||||
; For Core Parking, user must be added to the gamemode group (not required for Core Pinning):
|
||||
; sudo usermod -aG gamemode $(whoami)
|
||||
;park_cores=no
|
||||
;pin_cores=yes
|
||||
|
||||
[supervisor]
|
||||
; This section controls the new gamemode functions gamemode_request_start_for and gamemode_request_end_for
|
||||
; The whilelist and blacklist control which supervisor programs are allowed to make the above requests
|
||||
@ -88,10 +110,10 @@ inhibit_screensaver=1
|
||||
[custom]
|
||||
; Custom scripts (executed using the shell) when gamemode starts and ends
|
||||
;start=notify-send "GameMode started"
|
||||
; /home/me/bin/stop_ethmining.sh
|
||||
; /home/me/bin/stop_foldingathome.sh
|
||||
|
||||
;end=notify-send "GameMode ended"
|
||||
; /home/me/bin/start_ethmining.sh
|
||||
; /home/me/bin/start_foldingathome.sh
|
||||
|
||||
; Timeout for scripts (seconds). Scripts will be killed if they do not complete within this time.
|
||||
;script_timeout=10
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2017-2019, Feral Interactive
|
||||
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2017-2019, Feral Interactive
|
||||
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
@ -109,8 +109,8 @@ static inline int *alloc_fd_array(int n)
|
||||
return fds;
|
||||
}
|
||||
|
||||
// Helper to check if we are running inside a flatpak
|
||||
static int in_flatpak(void)
|
||||
// Helper to check if we are running inside a sandboxed framework like Flatpak or Snap
|
||||
static int in_sandbox(void)
|
||||
{
|
||||
static int status = -1;
|
||||
|
||||
@ -120,6 +120,10 @@ static int in_flatpak(void)
|
||||
|
||||
r = lstat("/.flatpak-info", &sb);
|
||||
status = r == 0 && sb.st_size > 0;
|
||||
|
||||
if (getenv("SNAP") != NULL) {
|
||||
status = 1;
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
@ -146,7 +150,7 @@ static int log_error(const char *fmt, ...)
|
||||
|
||||
static void hop_off_the_bus(DBusConnection **bus)
|
||||
{
|
||||
if (bus == NULL)
|
||||
if (bus == NULL || *bus == NULL)
|
||||
return;
|
||||
|
||||
dbus_connection_unref(*bus);
|
||||
@ -228,7 +232,7 @@ static int make_request(DBusConnection *bus, int native, int use_pidfds, const c
|
||||
native,
|
||||
use_pidfds);
|
||||
|
||||
// If we are inside a flatpak we need to talk to the portal instead
|
||||
// If we are inside a Flatpak or Snap we need to talk to the portal instead
|
||||
const char *dest = native ? DAEMON_DBUS_NAME : PORTAL_DBUS_NAME;
|
||||
const char *path = native ? DAEMON_DBUS_PATH : PORTAL_DBUS_PATH;
|
||||
const char *iface = native ? DAEMON_DBUS_IFACE : PORTAL_DBUS_IFACE;
|
||||
@ -302,7 +306,7 @@ static int gamemode_request(const char *method, pid_t for_pid)
|
||||
int native;
|
||||
int res = -1;
|
||||
|
||||
native = !in_flatpak();
|
||||
native = !in_sandbox();
|
||||
|
||||
/* pid[0] is the client, i.e. the game
|
||||
* pid[1] is the requestor, i.e. this process
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2017-2019, Feral Interactive
|
||||
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2017-2019, Feral Interactive
|
||||
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
@ -80,6 +80,8 @@ POSSIBILITY OF SUCH DAMAGE.
|
||||
#include <dlfcn.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
static char internal_gamemode_client_error_string[512] = { 0 };
|
||||
@ -231,6 +233,9 @@ __attribute__((always_inline)) static inline const char *gamemode_error_string(v
|
||||
return internal_gamemode_client_error_string;
|
||||
}
|
||||
|
||||
/* Assert for static analyser that the function is not NULL */
|
||||
assert(REAL_internal_gamemode_error_string != NULL);
|
||||
|
||||
return REAL_internal_gamemode_error_string();
|
||||
}
|
||||
|
||||
@ -254,6 +259,9 @@ int gamemode_request_start(void)
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Assert for static analyser that the function is not NULL */
|
||||
assert(REAL_internal_gamemode_request_start != NULL);
|
||||
|
||||
if (REAL_internal_gamemode_request_start() < 0) {
|
||||
#ifdef GAMEMODE_AUTO
|
||||
fprintf(stderr, "gamemodeauto: %s\n", gamemode_error_string());
|
||||
@ -280,6 +288,9 @@ int gamemode_request_end(void)
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Assert for static analyser that the function is not NULL */
|
||||
assert(REAL_internal_gamemode_request_end != NULL);
|
||||
|
||||
if (REAL_internal_gamemode_request_end() < 0) {
|
||||
#ifdef GAMEMODE_AUTO
|
||||
fprintf(stderr, "gamemodeauto: %s\n", gamemode_error_string());
|
||||
|
@ -25,7 +25,7 @@ gamemode_headers_includes = [
|
||||
]
|
||||
|
||||
# Small library to automatically use gamemode
|
||||
libgamemodeauto = both_libraries(
|
||||
libgamemodeauto = library(
|
||||
'gamemodeauto',
|
||||
sources: [
|
||||
'client_loader.c',
|
||||
|
67
meson.build
67
meson.build
@ -2,8 +2,9 @@ project(
|
||||
'gamemode',
|
||||
'c',
|
||||
default_options : ['c_std=c11', 'warning_level=3'],
|
||||
version: '1.6-dev',
|
||||
version: '1.8.2',
|
||||
license: 'BSD',
|
||||
meson_version: '>= 1.3.1',
|
||||
)
|
||||
|
||||
am_cflags = [
|
||||
@ -102,28 +103,41 @@ dep_threads = dependency('threads')
|
||||
# On non glibc systems this might be a stub, i.e. for musl
|
||||
libdl = cc.find_library('dl', required: false)
|
||||
|
||||
with_privileged_group = get_option('with-privileged-group')
|
||||
|
||||
# Determine the location for the systemd unit
|
||||
if sd_bus_provider == 'systemd'
|
||||
# If the path isn't explicitly set, ask systemd for the systemd user unit directory
|
||||
path_systemd_unit_dir = get_option('with-systemd-user-unit-dir')
|
||||
if path_systemd_unit_dir == ''
|
||||
message('Asking pkg-config for systemd\'s directories')
|
||||
pkgconfig_systemd = dependency('systemd')
|
||||
path_systemd_unit_dir = pkgconfig_systemd.get_pkgconfig_variable('systemduserunitdir')
|
||||
with_systemd_unit = get_option('with-systemd-user-unit')
|
||||
if with_systemd_unit
|
||||
path_systemd_unit_dir = get_option('with-systemd-user-unit-dir')
|
||||
if path_systemd_unit_dir == ''
|
||||
message('Asking pkg-config for systemd\'s \'systemduserunitdir\' directory')
|
||||
pkgconfig_systemd = dependency('systemd')
|
||||
path_systemd_unit_dir = pkgconfig_systemd.get_variable(pkgconfig: 'systemduserunitdir')
|
||||
endif
|
||||
endif
|
||||
if with_privileged_group != ''
|
||||
with_systemd_group = get_option('with-systemd-group')
|
||||
if with_systemd_group
|
||||
path_systemd_group_dir = get_option('with-systemd-group-dir')
|
||||
if path_systemd_group_dir == ''
|
||||
message('Asking pkg-config for systemd\'s \'sysusersdir\' directory')
|
||||
pkgconfig_systemd = dependency('systemd')
|
||||
path_systemd_group_dir = pkgconfig_systemd.get_variable(pkgconfig: 'sysusersdir')
|
||||
endif
|
||||
endif
|
||||
else
|
||||
with_systemd_group = false
|
||||
endif
|
||||
endif
|
||||
|
||||
with_limits_conf = get_option('with-pam-group')
|
||||
if with_limits_conf != ''
|
||||
ldata = configuration_data()
|
||||
ldata.set('LIMITSGROUP', with_limits_conf)
|
||||
# Install the limits.d configuration file
|
||||
configure_file(
|
||||
input: 'data/10-gamemode.conf.in',
|
||||
output: '10-gamemode.conf',
|
||||
configuration: ldata,
|
||||
install_dir: '/etc/security/limits.d',
|
||||
)
|
||||
if with_privileged_group != ''
|
||||
with_pam_renicing = get_option('with-pam-renicing')
|
||||
if with_pam_renicing
|
||||
path_pam_limits_dir = get_option('with-pam-limits-dir')
|
||||
endif
|
||||
else
|
||||
with_pam_renicing = false
|
||||
endif
|
||||
|
||||
# Set the dbus path as appropriate.
|
||||
@ -132,7 +146,9 @@ if path_dbus_service_dir == ''
|
||||
path_dbus_service_dir = join_paths(path_datadir, 'dbus-1', 'services')
|
||||
endif
|
||||
|
||||
path_polkit_action_dir = join_paths(path_datadir, 'polkit-1', 'actions')
|
||||
path_polkit_dir = join_paths(path_datadir, 'polkit-1')
|
||||
path_polkit_action_dir = join_paths(path_polkit_dir, 'actions')
|
||||
path_polkit_rule_dir = join_paths(path_polkit_dir, 'rules.d')
|
||||
|
||||
with_examples = get_option('with-examples')
|
||||
with_util = get_option('with-util')
|
||||
@ -142,6 +158,7 @@ pidfd_open = cc.has_function('pidfd_open', args: '-D_GNU_SOURCE')
|
||||
|
||||
cdata = configuration_data()
|
||||
cdata.set_quoted('LIBEXECDIR', path_libexecdir)
|
||||
cdata.set_quoted('SYSCONFDIR', path_sysconfdir)
|
||||
cdata.set_quoted('GAMEMODE_VERSION', meson.project_version())
|
||||
cdata.set10('HAVE_FN_PIDFD_OPEN', pidfd_open)
|
||||
|
||||
@ -196,11 +213,23 @@ report = [
|
||||
' includedir: @0@'.format(path_includedir),
|
||||
]
|
||||
|
||||
if with_pam_renicing
|
||||
report += [
|
||||
' PAM limits.d directory: @0@'.format(path_pam_limits_dir),
|
||||
]
|
||||
endif
|
||||
if sd_bus_provider == 'systemd'
|
||||
if with_systemd_unit
|
||||
report += [
|
||||
' systemd user unit directory: @0@'.format(path_systemd_unit_dir),
|
||||
]
|
||||
endif
|
||||
if with_systemd_group
|
||||
report += [
|
||||
' systemd group directory: @0@'.format(path_systemd_group_dir),
|
||||
]
|
||||
endif
|
||||
endif
|
||||
report += [
|
||||
' D-BUS service directory: @0@'.format(path_dbus_service_dir),
|
||||
]
|
||||
|
@ -1,15 +1,20 @@
|
||||
# limits.d
|
||||
option('with-pam-group', type: 'string', description: 'Install the limits.d configuration file to allow renicing as an unpriviledged user being part of the specified group')
|
||||
option('with-pam-renicing', type: 'boolean', description: 'Install the limits.d configuration file to allow renicing as a user being part of the privileged gamemode group', value: true)
|
||||
option('with-pam-limits-dir', type: 'string', description: 'Explicitly set the PAM limits.d directory', value: '/etc/security/limits.d')
|
||||
|
||||
# sd-bus provider
|
||||
option('with-sd-bus-provider', type: 'combo', choices: ['systemd', 'elogind', 'no-daemon'], value: 'systemd')
|
||||
|
||||
# systemd specific
|
||||
option('with-systemd-user-unit', type: 'boolean', description: 'Install systemd user unit', value: true)
|
||||
option('with-systemd-user-unit-dir', type: 'string', description: 'Explicitly set the systemd user unit directory')
|
||||
option('with-systemd-group', type: 'boolean', description: 'Install privileged gamemode group with systemd', value: true)
|
||||
option('with-systemd-group-dir', type: 'string', description: 'Explicitly set the systemd group directory')
|
||||
|
||||
# Not using systemd
|
||||
option('with-dbus-service-dir', type: 'string', description: 'Explicitly set the D-BUS session directory')
|
||||
|
||||
# General options
|
||||
option('with-examples', type: 'boolean', description: 'Build sample programs', value: 'true')
|
||||
option('with-util', type: 'boolean', description: 'Build the utilities', value: 'true')
|
||||
option('with-examples', type: 'boolean', description: 'Build sample programs', value: true)
|
||||
option('with-util', type: 'boolean', description: 'Build the utilities', value: true)
|
||||
option('with-privileged-group', type: 'string', description: 'Group that has access to privileged gamemode features', value: 'gamemode')
|
||||
|
@ -10,6 +10,21 @@ if [[ "$1" == "--pre-commit" ]]; then
|
||||
git-clang-format
|
||||
exit
|
||||
fi
|
||||
|
||||
if [[ "$CI" == "true" ]]; then
|
||||
# used in ci, assumes clean repo
|
||||
clang-format -i $(find . -name '*.[ch]' -not -path "*subprojects/*")
|
||||
GIT_DIFF_OUTPUT=$(git diff)
|
||||
if [[ ! -z ${GIT_DIFF_OUTPUT} ]]; then
|
||||
echo "Failed clang format check:"
|
||||
echo "${GIT_DIFF_OUTPUT}"
|
||||
exit 1
|
||||
else
|
||||
echo "Passed clang format check"
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
|
||||
CLANG_FORMAT_OUTPUT=$(git-clang-format HEAD^ HEAD --diff)
|
||||
if [[ ! ${CLANG_FORMAT_OUTPUT} == "no modified files to format" ]] && [[ ! -z ${CLANG_FORMAT_OUTPUT} ]]; then
|
||||
echo "Failed clang format check:"
|
||||
@ -17,4 +32,5 @@ if [[ ! ${CLANG_FORMAT_OUTPUT} == "no modified files to format" ]] && [[ ! -z ${
|
||||
exit 1
|
||||
else
|
||||
echo "Passed clang format check"
|
||||
exit 0
|
||||
fi
|
||||
|
@ -12,8 +12,8 @@ VERSION=$(git describe --tags --dirty)
|
||||
git archive HEAD --format=tar --prefix=${NAME}-${VERSION}/ --output=${NAME}-${VERSION}.tar
|
||||
# get code from subprojects
|
||||
meson subprojects download
|
||||
meson subprojects update
|
||||
tar -rf ${NAME}-${VERSION}.tar --exclude-vcs --transform="s,^subprojects,${NAME}-$VERSION/subprojects," subprojects/inih/
|
||||
meson subprojects update --reset
|
||||
tar -rf ${NAME}-${VERSION}.tar --exclude-vcs --transform="s,^subprojects,${NAME}-$VERSION/subprojects," subprojects/inih-r54/
|
||||
# compress archive
|
||||
xz -9 "${NAME}-${VERSION}.tar"
|
||||
|
||||
|
@ -1,11 +1,12 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Exit on failure
|
||||
set -e
|
||||
set -exo pipefail
|
||||
|
||||
# Ensure we are at the project root
|
||||
cd "$(dirname $0)"/..
|
||||
|
||||
# Collect scan-build output
|
||||
ninja scan-build -C builddir | tee /tmp/scan-build-results.txt
|
||||
ninja scan-build -C builddir | tee builddir/meson-logs/scan-build.txt
|
||||
|
||||
# Invert the output - if this string exists it's a fail
|
||||
! grep -E '[0-9]+ bugs? found.' /tmp/scan-build-results.txt
|
||||
|
||||
! grep -E '[0-9]+ bugs? found.' builddir/meson-logs/scan-build.txt
|
||||
|
@ -1,3 +1,9 @@
|
||||
[wrap-git]
|
||||
url = https://github.com/benhoyt/inih.git
|
||||
revision = r49
|
||||
[wrap-file]
|
||||
directory = inih-r60
|
||||
source_url = https://github.com/benhoyt/inih/archive/r60.tar.gz
|
||||
source_filename = inih-r60.tar.gz
|
||||
source_hash = 706aa05c888b53bd170e5d8aa8f8a9d9ccf5449dfed262d5103d1f292af26774
|
||||
|
||||
[provide]
|
||||
inih = inih_dep
|
||||
inireader = INIReader_dep
|
||||
|
141
util/cpucorectl.c
Normal file
141
util/cpucorectl.c
Normal file
@ -0,0 +1,141 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of Feral Interactive nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <linux/limits.h>
|
||||
#include <sched.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "common-cpu.h"
|
||||
#include "common-logging.h"
|
||||
|
||||
static int write_state(char *path, int state)
|
||||
{
|
||||
FILE *f = fopen(path, "w");
|
||||
|
||||
if (!f) {
|
||||
LOG_ERROR("Couldn't open file at %s (%s)\n", path, strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (putc(state, f) == EOF) {
|
||||
LOG_ERROR("Couldn't write to file at %s (%s)\n", path, strerror(errno));
|
||||
fclose(f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void log_state(const int state, const long first, const long last)
|
||||
{
|
||||
if (state == '0') {
|
||||
if (first == last)
|
||||
LOG_MSG("parked core %ld\n", first);
|
||||
else
|
||||
LOG_MSG("parked cores %ld - %ld\n", first, last);
|
||||
} else {
|
||||
if (first == last)
|
||||
LOG_MSG("unparked core %ld\n", first);
|
||||
else
|
||||
LOG_MSG("unparked cores %ld - %ld\n", first, last);
|
||||
}
|
||||
}
|
||||
|
||||
static int set_state(char *cpulist, int state)
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
long from, to;
|
||||
char *list = cpulist;
|
||||
|
||||
long first = -1, last = -1;
|
||||
|
||||
while ((list = parse_cpulist(list, &from, &to))) {
|
||||
for (long cpu = from; cpu < to + 1; cpu++) {
|
||||
if (snprintf(path, PATH_MAX, "/sys/devices/system/cpu/cpu%ld/online", cpu) < 0) {
|
||||
LOG_ERROR("snprintf failed, will not apply cpu core parking!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!write_state(path, state)) {
|
||||
/* on some systems one cannot park core #0 */
|
||||
if (cpu != 0) {
|
||||
if (state == '0') {
|
||||
LOG_ERROR("unable to park core #%ld, will not apply cpu core parking!\n",
|
||||
cpu);
|
||||
return -1;
|
||||
}
|
||||
|
||||
LOG_ERROR("unable to unpark core #%ld\n", cpu);
|
||||
}
|
||||
} else {
|
||||
if (first == -1) {
|
||||
first = cpu;
|
||||
last = cpu;
|
||||
} else if (last + 1 == cpu) {
|
||||
last = cpu;
|
||||
} else {
|
||||
log_state(state, first, last);
|
||||
first = cpu;
|
||||
last = cpu;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (first != -1)
|
||||
log_state(state, first, last);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
if (geteuid() != 0) {
|
||||
LOG_ERROR("This program must be run as root\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (argc == 3 && strncmp(argv[1], "online", 6) == 0) {
|
||||
if (!set_state(argv[2], '1'))
|
||||
return EXIT_FAILURE;
|
||||
} else if (argc == 3 && strncmp(argv[1], "offline", 7) == 0) {
|
||||
if (!set_state(argv[2], '0'))
|
||||
return EXIT_FAILURE;
|
||||
} else {
|
||||
fprintf(stderr, "usage: cpucorectl [online]|[offline] VALUE]\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2017-2019, Feral Interactive
|
||||
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2017-2019, Feral Interactive
|
||||
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
@ -70,7 +70,7 @@ static void print_usage_and_exit(void)
|
||||
static const char *get_nv_attr(const char *attr)
|
||||
{
|
||||
static char out[EXTERNAL_BUFFER_MAX];
|
||||
const char *exec_args[] = { "/usr/bin/nvidia-settings", "-q", attr, "-t", NULL };
|
||||
const char *exec_args[] = { "nvidia-settings", "-q", attr, "-t", NULL };
|
||||
if (run_external_process(exec_args, out, -1) != 0) {
|
||||
LOG_ERROR("Failed to get %s!\n", attr);
|
||||
out[0] = 0;
|
||||
@ -82,7 +82,7 @@ static const char *get_nv_attr(const char *attr)
|
||||
|
||||
static int set_nv_attr(const char *attr)
|
||||
{
|
||||
const char *exec_args_core[] = { "/usr/bin/nvidia-settings", "-a", attr, NULL };
|
||||
const char *exec_args_core[] = { "nvidia-settings", "-a", attr, NULL };
|
||||
if (run_external_process(exec_args_core, NULL, -1) != 0) {
|
||||
LOG_ERROR("Failed to set %s!\n", attr);
|
||||
return -1;
|
||||
|
@ -28,3 +28,48 @@ gpuclockctl = executable(
|
||||
install: true,
|
||||
install_dir: path_libexecdir,
|
||||
)
|
||||
|
||||
# Small target util to park and unpark cores
|
||||
cpucorectl_sources = [
|
||||
'cpucorectl.c',
|
||||
]
|
||||
|
||||
cpucorectl = executable(
|
||||
'cpucorectl',
|
||||
sources: cpucorectl_sources,
|
||||
dependencies: [
|
||||
link_daemon_common,
|
||||
],
|
||||
install: true,
|
||||
install_dir: path_libexecdir,
|
||||
)
|
||||
|
||||
# Small target util to set values in /proc/sys/
|
||||
procsysctl_sources = [
|
||||
'procsysctl.c',
|
||||
]
|
||||
|
||||
procsysctl = executable(
|
||||
'procsysctl',
|
||||
sources: procsysctl_sources,
|
||||
dependencies: [
|
||||
link_daemon_common,
|
||||
],
|
||||
install: true,
|
||||
install_dir: path_libexecdir,
|
||||
)
|
||||
|
||||
# Small target util to get and set platform profile
|
||||
platprofctl_sources = [
|
||||
'platprofctl.c',
|
||||
]
|
||||
|
||||
platprofctl = executable(
|
||||
'platprofctl',
|
||||
sources: platprofctl_sources,
|
||||
dependencies: [
|
||||
link_daemon_common,
|
||||
],
|
||||
install: true,
|
||||
install_dir: path_libexecdir,
|
||||
)
|
||||
|
84
util/platprofctl.c
Normal file
84
util/platprofctl.c
Normal file
@ -0,0 +1,84 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2025, the GameMode contributors
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of Feral Interactive nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include "common-logging.h"
|
||||
#include "common-profile.h"
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
/**
|
||||
* Sets platform profile to a value
|
||||
*/
|
||||
static int set_profile_state(const char *value)
|
||||
{
|
||||
int retval = EXIT_SUCCESS;
|
||||
|
||||
FILE *f = fopen(profile_path, "w");
|
||||
if (!f) {
|
||||
LOG_ERROR("Failed to open file for write %s\n", profile_path);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (fprintf(f, "%s\n", value) < 0) {
|
||||
LOG_ERROR("Failed to set platform profile to %s: %s", value, strerror(errno));
|
||||
retval = EXIT_FAILURE;
|
||||
}
|
||||
fclose(f);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Main entry point, dispatch to the appropriate helper
|
||||
*/
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
if (argc == 2 && strncmp(argv[1], "get", 3) == 0) {
|
||||
printf("%s", get_profile_state());
|
||||
} else if (argc == 3 && strncmp(argv[1], "set", 3) == 0) {
|
||||
const char *value = argv[2];
|
||||
|
||||
/* Must be root to set the state */
|
||||
if (geteuid() != 0) {
|
||||
LOG_ERROR("This program must be run as root\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
return set_profile_state(value);
|
||||
} else {
|
||||
fprintf(stderr, "usage: platprofctl [get] [set VALUE]\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
75
util/procsysctl.c
Normal file
75
util/procsysctl.c
Normal file
@ -0,0 +1,75 @@
|
||||
/*
|
||||
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
|
||||
All rights reserved.
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of Feral Interactive nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <unistd.h>
|
||||
#include "common-logging.h"
|
||||
#include "common-splitlock.h"
|
||||
|
||||
static bool write_value(const char *key, const char *value)
|
||||
{
|
||||
FILE *f = fopen(key, "w");
|
||||
|
||||
if (!f) {
|
||||
if (errno != ENOENT)
|
||||
LOG_ERROR("Couldn't open file at %s (%s)\n", key, strerror(errno));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fputs(value, f) == EOF) {
|
||||
LOG_ERROR("Couldn't write to file at %s (%s)\n", key, strerror(errno));
|
||||
fclose(f);
|
||||
return false;
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
if (geteuid() != 0) {
|
||||
LOG_ERROR("This program must be run as root\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (argc == 3) {
|
||||
if (strcmp(argv[1], "split_lock_mitigate") == 0) {
|
||||
if (!write_value(splitlock_path, argv[2]))
|
||||
return EXIT_FAILURE;
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
} else {
|
||||
fprintf(stderr, "unsupported key: '%s'\n", argv[1]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(stderr, "usage: procsysctl KEY VALUE\n");
|
||||
fprintf(stderr, "where KEY can by any of 'split_lock_mitigate'\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user