Commit b83390e0 authored by Nicolas Denoyelle's avatar Nicolas Denoyelle Committed by Nicolas Denoyelle
Browse files

Add basic hwloc areas

parent 9b41ec63
Pipeline #10478 passed with stages
in 3 minutes and 24 seconds
......@@ -76,6 +76,20 @@ AC_CHECK_HEADERS([numa.h],,[AC_MSG_ERROR([AML requires libnuma headers.])])
AC_CHECK_HEADERS([numaif.h],,[AC_MSG_ERROR([AML requires libnuma headers.])])
AC_CHECK_LIB(numa, mbind,,[AC_MSG_ERROR([AML requires libnuma.])])
# Hwloc support
###############
PKG_CHECK_MODULES([HWLOC], \
[hwloc >= 2.0], \
[have_hwloc=1], \
[have_hwloc=0])
if test "$have_hwloc" == "1"; then
AC_DEFINE([HAVE_HWLOC], [1], "hwloc library with ABI > 2.0 is installed.")
fi
AM_CONDITIONAL([HAVE_HWLOC], [test "$have_hwloc" = "1"])
AC_DEFINE_UNQUOTED([HAVE_HWLOC], [$have_hwloc], [Whether aml support hwloc library calls.])
AC_SUBST([HAVE_HWLOC],[$have_hwloc])
# check doxygen + sphinx for documentation build
################################################
......
......@@ -13,6 +13,12 @@ AM_CFLAGS += $(CUDA_CFLAGS)
AM_LDFLAGS += $(CUDA_LIBS)
endif
if HAVE_HWLOC
LIBS += $(HWLOC_LIBS)
AM_CFLAGS += $(HWLOC_CFLAGS)
AM_LDFLAGS += $(HWLOC_LIBS)
endif
# valgrind support
@VALGRIND_CHECK_RULES@
......@@ -39,6 +45,10 @@ if HAVE_CUDA
AREA_TESTS += area/0_aml_area_cuda
endif
if HAVE_HWLOC
AREA_TESTS += area/2_aml_area_hwloc
endif
UNIT_TESTS = $(HELLO_TESTS) $(AREA_TESTS) $(LAYOUT_TESTS) $(DMA_TESTS) $(TILING_TESTS)
check_PROGRAMS = $(UNIT_TESTS)
......
/*******************************************************************************
* Copyright 2019 UChicago Argonne, LLC.
* (c.f. AUTHORS, LICENSE)
*
* This file is part of the AML project.
* For more info, see https://xgitlab.cels.anl.gov/argo/aml
*
* SPDX-License-Identifier: BSD-3-Clause
*******************************************************************************/
#include <aml.h>
#include <aml/area/hwloc.h>
//!\\ You should initialize your own topology instead. @see <hwloc.h>
extern hwloc_topology_t aml_topology;
static const size_t size = (1 << 22); // 4 MB
/**
* Use static hwloc interleave area.
**/
int static_interleave_area()
{
void *data = aml_area_mmap(&aml_area_hwloc_interleave, size, NULL);
// Worked ?
if (data == NULL) {
aml_perror("aml_area_mmap");
return 1;
}
// Cleanup
aml_area_munmap(&aml_area_hwloc_interleave, data, size);
return 0;
}
int main(int argc, char **argv)
{
if (aml_init(&argc, &argv) != 0)
return 1;
if (static_interleave_area() != 0)
goto err;
// Cleanup success
aml_finalize();
return 0;
// Cleanup error
err:
aml_finalize();
return 1;
}
......@@ -80,6 +80,34 @@ Now we have an "allocator" of interleaved data.
Here we have allocated 8*4096 bytes of data across system memories.
Hwloc Area
----------
If you compiled AML with hwloc backend support and the supports hwloc library
at runtime, then you can use aml area features built on top of hwloc.
.. code-block:: c
#include <aml/utils/features.h>
#if AML_HAVE_BACKEND_HWLOC == 1
#include <aml/area/hwloc.h>
...
if (aml_support_backends(AML_BACKEND_HWLOC)) {
...
}
...
#endif
The backend provides static areas, e.g for interleaving data on all NUMA nodes:
.. code-block:: c
void *data = aml_area_mmap(&aml_area_hwloc_interleave, size, NULL);
and areas constructor based on hwloc memory binding policies and nodeset.
The backend may require that you provide objects from the current system
topology. Such a topology may be obtained through hwloc API.
CUDA Area
---------
......
......@@ -5,6 +5,10 @@ include_aml_area_HEADERS = \
aml/area/linux.h \
aml/area/cuda.h
if HAVE_HWLOC
include_aml_area_HEADERS+= aml/area/hwloc.h
endif
include_aml_layoutdir=$(includedir)/aml/layout
include_aml_layout_HEADERS = \
aml/layout/native.h \
......
/*******************************************************************************
* Copyright 2019 UChicago Argonne, LLC.
* (c.f. AUTHORS, LICENSE)
*
* This file is part of the AML project.
* For more info, see https://xgitlab.cels.anl.gov/argo/aml
*
* SPDX-License-Identifier: BSD-3-Clause
******************************************************************************/
#ifndef AML_AREA_HWLOC_H
#define AML_AREA_HWLOC_H
#include <hwloc.h>
#include <hwloc/distances.h>
/**
* @defgroup aml_area_hwloc "AML hwloc Areas"
* @brief Implementation of Areas with features from hwloc library.
*
* Implementation of AML areas with hwloc library.
* This building block relies on hwloc to provide mmap/munmap.
* hwloc areas inherit hwloc portability and provide constructor
* to customize binding of areas.
* This building block also include a static declaration of
* several default areas that can be used out of the box with
* abstract area API.
*
* @code
* #include <aml/area/hwloc.h>
* @endcode
* @see <hwloc.h>
* @see https://www.open-mpi.org/projects/hwloc/doc/v2.0.0/
* @{
**/
/**************************************************************************/
/* aml_area_hwloc */
/**************************************************************************/
// Struct definition
//////////////////////
/** User data stored inside aml hwloc area. **/
struct aml_area_hwloc_data {
/** Nodeset where to bind data **/
hwloc_bitmap_t nodeset;
/** Memory binding policy **/
hwloc_membind_policy_t policy;
};
// Methods
//////////////////////
/**
* Create an area with specific memory binding.
* @param[out] area: A pointer where to store newly created area.
* @param[in] nodeset: The location where to bind memory with this area.
* @param[in] policy: The binding policy of this area.
* @return -AML_ENOMEM if not enough memory was available to create the area.
* @return -AML_ENOTSUP if policy is not supported.
* @return -AML_EDOM if nodeset is out of allowed nodeset.
* @return AML_SUCCESS.
**/
int aml_area_hwloc_create(struct aml_area **area,
hwloc_bitmap_t nodeset,
const hwloc_membind_policy_t policy);
/**
* Free an area obtained with aml_area_hwloc_create().
* @param[in] area: The pointer to area to free.
* The content of the pointer is set to NULL.
**/
void aml_area_hwloc_destroy(struct aml_area **area);
/**
* Base function for mapping and binding memory.
* It is a wrapper to hwloc_alloc_membind().
* @param[in] data: struct aml_area_hwloc_data* initialized with a binding.
* @param[in] size: The size of memory to query.
* @param[in] opts: ignored.
* @return A pointer to mapped memory region.
* @return NULL on failure with aml_errno se to:
* - AML_ENOMEM if there is not enough memory available to fulfill
* the operation.
* - AML_ENOTSUP if area allocation policy is not supported.
* - AML_FAILURE if binding cannot be enforced.
**/
void *aml_area_hwloc_mmap(const struct aml_area_data *data,
size_t size,
struct aml_area_mmap_options *opts);
/**
* Free memory allocated by aml_area_hwloc_mmap().
* It is a wrapper to hwloc_alloc_free().
* @param[in] data: Unused.
* @param[in] ptr: The memory region to unmap.
* @param[in] size: The size of memory region to unmap.
* @return AML_SUCCESS.
**/
int aml_area_hwloc_munmap(const struct aml_area_data *data,
void *ptr,
size_t size);
/**
* @brief Check allocation binding against area binding settings.
*
* Check if the data allocated with a aml_area_hwloc is bound
* as expected to memory, i.e it is allocated on allowed NUMA nodes
* and with the good policy.
* @param[in] area: A aml_area_hwloc.
* @param[in] ptr: The data allocated with same area.
* @param[in] size: The data size in Bytes.
* @return 1 if ptr is bound according to area settings.
* @return 0 otherwise.
**/
int aml_area_hwloc_check_binding(struct aml_area *area, void *ptr, size_t size);
// static declarations
//////////////////////
/** Operations of aml_area_hwloc **/
extern struct aml_area_ops aml_area_hwloc_ops;
/** Convenience default area declaration without specific binding. **/
extern struct aml_area aml_area_hwloc_default;
/**
* Convenience area declaration with binding on every nodes of the
* topology and interleave allocation policy.
**/
extern struct aml_area aml_area_hwloc_interleave;
/**
* Convenience area declaration with binding on every nodes of the
* topology and firsttouch allocation policy.
**/
extern struct aml_area aml_area_hwloc_firsttouch;
/**
* @}
**/
#endif // AML_AREA_HWLOC_H
......@@ -27,11 +27,15 @@
#define AML_HAVE_BACKEND_LIBNUMA 1
/** Whether aml had cuda capabilities at compile time **/
#define AML_HAVE_BACKEND_CUDA @HAVE_CUDA@
/** Whether aml had hwloc capabilities at compile time **/
#define AML_HAVE_BACKEND_HWLOC @HAVE_HWLOC@
/** Flag for checking runtime suport for libnuma **/
#define AML_BACKEND_LIBNUMA (1UL<<1)
/** Flag for checking runtime suport for cuda **/
#define AML_BACKEND_CUDA (1UL<<2)
/** Flag for checking runtime suport for hwloc **/
#define AML_BACKEND_HWLOC (1UL<<3)
/**
* Check if a set of backends can be used at runtime.
......
......@@ -85,3 +85,13 @@ libaml_la_SOURCES+=area/cuda.c
# libaml_la_LIBADD+=libamlcuda.la
endif
#############################################
# hwloc sources
if HAVE_HWLOC
AM_CPPFLAGS += $(HWLOC_CFLAGS)
AM_LDFLAGS += $(HWLOC_LIBS)
libaml_la_SOURCES+=area/hwloc.c
endif
......@@ -6,12 +6,14 @@
* For more info, see https://xgitlab.cels.anl.gov/argo/aml
*
* SPDX-License-Identifier: BSD-3-Clause
*******************************************************************************/
*******************************************************************************/
#include "aml.h"
#include "config.h"
#include <string.h>
#include "aml.h"
const int aml_version_major = AML_VERSION_MAJOR;
const int aml_version_minor = AML_VERSION_MINOR;
const int aml_version_patch = AML_VERSION_PATCH;
......@@ -19,15 +21,50 @@ const char *aml_version_string = AML_VERSION_STRING;
int aml_errno;
#if HAVE_HWLOC == 1
#include <hwloc.h>
hwloc_topology_t aml_topology;
hwloc_const_bitmap_t allowed_nodeset;
int aml_topology_init(void)
{
if (hwloc_topology_init(&aml_topology) == -1)
return -1;
if (hwloc_topology_set_flags(
aml_topology,
HWLOC_TOPOLOGY_FLAG_THISSYSTEM_ALLOWED_RESOURCES) == -1)
return -1;
if (hwloc_topology_load(aml_topology) == -1)
return -1;
return 0;
}
#endif
int aml_init(int *argc, char **argv[])
{
// disable warnings
(void)argc; (void)argv;
(void)argc;
(void)argv;
// Initialize topology
#if HAVE_HWLOC == 1
int err_hwloc;
err_hwloc = aml_topology_init();
if (err_hwloc < 0)
return AML_FAILURE;
allowed_nodeset = hwloc_topology_get_allowed_nodeset(aml_topology);
#endif
return 0;
}
int aml_finalize(void)
{
// Destroy topology
#if HAVE_HWLOC == 1
hwloc_topology_destroy(aml_topology);
#endif
return 0;
}
/*******************************************************************************
* Copyright 2019 UChicago Argonne, LLC.
* (c.f. AUTHORS, LICENSE)
*
* This file is part of the AML project.
* For more info, see https://xgitlab.cels.anl.gov/argo/aml
*
* SPDX-License-Identifier: BSD-3-Clause
******************************************************************************/
#include <sys/mman.h>
#include "aml.h"
#include "aml/area/hwloc.h"
#define HWLOC_BINDING_FLAGS \
(HWLOC_MEMBIND_PROCESS | HWLOC_MEMBIND_NOCPUBIND | \
HWLOC_MEMBIND_BYNODESET | HWLOC_MEMBIND_STRICT)
/**
* Topology used by AML.
* Topology is initalized when calling aml_library_init().
**/
extern hwloc_topology_t aml_topology;
extern hwloc_const_bitmap_t allowed_nodeset;
/**************************************************************************/
/* aml_area_hwloc */
/**************************************************************************/
static int aml_area_hwloc_init(struct aml_area *area,
hwloc_bitmap_t nodeset,
const hwloc_membind_policy_t policy)
{
struct aml_area_hwloc_data *d =
(struct aml_area_hwloc_data *)area->data;
// Check support
const struct hwloc_topology_support *sup =
hwloc_topology_get_support(aml_topology);
if (policy == HWLOC_MEMBIND_BIND && !sup->membind->bind_membind)
return -AML_ENOTSUP;
if (policy == HWLOC_MEMBIND_FIRSTTOUCH &&
!sup->membind->firsttouch_membind)
return -AML_ENOTSUP;
if (policy == HWLOC_MEMBIND_INTERLEAVE &&
!sup->membind->interleave_membind)
return -AML_ENOTSUP;
if (policy == HWLOC_MEMBIND_NEXTTOUCH &&
!sup->membind->nexttouch_membind)
return -AML_ENOTSUP;
// Check nodeset does not include unallowed nodes
if (nodeset && !hwloc_bitmap_isincluded(nodeset, allowed_nodeset))
return -AML_EDOM;
// Set area nodeset and policy
if (nodeset == NULL)
d->nodeset = hwloc_bitmap_dup(allowed_nodeset);
else
d->nodeset = hwloc_bitmap_dup(nodeset);
d->policy = policy;
return AML_SUCCESS;
}
int aml_area_hwloc_create(struct aml_area **area,
hwloc_bitmap_t nodeset,
const hwloc_membind_policy_t policy)
{
int err = AML_SUCCESS;
struct aml_area *a;
struct aml_area_hwloc_data *data;
a = AML_INNER_MALLOC(struct aml_area, struct aml_area_hwloc_data);
if (a == NULL)
return -AML_ENOMEM;
a->ops = &aml_area_hwloc_ops;
data = AML_INNER_MALLOC_GET_FIELD(a, 2, struct aml_area,
struct aml_area_hwloc_data);
a->data = (struct aml_area_data *)data;
err = aml_area_hwloc_init(a, nodeset, policy);
if (err != AML_SUCCESS) {
free(a);
return err;
}
*area = a;
return AML_SUCCESS;
}
void aml_area_hwloc_destroy(struct aml_area **area)
{
if (area == NULL || *area == NULL)
return;
struct aml_area_hwloc_data *data =
(struct aml_area_hwloc_data *)(*area)->data;
hwloc_bitmap_free(data->nodeset);
free(*area);
*area = NULL;
}
void *aml_area_hwloc_mmap(const struct aml_area_data *data,
size_t size,
struct aml_area_mmap_options *opts)
{
(void)opts;
struct aml_area_hwloc_data *hwloc_data =
(struct aml_area_hwloc_data *)data;
hwloc_const_bitmap_t nodeset = hwloc_data->nodeset == NULL ?
allowed_nodeset :
hwloc_data->nodeset;
void *p = hwloc_alloc_membind(aml_topology, size, nodeset,
hwloc_data->policy, HWLOC_BINDING_FLAGS);
if (p == NULL) {
if (errno == EINVAL)
aml_errno = AML_EINVAL;
if (errno == ENOSYS)
aml_errno = AML_ENOTSUP;
if (errno == EXDEV)
aml_errno = AML_FAILURE;
if (errno == ENOMEM)
aml_errno = AML_ENOMEM;
return NULL;
}
return p;
}
int aml_area_hwloc_munmap(const struct aml_area_data *data,
void *ptr,
size_t size)
{
(void)data;
hwloc_free(aml_topology, ptr, size);
return AML_SUCCESS;
}
int aml_area_hwloc_check_binding(struct aml_area *area, void *ptr, size_t size)
{
int err;
hwloc_membind_policy_t policy;
hwloc_bitmap_t nodeset;
struct aml_area_hwloc_data *bind =
(struct aml_area_hwloc_data *)area->data;
nodeset = hwloc_bitmap_alloc();
if (nodeset == NULL)
return -AML_ENOMEM;
err = hwloc_get_area_membind(aml_topology, ptr, size, nodeset, &policy,
HWLOC_BINDING_FLAGS);
if (err < 0) {
err = AML_EINVAL;
goto out;
}
err = 1;
if (policy != bind->policy && bind->policy != HWLOC_MEMBIND_DEFAULT &&
bind->policy != HWLOC_MEMBIND_MIXED)
err = 0;
if (!hwloc_bitmap_isequal(nodeset, bind->nodeset))
err = 0;
out:
hwloc_bitmap_free(nodeset);
return err;
}
/**
* Static declaration of an area.
* No check is performed to enforced availability of policies and nodeset.
* If bad values are provided area function call will return
* an error.
* @param[in] name: The area instance name.
* @param[in] bitmap: The nodeset to set inside area (hwloc_bitmap_t).
* Can be NULL.
* @param[in] bind_policy: The memory allocation policy
* (hwloc_membind_policy_t).
**/
#define AML_AREA_HWLOC_DECL(name, bitmap, bind_policy) \
struct aml_area_hwloc_data __##name##_data = {.nodeset = bitmap, \
.policy = bind_policy}; \
struct aml_area name = { \
.ops = &aml_area_hwloc_ops, \
.data = (struct aml_area_data *)&__##name##_data}
struct aml_area_ops aml_area_hwloc_ops = {
.mmap = aml_area_hwloc_mmap,
.munmap = aml_area_hwloc_munmap,
.fprintf = NULL,
};
AML_AREA_HWLOC_DECL(aml_area_hwloc_default, NULL, HWLOC_MEMBIND_DEFAULT);
AML_AREA_HWLOC_DECL(aml_area_hwloc_interleave, NULL, HWLOC_MEMBIND_INTERLEAVE);
AML_AREA_HWLOC_DECL(aml_area_hwloc_firsttouch, NULL, HWLOC_MEMBIND_FIRSTTOUCH);
......@@ -6,14 +6,19 @@
* For more info, see https://xgitlab.cels.anl.gov/argo/aml
*
* SPDX-License-Identifier: BSD-3-Clause
*******************************************************************************/
*******************************************************************************/
#include "aml/utils/features.h"
#include "config.h"
#include "aml/utils/features.h"
#if HAVE_CUDA == 1
#include <cuda.h>
#include <cuda_runtime.h>
#endif
#if HAVE_HWLOC == 1
#include <hwloc.h>
extern hwloc_topology_t aml_topology;
#endif
static int aml_support_cuda(void)
{
......@@ -29,6 +34,21 @@ static int aml_support_cuda(void)
#endif
}
static int aml_support_hwloc(void)
{
#if HAVE_HWLOC == 0
return 0;
#else
const struct hwloc_topology_support *sup =
hwloc_topology_get_support(aml_topology);
if (!sup->discovery->numa || !sup->discovery->numa_memory)