Get ready for re-setting ioprio value on un-register

Implements tests for feature

	Fixes CLAMP macro
This commit is contained in:
Marc Di Luzio 2019-05-11 11:53:52 +01:00
parent 6d14149658
commit 2249a71355
7 changed files with 148 additions and 41 deletions

View File

@ -31,6 +31,7 @@ POSSIBILITY OF SUCH DAMAGE.
#define _GNU_SOURCE #define _GNU_SOURCE
#include "daemon_config.h" #include "daemon_config.h"
#include "helpers.h"
#include "logging.h" #include "logging.h"
/* Ben Hoyt's inih library */ /* Ben Hoyt's inih library */
@ -564,12 +565,30 @@ long config_get_ioprio_value(GameModeConfig *self)
long value = 0; long value = 0;
char ioprio_value[CONFIG_VALUE_MAX] = { 0 }; char ioprio_value[CONFIG_VALUE_MAX] = { 0 };
memcpy_locked_config(self, ioprio_value, &self->values.ioprio, sizeof(self->values.ioprio)); memcpy_locked_config(self, ioprio_value, &self->values.ioprio, sizeof(self->values.ioprio));
/* account for special string values */
if (0 == strncmp(ioprio_value, "off", sizeof(self->values.ioprio))) if (0 == strncmp(ioprio_value, "off", sizeof(self->values.ioprio)))
value = IOPRIO_DONT_SET; value = IOPRIO_DONT_SET;
else if (0 == strncmp(ioprio_value, "default", sizeof(self->values.ioprio))) else if (0 == strncmp(ioprio_value, "default", sizeof(self->values.ioprio)))
value = IOPRIO_RESET_DEFAULT; value = IOPRIO_RESET_DEFAULT;
else else
value = atoi(ioprio_value); value = atoi(ioprio_value);
/* Validate values */
if (IOPRIO_RESET_DEFAULT == value) {
LOG_ONCE(MSG, "IO priority will be reset to default behavior (based on CPU priority).\n");
value = 0;
} else {
/* maybe clamp the value */
long invalid_ioprio = value;
value = CLAMP(0, 7, value);
if (value != invalid_ioprio)
LOG_ONCE(ERROR,
"IO priority value %ld invalid, clamping to %ld\n",
invalid_ioprio,
value);
}
return value; return value;
} }

View File

