From 81e12006dc0c25f2f6bc6d9f42bcb6ec4510feb4 Mon Sep 17 00:00:00 2001 From: Nikolay Amiantov Date: Mon, 21 Nov 2016 16:02:36 +0300 Subject: [PATCH] Add nouveau switching method --- Makefile.am | 2 +- conf/bumblebee.conf.in | 1 + src/bbconfig.c | 13 ++-- src/bbconfig.h | 1 + src/switch/sw_nouveau.c | 165 ++++++++++++++++++++++++++++++++++++++++ src/switch/switching.c | 2 + src/switch/switching.h | 7 +- 7 files changed, 183 insertions(+), 8 deletions(-) create mode 100644 src/switch/sw_nouveau.c diff --git a/Makefile.am b/Makefile.am index 78e238a..2a77808 100644 --- a/Makefile.am +++ b/Makefile.am @@ -63,7 +63,7 @@ bin_optirun_SOURCES = src/module.c src/bbconfig.c src/bblogger.c src/bbrun.c \ bin_optirun_LDADD = ${glib_LIBS} ${kmod_LIBS} -lrt bin_bumblebeed_SOURCES = src/pci.c src/bbconfig.c src/bblogger.c src/bbrun.c \ src/bbsocket.c src/module.c src/bbsecondary.c src/switch/switching.c \ - src/switch/sw_bbswitch.c src/switch/sw_switcheroo.c \ + src/switch/sw_bbswitch.c src/switch/sw_switcheroo.c src/switch/sw_nouveau.c \ src/driver.c src/bumblebeed.c bin_bumblebeed_LDADD = ${x11_LIBS} ${libbsd_LIBS} ${glib_LIBS} ${kmod_LIBS} -lrt diff --git a/conf/bumblebee.conf.in b/conf/bumblebee.conf.in index 26bcdfe..a7801da 100644 --- a/conf/bumblebee.conf.in +++ b/conf/bumblebee.conf.in @@ -46,6 +46,7 @@ AllowFallbackToIGC=@CONF_FALLBACKSTART@ # detection resolves to NAME). # PMMethod: method to use for saving power by disabling the nvidia card, valid # values are: auto - automatically detect which PM method to use +# nouveau - use nouveau module with PM support # bbswitch - new in BB 3, recommended if available # switcheroo - vga_switcheroo method, use at your own risk # none - disable PM completely diff --git a/src/bbconfig.c b/src/bbconfig.c index 62a3306..2512f68 100644 --- a/src/bbconfig.c +++ b/src/bbconfig.c @@ -42,6 +42,7 @@ const char *bb_pm_method_string[PM_METHODS_COUNT] = { /* the below names are used in switch/switching.c */ "bbswitch", "switcheroo", + "nouveau", }; struct bb_status_struct bb_status; @@ -183,13 +184,13 @@ void print_usage(int exit_val) { -k, --driver-module NAME Name of kernel module to be loaded if different\n\ from the driver\n\ --pm-method METHOD method to use for disabling the discrete video card,\n\ - valid values are auto, bbswitch, switcheroo and\n\ - none. auto selects a sensible method,\n\ + valid values are auto, bbswitch, nouveau,\n + switcheroo and none. auto selects a sensible method,\n\ bbswitch (kernel module) is available for nvidia\n\ - and nouveau drivers,\n\ - switcheroo (vga_switcheroo) is usually for\n\ - nouveau and radeon drivers and none disables PM\n\ - completely\n", + and nouveau drivers, nouveau uses this driver's PM\n\ + faculities, switcheroo (vga_switcheroo) is usually for\n\ + nouveau and radeon drivers and none disables PM\n\ + completely\n", out); #ifdef WITH_PIDFILE fputs("\ diff --git a/src/bbconfig.h b/src/bbconfig.h index a19f5d3..86f84ff 100644 --- a/src/bbconfig.h +++ b/src/bbconfig.h @@ -101,6 +101,7 @@ enum bb_pm_method { PM_AUTO, /* at detection time, this value will be changed */ PM_BBSWITCH, PM_VGASWITCHEROO, + PM_NOUVEAU, PM_METHODS_COUNT /* not a method but a marker for the end */ }; const char *bb_pm_method_string[PM_METHODS_COUNT]; diff --git a/src/switch/sw_nouveau.c b/src/switch/sw_nouveau.c new file mode 100644 index 0000000..9f32fc7 --- /dev/null +++ b/src/switch/sw_nouveau.c @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2011-2013, The Bumblebee Project + * Author: Jaron Viƫtor AKA "Thulinma" + * Author: Peter Lekensteyn + * + * This file is part of Bumblebee. + * + * Bumblebee is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Bumblebee is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Bumblebee. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include "../bblogger.h" +#include "switching.h" +#include "../module.h" + +static int nouveau_fd = -1; + +/** + * Reports the status of nouveau + * + * @return SWITCH_OFF if nouveau is loaded (so card is power-managed), + * SWITCH_ON if card is on. + */ +enum switch_state nouveau_status(void) { + return nouveau_fd != -1 ? SWITCH_OFF : SWITCH_ON; +}//nouveau_status + +/** + * Whether nouveau is available for use + * + * @param info A struct containing information which would help with the + * decision whether nouveau is usable or not + * @return 1 if available for use for PM, 0 otherwise + */ +int nouveau_is_available(struct switch_info info) { + if (strcmp(info.configured_pm, "nouveau") != 0) { + bb_log(LOG_INFO, "Skipping nouveau PM method because it is not" + " explicitly selected in the configuration.\n"); + return 0; + } + return module_is_available("nouveau"); +} + +/** + * Unloads nouveau, leaving card unmanaged. + */ +void nouveau_on(void) { + /* Close nouveau device */ + if (nouveau_fd != -1) { + close(nouveau_fd); + nouveau_fd = -1; + } + + if (!module_unload("nouveau")) { + bb_log(LOG_ERR, "couldn't unload nouveau\n"); + } else { + bb_log(LOG_DEBUG, "nouveau successfully unloaded\n"); + } +}//nouveau_on + +static const open_retry_delay_us = 5000; + +/** + * Loads nouveau, hopefully powering card down. + */ +void nouveau_off(void) { + DIR *dp; + struct dirent *ep; + int card_no = -1; + char buf[PATH_MAX]; + int open_retries = 10; + + if (nouveau_fd != -1) { + return; + } + + if (!module_load("nouveau", "nouveau", "runpm=1 modeset=2")) { + bb_log(LOG_WARNING, "couldn't load nouveau\n"); + return; + } + + /* Find card's device node by enumerating through existing cards. */ + dp = opendir("/sys/class/drm"); + if (dp == NULL) { + bb_log(LOG_WARNING, "couldn't open /sys/class/drm: %s\n", strerror(errno)); + return; + } + + while ((ep = readdir(dp))) { + int candidate_no; + char dummy; + // We use dummy to determine if name is "cardN" or "cardN-something" and skip the latter. + int read_count = sscanf(ep->d_name, "card%i%c", &candidate_no, &dummy); + if (read_count == 1) { + char* driver; + ssize_t link_size = 0; + + snprintf(buf, sizeof(buf), "/sys/class/drm/card%i/device/driver", candidate_no); + link_size = readlink(buf, buf, sizeof(buf)); + if (link_size < 0 || link_size == sizeof(buf)) { + bb_log(LOG_DEBUG, "couldn't read driver link for card %i\n", candidate_no); + continue; + } + buf[link_size] = '\0'; + // We get a path like "../../../../bus/pci/drivers/driver_name" and want to test if driver is nouveau. + driver = strrchr(buf, '/'); + if(driver == NULL) { + continue; + } + if(strcmp(driver + 1, "nouveau") == 0) { + card_no = candidate_no; + } + } + } + + if(closedir(dp) != 0) { + bb_log(LOG_WARNING, "couldn't close /sys/class/drm: %s\n", strerror(errno)); + } + + if(card_no == -1) { + bb_log(LOG_WARNING, "couldn't find discrete card handled by nouveau\n"); + return; + } + + /* Open nouveau device and place exclusive lock to prevent Xorg from using it */ + snprintf(buf, sizeof(buf), "/dev/dri/card%i", card_no); + bb_log(LOG_DEBUG, "found nouveau device: %s\n", buf); + /* udev needs time to create a device node */ + for(; open_retries != 0; open_retries--) { + nouveau_fd = open(buf, 0); + if (nouveau_fd != -1) { + break; + } else { + usleep(open_retry_delay_us); + } + }; + + if (nouveau_fd == -1) { + bb_log(LOG_WARNING, "couldn't open nouveau device: %s\n", strerror(errno)); + return; + } + if (flock(nouveau_fd, LOCK_EX) < 0) { + bb_log(LOG_WARNING, "couldn't exclusively lock nouveau device: %s\n", strerror(errno)); + return; + } + + bb_log(LOG_DEBUG, "successfully loaded and locked nouveau\n"); +}//nouveau_off diff --git a/src/switch/switching.c b/src/switch/switching.c index 7ed716e..e351fb1 100644 --- a/src/switch/switching.c +++ b/src/switch/switching.c @@ -25,6 +25,8 @@ /* increase SWITCHERS_COUNT in switching.h when more methods are added */ struct switching_method switching_methods[SWITCHERS_COUNT] = { + {"nouveau", 2, nouveau_status, nouveau_is_available, + nouveau_on, nouveau_off}, {"bbswitch", 1, bbswitch_status, bbswitch_is_available, bbswitch_on, bbswitch_off}, {"switcheroo", 0, switcheroo_status, switcheroo_is_available, diff --git a/src/switch/switching.h b/src/switch/switching.h index e0a54bc..8797206 100644 --- a/src/switch/switching.h +++ b/src/switch/switching.h @@ -48,6 +48,11 @@ struct switching_method { void (*off)(void); /* attempts to disable a card */ }; +enum switch_state nouveau_status(void); +int nouveau_is_available(struct switch_info); +void nouveau_on(void); +void nouveau_off(void); + enum switch_state bbswitch_status(void); int bbswitch_is_available(struct switch_info); void bbswitch_on(void); @@ -59,7 +64,7 @@ void switcheroo_on(void); void switcheroo_off(void); /* number of switchers as defined in switching.c */ -#define SWITCHERS_COUNT 2 +#define SWITCHERS_COUNT 3 struct switching_method switching_methods[SWITCHERS_COUNT]; /* A switching method that can be used or NULL if none */