Since yesterday evening I bought a new (used) GPU for my Farm PC (also I bought a motherboard, both for 212 UAH each), here is some links:
- https://forums.debian.net/viewtopic.php?p=724712
- https://nouveau.freedesktop.org/FeatureMatrix.html
- https://forums.debian.net/viewtopic.php?f=16&t=146141
.. only by accident, I’ve discovered that actually the nouveau driver performs significantly better than proprietary NV driver -> if I manually change the pstate of my gfx card to performance mode:
Rationale: Nouveau driver currently does not change the pstates automatically, because nVidia is not willing to publish full technical documentation for their GPUs and additionaly, often there are bugs in the firmware, which are bypassed by using some undocumented hacks in propietary drivers - so basically there is a risk that changing the pstate will crash the GPU or even the entire system. However, in many cases switching between selected “stable” pstates works perfectly correct, and it can be done automatically, using the following method:
Step 1: Test/discover the “stable” pstates:
sudo cat sys/kernel/debug/dri/<gfx_card_number>/pstate, default gfx_card_number == 0
- The “performance” pstate is the one with the highest GPU/memory clock
- The “powersave” pstate is obvoiusly the one with the lowest GPU/memory clock
- There can be also pstates with “middle” performance settings -> you have to experimentally find the best/stable pstates.
My card reports the following pstates:
> sudo cat /sys/kernel/debug/dri/0/pstate
07: core 405 MHz memory 810 MHz <- powersave pstate (stable)
0f: core 270-1241 MHz memory 5400 MHz <- performance pstate (stable)
AC: core 405 MHz memory 810 MHz <- broken/unstable pstate
Testing pstates:
sudo echo <pstate_value> /sys/kernel/debug/dri/<gfx_card_number>/pstate
a) change the pstate to one of the performance modes
b) run some game and check if it works correctly
c) change the pstate back to powersave mode
NOTE: If during testing Your screen will get “messed”, then usually the only way to recover is to perform a HARD RESET of your PC. Save Your work before experimenting.
If (unlikely) You can’t find stable pstates by manually switching between them, then Your GFX card has a broken firmware -> STOP -> You can’t do anything about this. Stop reading this thread ;)
Step 2:
A binary application for changing pstates is needed, because only binary executables can have the suid bit set. Otherwise, You’ll need to manually change the pstates as ROOT, what definitely would be a pain in the ass… (not to mention security issues) I wrote a simple application, which does the job - it can set the NV pstate trough nouveau debugfs interface.
It does exactly the same thing as the terminal command:
<sudo> echo <pstate_value> /sys/kernel/debug/dri/<gfx_card_number>/pstate,
but as an executable, it can have the suid bit set.
Compile the application:
gcc -O1 -s -o nv_pstate nv_pstate.c
Install (copy) the executable to /usr/local/bin/nv_pstate
Set the suid bit: (allow running with root privileges, without asking for password)
chmod u+s /usr/local/bin/nv_pstate
Step 3: Create startup script for Your application(s):
#!/bin/bash
#switch to performance pstate
nv_pstate -c0 -s0xf #(example values)
<your application here>
#switch back to powersave pstate
nv_pstate -c0 -s0x7
Step 4:
Create a launcher in Your DE (desktop icon/menu item), which invokes the startup script.
DONE.
One more thing: I've discovered, that by default my card is running in some strange mode: performance voltage level and powersave clocking. If I change the pstate:
>nv_pstate -c0 -s0x7
on system startup, then the card is running with both voltages and clocks defined for powersave mode.
In the result, in the idle state, the GPU temp. dropped from 40 to 38 deg.C and the fan runs @850rpm instead of 950 :)
EDIT:
How to add systemd service for switching pstate to powersave mode on system startup:
Create service unit file: /etc/systemd/system/nv_pstate.service, with the following content:
Code: Select all
[Unit]
Description=switch nv card to a powersave mode (pstate)
[Service]
Type=oneshot
Restart=no
ExecStart=/usr/local/bin/nv_pstate -c0 -s0x7 #(example values)
[Install]
WantedBy=graphical.target
Then run:
Code: Select all
>systemctl daemon-reload
>systemctl enable nv_pstate.service
Regards.
Here's the source code: (copy the source and save it as nv_pstate.c)
Code: Select all
/* nv_pstate: Change pstate of nvidia gfx card. This program uses nouveau driver interface exposed trough kernel debugfs: /sys/kernel/debug/dri/<card_number>/pstate
nv_pstate is intended to be used in start-up scripts for games or other gfx-intensive programs - and therefore it must have suid bit set
WARNING: support for changing pstates is experimental - use at Your own risk!
Author : Tomasz Pawlak e-mail : tomasz.pawlak@wp.eu License: GPLv3 */
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <errno.h> #include <getopt.h>
#define ERR_PRINT(…)
fprintf(stderr, VA_ARGS)
#define LOG_PRINT(…)
fprintf(stdout, VA_ARGS)
#define PROGNAME “nv_pstate” #define MAX_CARD_NR 255 #define MAX_PSTATE 255
const char VERSION [] = “1.0”; const char PROG_NAME [] = PROGNAME; const char HLP_UINFO [] = “Use " PROGNAME " –help or -h to display usage info\n”; const char BAD_PSTATE[] = “[E!] Invalid/missing pstate value!\n”;
/* cmd line args */ struct option long_options[] = { {“card " , required_argument, NULL, ‘c’}, {“pstate " , required_argument, NULL, ’s’}, {“verbose” , no_argument , NULL, ‘V’}, {“help " , no_argument , NULL, ‘h’}, {“version” , no_argument , NULL, ‘v’}, {0, 0, 0, 0} }; #define SHORT_OPTS “c:s:Vhv”
#define PATH_SZ 48 #define PATH_MAX (PATH_SZ-1)
typedef struct { int card ; int verbose; int pstate ; char path [PATH_SZ]; } config_t;
void cfg_print(const config_t *const cfg) { LOG_PRINT(“Configuration:\n” “card : %d\n” “path : %s\n” “pstate: 0x%02X\n”, cfg->card, cfg->path, cfg->pstate ); }
int get_cmdline_opts(int inargc, char* const* inargv, config_t *const cfg) {
static const char BUILD[] = “build: " DATE “, " TIME;
static const char HELP[] = “Options:\n” " -c –card gfx card number\n” " -s –pstate pstate value\n” " -V –verbose verbose mode\n” " -h –help this short usage info\n” " -v –version display program version\n”;
int opt_rv; /* 0 = continue main(), 1 = exit_ok, -1 = exit_error */ int tmpi; long tmpl; char *endptr;
opt_rv = 0;
while ((tmpi = getopt_long(inargc, inargv, SHORT_OPTS, long_options, NULL)) != EOF) { errno = 0;
switch (tmpi) {
case 'c': /* card */
tmpl = strtol(optarg, &endptr, 0);
if ( (errno != 0) || (tmpl < 0) || (tmpl > MAX_CARD_NR) ) {
ERR_PRINT( "[E!] Invalid card number: \'%s\'\n", optarg);
opt_rv = -1;
}
cfg->card = (int) tmpl;
break;
case 's': /* pstate */
tmpl = strtol(optarg, &endptr, 0);
if ( (errno != 0) || (tmpl < 0) || (tmpl > MAX_PSTATE) ) {
ERR_PRINT( BAD_PSTATE );
opt_rv = -1;
}
cfg->pstate = (int) tmpl;
break;
case 'V': /* verbose */
cfg->verbose = 1;
break;
case 'h': /* help */
LOG_PRINT( HELP );
opt_rv = 1;
break;
case 'v': /* version */
LOG_PRINT("%s version %s, %s\n", PROG_NAME, VERSION, BUILD);
opt_rv = 1;
break;
case '?': /* error */
opt_rv = -1;
break;
}
if (opt_rv != 0) return opt_rv;
} return opt_rv; }
int write_pstate(const config_t *const cfg) { char str_pstate[8]; int fds; int slen; int wlen; int retv = 0;
if (cfg->verbose) cfg_print(cfg);
if (cfg->pstate < 0) { ERR_PRINT( BAD_PSTATE ); return -1; }
slen = snprintf(str_pstate, 8, “%x%c”, cfg->pstate, 0); if (slen < 1) { ERR_PRINT( “[E!] Failed conversion: pstate -> string.\n”); return -1; }
errno = 0; fds = open(cfg->path, O_WRONLY); if (fds < 0) { ERR_PRINT( “[E!] Failed to open file:\n"%s"\n%s\n”, cfg->path, strerror(errno) ); return -1; }
slen ++ ; //++ NULL byte errno = 0; wlen = write(fds, str_pstate, slen); if (wlen < slen) { ERR_PRINT( “[E!] Failed writing pstate:\n"%s"\n%s\n”, cfg->path, strerror(errno) ); retv = -1; }
close(fds);
if ( (retv == 0) && (cfg->verbose) ) LOG_PRINT("[i] pstate changed to 0x%02X\n", cfg->pstate);
return retv; }
int main( int argc, char argv[] ) { / default debugfs path for nouveau driver */ static const char def_path[] = “/sys/kernel/debug/dri/%u/pstate”;
config_t cfg; int retv;
/* init config */ memset(&cfg, 0, sizeof(config_t) ); cfg.card = -1; cfg.pstate = -1;
retv = get_cmdline_opts(argc, argv, &cfg); if (0 != retv) { if (0 > retv) goto exit_err; goto exit; }
/* If card number is given, use the default debugfs path */ if (cfg.card >= 0) { snprintf(cfg.path, PATH_MAX, def_path, cfg.card); retv = write_pstate(&cfg); return retv; }
exit_err: ERR_PRINT( “[E!] Incorrect and/or missing parameters.\n%s”, HLP_UINFO ); exit: if (cfg.verbose != 0) cfg_print(&cfg); return retv; }