OOM Killer value always one less than set

kernelout of memoryproc

I am trying to set the oom_adj value for the out of memory killer, and each time I do (regardless of the process) I get back exactly one less than I set (at least for positive integers. I haven't tried negative integers since I want these processes to be killed by OOM Killer first).

[root@server ~]# echo 10 > /proc/12581/oom_adj
[root@server ~]# cat /proc/12581/oom_adj
9
[root@server ~]# echo 9 > /proc/12581/oom_adj
[root@server ~]# cat /proc/12581/oom_adj
8
[root@server ~]# echo 8 > /proc/12581/oom_adj
[root@server ~]# cat /proc/12581/oom_adj
7
[root@server ~]# echo 7 > /proc/12581/oom_adj
[root@server ~]# cat /proc/12581/oom_adj
6
[root@server ~]# 

Is this expected behavior? If not, why is this happening?

Best Answer

oom_adj is deprecated and provided for legacy purposes only. Internally Linux uses oom_score_adj which has a greater range: oom_adj goes up to 15 while oom_score_adj goes up to 1000.

Whenever you write to oom_adj (let's say 9) the kernel does this:

oom_adj = (oom_adj * OOM_SCORE_ADJ_MAX) / -OOM_DISABLE;

and stores that to oom_score_adj. OOM_SCORE_ADJ_MAX is 1000 and OOM_DISABLE is -17.

So for 9 you'll get oom_adj=(9 * 1000) / 17 ~= 529.411 and since these values are integers, oom_score_adj will hold 529.

Now when you read oom_adj the kernel will do this:

oom_adj = (task->signal->oom_score_adj * -OOM_DISABLE) / OOM_SCORE_ADJ_MAX;

So for 529 you'll get: oom_adj = (529 * 17) / 1000 = 8.993 and since the kernel is using integers and integer arithmetic, this will become 8.

So there... you write 9 and you get 8 because of fixed point / integer arithmetic.