Use real-time scheduling policy for kwin_wayland

Summary:
The base idea behind this change is to keep the system responsive no
matter what other processes do. All input and rendering needs to go
through the windowing system, so keeping it responsive is important.

Currently KWin competes with all other processes for resources and this
can render the system unusable. Consider some processes running amok. In
this case the user might not be able to easily close the applications as
KWin does not get the cpu time to perform the input tasks requested by
the user.

Or in the case of playing a demanding game it is important that KWin
gets scheduled to forward the pointer input events. The user doesn't
want that the game (or another process) wins against the windowing
sytem.

The disadvantage is that KWin always wins against other processes with
real time scheduling. This could result in KWin running amok stalling
the system. On the other hand this is no change to the current situation
as if KWin runs amok the sytem is unusable.

The change uses libcap to set CAP_SYS_NICE on kwin_wayland executable.
KWin_wayland on start sets the scheduling policy to SCHED_RR with the
lowest possible priority. Thus any other SCHED_RR process will win
against KWin. So real time processes are not affected by this change!

After adjusting the scheduling (which requires CAP_SYS_NICE) KWin drops
this capability again.

Test Plan:
Verified that KWin adjusts the scheduler, that it is not passed
to child processes, that the capability gets dropped and not passed to
child processes.

Reviewers: #kwin, #plasma

Subscribers: plasma-devel, kwin

Tags: #kwin

Differential Revision: https://phabricator.kde.org/D7757
This commit is contained in:
Martin Flöser 2017-09-10 12:05:29 +02:00
parent 2a34fdde4c
commit 7c8003f7f6
4 changed files with 124 additions and 0 deletions

View file

@ -279,6 +279,13 @@ set_package_properties(Xwayland PROPERTIES
PURPOSE "Needed for running kwin_wayland" PURPOSE "Needed for running kwin_wayland"
) )
find_package(Libcap)
set_package_properties(Libcap PROPERTIES
TYPE OPTIONAL
PURPOSE "Needed for running kwin_wayland with real-time scheduling policy"
)
set(HAVE_LIBCAP ${Libcap_FOUND})
########### configure tests ############### ########### configure tests ###############
include(CMakeDependentOption) include(CMakeDependentOption)
@ -339,6 +346,11 @@ add_feature_info("linux/fb.h"
HAVE_LINUX_FB_H HAVE_LINUX_FB_H
"Required for the fbdev backend") "Required for the fbdev backend")
check_symbol_exists(SCHED_RESET_ON_FORK "sched.h" HAVE_SCHED_RESET_ON_FORK)
add_feature_info("SCHED_RESET_ON_FORK"
HAVE_SCHED_RESET_ON_FORK
"Required for running kwin_wayland with real-time scheduling")
########### global ############### ########### global ###############
set(kwin_effects_dbus_xml ${CMAKE_CURRENT_SOURCE_DIR}/org.kde.kwin.Effects.xml) set(kwin_effects_dbus_xml ${CMAKE_CURRENT_SOURCE_DIR}/org.kde.kwin.Effects.xml)
@ -624,8 +636,20 @@ install(TARGETS kwin_x11 ${INSTALL_TARGETS_DEFAULT_ARGS} )
add_executable(kwin_wayland main_wayland.cpp) add_executable(kwin_wayland main_wayland.cpp)
target_link_libraries(kwin_wayland kwin) target_link_libraries(kwin_wayland kwin)
if (HAVE_LIBCAP)
target_link_libraries(kwin_wayland ${Libcap_LIBRARIES})
endif()
install(TARGETS kwin_wayland ${INSTALL_TARGETS_DEFAULT_ARGS} ) install(TARGETS kwin_wayland ${INSTALL_TARGETS_DEFAULT_ARGS} )
if (HAVE_LIBCAP)
install(
CODE "execute_process(
COMMAND
${SETCAP_EXECUTABLE}
CAP_SYS_NICE=+ep
${CMAKE_INSTALL_FULL_BINDIR}/kwin_wayland)"
)
endif()
add_subdirectory(platformsupport) add_subdirectory(platformsupport)
add_subdirectory(plugins) add_subdirectory(plugins)

View file

