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. Open discussion points: This call will return all huge page sizes as reported by the kernel. Not all of these sizes may be usable by the programmer since mount points may not be available for all sizes. To test whether a size will be usable by libhugetlbfs, hugetlbfs_find_path_for_size() can be called on a specific size to see if a mount point is configured. I think this requirement is sensible. The only problem I see is that hugetlbfs_find_path_for_size() is not an obvious function name for this usage. Signed-off-by: Adam Litke <[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 21f788d..a48fa36 100644 --- a/hugeutils.c +++ b/hugeutils.c @@ -552,6 +552,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..34e9957 --- /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