From: Adam Litke <[EMAIL PROTECTED]> Now that libhugetlbfs supports multiple huge page sizes, it would be nice to have a way to discover the huge page sizes that are supported by the system. This new call should adhere to the semantics established by precedent implementations (see http://docs.sun.com/app/docs/doc/816-5168/getpagesizes-3c?a=view). The following patch implements a gethugepagesizes() call with these prevailing semantics as described below:
NAME gethugepagesizes() - Get the system supported huge page sizes SYNOPSIS #include <hugetlbfs.h> int gethugepagesizes(long pagesizes[], int n_elem) DESCRIPTION The gethugepagesizes() function returns either the number of system supported huge page sizes or the sizes themselves. If pagesizes is NULL and n_elem is 0, then the number of huge pages the system supports is returned. Otherwise, pagesizes is filled with at most n_elem page sizes. RETURN VALUE On success, either the number of huge page sizes supported by the sys- tem or the number of huge page sizes stored in pagesizes is returned. On failure, -1 is returned and errno is set appropriately. ERRORS EINVAL n_elem is less than zero or n_elem is greater than zero and pagesizes is NULL. Also see opendir(3) for other possible values for errno. Signed-off-by: Adam Litke <[EMAIL PROTECTED]> Acked-by: Mel Gorman <[EMAIL PROTECTED]> Signed-off-by: Andy Whitcroft <[EMAIL PROTECTED]> --- hugetlbfs.h | 1 + hugeutils.c | 65 +++++++++++++ tests/Makefile | 3 +- tests/gethugepagesizes.c | 226 ++++++++++++++++++++++++++++++++++++++++++++++ tests/run_tests.sh | 1 + 5 files changed, 295 insertions(+), 1 deletions(-) create mode 100644 tests/gethugepagesizes.c diff --git a/hugetlbfs.h b/hugetlbfs.h index fbff43b..985e138 100644 --- a/hugetlbfs.h +++ b/hugetlbfs.h @@ -22,6 +22,7 @@ #define HUGETLBFS_MAGIC 0x958458f6 long gethugepagesize(void); +int gethugepagesizes(long pagesizes[], int n_elem); int hugetlbfs_test_path(const char *mount); long hugetlbfs_test_pagesize(const char *mount); const char *hugetlbfs_find_path(void); diff --git a/hugeutils.c b/hugeutils.c index 3c78788..f30063d 100644 --- a/hugeutils.c +++ b/hugeutils.c @@ -561,6 +561,71 @@ long gethugepagesize(void) return hpage_size; } +int gethugepagesizes(long pagesizes[], int n_elem) +{ + long default_size; + DIR *sysfs; + struct dirent *ent; + int nr_sizes = 0; + + if (n_elem < 0) { + errno = EINVAL; + return -1; + } + + if (n_elem > 0 && pagesizes == NULL) { + errno = EINVAL; + return -1; + } + + errno = 0; + + /* Get the system default size from /proc/meminfo */ + default_size = read_meminfo("Hugepagesize:") * 1024; + if (default_size < 0) + return 0; + + if (n_elem && pagesizes) + pagesizes[nr_sizes] = default_size; + nr_sizes++; + if (n_elem && (nr_sizes == n_elem)) + return nr_sizes; + + /* + * Scan sysfs to look for other sizes. + * Non-existing dir is not an error, we got one size from /proc/meminfo. + */ + sysfs = opendir(SYSFS_HUGEPAGES_DIR); + if (!sysfs) { + if (errno == ENOENT) { + errno = 0; + return nr_sizes; + } else + return -1; + } + while ((ent = readdir(sysfs)) && + ((n_elem == 0) || (nr_sizes < n_elem))) { + long size; + + if (strncmp(ent->d_name, "hugepages-", 10)) + continue; + + size = strtol(ent->d_name + 10, NULL, 10); + if (size == LONG_MIN || size == LONG_MAX) + continue; + size *= 1024; /* Convert from KB to Bytes */ + + if (size == default_size) + continue; + if (n_elem && pagesizes) + pagesizes[nr_sizes] = size; + nr_sizes++; + } + closedir(sysfs); + + return nr_sizes; +} + int hugetlbfs_test_path(const char *mount) { struct statfs64 sb; diff --git a/tests/Makefile b/tests/Makefile index beee18e..fdbe864 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -7,7 +7,8 @@ LIB_TESTS = gethugepagesize test_root find_path unlinked_fd misalign \ truncate_reserve_wraparound truncate_sigbus_versus_oom \ map_high_truncate_2 truncate_above_4GB direct \ misaligned_offset brk_near_huge task-size-overrun stack_grow_into_huge \ - counters quota heap-overflow get_huge_pages shmoverride_linked + counters quota heap-overflow get_huge_pages shmoverride_linked \ + gethugepagesizes LIB_TESTS_64 = straddle_4GB huge_at_4GB_normal_below \ huge_below_4GB_normal_above NOLIB_TESTS = malloc malloc_manysmall dummy heapshrink shmoverride_unlinked diff --git a/tests/gethugepagesizes.c b/tests/gethugepagesizes.c new file mode 100644 index 0000000..44aff68 --- /dev/null +++ b/tests/gethugepagesizes.c @@ -0,0 +1,226 @@ +/* + * libhugetlbfs - Easy use of Linux hugepages + * Copyright (C) 2008 Adam Litke, IBM Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#define _GNU_SOURCE + +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <dirent.h> +#include <dlfcn.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <hugetlbfs.h> + +#include "hugetests.h" + +#define REAL_SYSFS_DIR "/sys/kernel/mm/hugepages/" +char fake_sysfs[] = "/tmp/sysfs-XXXXXX"; +DIR *(*real_opendir)(const char *name); +int cleanup_dir = 0; + +long (*real_read_meminfo)(const char *tag); +enum { + OVERRIDE_OFF, /* Pass-through to real function */ + OVERRIDE_ON, /* Ovewrride with local function */ + OVERRIDE_MISSING, /* Emulate missing support */ +}; +int meminfo_state = OVERRIDE_OFF; +int sysfs_state = OVERRIDE_OFF; + +/* + * Override opendir so we'll open the fake sysfs dir if intended + */ +DIR *opendir(const char *name) +{ + if (!real_opendir) + real_opendir = dlsym(RTLD_NEXT, "opendir"); + + /* Only override calls to the sysfs dir */ + if (strcmp(name, REAL_SYSFS_DIR)) + return real_opendir(name); + + switch (sysfs_state) { + case OVERRIDE_OFF: + return real_opendir(name); + case OVERRIDE_ON: + /* Only safe to override of fake_sysfs was set up */ + if (cleanup_dir) + return real_opendir(fake_sysfs); + else + FAIL("Trying to override opendir before initializing " + "fake_sysfs directory\n"); + default: + errno = ENOENT; + return NULL; + } +} + +/* + * Override read_meminfo to simulate various conditions + */ +long read_meminfo(const char *tag) +{ + if (!real_read_meminfo) + real_read_meminfo = dlsym(RTLD_NEXT, "read_meminfo"); + + /* Only override calls that check the page size */ + if (strcmp(tag, "Hugepagesize:")) + return real_read_meminfo(tag); + + switch (meminfo_state) { + case OVERRIDE_OFF: return real_read_meminfo(tag); + case OVERRIDE_ON: return 16 * 1024; + default: return -1; + } +} + +void cleanup_fake_sysfs(void) +{ + DIR *dir; + struct dirent *ent; + char fname[PATH_MAX+1]; + + cleanup_dir = 0; + dir = real_opendir(fake_sysfs); + if (!dir) + FAIL("opendir %s: %s", fake_sysfs, strerror(errno)); + + while ((ent = readdir(dir))) { + if (strncmp(ent->d_name, "hugepages-", 10)) + continue; + snprintf(fname, PATH_MAX, "%s/%s", fake_sysfs, + ent->d_name); + if (rmdir(fname)) + FAIL("rmdir %s: %s", fake_sysfs, strerror(errno)); + } + closedir(dir); + if (rmdir(fake_sysfs)) + FAIL("rmdir %s: %s", fake_sysfs, strerror(errno)); +} + +void setup_fake_sysfs(long sizes[], int n_elem) +{ + int i; + char fname[PATH_MAX+1]; + + if (cleanup_dir) + cleanup_fake_sysfs(); + + if (!mkdtemp(fake_sysfs)) + FAIL("mkdtemp: %s", strerror(errno)); + cleanup_dir = 1; + + for (i = 0; i < n_elem; i++) { + snprintf(fname, PATH_MAX, "%s/hugepages-%lukB", fake_sysfs, + sizes[i] / 1024); + if (mkdir(fname, 0700)) + FAIL("mkdir %s: %s", fname, strerror(errno)); + } +} + +void cleanup(void) +{ + if (cleanup_dir) + cleanup_fake_sysfs(); +} + +#define INIT_SIZES(a, v1, v2, v3) {a[0] = v1; a[1] = v2; a[2] = v3; a[3] = -1;} + +void expect_sizes(int line, int expected, int actual, + long expected_sizes[], long actual_sizes[]) +{ + int i, j; + if (expected != actual) + FAIL("Line %i: Wrong number of sizes returned -- expected %i " + "got %i", line, expected, actual); + + for (i = 0; i < expected; i++) { + for (j = 0; j < actual; j++) + if (expected_sizes[i] == actual_sizes[j]) + break; + if (j >= actual) + FAIL("Line %i: Expected size %li not found in actual " + "results", line, expected_sizes[i]); + } +} + +int main(int argc, char *argv[]) +{ + long expected_sizes[4], actual_sizes[4], meminfo_size; + int nr_sizes; + + test_init(argc, argv); + + /* + * === + * Argment error checking tests + * === + */ + meminfo_state = OVERRIDE_OFF; + sysfs_state = OVERRIDE_OFF; + if (gethugepagesizes(actual_sizes, -1) != -1 || errno != EINVAL) + FAIL("Mishandled params (n_elem < 0)"); + if (gethugepagesizes(NULL, 1) != -1 || errno != EINVAL) + FAIL("Mishandled params (pagesizes == NULL, n_elem > 0)"); + + /* + * === + * Test some corner cases using a fake system configuration + * === + */ + + /* + * Check handling when /proc/meminfo indicates no huge page support + */ + meminfo_state = OVERRIDE_MISSING; + if (gethugepagesizes(actual_sizes, 1) != 0 || errno != 0) + FAIL("Incorrect handling when huge page support is missing"); + + /* + * When the sysfs heirarchy is not present ... + */ + sysfs_state = OVERRIDE_MISSING; + + /* ... only the meminfo size is returned. */ + meminfo_state = OVERRIDE_ON; + meminfo_size = read_meminfo("Hugepagesize:") * 1024; + INIT_SIZES(expected_sizes, meminfo_size, -1, -1); + + /* Use 2 to give the function the chance to return too many sizes */ + nr_sizes = gethugepagesizes(actual_sizes, 2); + expect_sizes(__LINE__, 1, nr_sizes, expected_sizes, actual_sizes); + + /* + * When sysfs defines additional sizes ... + */ + INIT_SIZES(expected_sizes, meminfo_size, 1024, 2048); + setup_fake_sysfs(expected_sizes, 3); + sysfs_state = OVERRIDE_ON; + + /* ... make sure all sizes are returned without duplicates */ + nr_sizes = gethugepagesizes(actual_sizes, 4); + expect_sizes(__LINE__, 3, nr_sizes, expected_sizes, actual_sizes); + + /* ... we can check how many sizes are supported. */ + if (gethugepagesizes(NULL, 0) != 3) + FAIL("Unable to check the number of supported sizes"); + + PASS(); +} diff --git a/tests/run_tests.sh b/tests/run_tests.sh index d616731..4fe6eed 100755 --- a/tests/run_tests.sh +++ b/tests/run_tests.sh @@ -249,6 +249,7 @@ functional_tests () { # Library tests requiring kernel hugepage support run_test gethugepagesize + run_test gethugepagesizes run_test HUGETLB_VERBOSE=1 empty_mounts run_test HUGETLB_VERBOSE=1 large_mounts -- 1.6.0.1.451.gc8d31 ------------------------------------------------------------------------- This SF.Net email is sponsored by the Moblin Your Move Developer's challenge Build the coolest Linux based applications with Moblin SDK & win great prizes Grand prize is a trip for two to an Open Source event anywhere in the world http://moblin-contest.org/redirect.php?banner_id=100&url=/ _______________________________________________ Libhugetlbfs-devel mailing list Libhugetlbfs-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/libhugetlbfs-devel