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

はじめに

vm.overcommit_memory=2の時にカーネルが使うメモリが枯渇しないようにvm.overcommit_ratioの値を調整しておく必要があるとかないとかの話があったので、現時点での最新カーネルでは実際どうなっているのか調べてみた。

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

とりあえずGitHubに上がっているLinuxカーネルのソースコードを読んでみることにした。
この時のKernelバージョンは 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_memory=2の時はvm.overcommit_ratioを99とかに調整するが、ドキュメントをみるとより安全な状態にしておくためにはvm.admin_reserve_kbytesの値も調整が必要そうだ。

参考

このエントリーをはてなブックマークに追加

コメントを残す

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