From 8fa223daba1963c34cc828075ce6773ff01fafe3 Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Mon, 16 Apr 2018 12:53:39 +0200 Subject: choom: new command to adjust OOM-killer score value Let's provide command line tool, man page with OOM description and bash-completion. It seems better than force end-users to use "echo" to /proc. Addresses: https://github.com/karelzak/util-linux/issues/609 Signed-off-by: Karel Zak --- .gitignore | 1 + Documentation/TODO | 5 -- bash-completion/choom | 29 ++++++++++ configure.ac | 4 ++ sys-utils/Makemodule.am | 7 +++ sys-utils/choom.1 | 82 ++++++++++++++++++++++++++ sys-utils/choom.c | 150 ++++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 273 insertions(+), 5 deletions(-) create mode 100644 bash-completion/choom create mode 100644 sys-utils/choom.1 create mode 100644 sys-utils/choom.c diff --git a/.gitignore b/.gitignore index a0772d177..9f2fb17db 100644 --- a/.gitignore +++ b/.gitignore @@ -71,6 +71,7 @@ ylwrap /cfdisk /chcpu /chfn +/choom /chmem /chrt /chsh diff --git a/Documentation/TODO b/Documentation/TODO index 43c4f50d0..101e55c83 100644 --- a/Documentation/TODO +++ b/Documentation/TODO @@ -8,11 +8,6 @@ cal - support another --reforms, see for example freebsd version https://github.com/freebsd/freebsd/blob/master/usr.bin/ncal/ncal.c#L72 -choom ------ - - add new tool to set/get process OOM setting - https://github.com/karelzak/util-linux/issues/609 - column ------ - add option to NOT ignore empty lines diff --git a/bash-completion/choom b/bash-completion/choom new file mode 100644 index 000000000..7e16df17a --- /dev/null +++ b/bash-completion/choom @@ -0,0 +1,29 @@ +_choom_module() +{ + local cur prev OPTS + COMPREPLY=() + cur="${COMP_WORDS[COMP_CWORD]}" + prev="${COMP_WORDS[COMP_CWORD-1]}" + case $prev in + '-n'|'--adjust') + COMPREPLY=( $(compgen -W "{-1000..1000}" -- $cur) ) + return 0 + ;; + '-p'|'--pid') + local PIDS + PIDS=$(cd /proc && echo [0-9]*) + COMPREPLY=( $(compgen -W "$PIDS" -- $cur) ) + return 0 + ;; + '-h'|'--help'|'-V'|'--version') + return 0 + ;; + esac + OPTS="--adjust + --pid + --help + --version" + COMPREPLY=( $(compgen -W "${OPTS[*]}" -- $cur) ) + return 0 +} +complete -F _choom_module choom diff --git a/configure.ac b/configure.ac index 3ad35abec..8dbb062ac 100644 --- a/configure.ac +++ b/configure.ac @@ -1664,6 +1664,10 @@ AC_ARG_ENABLE([ipcs], UL_BUILD_INIT([ipcs]) AM_CONDITIONAL([BUILD_IPCS], [test "x$build_ipcs" = xyes]) +UL_BUILD_INIT([choom], [check]) +UL_REQUIRES_LINUX([choom]) +AM_CONDITIONAL([BUILD_CHOOM], [test "x$build_choom" = xyes]) + UL_BUILD_INIT([lsipc], [check]) UL_REQUIRES_LINUX([lsipc]) UL_REQUIRES_BUILD([lsipc], [libsmartcols]) diff --git a/sys-utils/Makemodule.am b/sys-utils/Makemodule.am index c2e99ca2d..db76e2621 100644 --- a/sys-utils/Makemodule.am +++ b/sys-utils/Makemodule.am @@ -20,6 +20,13 @@ flock_SOURCES = sys-utils/flock.c lib/monotonic.c lib/timer.c flock_LDADD = $(LDADD) libcommon.la $(REALTIME_LIBS) endif +#if BUILD_CHOOM +usrbin_exec_PROGRAMS += choom +dist_man_MANS += sys-utils/choom.1 +choom_SOURCES = sys-utils/choom.c +choom_LDADD = $(LDADD) libcommon.la +#endif + if BUILD_IPCMK usrbin_exec_PROGRAMS += ipcmk dist_man_MANS += sys-utils/ipcmk.1 diff --git a/sys-utils/choom.1 b/sys-utils/choom.1 new file mode 100644 index 000000000..68496d112 --- /dev/null +++ b/sys-utils/choom.1 @@ -0,0 +1,82 @@ +.TH CHOOM 1 "April 2018" "util-linux" "User Commands" +.SH NAME +choom \- display and adjust OOM-killer score. +.SH SYNOPSIS +.B choom +.RB \-p +.IR pid +.sp +.B choom +.RB \-p +.IR pid +.RB \-n +.IR number +.sp +.B choom +.RB \-n +.IR number +.IR command\ [ argument ...] + +.SH DESCRIPTION +The \fBchoom\fP command displays and adjusts Out-Of-Memory killer score setting. + +.SH OPTIONS +.TP +.BR \-p ", " \-\-pid " \fIpid\fP +Specifies process ID. +.TP +.BR \-n , " \-\-adjust " \fIvalue\fP +Specify the adjust score value. +.TP +.BR \-h ", " \-\-help +Display help text and exit. +.TP +.BR \-V ", " \-\-version +Display version information and exit. +.SH NOTES +Linux kernel uses the badness heuristic to select which process gets killed in +out of memory conditions. + +The badness heuristic assigns a value to each candidate task ranging from 0 +(never kill) to 1000 (always kill) to determine which process is targeted. The +units are roughly a proportion along that range of allowed memory the process +may allocate from based on an estimation of its current memory and swap use. +For example, if a task is using all allowed memory, its badness score will be +1000. If it is using half of its allowed memory, its score will be 500. + +There is an additional factor included in the badness score: the current memory +and swap usage is discounted by 3% for root processes. + +The amount of "allowed" memory depends on the context in which the oom killer +was called. If it is due to the memory assigned to the allocating task's cpuset +being exhausted, the allowed memory represents the set of mems assigned to that +cpuset. If it is due to a mempolicy's node(s) being exhausted, the allowed +memory represents the set of mempolicy nodes. If it is due to a memory +limit (or swap limit) being reached, the allowed memory is that configured +limit. Finally, if it is due to the entire system being out of memory, the +allowed memory represents all allocatable resources. + +The adjust score value is added to the badness score before it is used to +determine which task to kill. Acceptable values range from -1000 to +1000. +This allows userspace to polarize the preference for oom killing either by +always preferring a certain task or completely disabling it. The lowest +possible value, -1000, is equivalent to disabling oom killing entirely for that +task since it will always report a badness score of 0. + +Setting a adjust score value of +500, for example, is roughly equivalent to +allowing the remainder of tasks sharing the same system, cpuset, mempolicy, or +memory controller resources to use at least 50% more memory. A value of -500, +on the other hand, would be roughly equivalent to discounting 50% of the task's +allowed memory from being considered as scoring against the task. + +.SH AUTHORS +.nf +Karel Zak +.fi +.SH SEE ALSO +.BR proc (5) +.SH AVAILABILITY +The \fBchoom\fP command is part of the util-linux package and is available from +.UR https://\:www.kernel.org\:/pub\:/linux\:/utils\:/util-linux/ +Linux Kernel Archive +.UE . diff --git a/sys-utils/choom.c b/sys-utils/choom.c new file mode 100644 index 000000000..080267f91 --- /dev/null +++ b/sys-utils/choom.c @@ -0,0 +1,150 @@ +/* + * choom - Change OOM score setting + * + * Copyright (C) 2018 Karel Zak + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it would 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 this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#include +#include +#include +#include +#include +#include + +#include + +#include "nls.h" +#include "c.h" +#include "path.h" +#include "strutils.h" +#include "closestream.h" + +static void __attribute__((__noreturn__)) usage(void) +{ + FILE *out = stdout; + fputs(USAGE_HEADER, out); + fprintf(out, + _(" %1$s [options] -p pid\n" + " %1$s [options] -n number -p pid\n" + " %1$s [options] -n number command [args...]]\n"), + program_invocation_short_name); + + fputs(USAGE_SEPARATOR, out); + fputs(_("Display and adjust OOM-killer score.\n"), out); + + fputs(USAGE_OPTIONS, out); + fputs(_(" -n, --adjust specify the adjust score value\n"), out); + fputs(_(" -p, --pid process ID\n"), out); + fputs(USAGE_SEPARATOR, out); + printf(USAGE_HELP_OPTIONS(24)); + printf(USAGE_MAN_TAIL("choom(1)")); + exit(EXIT_SUCCESS); +} + +static int get_score(const pid_t pid) +{ + return path_read_s32("/proc/%d/oom_score", (int) pid); +} + +static int get_score_adj(const pid_t pid) +{ + return path_read_s32("/proc/%d/oom_score_adj", (int) pid); +} + +static int set_score_adj(const pid_t pid, int adj) +{ + char buf[sizeof(stringify_value(OOM_SCORE_ADJ_MIN))]; + + snprintf(buf, sizeof(buf), "%d", adj); + + if (path_write_str(buf, "/proc/%d/oom_score_adj", (int) pid) < 0) + return -1; + return 0; +} + +int main(int argc, char **argv) +{ + pid_t pid = 0; + int c, adj = 0, has_adj = 0; + + static const struct option longopts[] = { + { "adjust", required_argument, NULL, 'n' }, + { "pid", required_argument, NULL, 'p' }, + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'V' }, + { NULL, 0, NULL, 0 } + }; + + setlocale(LC_ALL, ""); + bindtextdomain(PACKAGE, LOCALEDIR); + textdomain(PACKAGE); + atexit(close_stdout); + + while ((c = getopt_long(argc, argv, "hn:p:V", longopts, NULL)) != -1) { + switch (c) { + case 'p': + pid = strtos32_or_err(optarg, _("invalid PID argument")); + break; + case 'n': + adj = strtos32_or_err(optarg, _("invalid adjust argument")); + has_adj = 1; + break; + case 'V': + printf(UTIL_LINUX_VERSION); + return EXIT_SUCCESS; + case 'h': + usage(); + default: + errtryhelp(EXIT_FAILURE); + } + } + + if (optind < argc && pid) { + warnx(_("invalid argument: %s"), argv[optind]); + errtryhelp(EXIT_FAILURE); + } + if (!pid && argc - optind < 1) { + warnx(_("no PID or COMMAND specified")); + errtryhelp(EXIT_FAILURE); + } + if (optind < argc && !has_adj) { + warnx(_("no OOM score adjust value specified")); + errtryhelp(EXIT_FAILURE); + } + + /* Show */ + if (!has_adj) { + printf(_("pid %d's current OOM score: %d\n"), pid, get_score(pid)); + printf(_("pid %d's current OOM score adjust value: %d\n"), pid, get_score_adj(pid)); + + /* Change */ + } else if (pid) { + int old = get_score_adj(pid); + + if (set_score_adj(pid, adj)) + err(EXIT_FAILURE, _("failed to set score adjust value")); + + printf(_("pid %d's OOM score adjust value changed from %d to %d\n"), pid, old, adj); + + /* Start new process */ + } else { + argv += optind; + execvp(argv[0], argv); + errexec(argv[0]); + } + + return EXIT_SUCCESS; +} -- cgit v1.2.3-55-g7522