From 5fb6ebd00d7a9407078a869ee740538743434f76 Mon Sep 17 00:00:00 2001 From: tangxinfa Date: Sun, 23 Feb 2020 15:53:36 +0800 Subject: [PATCH 01/10] fix: avoid use sudo. --- Makefile | 12 ++ nvidia-xrun | 59 ++----- nvidia-xrun-util.c | 400 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 428 insertions(+), 43 deletions(-) create mode 100644 Makefile create mode 100644 nvidia-xrun-util.c diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..ce9a9ce --- /dev/null +++ b/Makefile @@ -0,0 +1,12 @@ +.PHONY: all clean permission + +all: ${CURDIR}/nvidia-xrun-util + +$(CURDIR)/nvidia-xrun-util: $(CURDIR)/nvidia-xrun-util.c + gcc -g -O0 ${CURDIR}/nvidia-xrun-util.c -lm -o ${CURDIR}/nvidia-xrun-util + +permission: $(CURDIR)/nvidia-xrun-util + sudo chown root:root ${CURDIR}/nvidia-xrun-util && sudo chmod gu+s ${CURDIR}/nvidia-xrun-util + +clean: + -rm ${CURDIR}/nvidia-xrun-util diff --git a/nvidia-xrun b/nvidia-xrun index 56d9855..7d5f9ea 100755 --- a/nvidia-xrun +++ b/nvidia-xrun @@ -18,51 +18,30 @@ function execute { fi } -function turn_off_gpu { - if [[ "$REMOVE_DEVICE" == '1' ]]; then - echo 'Removing Nvidia bus from the kernel' - execute "sudo tee /sys/bus/pci/devices/${DEVICE_BUS_ID}/remove <<<1" - else - echo 'Enabling powersave for the graphic card' - execute "sudo tee /sys/bus/pci/devices/${DEVICE_BUS_ID}/power/control << +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +bool dry_run = false; + +void trim_pair(char *str, int (*begin_match)(int c), int (*end_match)(int c)) { + char *begin = str; + char *end; + + if (str == NULL) { + return; + } + + while (begin_match(*begin)) { + begin++; + } + + if (*begin == '\0') { + str[0] = '\0'; + return; + } + + end = begin + strlen(begin) - 1; + while (end > begin && end_match(*end)) { + end--; + } + + end = end + 1; + *end = '\0'; + + memmove(str, begin, end - begin + 1); +} + +int is_quote(int c) { return c == '"'; } + +int is_array_begin(int c) { return c == '('; } + +int is_array_end(int c) { return c == ')'; } + +void trim_space(char *str) { trim_pair(str, &isspace, &isspace); } +void trim_quote(char *str) { trim_pair(str, &is_quote, &is_quote); } +void trim_array(char *str) { trim_pair(str, &is_array_begin, &is_array_end); } + +void parse_values(const char *data, char *values[], int count) { + int index = 0; + for (const char *end, *begin = data; *begin; begin = end) { + // Skip leading spaces. + while (isspace(*begin)) { + ++begin; + } + + if (is_quote(*begin)) { + ++begin; + end = begin; + while (*end) { + if (is_quote(*end)) { + break; + } + ++end; + } + if (index >= count) { + fprintf(stderr, "warn: values(%s) exceeded max count(%d)\n", data, + count); + return; + } + values[index] = strndup(begin, end - begin); + ++index; + if (*end) { + ++end; + } + } else { + end = begin; + while (*end) { + if (isspace(*end)) { + break; + } + ++end; + } + if (index >= count) { + fprintf(stderr, "warn: values(%s) exceeded max count(%d)\n", data, + count); + return; + } + values[index] = strndup(begin, end - begin); + ++index; + } + } +} + +void free_values(char *values[], int count) { + for (int i = 0; i < count; ++i) { + if (values[i]) { + free(values[i]); + values[i] = NULL; + } + } +} + +typedef struct { + bool enable_pm; + bool remove_device; + char device_bus_id[64]; + char controller_bus_id[64]; + int bus_rescan_wait_sec; + char *modules_load[64]; + char *modules_unload[64]; +} Conf; + +bool conf_load(Conf *conf, const char *file) { + FILE *f = fopen(file, "r"); + if (f == NULL) { + fprintf(stderr, "open config file %s failed: %d\n", file, errno); + return false; + } + char line[1024] = {'\0'}; + while (fgets(line, sizeof(line), f)) { + trim_space(line); + if (line[0] == '#') { + continue; // Skip comment line + } + char *value = strchr(line, '='); + if (value == NULL) { + continue; + } + *value = '\0'; + ++value; + trim_space(line); + trim_space(value); + if (strcmp(line, "ENABLE_PM") == 0) { + trim_quote(value); + conf->enable_pm = (value[0] == '1'); + } else if (strcmp(line, "REMOVE_DEVICE") == 0) { + trim_quote(value); + conf->remove_device = (value[0] == '1'); + } else if (strcmp(line, "CONTROLLER_BUS_ID") == 0) { + trim_quote(value); + snprintf(conf->controller_bus_id, sizeof(conf->controller_bus_id), "%s", + value); + } else if (strcmp(line, "DEVICE_BUS_ID") == 0) { + trim_quote(value); + snprintf(conf->device_bus_id, sizeof(conf->device_bus_id), "%s", value); + } else if (strcmp(line, "BUS_RESCAN_WAIT_SEC") == 0) { + trim_quote(value); + conf->bus_rescan_wait_sec = atoi(value); + } else if (strcmp(line, "MODULES_LOAD") == 0) { + trim_array(value); + parse_values(value, conf->modules_load, + sizeof(conf->modules_load) / sizeof(conf->modules_load[0])); + } else if (strcmp(line, "MODULES_UNLOAD") == 0) { + trim_array(value); + parse_values(value, conf->modules_unload, + sizeof(conf->modules_unload) / + sizeof(conf->modules_unload[0])); + } else { + fprintf(stderr, "ignore: unknown config item '%s'\n", line); + } + } + fclose(f); + return true; +} + +void conf_free(Conf *conf) { + free_values(conf->modules_load, sizeof(conf->modules_load)/sizeof(conf->modules_load[0])); + free_values(conf->modules_unload, sizeof(conf->modules_unload)/sizeof(conf->modules_unload[0])); +} + +void conf_dump(const Conf *conf) { + printf("enable_pm\n\t%d\n" + "remove_device\n\t%d\n" + "device_bus_id\n\t%s\n" + "controller_bus_id\n\t%s\n" + "bus_rescan_wait_sec\n\t%d\n", + conf->enable_pm, conf->remove_device, conf->device_bus_id, + conf->controller_bus_id, conf->bus_rescan_wait_sec); + printf("modules_load\n"); + int i; + for (i = 0; i < sizeof(conf->modules_load) / sizeof(conf->modules_load[0]); + ++i) { + if (conf->modules_load[i]) { + printf("\t%s\n", conf->modules_load[i]); + } + } + printf("modules_unload\n"); + for (i = 0; + i < sizeof(conf->modules_unload) / sizeof(conf->modules_unload[0]); + ++i) { + if (conf->modules_unload[i]) { + printf("\t%s\n", conf->modules_unload[i]); + } + } +} + +void update_file(const char *file, const char *str) { + if (dry_run) { + printf(">>Dry run. Command: echo '%s' > %s\n", str, file); + return; + } + int fd = open(file, O_WRONLY); + if (fd != -1) { + write(fd, str, strlen(str)); + close(fd); + } +} + +void turn_off_gpu(const Conf *conf) { + char file[PATH_MAX] = {'\0'}; + if (conf->remove_device) { + puts("Removing Nvidia bus from the kernel"); + snprintf(file, sizeof(file), "/sys/bus/pci/devices/%s/remove", + conf->device_bus_id); + update_file(file, "1"); + } else { + puts("Enabling powersave for the graphic card"); + snprintf(file, sizeof(file), "/sys/bus/pci/devices/%s/power/control", + conf->device_bus_id); + update_file(file, "auto"); + } + + puts("Enabling powersave for the PCIe controller"); + snprintf(file, sizeof(file), "/sys/bus/pci/devices/%s/power/control", + conf->controller_bus_id); + update_file(file, "auto"); +} + +void turn_on_gpu(const Conf *conf) { + puts("Turning the PCIe controller on to allow card rescan"); + char file[PATH_MAX] = {'\0'}; + snprintf(file, sizeof(file), "/sys/bus/pci/devices/%s/power/control", + conf->controller_bus_id); + update_file(file, "on"); + + puts("Waiting 1 second"); + if (dry_run) { + printf(">>Dry run. Command: sleep 1\n"); + } else { + sleep(1); + } + + snprintf(file, sizeof(file), "/sys/bus/pci/devices/%s", conf->device_bus_id); + struct stat st; + if (stat(file, &st) != 0 || !S_ISDIR(st.st_mode)) { + puts("Rescanning PCI devices"); + update_file("/sys/bus/pci/rescan", "1"); + printf("Waiting %d second for rescan\n", conf->bus_rescan_wait_sec); + if (dry_run) { + printf(">>Dry run. Command: sleep %d\n", conf->bus_rescan_wait_sec); + } else { + sleep(conf->bus_rescan_wait_sec); + } + } + + puts("Turning the card on"); + snprintf(file, sizeof(file), "/sys/bus/pci/devices/%s/power/control", + conf->device_bus_id); + update_file(file, "on"); +} + +void load_module(const char *module) { + printf("Loading module %s\n", module); + + if (dry_run) { + printf(">>Dry run. Command: modprobe '%s'\n", module); + return; + } + + pid_t pid = fork(); + + if (pid == -1) { + perror("fork"); + return; + } + + if (pid > 0) { + waitpid(pid, NULL, 0); + return; + } + + char* args[64] = {NULL}; + args[0] = strdup("/usr/bin/modprobe"); + parse_values(module, args + 1, sizeof(args)/sizeof(args[0]) - 2); + execvp(args[0], args); + free_values(args, sizeof(args)/sizeof(args[0])); + exit(EXIT_FAILURE); +} + +void unload_module(const char *module) { + printf("Unloading module %s\n", module); + if (dry_run) { + printf(">>Dry run. Command: modprobe -r '%s'\n", module); + return; + } + + pid_t pid = fork(); + + if (pid == -1) { + perror("fork"); + return; + } + + if (pid > 0) { + waitpid(pid, NULL, 0); + return; + } + + execl("/usr/bin/modprobe", "/usr/bin/modprobe", "-r", module, NULL); + exit(EXIT_FAILURE); +} + +void load_modules(const Conf *conf) { + int i; + + for (i = 0; i < sizeof(conf->modules_load) / sizeof(conf->modules_load[0]); + ++i) { + if (conf->modules_load[i]) { + load_module(conf->modules_load[i]); + } + } +} + +void unload_modules(const Conf *conf) { + int i; + + for (i = 0; + i < sizeof(conf->modules_unload) / sizeof(conf->modules_unload[0]); + ++i) { + if (conf->modules_unload[i]) { + unload_module(conf->modules_unload[i]); + } + } +} + +void usage(int argc, char *argv[]) { + fprintf(stderr, + "Usage: %s " + " [dry_run]\n", + argv[0]); +} + +int main(int argc, char *argv[]) { + Conf conf = {'\0'}; + if (!conf_load(&conf, "/etc/default/nvidia-xrun")) { + return EXIT_FAILURE; + } + + if (argc > 1 && argc < 4) { + dry_run = (argc > 2 && strcmp(argv[2], "1") == 0); + if (!dry_run) { + if (setuid(0) == -1) { + perror("setuid"); + return EXIT_FAILURE; + } + if (setgid(0) == -1) { + perror("setgid"); + return EXIT_FAILURE; + } + } + + if (strcmp(argv[1], "turn_off_gpu") == 0) { + if (conf.enable_pm) { + turn_off_gpu(&conf); + } + return 0; + } else if (strcmp(argv[1], "turn_on_gpu") == 0) { + if (conf.enable_pm) { + turn_on_gpu(&conf); + } + return 0; + } else if (strcmp(argv[1], "force_turn_off_gpu") == 0) { + turn_off_gpu(&conf); + return 0; + } else if (strcmp(argv[1], "force_turn_on_gpu") == 0) { + turn_on_gpu(&conf); + return 0; + } else if (strcmp(argv[1], "load_modules") == 0) { + load_modules(&conf); + return 0; + } else if (strcmp(argv[1], "unload_modules") == 0) { + unload_modules(&conf); + return 0; + } else if (strcmp(argv[1], "dump_conf") == 0) { + conf_dump(&conf); + return 0; + } + } + + usage(argc, argv); + + return EXIT_FAILURE; +} From a4135b876f5dc52ffc8802460d64a2a01f190f5f Mon Sep 17 00:00:00 2001 From: tangxinfa Date: Mon, 24 Feb 2020 11:41:42 +0800 Subject: [PATCH 02/10] fix: modprobe may hang sometimes --- nvidia-xrun-util.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/nvidia-xrun-util.c b/nvidia-xrun-util.c index 00561c2..5c5cacc 100644 --- a/nvidia-xrun-util.c +++ b/nvidia-xrun-util.c @@ -286,8 +286,10 @@ void load_module(const char *module) { } char* args[64] = {NULL}; - args[0] = strdup("/usr/bin/modprobe"); - parse_values(module, args + 1, sizeof(args)/sizeof(args[0]) - 2); + args[0] = strdup("/usr/bin/timeout"); + args[1] = strdup("10"); + args[2] = strdup("/usr/bin/modprobe"); + parse_values(module, args + 3, sizeof(args)/sizeof(args[0]) - 4); execvp(args[0], args); free_values(args, sizeof(args)/sizeof(args[0])); exit(EXIT_FAILURE); @@ -312,7 +314,7 @@ void unload_module(const char *module) { return; } - execl("/usr/bin/modprobe", "/usr/bin/modprobe", "-r", module, NULL); + execl("/usr/bin/timeout", "/usr/bin/timeout", "10", "/usr/bin/modprobe", "-r", module, NULL); exit(EXIT_FAILURE); } From ba5c8bb3a08a284a38679e7445f4f28206be83be Mon Sep 17 00:00:00 2001 From: Jo-Blade <59778661+Jo-Blade@users.noreply.github.com> Date: Sun, 31 May 2020 11:33:49 +0200 Subject: [PATCH 03/10] Update README.md Explain all modification in this repo --- README.md | 55 +++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 35 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 481fd40..0748017 100644 --- a/README.md +++ b/README.md @@ -4,11 +4,16 @@ It started with a revelation that bumblebee in current state offers very poor pe ## Usage: 1. switch to free tty - 1. login - 1. run `nvidia-xrun [app]` - 1. enjoy + 2. login + 3. run `nvidia-xrun [return tty] [app]` + 4. enjoy -Currently sudo is required as the script needs to wake up GPU, modprobe the nvidia driver and perform cleanup afterwards. +## Usage from existing X session: + 1. open a terminal emulator (as Xterm) + 2. run `nvidia-xrun-util start_from_X` you can specify an app with `--exec="[app]"` or the tty number to switch before the nvidia X session finished with `--actualVt=[return tty]` (by default, it switch back to the actual tty, before the script was run) + 3. enjoy + + **This version needs no sudo right for the current user** The systemd service can be used to completely remove the card from the kernel device tree (so that it won't even show in `lspci` output), and this will @@ -25,17 +30,24 @@ When the nvidia-xrun command is used, the device is added again to the tree so t ## Structure * **nvidia-xrun** - uses following dir structure: -* **/usr/bin/nvidia-xrun** - the executable script +* **/usr/bin/nvidia-xrun** - the executable bash script +* **/usr/bin/nvidia-xrun-util** - the executable binary contains all admin commands * **/etc/X11/nvidia-xorg.conf** - the main X confing file * **/etc/X11/xinit/nvidia-xinitrc** - xinitrc config file. Contains the setting of provider output source * **/etc/X11/xinit/nvidia-xinitrc.d** - custom xinitrc scripts directory * **/etc/X11/nvidia-xorg.conf.d** - custom X config directory * **/etc/systemd/system/nvidia-xrun-pm.service** systemd service * **/etc/default/nvidia-xrun** - nvidia-xrun config file -* **/usr/share/xsessions/nvidia-xrun-openbox.desktop** - xsession file for openbox -* **/usr/share/xsessions/nvidia-xrun-plasma.desktop** - xsession file for plasma +* **[OPTIONAL] /usr/share/xsession/nvidia-gnome.desktop** - gnome-session entry using nvidia-xrun in the gdm login manager * **[OPTIONAL] $XDG_CONFIG_HOME/X11/nvidia-xinitrc** - user-level custom xinit script file. You can put here your favourite window manager for example +## Modifications in this repository +(I'm sorry for my bad english, I'm a french student) + This repository is a fork of the tangxinfa repository (branch "fix-no-sudo") who permit to use nvidia-xrun without sudo rights by separating all sudo commands in the binary "nidia-xrun-util" (run with setuid root). + I've modified the binary to start "nvidia-xrun" in a new user session in a new tty using **systemd-run** (https://unix.stackexchange.com/questions/554592/how-to-manually-run-init-start-a-xorg-server-on-a-different-vt-tty/554603#554603). + When the `nvidia-xrun-util start_from_X` command start, it wait one second before switch to the tty8 (to prevent swithing back to the tty1 at first session ending in gdm). After, it will run the nvidia-xrun command in the tty8 as user. When the session finished, it switch back to the previous tty or the tty specified by the user (`--actualVt=[tty number]`). + For the time, you cannot modify the tty opened by the `nvidia-xrun-util start_from_X` because it is hard coded. That means if you run the command twice, it will wait before the first nvidia X ending before starting a new one. (the classic `nvidia-xrun` command is no affected because it run in the current tty) + **To make possible switching back to the previous tty, the nvidia-xrun command has been changed! You must specify the tty number before the app to execute like this `nvidia-xrun 1 xterm`** ## Setting the right bus id Usually the 1:0:0 bus is correct. If this is not your case(you can find out through lspci or bbswitch output mesages) you can create @@ -88,19 +100,6 @@ With this you do not need to specify the app and you can simply run: nvidia-xrun -## AUR Package -The Arch Linux User Repository package can be found [here](https://aur.archlinux.org/packages/nvidia-xrun/). - -## COPR Repository for Enterprise Linux, Fedora, Mageia, and openSUSE -The RPM packages and repository details for all supported distributions can be found on the [ekultails/nvidia-xrun](https://copr.fedorainfracloud.org/coprs/ekultails/nvidia-xrun/) COPR overview page. - -### Install (Enterprise Linux and Fedora) - -``` -sudo dnf copr enable ekultails/nvidia-xrun -sudo dnf install nvidia-xrun -``` - ## Troubleshooting ### Steam issues Yes unfortunately running Steam directly with nvidia-xrun does not work well - I recommend to use some window manager like openbox. @@ -127,3 +126,19 @@ In that case, you should add `--ignore-install` to `modprobe` calls in `nvidia-x Check https://wiki.archlinux.org/index.php/Vulkan * remove package vulkan-intel * set VK_ICD_FILENAMES=/usr/share/vulkan/icd.d/nvidia_icd.json + +### Xorg cannot start in Debian +You should comment all "files" section in /etc/X11/nvidia-xorg.conf like this: +`#Section "Files" +# ModulePath "/usr/lib/nvidia" +# ModulePath "/usr/lib32/nvidia" +# ModulePath "/usr/lib32/nvidia/xorg/modules" +# ModulePath "/usr/lib32/xorg/modules" +# ModulePath "/usr/lib64/nvidia/xorg/modules" +# ModulePath "/usr/lib64/nvidia/xorg" +# ModulePath "/usr/lib64/xorg/modules" +#EndSection` + +### cannot unload "nvidia-drm" before nvidia-xrun +I don't know why, in my debian loading "nvidia_drm modeset=1" cause nvidia_drm cannot be unloaded without kill all X server (even intel graphic X server). More if the script try to remove the nvidia card at this moment, it cause a kernel bug who cause shutdown infinite loop (you must make a forced outage) and you will not be able to kill the "nvidia-xrun-util turn_off_gpu" process. +I must replace **"nvidia_drm modeset=1"** by **nvidia_drm** in /etc/default/nvidia-xrun From 4537632517bcc1936d070684b8ac5e4b22a1362f Mon Sep 17 00:00:00 2001 From: Jo-Blade <59778661+Jo-Blade@users.noreply.github.com> Date: Sun, 31 May 2020 11:47:48 +0200 Subject: [PATCH 04/10] Update README.md --- README.md | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0748017..ecbf2e5 100644 --- a/README.md +++ b/README.md @@ -100,6 +100,21 @@ With this you do not need to specify the app and you can simply run: nvidia-xrun +## Run graphically from gdm + 1. For convenience you can create `sudo nano /usr/share/xsession/[your session].desktop` and put there your favourite window manager: +``` + [Desktop Entry] +Encoding=UTF-8 +Name=[the name in the gdm session list] +Comment=[comment in the gdm session list] +Type=Application +Exec=/usr/bin/nvidia-xrun-util start_from_X --actualVt=1 --exec="[put it your session script]" +``` + 2. Restart gdm `sudo systemctl restart gdm` + 3. Now, you will be able to select your new nvidia-xrun session in the gdm list when the computer start. + +In fact, gdm will spawn a new X server who run the `nvidia-xrun-util start_from_X` command and stop. (that make computer switch back to tty1, however, because the nvidia-xrun-util process wait 1 second before starting, the computer will just after switch to the tty8). The argument `--actualVt=1` make the script switch back the tty1 (who contains the gdm session manager) instead of the tty where the script was started. + ## Troubleshooting ### Steam issues Yes unfortunately running Steam directly with nvidia-xrun does not work well - I recommend to use some window manager like openbox. @@ -129,7 +144,8 @@ Check https://wiki.archlinux.org/index.php/Vulkan ### Xorg cannot start in Debian You should comment all "files" section in /etc/X11/nvidia-xorg.conf like this: -`#Section "Files" +``` +#Section "Files" # ModulePath "/usr/lib/nvidia" # ModulePath "/usr/lib32/nvidia" # ModulePath "/usr/lib32/nvidia/xorg/modules" @@ -137,7 +153,8 @@ You should comment all "files" section in /etc/X11/nvidia-xorg.conf like this: # ModulePath "/usr/lib64/nvidia/xorg/modules" # ModulePath "/usr/lib64/nvidia/xorg" # ModulePath "/usr/lib64/xorg/modules" -#EndSection` +#EndSection +``` ### cannot unload "nvidia-drm" before nvidia-xrun I don't know why, in my debian loading "nvidia_drm modeset=1" cause nvidia_drm cannot be unloaded without kill all X server (even intel graphic X server). More if the script try to remove the nvidia card at this moment, it cause a kernel bug who cause shutdown infinite loop (you must make a forced outage) and you will not be able to kill the "nvidia-xrun-util turn_off_gpu" process. From b1581e746776e65c33fe4281c8d28dcf99f10397 Mon Sep 17 00:00:00 2001 From: Jo-Blade <59778661+Jo-Blade@users.noreply.github.com> Date: Sun, 31 May 2020 11:52:21 +0200 Subject: [PATCH 05/10] add nvidia-xrun switch back to the specified tty --- nvidia-xrun | 3 +++ 1 file changed, 3 insertions(+) diff --git a/nvidia-xrun b/nvidia-xrun index 7d5f9ea..b109552 100755 --- a/nvidia-xrun +++ b/nvidia-xrun @@ -1,4 +1,5 @@ #!/usr/bin/env bash +CHVT=$1; shift DRY_RUN=0 function printHelp { @@ -115,3 +116,5 @@ unload_modules # --------- TURNING OFF GPU ---------- turn_off_gpu + +chvt $CHVT From cb4af3f357a3b9696888a7adeb8a543ed8b6371a Mon Sep 17 00:00:00 2001 From: Jo-Blade <59778661+Jo-Blade@users.noreply.github.com> Date: Sun, 31 May 2020 11:56:51 +0200 Subject: [PATCH 06/10] add start_from_X command change "/usr/bin/modprobe" by "/usr/sbin/modprobe" to match with debian file localisation --- nvidia-xrun-util.c | 361 +++++++++++++++++++++++++++++++++------------ 1 file changed, 269 insertions(+), 92 deletions(-) diff --git a/nvidia-xrun-util.c b/nvidia-xrun-util.c index 5c5cacc..a1bd5c0 100644 --- a/nvidia-xrun-util.c +++ b/nvidia-xrun-util.c @@ -11,27 +11,36 @@ #include #include +#include +#include +#include + bool dry_run = false; -void trim_pair(char *str, int (*begin_match)(int c), int (*end_match)(int c)) { +void trim_pair(char *str, int (*begin_match)(int c), int (*end_match)(int c)) +{ char *begin = str; char *end; - if (str == NULL) { + if (str == NULL) + { return; } - while (begin_match(*begin)) { + while (begin_match(*begin)) + { begin++; } - if (*begin == '\0') { + if (*begin == '\0') + { str[0] = '\0'; return; } end = begin + strlen(begin) - 1; - while (end > begin && end_match(*end)) { + while (end > begin && end_match(*end)) + { end--; } @@ -51,42 +60,55 @@ void trim_space(char *str) { trim_pair(str, &isspace, &isspace); } void trim_quote(char *str) { trim_pair(str, &is_quote, &is_quote); } void trim_array(char *str) { trim_pair(str, &is_array_begin, &is_array_end); } -void parse_values(const char *data, char *values[], int count) { +void parse_values(const char *data, char *values[], int count) +{ int index = 0; - for (const char *end, *begin = data; *begin; begin = end) { + for (const char *end, *begin = data; *begin; begin = end) + { // Skip leading spaces. - while (isspace(*begin)) { + while (isspace(*begin)) + { ++begin; } - if (is_quote(*begin)) { + if (is_quote(*begin)) + { ++begin; end = begin; - while (*end) { - if (is_quote(*end)) { + while (*end) + { + if (is_quote(*end)) + { break; } ++end; } - if (index >= count) { + if (index >= count) + { fprintf(stderr, "warn: values(%s) exceeded max count(%d)\n", data, count); return; } values[index] = strndup(begin, end - begin); ++index; - if (*end) { + if (*end) + { ++end; } - } else { + } + else + { end = begin; - while (*end) { - if (isspace(*end)) { + while (*end) + { + if (isspace(*end)) + { break; } ++end; } - if (index >= count) { + if (index >= count) + { fprintf(stderr, "warn: values(%s) exceeded max count(%d)\n", data, count); return; @@ -97,16 +119,20 @@ void parse_values(const char *data, char *values[], int count) { } } -void free_values(char *values[], int count) { - for (int i = 0; i < count; ++i) { - if (values[i]) { +void free_values(char *values[], int count) +{ + for (int i = 0; i < count; ++i) + { + if (values[i]) + { free(values[i]); values[i] = NULL; } } } -typedef struct { +typedef struct +{ bool enable_pm; bool remove_device; char device_bus_id[64]; @@ -116,52 +142,72 @@ typedef struct { char *modules_unload[64]; } Conf; -bool conf_load(Conf *conf, const char *file) { +bool conf_load(Conf *conf, const char *file) +{ FILE *f = fopen(file, "r"); - if (f == NULL) { + if (f == NULL) + { fprintf(stderr, "open config file %s failed: %d\n", file, errno); return false; } char line[1024] = {'\0'}; - while (fgets(line, sizeof(line), f)) { + while (fgets(line, sizeof(line), f)) + { trim_space(line); - if (line[0] == '#') { + if (line[0] == '#') + { continue; // Skip comment line } char *value = strchr(line, '='); - if (value == NULL) { + if (value == NULL) + { continue; } *value = '\0'; ++value; trim_space(line); trim_space(value); - if (strcmp(line, "ENABLE_PM") == 0) { + if (strcmp(line, "ENABLE_PM") == 0) + { trim_quote(value); conf->enable_pm = (value[0] == '1'); - } else if (strcmp(line, "REMOVE_DEVICE") == 0) { + } + else if (strcmp(line, "REMOVE_DEVICE") == 0) + { trim_quote(value); conf->remove_device = (value[0] == '1'); - } else if (strcmp(line, "CONTROLLER_BUS_ID") == 0) { + } + else if (strcmp(line, "CONTROLLER_BUS_ID") == 0) + { trim_quote(value); snprintf(conf->controller_bus_id, sizeof(conf->controller_bus_id), "%s", value); - } else if (strcmp(line, "DEVICE_BUS_ID") == 0) { + } + else if (strcmp(line, "DEVICE_BUS_ID") == 0) + { trim_quote(value); snprintf(conf->device_bus_id, sizeof(conf->device_bus_id), "%s", value); - } else if (strcmp(line, "BUS_RESCAN_WAIT_SEC") == 0) { + } + else if (strcmp(line, "BUS_RESCAN_WAIT_SEC") == 0) + { trim_quote(value); conf->bus_rescan_wait_sec = atoi(value); - } else if (strcmp(line, "MODULES_LOAD") == 0) { + } + else if (strcmp(line, "MODULES_LOAD") == 0) + { trim_array(value); parse_values(value, conf->modules_load, sizeof(conf->modules_load) / sizeof(conf->modules_load[0])); - } else if (strcmp(line, "MODULES_UNLOAD") == 0) { + } + else if (strcmp(line, "MODULES_UNLOAD") == 0) + { trim_array(value); parse_values(value, conf->modules_unload, sizeof(conf->modules_unload) / sizeof(conf->modules_unload[0])); - } else { + } + else + { fprintf(stderr, "ignore: unknown config item '%s'\n", line); } } @@ -169,12 +215,14 @@ bool conf_load(Conf *conf, const char *file) { return true; } -void conf_free(Conf *conf) { - free_values(conf->modules_load, sizeof(conf->modules_load)/sizeof(conf->modules_load[0])); - free_values(conf->modules_unload, sizeof(conf->modules_unload)/sizeof(conf->modules_unload[0])); +void conf_free(Conf *conf) +{ + free_values(conf->modules_load, sizeof(conf->modules_load) / sizeof(conf->modules_load[0])); + free_values(conf->modules_unload, sizeof(conf->modules_unload) / sizeof(conf->modules_unload[0])); } -void conf_dump(const Conf *conf) { +void conf_dump(const Conf *conf) +{ printf("enable_pm\n\t%d\n" "remove_device\n\t%d\n" "device_bus_id\n\t%s\n" @@ -185,41 +233,52 @@ void conf_dump(const Conf *conf) { printf("modules_load\n"); int i; for (i = 0; i < sizeof(conf->modules_load) / sizeof(conf->modules_load[0]); - ++i) { - if (conf->modules_load[i]) { + ++i) + { + if (conf->modules_load[i]) + { printf("\t%s\n", conf->modules_load[i]); } } printf("modules_unload\n"); for (i = 0; i < sizeof(conf->modules_unload) / sizeof(conf->modules_unload[0]); - ++i) { - if (conf->modules_unload[i]) { + ++i) + { + if (conf->modules_unload[i]) + { printf("\t%s\n", conf->modules_unload[i]); } } } -void update_file(const char *file, const char *str) { - if (dry_run) { +void update_file(const char *file, const char *str) +{ + if (dry_run) + { printf(">>Dry run. Command: echo '%s' > %s\n", str, file); return; } int fd = open(file, O_WRONLY); - if (fd != -1) { + if (fd != -1) + { write(fd, str, strlen(str)); close(fd); } } -void turn_off_gpu(const Conf *conf) { +void turn_off_gpu(const Conf *conf) +{ char file[PATH_MAX] = {'\0'}; - if (conf->remove_device) { + if (conf->remove_device) + { puts("Removing Nvidia bus from the kernel"); snprintf(file, sizeof(file), "/sys/bus/pci/devices/%s/remove", conf->device_bus_id); update_file(file, "1"); - } else { + } + else + { puts("Enabling powersave for the graphic card"); snprintf(file, sizeof(file), "/sys/bus/pci/devices/%s/power/control", conf->device_bus_id); @@ -232,7 +291,8 @@ void turn_off_gpu(const Conf *conf) { update_file(file, "auto"); } -void turn_on_gpu(const Conf *conf) { +void turn_on_gpu(const Conf *conf) +{ puts("Turning the PCIe controller on to allow card rescan"); char file[PATH_MAX] = {'\0'}; snprintf(file, sizeof(file), "/sys/bus/pci/devices/%s/power/control", @@ -240,21 +300,28 @@ void turn_on_gpu(const Conf *conf) { update_file(file, "on"); puts("Waiting 1 second"); - if (dry_run) { + if (dry_run) + { printf(">>Dry run. Command: sleep 1\n"); - } else { + } + else + { sleep(1); } snprintf(file, sizeof(file), "/sys/bus/pci/devices/%s", conf->device_bus_id); struct stat st; - if (stat(file, &st) != 0 || !S_ISDIR(st.st_mode)) { + if (stat(file, &st) != 0 || !S_ISDIR(st.st_mode)) + { puts("Rescanning PCI devices"); update_file("/sys/bus/pci/rescan", "1"); printf("Waiting %d second for rescan\n", conf->bus_rescan_wait_sec); - if (dry_run) { + if (dry_run) + { printf(">>Dry run. Command: sleep %d\n", conf->bus_rescan_wait_sec); - } else { + } + else + { sleep(conf->bus_rescan_wait_sec); } } @@ -265,83 +332,98 @@ void turn_on_gpu(const Conf *conf) { update_file(file, "on"); } -void load_module(const char *module) { +void load_module(const char *module) +{ printf("Loading module %s\n", module); - if (dry_run) { + if (dry_run) + { printf(">>Dry run. Command: modprobe '%s'\n", module); return; } pid_t pid = fork(); - if (pid == -1) { + if (pid == -1) + { perror("fork"); return; } - if (pid > 0) { + if (pid > 0) + { waitpid(pid, NULL, 0); return; } - char* args[64] = {NULL}; + char *args[64] = {NULL}; args[0] = strdup("/usr/bin/timeout"); args[1] = strdup("10"); - args[2] = strdup("/usr/bin/modprobe"); - parse_values(module, args + 3, sizeof(args)/sizeof(args[0]) - 4); + args[2] = strdup("/usr/sbin/modprobe"); + parse_values(module, args + 3, sizeof(args) / sizeof(args[0]) - 4); execvp(args[0], args); - free_values(args, sizeof(args)/sizeof(args[0])); + free_values(args, sizeof(args) / sizeof(args[0])); exit(EXIT_FAILURE); } -void unload_module(const char *module) { +void unload_module(const char *module) +{ printf("Unloading module %s\n", module); - if (dry_run) { + if (dry_run) + { printf(">>Dry run. Command: modprobe -r '%s'\n", module); return; } pid_t pid = fork(); - if (pid == -1) { + if (pid == -1) + { perror("fork"); return; } - if (pid > 0) { + if (pid > 0) + { waitpid(pid, NULL, 0); return; } - execl("/usr/bin/timeout", "/usr/bin/timeout", "10", "/usr/bin/modprobe", "-r", module, NULL); + execl("/usr/bin/timeout", "/usr/bin/timeout", "10", "/usr/sbin/modprobe", "-r", module, NULL); exit(EXIT_FAILURE); } -void load_modules(const Conf *conf) { +void load_modules(const Conf *conf) +{ int i; for (i = 0; i < sizeof(conf->modules_load) / sizeof(conf->modules_load[0]); - ++i) { - if (conf->modules_load[i]) { + ++i) + { + if (conf->modules_load[i]) + { load_module(conf->modules_load[i]); } } } -void unload_modules(const Conf *conf) { +void unload_modules(const Conf *conf) +{ int i; for (i = 0; i < sizeof(conf->modules_unload) / sizeof(conf->modules_unload[0]); - ++i) { - if (conf->modules_unload[i]) { + ++i) + { + if (conf->modules_unload[i]) + { unload_module(conf->modules_unload[i]); } } } -void usage(int argc, char *argv[]) { +void usage(int argc, char *argv[]) +{ fprintf(stderr, "Usage: %s " " 1 && argc < 4) { + if (argc > 1 && argc < 5) + { dry_run = (argc > 2 && strcmp(argv[2], "1") == 0); - if (!dry_run) { - if (setuid(0) == -1) { + if (!dry_run) + { + if (setuid(0) == -1) + { perror("setuid"); return EXIT_FAILURE; } - if (setgid(0) == -1) { + if (setgid(0) == -1) + { perror("setgid"); return EXIT_FAILURE; } } - if (strcmp(argv[1], "turn_off_gpu") == 0) { - if (conf.enable_pm) { + if (strcmp(argv[1], "turn_off_gpu") == 0) + { + if (conf.enable_pm) + { turn_off_gpu(&conf); } return 0; - } else if (strcmp(argv[1], "turn_on_gpu") == 0) { - if (conf.enable_pm) { + } + else if (strcmp(argv[1], "turn_on_gpu") == 0) + { + if (conf.enable_pm) + { turn_on_gpu(&conf); } return 0; - } else if (strcmp(argv[1], "force_turn_off_gpu") == 0) { + } + else if (strcmp(argv[1], "force_turn_off_gpu") == 0) + { turn_off_gpu(&conf); return 0; - } else if (strcmp(argv[1], "force_turn_on_gpu") == 0) { + } + else if (strcmp(argv[1], "force_turn_on_gpu") == 0) + { turn_on_gpu(&conf); return 0; - } else if (strcmp(argv[1], "load_modules") == 0) { + } + else if (strcmp(argv[1], "load_modules") == 0) + { load_modules(&conf); return 0; - } else if (strcmp(argv[1], "unload_modules") == 0) { + } + else if (strcmp(argv[1], "unload_modules") == 0) + { unload_modules(&conf); return 0; - } else if (strcmp(argv[1], "dump_conf") == 0) { + } + else if (strcmp(argv[1], "dump_conf") == 0) + { conf_dump(&conf); return 0; } - } + else if (strcmp(argv[1], "start_from_X") == 0) + { + printf("start nvidia-xrun from existing X server\n"); + start_from_X(argc, argv); + return 0; + } - usage(argc, argv); + usage(argc, argv); - return EXIT_FAILURE; + return EXIT_FAILURE; + } } From bc4986cf0ff9d0c9c4774b4fa5cef0215d4d7c35 Mon Sep 17 00:00:00 2001 From: Jo-Blade <59778661+Jo-Blade@users.noreply.github.com> Date: Sun, 31 May 2020 11:58:42 +0200 Subject: [PATCH 07/10] start nvidia-xrun from gdm session list --- launchers/nvidia-test.desktop | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 launchers/nvidia-test.desktop diff --git a/launchers/nvidia-test.desktop b/launchers/nvidia-test.desktop new file mode 100644 index 0000000..1cbe7fd --- /dev/null +++ b/launchers/nvidia-test.desktop @@ -0,0 +1,6 @@ +[Desktop Entry] +Encoding=UTF-8 +Name=Gnome (nvidia-xrun) +Comment=Log in Gnome X11 (running with nvidia-xrun) +Type=Application +Exec=/usr/bin/nvidia-xrun-util start_from_X --actualVt=1 --exec=gnome-session From b61cd5d614649410b76122afadde0c8514784b65 Mon Sep 17 00:00:00 2001 From: Jo-Blade <59778661+Jo-Blade@users.noreply.github.com> Date: Sun, 31 May 2020 12:00:31 +0200 Subject: [PATCH 08/10] start nvidia-xrun from gdm session list --- launchers/{nvidia-test.desktop => nvidia-gnome.desktop} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename launchers/{nvidia-test.desktop => nvidia-gnome.desktop} (87%) diff --git a/launchers/nvidia-test.desktop b/launchers/nvidia-gnome.desktop similarity index 87% rename from launchers/nvidia-test.desktop rename to launchers/nvidia-gnome.desktop index 1cbe7fd..1bf34d9 100644 --- a/launchers/nvidia-test.desktop +++ b/launchers/nvidia-gnome.desktop @@ -1,6 +1,6 @@ [Desktop Entry] Encoding=UTF-8 -Name=Gnome (nvidia-xrun) +Name=GNOME (nvidia-xrun) Comment=Log in Gnome X11 (running with nvidia-xrun) Type=Application Exec=/usr/bin/nvidia-xrun-util start_from_X --actualVt=1 --exec=gnome-session From d0d03c435f215715935592f5afa16c98486b1eb1 Mon Sep 17 00:00:00 2001 From: Jo-Blade <59778661+Jo-Blade@users.noreply.github.com> Date: Tue, 2 Jun 2020 09:37:46 +0200 Subject: [PATCH 09/10] update path for modprobe to /usr/sbin --- nvidia-xrun-util.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/nvidia-xrun-util.c b/nvidia-xrun-util.c index a1bd5c0..3f8080c 100644 --- a/nvidia-xrun-util.c +++ b/nvidia-xrun-util.c @@ -357,10 +357,12 @@ void load_module(const char *module) } char *args[64] = {NULL}; - args[0] = strdup("/usr/bin/timeout"); - args[1] = strdup("10"); - args[2] = strdup("/usr/sbin/modprobe"); - parse_values(module, args + 3, sizeof(args) / sizeof(args[0]) - 4); + args[0] = strdup("env"); + args[1] = strdup("PATH=/usr/sbin:/usr/bin"); + args[2] = strdup("timeout"); + args[3] = strdup("10"); + args[4] = strdup("modprobe"); + parse_values(module, args + 5, sizeof(args) / sizeof(args[0]) - 4); execvp(args[0], args); free_values(args, sizeof(args) / sizeof(args[0])); exit(EXIT_FAILURE); @@ -389,7 +391,10 @@ void unload_module(const char *module) return; } - execl("/usr/bin/timeout", "/usr/bin/timeout", "10", "/usr/sbin/modprobe", "-r", module, NULL); + //execl("/usr/bin/timeout", "/usr/bin/timeout", "10", "/usr/sbin/modprobe", "-r", module, NULL); + execl("env", "env", "PATH=/usr/sbin:/usr/bin", + "timeout", "10", "modprobe", "-r", module, NULL); + exit(EXIT_FAILURE); } From 0495f4465aca7c29039205c78fd0bfffc7630d57 Mon Sep 17 00:00:00 2001 From: Jo-Blade <59778661+Jo-Blade@users.noreply.github.com> Date: Wed, 1 Nov 2023 14:57:54 +0100 Subject: [PATCH 10/10] Update README.md --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index ecbf2e5..30467ee 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,6 @@ +**IMPORTANT: Please do not use anymore. Nvida has released for some years new proprietary drivers that works well. So this project is not usefull anymore.** +That's why it has been archived. + # nvidia-xrun These utility scripts aim to make the life easier for nvidia cards users. It started with a revelation that bumblebee in current state offers very poor performance. This solution offers a bit more complicated procedure but offers a full GPU utilization(in terms of linux drivers)