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 <kzak@redhat.com>
This commit is contained in:
Karel Zak 2018-04-16 12:53:39 +02:00
parent 6ceb17c8a7
commit 8fa223daba
7 changed files with 273 additions and 5 deletions

1
.gitignore vendored
View File

@ -71,6 +71,7 @@ ylwrap
/cfdisk
/chcpu
/chfn
/choom
/chmem
/chrt
/chsh

View File

@ -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

29
bash-completion/choom Normal file
View File

@ -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

View File

@ -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])

View File

@ -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

82
sys-utils/choom.1 Normal file
View File

@ -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 <kzak@redhat.com>
.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 .

150
sys-utils/choom.c Normal file
View File

@ -0,0 +1,150 @@
/*
* choom - Change OOM score setting
*
* Copyright (C) 2018 Karel Zak <kzak@redhat.com>
*
* 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <getopt.h>
#include <errno.h>
#include <linux/oom.h>
#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 <num> specify the adjust score value\n"), out);
fputs(_(" -p, --pid <num> 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;
}