From: Rémi Bernon Subject: [PATCH v2 1/2] wineserver: Implement thread priorities with sched_setscheduler / setpriority Message-Id: <20190708134652.11299-1-rbernon@codeweavers.com> Date: Mon, 8 Jul 2019 15:46:51 +0200 This does not report permission errors in order to avoid any breaking change, only the parameter checks that were already there are returning errors. Signed-off-by: Rémi Bernon --- configure.ac | 19 ++++++ include/config.h.in | 6 ++ server/process.c | 20 +++++- server/thread.c | 152 ++++++++++++++++++++++++++++++++++++++++---- server/thread.h | 1 + 5 files changed, 184 insertions(+), 14 deletions(-) diff --git a/configure.ac b/configure.ac index a7c45ace738..4eca35b06f5 100644 --- a/configure.ac +++ b/configure.ac @@ -2234,6 +2234,25 @@ then AC_DEFINE(HAVE_SCHED_SETAFFINITY, 1, [Define to 1 if you have the `sched_setaffinity' function.]) fi +AC_CACHE_CHECK([for sched_setscheduler],wine_cv_have_sched_setscheduler, + AC_LINK_IFELSE([AC_LANG_PROGRAM( +[[#define _GNU_SOURCE +#include ]], [[sched_setscheduler(0, 0, 0);]])],[wine_cv_have_sched_setscheduler=yes],[wine_cv_have_sched_setscheduler=no])) +if test "$wine_cv_have_sched_setscheduler" = "yes" +then + AC_DEFINE(HAVE_SCHED_SETSCHEDULER, 1, [Define to 1 if you have the `sched_setscheduler' function.]) +fi + +AC_CACHE_CHECK([for setpriority],wine_cv_have_setpriority, + AC_LINK_IFELSE([AC_LANG_PROGRAM( +[[#define _GNU_SOURCE +#include +#include ]], [[setpriority(0, 0, 0);]])],[wine_cv_have_setpriority=yes],[wine_cv_have_setpriority=no])) +if test "$wine_cv_have_setpriority" = "yes" +then + AC_DEFINE(HAVE_SETPRIORITY, 1, [Define to 1 if you have the `setpriority' function.]) +fi + AC_CACHE_CHECK([for fallocate],wine_cv_have_fallocate, AC_LINK_IFELSE([AC_LANG_PROGRAM( [[#define _GNU_SOURCE diff --git a/include/config.h.in b/include/config.h.in index ce5c1570541..1acc02b7173 100644 --- a/include/config.h.in +++ b/include/config.h.in @@ -788,6 +788,9 @@ /* Define to 1 if you have the `sched_setaffinity' function. */ #undef HAVE_SCHED_SETAFFINITY +/* Define to 1 if you have the `sched_setscheduler' function. */ +#undef HAVE_SCHED_SETSCHEDULER + /* Define to 1 if you have the `sched_yield' function. */ #undef HAVE_SCHED_YIELD @@ -812,6 +815,9 @@ /* Define to 1 if you have the `select' function. */ #undef HAVE_SELECT +/* Define to 1 if you have the `setpriority' function. */ +#undef HAVE_SETPRIORITY + /* Define to 1 if you have the `setproctitle' function. */ #undef HAVE_SETPROCTITLE diff --git a/server/process.c b/server/process.c index b67bd88da47..bf216a76f78 100644 --- a/server/process.c +++ b/server/process.c @@ -1464,6 +1464,24 @@ DECL_HANDLER(get_process_vm_counters) release_object( process ); } +static void set_process_priority( struct process *process, int priority ) +{ + struct thread *thread; + + if (!process->running_threads) + { + set_error( STATUS_PROCESS_IS_TERMINATING ); + return; + } + + LIST_FOR_EACH_ENTRY( thread, &process->thread_list, struct thread, proc_entry ) + { + set_thread_priority( thread, priority, thread->priority ); + } + + process->priority = priority; +} + static void set_process_affinity( struct process *process, affinity_t affinity ) { struct thread *thread; @@ -1489,7 +1507,7 @@ DECL_HANDLER(set_process_info) if ((process = get_process_from_handle( req->handle, PROCESS_SET_INFORMATION ))) { - if (req->mask & SET_PROCESS_INFO_PRIORITY) process->priority = req->priority; + if (req->mask & SET_PROCESS_INFO_PRIORITY) set_process_priority( process, req->priority ); if (req->mask & SET_PROCESS_INFO_AFFINITY) set_process_affinity( process, req->affinity ); release_object( process ); } diff --git a/server/thread.c b/server/thread.c index d5742a1bf96..1b8819bf93f 100644 --- a/server/thread.c +++ b/server/thread.c @@ -38,6 +38,12 @@ #ifdef HAVE_SCHED_H #include #endif +#ifdef HAVE_SYS_TIME_H +#include +#endif +#ifdef HAVE_SYS_RESOURCE_H +#include +#endif #include "ntstatus.h" #define WIN32_NO_STATUS @@ -516,28 +522,147 @@ affinity_t get_thread_affinity( struct thread *thread ) return mask; } +#if defined(HAVE_SCHED_SETSCHEDULER) || defined(HAVE_SETPRIORITY) +static int get_unix_priority( int priority_class, int priority ) +{ + switch (priority_class) { + case PROCESS_PRIOCLASS_IDLE: + switch (priority) { + case THREAD_PRIORITY_IDLE: return 15; + case THREAD_PRIORITY_LOWEST: return 10; + case THREAD_PRIORITY_BELOW_NORMAL: return 8; + case THREAD_PRIORITY_NORMAL: return 6; + case THREAD_PRIORITY_ABOVE_NORMAL: return 4; + case THREAD_PRIORITY_HIGHEST: return 2; + case THREAD_PRIORITY_TIME_CRITICAL: return -15; + } + case PROCESS_PRIOCLASS_BELOW_NORMAL: + switch (priority) { + case THREAD_PRIORITY_IDLE: return 15; + case THREAD_PRIORITY_LOWEST: return 8; + case THREAD_PRIORITY_BELOW_NORMAL: return 6; + case THREAD_PRIORITY_NORMAL: return 4; + case THREAD_PRIORITY_ABOVE_NORMAL: return 2; + case THREAD_PRIORITY_HIGHEST: return 0; + case THREAD_PRIORITY_TIME_CRITICAL: return -15; + } + case PROCESS_PRIOCLASS_NORMAL: + switch (priority) { + case THREAD_PRIORITY_IDLE: return 15; + case THREAD_PRIORITY_LOWEST: return 4; + case THREAD_PRIORITY_BELOW_NORMAL: return 2; + case THREAD_PRIORITY_NORMAL: return 0; + case THREAD_PRIORITY_ABOVE_NORMAL: return -2; + case THREAD_PRIORITY_HIGHEST: return -4; + case THREAD_PRIORITY_TIME_CRITICAL: return -15; + } + case PROCESS_PRIOCLASS_ABOVE_NORMAL: + switch (priority) { + case THREAD_PRIORITY_IDLE: return 15; + case THREAD_PRIORITY_LOWEST: return 0; + case THREAD_PRIORITY_BELOW_NORMAL: return -2; + case THREAD_PRIORITY_NORMAL: return -4; + case THREAD_PRIORITY_ABOVE_NORMAL: return -6; + case THREAD_PRIORITY_HIGHEST: return -8; + case THREAD_PRIORITY_TIME_CRITICAL: return -15; + } + case PROCESS_PRIOCLASS_HIGH: + switch (priority) { + case THREAD_PRIORITY_IDLE: return 15; + case THREAD_PRIORITY_LOWEST: return -2; + case THREAD_PRIORITY_BELOW_NORMAL: return -4; + case THREAD_PRIORITY_NORMAL: return -6; + case THREAD_PRIORITY_ABOVE_NORMAL: return -8; + case THREAD_PRIORITY_HIGHEST: return -10; + case THREAD_PRIORITY_TIME_CRITICAL: return -15; + } + case PROCESS_PRIOCLASS_REALTIME: + switch (priority) { + case THREAD_PRIORITY_IDLE: return 1; + case -7: + case -6: + case -5: + case -4: + case -3: + case THREAD_PRIORITY_LOWEST: + case THREAD_PRIORITY_BELOW_NORMAL: + case THREAD_PRIORITY_NORMAL: + case THREAD_PRIORITY_ABOVE_NORMAL: + case THREAD_PRIORITY_HIGHEST: + case 3: + case 4: + case 5: + case 6: + return priority + 9; + case THREAD_PRIORITY_TIME_CRITICAL: + return 16; + } + } + return 0; +} +#endif + #define THREAD_PRIORITY_REALTIME_HIGHEST 6 #define THREAD_PRIORITY_REALTIME_LOWEST -7 +int set_thread_priority( struct thread* thread, int priority_class, int priority ) +{ + int max = THREAD_PRIORITY_HIGHEST; + int min = THREAD_PRIORITY_LOWEST; + if (priority_class == PROCESS_PRIOCLASS_REALTIME) + { + max = THREAD_PRIORITY_REALTIME_HIGHEST; + min = THREAD_PRIORITY_REALTIME_LOWEST; + } + + if ((priority < min || priority > max) && + priority != THREAD_PRIORITY_IDLE && + priority != THREAD_PRIORITY_TIME_CRITICAL) + { + errno = EINVAL; + return -1; + } + + if (thread->process->priority == priority_class && + thread->priority == priority) + return 0; + + thread->priority = priority; + if (thread->unix_tid == -1) + return 0; + + if (priority_class == PROCESS_PRIOCLASS_REALTIME) + { +#ifdef HAVE_SCHED_SETSCHEDULER + struct sched_param param; + if (sched_getparam( thread->unix_tid, ¶m ) != 0) + return 0; /* ignore errors for now */ + + param.sched_priority = get_unix_priority( priority_class, priority ); + if (sched_setscheduler( thread->unix_tid, SCHED_RR|SCHED_RESET_ON_FORK, ¶m ) == 0) + return 0; +#endif + } + else + { +#ifdef HAVE_SETPRIORITY + if (setpriority( PRIO_PROCESS, thread->unix_tid, + get_unix_priority( priority_class, priority ) ) == 0) + return 0; +#endif + } + + return 0; /* ignore errors for now */ +} + /* set all information about a thread */ static void set_thread_info( struct thread *thread, const struct set_thread_info_request *req ) { if (req->mask & SET_THREAD_INFO_PRIORITY) { - int max = THREAD_PRIORITY_HIGHEST; - int min = THREAD_PRIORITY_LOWEST; - if (thread->process->priority == PROCESS_PRIOCLASS_REALTIME) - { - max = THREAD_PRIORITY_REALTIME_HIGHEST; - min = THREAD_PRIORITY_REALTIME_LOWEST; - } - if ((req->priority >= min && req->priority <= max) || - req->priority == THREAD_PRIORITY_IDLE || - req->priority == THREAD_PRIORITY_TIME_CRITICAL) - thread->priority = req->priority; - else - set_error( STATUS_INVALID_PARAMETER ); + if (set_thread_priority( thread, thread->process->priority, req->priority )) + file_set_error(); } if (req->mask & SET_THREAD_INFO_AFFINITY) { @@ -1368,6 +1493,7 @@ DECL_HANDLER(init_thread) process->unix_pid = -1; /* can happen with linuxthreads */ init_thread_context( current ); generate_debug_event( current, CREATE_THREAD_DEBUG_EVENT, &req->entry ); + set_thread_priority( current, current->process->priority, current->priority ); set_thread_affinity( current, current->affinity ); } debug_level = max( debug_level, req->debug_level ); diff --git a/server/thread.h b/server/thread.h index 40f0eecbf1b..5e58a1bc683 100644 --- a/server/thread.h +++ b/server/thread.h @@ -126,6 +126,7 @@ extern int thread_add_inflight_fd( struct thread *thread, int client, int server extern int thread_get_inflight_fd( struct thread *thread, int client ); extern struct thread_snapshot *thread_snap( int *count ); extern struct token *thread_get_impersonation_token( struct thread *thread ); +extern int set_thread_priority( struct thread *thread, int priority_class, int priority ); extern int set_thread_affinity( struct thread *thread, affinity_t affinity ); extern int is_cpu_supported( enum cpu_type cpu ); extern unsigned int get_supported_cpu_mask(void); -- 2.20.1