Blame kernel-features.c

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
}