@ -44,6 +44,7 @@ POSSIBILITY OF SUCH DAMAGE.
*/ */
#define IOPRIO_RESET_DEFAULT -1 #define IOPRIO_RESET_DEFAULT -1
#define IOPRIO_DONT_SET -2 #define IOPRIO_DONT_SET -2
#define IOPRIO_DEFAULT 4
/* /*
* Opaque config context type * Opaque config context type

View File

@ -86,6 +86,25 @@ static inline int ioprio_set(int which, int who, int ioprio)
return (int)syscall(SYS_ioprio_set, which, who, ioprio); return (int)syscall(SYS_ioprio_set, which, who, ioprio);
} }
static inline int ioprio_get(int which, int who)
{
return (int)syscall(SYS_ioprio_get, which, who);
}
/**
* Get the i/o priorities
*/
int game_mode_get_ioprio(const pid_t client)
{
int ret = ioprio_get(IOPRIO_WHO_PROCESS, client);
if (ret == -1) {
LOG_ERROR("Failed to get ioprio value for [%d] with error %s\n", client, strerror(errno));
ret = IOPRIO_DONT_SET;
}
/* We support only IOPRIO_CLASS_BE as IOPRIO_CLASS_RT required CAP_SYS_ADMIN */
return IOPRIO_PRIO_DATA(ret);
}
/** /**
* Apply io priorities * Apply io priorities
* *
@ -93,49 +112,51 @@ static inline int ioprio_set(int which, int who, int ioprio)
* and can possibly reduce lags or latency when a game has to load assets * and can possibly reduce lags or latency when a game has to load assets
* on demand. * on demand.
*/ */
void game_mode_apply_ioprio(const GameModeContext *self, const pid_t client) void game_mode_apply_ioprio(const GameModeContext *self, const pid_t client, int expected)
{ {
if (expected == IOPRIO_DONT_SET)
/* Silently bail if fed a don't set (invalid) */
return;
GameModeConfig *config = game_mode_config_from_context(self); GameModeConfig *config = game_mode_config_from_context(self);
LOG_MSG("Setting scheduling policies...\n"); /* read configuration "ioprio" (0..7) */
/*
* read configuration "ioprio" (0..7)
*/
int ioprio = (int)config_get_ioprio_value(config); int ioprio = (int)config_get_ioprio_value(config);
if (IOPRIO_RESET_DEFAULT == ioprio) {
LOG_MSG("IO priority will be reset to default behavior (based on CPU priority).\n");
ioprio = 0;
} else if (IOPRIO_DONT_SET == ioprio) {
return;
} else {
/* maybe clamp the value */
int invalid_ioprio = ioprio;
ioprio = CLAMP(0, 7, ioprio);
if (ioprio != invalid_ioprio)
LOG_ONCE(ERROR,
"IO priority value %d invalid, clamping to %d\n",
invalid_ioprio,
ioprio);
/* We support only IOPRIO_CLASS_BE as IOPRIO_CLASS_RT required CAP_SYS_ADMIN */ /* Special value to simply not set the value */
ioprio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, ioprio); if (ioprio == IOPRIO_DONT_SET)
return;
LOG_MSG("Setting ioprio value...\n");
/* If fed the default, we'll try and reset the value back */
if (expected != IOPRIO_DEFAULT) {
expected = (int)ioprio;
ioprio = IOPRIO_DEFAULT;
} }
/* int current = game_mode_get_ioprio(client);
* Actually apply the io priority if (current == IOPRIO_DONT_SET) {
*/ /* Couldn't get the ioprio value, let's bail */
int c = IOPRIO_PRIO_CLASS(ioprio), p = IOPRIO_PRIO_DATA(ioprio); return;
if (ioprio_set(IOPRIO_WHO_PROCESS, client, ioprio) == 0) { } else if (current != expected) {
if (0 == ioprio) /* Don't try and adjust the ioprio value if the value we got doesn't match default */
LOG_MSG("Resetting client [%d] IO priority.\n", client); LOG_ERROR("Refused to set ioprio on client [%d]: prio was (%d) but we expected (%d)\n",
else
LOG_MSG("Setting client [%d] IO priority to (%d,%d).\n", client, c, p);
} else {
LOG_ERROR("Setting client [%d] IO priority to (%d,%d) failed with error %d, ignoring\n",
client, client,
c, current,
p, expected);
errno); } else {
/*
* For now we only support IOPRIO_CLASS_BE
* IOPRIO_CLASS_RT requires CAP_SYS_ADMIN but should be possible with a polkit process
*/
int p = ioprio;
ioprio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, ioprio);
if (ioprio_set(IOPRIO_WHO_PROCESS, client, ioprio) != 0) {
LOG_ERROR("Setting client [%d] IO priority to (%d) failed with error %d, ignoring\n",
client,
p,
errno);
}
} }
} }

View File

