|
Packit |
2d622a |
/*
|
|
Packit |
2d622a |
* libhugetlbfs - Easy use of Linux hugepages
|
|
Packit |
2d622a |
* Copyright (C) 2008 Adam Litke, IBM Corporation.
|
|
Packit |
2d622a |
*
|
|
Packit |
2d622a |
* This library is free software; you can redistribute it and/or
|
|
Packit |
2d622a |
* modify it under the terms of the GNU Lesser General Public License
|
|
Packit |
2d622a |
* as published by the Free Software Foundation; either version 2.1 of
|
|
Packit |
2d622a |
* the License, or (at your option) any later version.
|
|
Packit |
2d622a |
*
|
|
Packit |
2d622a |
* This library is distributed in the hope that it will be useful, but
|
|
Packit |
2d622a |
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
2d622a |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Packit |
2d622a |
* Lesser General Public License for more details.
|
|
Packit |
2d622a |
*
|
|
Packit |
2d622a |
* You should have received a copy of the GNU Lesser General Public
|
|
Packit |
2d622a |
* License along with this library; if not, write to the Free Software
|
|
Packit |
2d622a |
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
Packit |
2d622a |
*/
|
|
Packit |
2d622a |
#define _GNU_SOURCE /* For strchrnul */
|
|
Packit |
2d622a |
|
|
Packit |
2d622a |
#include <stdio.h>
|
|
Packit |
2d622a |
#include <stdlib.h>
|
|
Packit |
2d622a |
#include <string.h>
|
|
Packit |
2d622a |
#include <unistd.h>
|
|
Packit |
2d622a |
#include <errno.h>
|
|
Packit |
2d622a |
#include <string.h>
|
|
Packit |
2d622a |
#include <sys/utsname.h>
|
|
Packit |
2d622a |
#include "kernel-features.h"
|
|
Packit |
2d622a |
#include "hugetlbfs.h"
|
|
Packit |
2d622a |
#include "libhugetlbfs_privutils.h"
|
|
Packit |
2d622a |
#include "libhugetlbfs_internal.h"
|
|
Packit |
2d622a |
#include "libhugetlbfs_debug.h"
|
|
Packit |
2d622a |
|
|
Packit |
2d622a |
static struct kernel_version running_kernel_version;
|
|
Packit |
2d622a |
|
|
Packit |
2d622a |
/* This mask should always be 32 bits, regardless of the platform word size */
|
|
Packit |
2d622a |
static unsigned int feature_mask;
|
|
Packit |
2d622a |
|
|
Packit |
2d622a |
static struct feature kernel_features[] = {
|
|
Packit |
2d622a |
[HUGETLB_FEATURE_PRIVATE_RESV] = {
|
|
Packit |
2d622a |
.name = "private_reservations",
|
|
Packit |
2d622a |
.required_version = "2.6.27-rc1",
|
|
Packit |
2d622a |
},
|
|
Packit |
2d622a |
[HUGETLB_FEATURE_SAFE_NORESERVE] = {
|
|
Packit |
2d622a |
.name = "noreserve_safe",
|
|
Packit |
2d622a |
.required_version = "2.6.34",
|
|
Packit |
2d622a |
},
|
|
Packit |
2d622a |
[HUGETLB_FEATURE_MAP_HUGETLB] = {
|
|
Packit |
2d622a |
.name = "map_hugetlb",
|
|
Packit |
2d622a |
.required_version = "2.6.32",
|
|
Packit |
2d622a |
}
|
|
Packit |
2d622a |
};
|
|
Packit |
2d622a |
|
|
Packit |
2d622a |
static void debug_kernel_version(void)
|
|
Packit |
2d622a |
{
|
|
Packit |
2d622a |
struct kernel_version *ver = &running_kernel_version;
|
|
Packit |
2d622a |
|
|
Packit |
2d622a |
INFO("Parsed kernel version: [%u] . [%u] . [%u] ",
|
|
Packit |
2d622a |
ver->major, ver->minor, ver->release);
|
|
Packit |
2d622a |
if (ver->post)
|
|
Packit |
2d622a |
INFO_CONT(" [post-release: %u]\n", ver->post);
|
|
Packit |
2d622a |
else if (ver->pre)
|
|
Packit |
2d622a |
INFO_CONT(" [pre-release: %u]\n", ver->pre);
|
|
Packit |
2d622a |
else
|
|
Packit |
2d622a |
INFO_CONT("\n");
|
|
Packit |
2d622a |
}
|
|
Packit |
2d622a |
|
|
Packit |
2d622a |
static int str_to_ver(const char *str, struct kernel_version *ver)
|
|
Packit |
2d622a |
{
|
|
Packit |
2d622a |
char *start;
|
|
Packit |
2d622a |
char *end;
|
|
Packit |
2d622a |
|
|
Packit |
2d622a |
/* Clear out version struct */
|
|
Packit |
2d622a |
ver->major = ver->minor = ver->release = ver->post = ver->pre = 0;
|
|
Packit |
2d622a |
|
|
Packit |
2d622a |
/*
|
|
Packit |
2d622a |
* The kernel always starts x.y.z
|
|
Packit |
2d622a |
*
|
|
Packit |
2d622a |
* Note: strtol is used in place of sscanf because when heap override is
|
|
Packit |
2d622a |
* used this step happens before the _morecore replacement and sscanf
|
|
Packit |
2d622a |
* does an internal heap allocation. This mean that the next allocation
|
|
Packit |
2d622a |
* from the heap would be on small pages until the first block allocated
|
|
Packit |
2d622a |
* by _morecore is exhausted
|
|
Packit |
2d622a |
*/
|
|
Packit |
2d622a |
errno = 0;
|
|
Packit |
2d622a |
ver->major = strtol(str, &end, 10);
|
|
Packit |
2d622a |
if (!ver->major && errno == EINVAL) {
|
|
Packit |
2d622a |
ERROR("Unable to determine base kernel version: %s\n",
|
|
Packit |
2d622a |
strerror(errno));
|
|
Packit |
2d622a |
return -1;
|
|
Packit |
2d622a |
}
|
|
Packit |
2d622a |
|
|
Packit |
2d622a |
start = end + 1;
|
|
Packit |
2d622a |
errno = 0;
|
|
Packit |
2d622a |
ver->minor = strtol(start, &end, 10);
|
|
Packit |
2d622a |
if (!ver->minor && errno == EINVAL) {
|
|
Packit |
2d622a |
ERROR("Unable to determine base kernel version: %s\n",
|
|
Packit |
2d622a |
strerror(errno));
|
|
Packit |
2d622a |
return -1;
|
|
Packit |
2d622a |
}
|
|
Packit |
2d622a |
|
|
Packit |
2d622a |
start = end + 1;
|
|
Packit |
2d622a |
errno = 0;
|
|
Packit |
2d622a |
ver->release = strtol(start, &end, 10);
|
|
Packit |
2d622a |
if (!ver->release && errno == EINVAL) {
|
|
Packit |
2d622a |
ERROR("Unable to determine base kernel version: %s\n",
|
|
Packit |
2d622a |
strerror(errno));
|
|
Packit |
2d622a |
return -1;
|
|
Packit |
2d622a |
}
|
|
Packit |
2d622a |
|
|
Packit |
2d622a |
/* Try to match a post/stable version */
|
|
Packit |
2d622a |
start = end + 1;
|
|
Packit |
2d622a |
if (*end == '.') {
|
|
Packit |
2d622a |
ver->post = strtol(start, &end, 10);
|
|
Packit |
2d622a |
if (!ver->post && errno == EINVAL)
|
|
Packit |
2d622a |
return 0;
|
|
Packit |
2d622a |
}
|
|
Packit |
2d622a |
|
|
Packit |
2d622a |
/* Try to match a preN/rcN version */
|
|
Packit |
2d622a |
start = end + 1;
|
|
Packit |
2d622a |
if (*end == '-') {
|
|
Packit |
2d622a |
if (*start == 'r' && *(start + 1) == 'c')
|
|
Packit |
2d622a |
start += 2;
|
|
Packit |
2d622a |
else if (*start == 'p' &&
|
|
Packit |
2d622a |
*(start + 1) == 'r' &&
|
|
Packit |
2d622a |
*(start + 2) == 'e')
|
|
Packit |
2d622a |
start += 3;
|
|
Packit |
2d622a |
else {
|
|
Packit |
2d622a |
/*
|
|
Packit |
2d622a |
* For now we ignore any extraversions besides
|
|
Packit |
2d622a |
* pre and rc versions and treat them as equal
|
|
Packit |
2d622a |
* to the base version.
|
|
Packit |
2d622a |
*/
|
|
Packit |
2d622a |
return 0;
|
|
Packit |
2d622a |
}
|
|
Packit |
2d622a |
|
|
Packit |
2d622a |
ver->pre = strtol(start, &end, 10);
|
|
Packit |
2d622a |
}
|
|
Packit |
2d622a |
|
|
Packit |
2d622a |
return 0;
|
|
Packit |
2d622a |
}
|
|
Packit |
2d622a |
|
|
Packit |
2d622a |
static int int_cmp(int a, int b)
|
|
Packit |
2d622a |
{
|
|
Packit |
2d622a |
if (a < b)
|
|
Packit |
2d622a |
return -1;
|
|
Packit |
2d622a |
if (a > b)
|
|
Packit |
2d622a |
return 1;
|
|
Packit |
2d622a |
else
|
|
Packit |
2d622a |
return 0;
|
|
Packit |
2d622a |
}
|
|
Packit |
2d622a |
|
|
Packit |
2d622a |
/*
|
|
Packit |
2d622a |
* Pre-release kernels have the following compare rules:
|
|
Packit |
2d622a |
* X.Y.(Z - 1) < X.Y.Z-rcN < X.Y.X
|
|
Packit |
2d622a |
* This order can be enforced by simply decrementing the release (for
|
|
Packit |
2d622a |
* comparison purposes) when there is a pre/rc modifier in effect.
|
|
Packit |
2d622a |
*/
|
|
Packit |
2d622a |
static int ver_cmp_release(struct kernel_version *ver)
|
|
Packit |
2d622a |
{
|
|
Packit |
2d622a |
if (ver->pre)
|
|
Packit |
2d622a |
return ver->release - 1;
|
|
Packit |
2d622a |
else
|
|
Packit |
2d622a |
return ver->release;
|
|
Packit |
2d622a |
}
|
|
Packit |
2d622a |
|
|
Packit |
2d622a |
static int ver_cmp(struct kernel_version *a, struct kernel_version *b)
|
|
Packit |
2d622a |
{
|
|
Packit |
2d622a |
int ret, a_release, b_release;
|
|
Packit |
2d622a |
|
|
Packit |
2d622a |
if ((ret = int_cmp(a->major, b->major)) != 0)
|
|
Packit |
2d622a |
return ret;
|
|
Packit |
2d622a |
|
|
Packit |
2d622a |
if ((ret = int_cmp(a->minor, b->minor)) != 0)
|
|
Packit |
2d622a |
return ret;
|
|
Packit |
2d622a |
|
|
Packit |
2d622a |
a_release = ver_cmp_release(a);
|
|
Packit |
2d622a |
b_release = ver_cmp_release(b);
|
|
Packit |
2d622a |
if ((ret = int_cmp(a_release, b_release)) != 0)
|
|
Packit |
2d622a |
return ret;
|
|
Packit |
2d622a |
|
|
Packit |
2d622a |
if ((ret = int_cmp(a->post, b->post)) != 0)
|
|
Packit |
2d622a |
return ret;
|
|
Packit |
2d622a |
|
|
Packit |
2d622a |
if ((ret = int_cmp(a->pre, b->pre)) != 0)
|
|
Packit |
2d622a |
return ret;
|
|
Packit |
2d622a |
|
|
Packit |
2d622a |
/* We ignore forks (such as -mm and -mjb) */
|
|
Packit |
2d622a |
return 0;
|
|
Packit |
2d622a |
}
|
|
Packit |
2d622a |
|
|
Packit |
2d622a |
int test_compare_kver(const char *a, const char *b)
|
|
Packit |
2d622a |
{
|
|
Packit |
2d622a |
struct kernel_version ka, kb;
|
|
Packit |
2d622a |
|
|
Packit |
2d622a |
if (str_to_ver(a, &ka) < 0)
|
|
Packit |
2d622a |
return -EINVAL;
|
|
Packit |
2d622a |
if (str_to_ver(b, &kb) < 0)
|
|
Packit |
2d622a |
return -EINVAL;
|
|
Packit |
2d622a |
return ver_cmp(&ka, &kb;;
|
|
Packit |
2d622a |
}
|
|
Packit |
2d622a |
|
|
Packit |
2d622a |
int hugetlbfs_test_feature(int feature_code)
|
|
Packit |
2d622a |
{
|
|
Packit |
2d622a |
if (feature_code >= HUGETLB_FEATURE_NR) {
|
|
Packit |
2d622a |
ERROR("hugetlbfs_test_feature: invalid feature code\n");
|
|
Packit |
2d622a |
return -EINVAL;
|
|
Packit |
2d622a |
}
|
|
Packit |
2d622a |
return feature_mask & (1 << feature_code);
|
|
Packit |
2d622a |
}
|
|
Packit |
2d622a |
|
|
Packit |
2d622a |
static void print_valid_features(void)
|
|
Packit |
2d622a |
{
|
|
Packit |
2d622a |
int i;
|
|
Packit |
2d622a |
|
|
Packit |
2d622a |
ERROR("HUGETLB_FEATURES=\"<feature>[,<feature>] ...\"\n");
|
|
Packit |
2d622a |
ERROR_CONT("Valid features:\n");
|
|
Packit |
2d622a |
for (i = 0; i < HUGETLB_FEATURE_NR; i++)
|
|
Packit |
2d622a |
ERROR_CONT("\t%s, no_%s\n", kernel_features[i].name,
|
|
Packit |
2d622a |
kernel_features[i].name);
|
|
Packit |
2d622a |
}
|
|
Packit |
2d622a |
|
|
Packit |
2d622a |
static int check_features_env_valid(const char *env)
|
|
Packit |
2d622a |
{
|
|
Packit |
2d622a |
const char *pos = env;
|
|
Packit |
2d622a |
int i;
|
|
Packit |
2d622a |
|
|
Packit |
2d622a |
while (pos && *pos != '\0') {
|
|
Packit |
2d622a |
int match = 0;
|
|
Packit |
2d622a |
char *next;
|
|
Packit |
2d622a |
|
|
Packit |
2d622a |
if (*pos == ',')
|
|
Packit |
2d622a |
pos++;
|
|
Packit |
2d622a |
next = strchrnul(pos, ',');
|
|
Packit |
2d622a |
if (strncmp(pos, "no_", 3) == 0)
|
|
Packit |
2d622a |
pos += 3;
|
|
Packit |
2d622a |
|
|
Packit |
2d622a |
for (i = 0; i < HUGETLB_FEATURE_NR; i++) {
|
|
Packit |
2d622a |
char *name = kernel_features[i].name;
|
|
Packit |
2d622a |
if (strncmp(pos, name, next - pos) == 0) {
|
|
Packit |
2d622a |
match = 1;
|
|
Packit |
2d622a |
break;
|
|
Packit |
2d622a |
}
|
|
Packit |
2d622a |
}
|
|
Packit |
2d622a |
if (!match) {
|
|
Packit |
2d622a |
print_valid_features();
|
|
Packit |
2d622a |
return -1;
|
|
Packit |
2d622a |
}
|
|
Packit |
2d622a |
pos = next;
|
|
Packit |
2d622a |
}
|
|
Packit |
2d622a |
return 0;
|
|
Packit |
2d622a |
}
|
|
Packit |
2d622a |
|
|
Packit |
2d622a |
void setup_features()
|
|
Packit |
2d622a |
{
|
|
Packit |
2d622a |
struct utsname u;
|
|
Packit |
2d622a |
int i;
|
|
Packit |
2d622a |
|
|
Packit |
2d622a |
if (uname(&u)) {
|
|
Packit |
2d622a |
ERROR("Getting kernel version failed: %s\n", strerror(errno));
|
|
Packit |
2d622a |
return;
|
|
Packit |
2d622a |
}
|
|
Packit |
2d622a |
|
|
Packit |
2d622a |
str_to_ver(u.release, &running_kernel_version);
|
|
Packit |
2d622a |
debug_kernel_version();
|
|
Packit |
2d622a |
|
|
Packit |
2d622a |
/* Check if the user has overrided any features */
|
|
Packit |
2d622a |
if (__hugetlb_opts.features &&
|
|
Packit |
2d622a |
check_features_env_valid(__hugetlb_opts.features) == -1) {
|
|
Packit |
2d622a |
ERROR("HUGETLB_FEATURES was invalid -- ignoring.\n");
|
|
Packit |
2d622a |
__hugetlb_opts.features = NULL;
|
|
Packit |
2d622a |
}
|
|
Packit |
2d622a |
|
|
Packit |
2d622a |
for (i = 0; i < HUGETLB_FEATURE_NR; i++) {
|
|
Packit |
2d622a |
struct kernel_version ver;
|
|
Packit |
2d622a |
char *name = kernel_features[i].name;
|
|
Packit |
2d622a |
char *pos;
|
|
Packit |
2d622a |
|
|
Packit |
2d622a |
str_to_ver(kernel_features[i].required_version, &ver);
|
|
Packit |
2d622a |
|
|
Packit |
2d622a |
/* Has the user overridden feature detection? */
|
|
Packit |
2d622a |
if (__hugetlb_opts.features &&
|
|
Packit |
2d622a |
(pos = strstr(__hugetlb_opts.features, name))) {
|
|
Packit |
2d622a |
INFO("Overriding feature %s: ", name);
|
|
Packit |
2d622a |
/* If feature is preceeded by 'no_' then turn it off */
|
|
Packit |
2d622a |
if (((pos - 3) >= __hugetlb_opts.features) &&
|
|
Packit |
2d622a |
!strncmp(pos - 3, "no_", 3))
|
|
Packit |
2d622a |
INFO_CONT("no\n");
|
|
Packit |
2d622a |
else {
|
|
Packit |
2d622a |
INFO_CONT("yes\n");
|
|
Packit |
2d622a |
feature_mask |= (1UL << i);
|
|
Packit |
2d622a |
}
|
|
Packit |
2d622a |
continue;
|
|
Packit |
2d622a |
}
|
|
Packit |
2d622a |
|
|
Packit |
2d622a |
/* Is the running kernel version newer? */
|
|
Packit |
2d622a |
if (ver_cmp(&running_kernel_version, &ver) >= 0) {
|
|
Packit |
2d622a |
INFO("Feature %s is present in this kernel\n",
|
|
Packit |
2d622a |
kernel_features[i].name);
|
|
Packit |
2d622a |
feature_mask |= (1UL << i);
|
|
Packit |
2d622a |
}
|
|
Packit |
2d622a |
}
|
|
Packit |
2d622a |
}
|