On Fri, Oct 10, 2008 at 02:20:54PM +0100, Mel Gorman wrote:
> On (09/10/08 20:20), Andy Whitcroft didst pronounce:
> > 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;
> 
> If the sysfs entry exists but we fail to open it, errno will be set to
> something specified by opendir and we fail. Failing I agree with but
> maybe errno should be set to EACCESS  to indicate that other pagesizes
> existed but we couldn't read them?

We are assuming opendir will leave it as something sane.  EACCESS
doesn't really tell us what was wrong.

> > +   }
> > +   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
> >  
> 
> Other than errno possibly being something stupid due to opendir()
> failing

We assume errno is only valid when we return -1.  As such as long as
opendir sets it to something we are ok.  Not sure we really care what
the result is other than us wanting to say EINVAL.

> Acked-by: Mel Gorman <[EMAIL PROTECTED]>

-apw

-------------------------------------------------------------------------
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

Reply via email to