@ -579,6 +579,53 @@ int run_renice_tests(struct GameModeConfig *config)
return ret; return ret;
} }
int run_ioprio_tests(struct GameModeConfig *config)
{
/* read configuration "ioprio" */
long int ioprio = config_get_ioprio_value(config);
if (ioprio == IOPRIO_DONT_SET) {
return 1; /* not configured */
}
/* Verify ioprio starts at 0 */
int val = game_mode_get_ioprio(getpid());
if (val != IOPRIO_DEFAULT) {
LOG_ERROR("Initial ioprio value is non-default\nExpected: %d, Was: %d\n",
IOPRIO_DEFAULT,
val);
return -1;
}
int ret = 0;
/* Ask for gamemode for ourselves */
gamemode_request_start();
/* Check renice is now requested value */
val = game_mode_get_ioprio(getpid());
if (val != ioprio) {
LOG_ERROR(
"ioprio value not set correctly after gamemode_request_start\nExpected: %ld, Was: %d\n",
ioprio,
val);
ret = -1;
}
/* End gamemode for ourselves */
gamemode_request_end();
/* Check ioprio is returned to correct value */
val = game_mode_get_ioprio(getpid());
if (val != IOPRIO_DEFAULT) {
LOG_ERROR("ioprio value non-default after gamemode_request_end\nExpected: %d, Was: %d\n",
IOPRIO_DEFAULT,
val);
ret = -1;
}
return ret;
}
/** /**
* game_mode_run_feature_tests runs a set of tests for each current feature (based on the current * game_mode_run_feature_tests runs a set of tests for each current feature (based on the current
* config) returns 0 for success, -1 for failure * config) returns 0 for success, -1 for failure
@ -656,6 +703,22 @@ static int game_mode_run_feature_tests(struct GameModeConfig *config)
} }
} }
/* Was the process ioprio set? */
{
LOG_MSG("::: Verifying ioprio\n");
int iopriostatus = run_ioprio_tests(config);
if (iopriostatus == 1)
LOG_MSG("::: Passed (no ioprio configured)\n");
else if (iopriostatus == 0)
LOG_MSG("::: Passed\n");
else {
LOG_MSG("::: Failed!\n");
// Ioprio should be expected to work, if set
status = 1;
}
}
/* Was the scheduling applied? */ /* Was the scheduling applied? */
/* Were io priorities changed? */ /* Were io priorities changed? */
/* Note: These don't get cleared up on un-register, so will have already been applied */ /* Note: These don't get cleared up on un-register, so will have already been applied */

View File

@ -72,6 +72,7 @@ struct GameModeContext {
struct GameModeGPUInfo *target_gpu; /**<Target GPU info for the current GPU */ struct GameModeGPUInfo *target_gpu; /**<Target GPU info for the current GPU */
int initial_renice; /**<Initial renice value */ int initial_renice; /**<Initial renice value */
int initial_ioprio; /**<Initial ioprio value */
/* Reaper control */ /* Reaper control */
struct { struct {
@ -418,12 +419,13 @@ int game_mode_context_register(GameModeContext *self, pid_t client, pid_t reques
self->initial_renice = game_mode_get_renice(client); self->initial_renice = game_mode_get_renice(client);
game_mode_apply_renice(self, client, 0 /* expect zero value to start with */); game_mode_apply_renice(self, client, 0 /* expect zero value to start with */);
/* Store current ioprio value and apply */
self->initial_ioprio = game_mode_get_ioprio(client);
game_mode_apply_ioprio(self, client, IOPRIO_DEFAULT);
/* Apply scheduler policies */ /* Apply scheduler policies */
game_mode_apply_scheduling(self, client); game_mode_apply_scheduling(self, client);
/* Apply io priorities */
game_mode_apply_ioprio(self, client);
game_mode_client_count_changed(); game_mode_client_count_changed();
return 0; return 0;

View File

@ -122,7 +122,8 @@ char *game_mode_lookup_user_home(void);
* Provides internal API functions specific to adjusting process * Provides internal API functions specific to adjusting process
* IO priorities. * IO priorities.
*/ */
void game_mode_apply_ioprio(const GameModeContext *self, const pid_t client); int game_mode_get_ioprio(const pid_t client);
void game_mode_apply_ioprio(const GameModeContext *self, const pid_t client, int expected);
/** gamemode-proc.c /** gamemode-proc.c
* Provides internal API functions specific to working with process * Provides internal API functions specific to working with process

View File

@ -39,7 +39,7 @@ POSSIBILITY OF SUCH DAMAGE.
/** /**
* Value clamping helper, works like MIN/MAX but constraints a value within the range. * Value clamping helper, works like MIN/MAX but constraints a value within the range.
*/ */
#define CLAMP(lbound, ubound, value) MIN(MIN(lbound, ubound), MAX(MAX(lbound, ubound), value)) #define CLAMP(l, u, value) MAX(MIN(l, u), MIN(MAX(l, u), value))
/** /**
* Little helper to safely print into a buffer, returns a pointer into the buffer * Little helper to safely print into a buffer, returns a pointer into the buffer