vm.overcommit_memory=2の時はvm.admin_reserve_kbytesの値も調整したほうが良いみたい

はじめに

Linuxのカーネルパラメータvm.overcommit_memory=2(メモリのオーバーコミットをさせない設定)の時、vm.overcommit_ratioの値を99とかではなく、カーネルが使うメモリを確保するために80とかにして余裕を持たせておく必要があるとかないとかの話になって、現時点での最新カーネルでは実際どうなっているのか調べてみたので書いておく。

ソースコードリーディング

とりあえずGitHubに上がっているLinuxカーネルのソースコードを読んでみることにした。
この時のLinuxカーネルのバージョンは Linux Kernel 4.16 RC のものだったと思う。

overcommit_memoryとかでgrepしながら追ってみると該当部分の処理はlinux/mm/util.cにあるっぽい。
以下の__vm_enough_memory()というところ。
コメントの記載をみると、新しいプロセスの割り当て時に十分なメモリがあるかどうかチェックしているようだ。

/*
 * Check that a process has enough memory to allocate a new virtual
 * mapping. 0 means there is enough memory for the allocation to
 * succeed and -ENOMEM implies there is not.
 *
 * We currently support three overcommit policies, which are set via the
 * vm.overcommit_memory sysctl.  See Documentation/vm/overcommit-accounting
 *
 * Strict overcommit modes added 2002 Feb 26 by Alan Cox.
 * Additional code 2002 Jul 20 by Robert Love.
 *
 * cap_sys_admin is 1 if the process has admin privileges, 0 otherwise.
 *
 * Note this is a helper function intended to be used by LSMs which
 * wish to use this logic.
 */
int __vm_enough_memory(struct mm_struct *mm, long pages, int cap_sys_admin)
{
        :
	if (sysctl_overcommit_memory == OVERCOMMIT_ALWAYS)
		return 0;

	if (sysctl_overcommit_memory == OVERCOMMIT_GUESS) {
        :
		/*
		 * Reserve some for root
		 */
		if (!cap_sys_admin)
			free -= sysctl_admin_reserve_kbytes >> (PAGE_SHIFT - 10);

		if (free > pages)
			return 0;

		goto error;
	}

	allowed = vm_commit_limit();
	/*
	 * Reserve some for root
	 */
	if (!cap_sys_admin)
		allowed -= sysctl_admin_reserve_kbytes >> (PAGE_SHIFT - 10);
        :
}

OVERCOMMIT_NEVER(vm.overcommit_memory=2つまりオーバーコミットしない)の場合は同一ファイル内のvm_commit_limit()が呼び出されている。

/*
 * Committed memory limit enforced when OVERCOMMIT_NEVER policy is used
 */
unsigned long vm_commit_limit(void)
{
	unsigned long allowed;

	if (sysctl_overcommit_kbytes)
		allowed = sysctl_overcommit_kbytes >> (PAGE_SHIFT - 10);
	else
		allowed = ((totalram_pages - hugetlb_total_pages())
			   * sysctl_overcommit_ratio / 100);
	allowed += total_swap_pages;

	return allowed;
}

vm.overcommit_memoryの値を見てovercommit可能なら0を、できないようなら-ENOMEMを返すようだ。

overcommitができない場合というのは、設定で許可していない場合、またはovercommit可能かどうか計算した結果NGだった場合、のいずれかの模様。

ちなみにsecurity/security.c内のsecurity_vm_enough_memory_mm()でチェックされて呼ばれているみたい。

int security_vm_enough_memory_mm(struct mm_struct *mm, long pages)
{
	struct security_hook_list *hp;
	int cap_sys_admin = 1;
	int rc;

	/*
	 * The module will respond with a positive value if
	 * it thinks the __vm_enough_memory() call should be
	 * made with the cap_sys_admin set. If all of the modules
	 * agree that it should be set it will. If any module
	 * thinks it should not be set it won't.
	 */
	hlist_for_each_entry(hp, &security_hook_heads.vm_enough_memory, list) {
		rc = hp->hook.vm_enough_memory(mm, pages);
		if (rc <= 0) {
			cap_sys_admin = 0;
			break;
		}
	}
	return __vm_enough_memory(mm, pages, cap_sys_admin);
}

overcommitが何等かで制御されるケース、つまりvm.overcommit_memory=0 or vm.overcommit_memory=2の場合は、if(!cap_sys_admin)でプロセスに管理者権限があるかどうかチェックされており、cap_sys_adminが0の時、つまり管理者権限がない時にはadmin_reserve_kbytes分のメモリが確保されるようになっているっぽい。
(vm.overcommit_memory=0のときは free として、vm.overcommit_memory=2のときは予約ページとして確保されている?)

admin_reserve_kbytesはドキュメント(/Documentation/sysctl/vm.txt)に記載があった。
vm.overcommit_memory=2とした時、何かあった場合に管理者がログインしてプロセスを強制終了したりできるようにするためには、sshdまたはlogin + bashあたりが実行できるサイズを確保できるよう十分な値に調整する必要がある値のようだ。

admin_reserve_kbytes

The amount of free memory in the system that should be reserved for users
with the capability cap_sys_admin.

admin_reserve_kbytes defaults to min(3% of free pages, 8MB)

That should provide enough for the admin to log in and kill a process,
if necessary, under the default overcommit 'guess' mode.

Systems running under overcommit 'never' should increase this to account
for the full Virtual Memory Size of programs used to recover. Otherwise,
root may not be able to log in to recover the system.

How do you calculate a minimum useful reserve?

sshd or login + bash (or some other shell) + top (or ps, kill, etc.)

For overcommit 'guess', we can sum resident set sizes (RSS).
On x86_64 this is about 8MB.

For overcommit 'never', we can take the max of their virtual sizes (VSZ)
and add the sum of their RSS.
On x86_64 this is about 128MB.

Changing this takes effect whenever an application requests memory.

ちなみにUbuntu16.04で見ると、デフォルト値のパラメータはvm.overcommit_memory=0vm.admin_reserve_kbytes=8192でドキュメントの通りだった。

vm.admin_reserve_kbytes = 8192
vm.overcommit_kbytes = 0
vm.overcommit_memory = 0
vm.overcommit_ratio = 50

おわりに

vm.overcommit_memoryの値における挙動についてソースコードを読みながら調べてみた。
vm.overcommit_ratioはカーネル空間、ユーザ空間を分けて考えているわけでななさそうなので、vm.overcommit_memory=2の時はvm.overcommit_ratio99くらいにしておけば良さそう。
ただ、ドキュメントによると、オーバーコミットできないときにメモリ不足等になった場合に備えるにはvm.admin_reserve_kbytesの値を、sshdやbashが実行できる程度のメモリを確保できるように確認して必要に応じて調整しておく必要がありそうだ。

参考

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です