@ -0,0 +1,59 @@
# Try to find the setcap binary and cap libraries
#
# This will define:
#
# Libcap_FOUND - system has the cap library and setcap binary
# Libcap_LIBRARIES - cap libraries to link against
# SETCAP_EXECUTABLE - path of the setcap binary
# In addition, the following targets are defined:
#
# Libcap::SetCapabilities
#
# Copyright (c) 2014, Hrvoje Senjan, <hrvoje.senjan@gmail.com>
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# 3. The name of the author may not be used to endorse or promote products
# derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
find_program(SETCAP_EXECUTABLE NAMES setcap DOC "The setcap executable")
find_library(Libcap_LIBRARIES NAMES cap DOC "The cap (capabilities) library")
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Libcap FOUND_VAR Libcap_FOUND
REQUIRED_VARS SETCAP_EXECUTABLE Libcap_LIBRARIES)
if(Libcap_FOUND AND NOT TARGET Libcap::SetCapabilities)
add_executable(Libcap::SetCapabilities IMPORTED)
set_target_properties(Libcap::SetCapabilities PROPERTIES
IMPORTED_LOCATION "${SETCAP_EXECUTABLE}"
)
endif()
mark_as_advanced(SETCAP_EXECUTABLE Libcap_LIBRARIES)
include(FeatureSummary)
set_package_properties(Libcap PROPERTIES
URL https://sites.google.com/site/fullycapable/
DESCRIPTION "Capabilities are a measure to limit the omnipotence of the superuser.")

View file

@ -24,6 +24,8 @@
#cmakedefine01 HAVE_SYS_SYSMACROS_H #cmakedefine01 HAVE_SYS_SYSMACROS_H
#cmakedefine01 HAVE_BREEZE_DECO #cmakedefine01 HAVE_BREEZE_DECO
#cmakedefine01 HAVE_UDEV #cmakedefine01 HAVE_UDEV
#cmakedefine01 HAVE_LIBCAP
#cmakedefine01 HAVE_SCHED_RESET_ON_FORK
#if HAVE_BREEZE_DECO #if HAVE_BREEZE_DECO
#define BREEZE_KDECORATION_PLUGIN_ID "${BREEZE_KDECORATION_PLUGIN_ID}" #define BREEZE_KDECORATION_PLUGIN_ID "${BREEZE_KDECORATION_PLUGIN_ID}"
#endif #endif

View file

@ -63,6 +63,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <sys/procctl.h> #include <sys/procctl.h>
#endif #endif
#if HAVE_LIBCAP
#include <sys/capability.h>
#endif
#include <sched.h>
#include <iostream> #include <iostream>
#include <iomanip> #include <iomanip>
@ -438,6 +444,37 @@ static void unsetDumpable(int sig)
return; return;
} }
void gainRealTime()
{
#if HAVE_SCHED_RESET_ON_FORK
const int minPriority = sched_get_priority_min(SCHED_RR);
struct sched_param sp;
sp.sched_priority = minPriority;
sched_setscheduler(0, SCHED_RR | SCHED_RESET_ON_FORK, &sp);
#endif
}
void dropNiceCapability()
{
#if HAVE_LIBCAP
cap_t caps = cap_get_proc();
if (!caps) {
return;
}
cap_value_t capList[] = { CAP_SYS_NICE };
if (cap_set_flag(caps, CAP_PERMITTED, 1, capList, CAP_CLEAR) == -1) {
cap_free(caps);
return;
}
if (cap_set_flag(caps, CAP_EFFECTIVE, 1, capList, CAP_CLEAR) == -1) {
cap_free(caps);
return;
}
cap_set_proc(caps);
cap_free(caps);
#endif
}
} // namespace } // namespace
int main(int argc, char * argv[]) int main(int argc, char * argv[])
@ -445,6 +482,8 @@ int main(int argc, char * argv[])
KWin::disablePtrace(); KWin::disablePtrace();
KWin::Application::setupMalloc(); KWin::Application::setupMalloc();
KWin::Application::setupLocalizedString(); KWin::Application::setupLocalizedString();
KWin::gainRealTime();
KWin::dropNiceCapability();
if (signal(SIGTERM, KWin::sighandler) == SIG_IGN) if (signal(SIGTERM, KWin::sighandler) == SIG_IGN)
signal(SIGTERM, SIG_IGN); signal(SIGTERM, SIG_IGN);