Historically we have not distinguished between kernel wirings and user wirings for accounting purposes. User wirings (via mlock(2)) were subject to a global limit on the number of wired pages, so if large swaths of physical memory were wired by the kernel, as happens with the ZFS ARC among other things, the limit could be exceeded, causing user wirings to fail. The change adds a new counter, v_user_wire_count, which counts the number of virtual pages wired by user processes via mlock(2) and mlockall(2). Only user-wired pages are subject to the system-wide limit which helps provide some safety against deadlocks. In particular, while sources of kernel wirings typically support some backpressure mechanism, there is no way to reclaim user-wired pages shorting of killing the wiring process. The limit is exported as vm.max_user_wired, renamed from vm.max_wired, and changed from u_int to u_long. The choice to count virtual user-wired pages rather than physical pages was done for simplicity. There are mechanisms that can cause user-wired mappings to be destroyed while maintaining a wiring of the backing physical page; these make it difficult to accurately track user wirings at the physical page layer. The change also closes some holes which allowed user wirings to succeed even when they would cause the system limit to be exceeded. For instance, mmap() may now fail with ENOMEM in a process that has called mlockall(MCL_FUTURE) if the new mapping would cause the user wiring limit to be exceeded. Note that bhyve -S is subject to the user wiring limit, which defaults to 1/3 of physical RAM. Users that wish to exceed the limit must tune vm.max_user_wired. Reviewed by: kib, ngie (mlock() test changes) Tested by: pho (earlier version) MFC after: 45 days Sponsored by: Netflix Differential Revision: https://reviews.freebsd.org/D19908
115 lines
3.1 KiB
C
115 lines
3.1 KiB
C
/*-
|
|
* Copyright (C) 2016 Bryan Drewery <bdrewery@FreeBSD.org>
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*/
|
|
|
|
/*
|
|
* Helper for mlock(3) to avoid EAGAIN errors
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
__FBSDID("$FreeBSD$");
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/sysctl.h>
|
|
|
|
#include <atf-c.h>
|
|
#include <errno.h>
|
|
#include <limits.h>
|
|
#include <stdio.h>
|
|
|
|
#define VM_MAX_WIRED "vm.max_user_wired"
|
|
|
|
static void
|
|
vm_max_wired_sysctl(u_long *old_value, u_long *new_value)
|
|
{
|
|
size_t old_len;
|
|
size_t new_len = (new_value == NULL ? 0 : sizeof(*new_value));
|
|
|
|
if (old_value == NULL)
|
|
printf("Setting the new value to %lu\n", *new_value);
|
|
else {
|
|
ATF_REQUIRE_MSG(sysctlbyname(VM_MAX_WIRED, NULL, &old_len,
|
|
new_value, new_len) == 0,
|
|
"sysctlbyname(%s) failed: %s", VM_MAX_WIRED, strerror(errno));
|
|
}
|
|
|
|
ATF_REQUIRE_MSG(sysctlbyname(VM_MAX_WIRED, old_value, &old_len,
|
|
new_value, new_len) == 0,
|
|
"sysctlbyname(%s) failed: %s", VM_MAX_WIRED, strerror(errno));
|
|
|
|
if (old_value != NULL)
|
|
printf("Saved the old value (%lu)\n", *old_value);
|
|
}
|
|
|
|
void
|
|
set_vm_max_wired(u_long new_value)
|
|
{
|
|
FILE *fp;
|
|
u_long old_value;
|
|
|
|
fp = fopen(VM_MAX_WIRED, "w");
|
|
if (fp == NULL) {
|
|
atf_tc_skip("could not open %s for writing: %s",
|
|
VM_MAX_WIRED, strerror(errno));
|
|
return;
|
|
}
|
|
|
|
vm_max_wired_sysctl(&old_value, NULL);
|
|
|
|
ATF_REQUIRE_MSG(fprintf(fp, "%lu", old_value) > 0,
|
|
"saving %s failed", VM_MAX_WIRED);
|
|
|
|
fclose(fp);
|
|
|
|
vm_max_wired_sysctl(NULL, &new_value);
|
|
}
|
|
|
|
void
|
|
restore_vm_max_wired(void)
|
|
{
|
|
FILE *fp;
|
|
u_long saved_max_wired;
|
|
|
|
fp = fopen(VM_MAX_WIRED, "r");
|
|
if (fp == NULL) {
|
|
perror("fopen failed\n");
|
|
return;
|
|
}
|
|
|
|
if (fscanf(fp, "%lu", &saved_max_wired) != 1) {
|
|
perror("fscanf failed\n");
|
|
fclose(fp);
|
|
return;
|
|
}
|
|
|
|
fclose(fp);
|
|
printf("old value in %s: %lu\n", VM_MAX_WIRED, saved_max_wired);
|
|
|
|
if (saved_max_wired == 0) /* This will cripple the test host */
|
|
return;
|
|
|
|
vm_max_wired_sysctl(NULL, &saved_max_wired);
|
|
}
|