├── .codespell_ignore ├── .gitignore ├── .gitmodules ├── .pre-commit-config.yaml ├── LICENSE ├── README.md ├── hmalloc ├── .clang-format ├── CMakeLists.txt ├── doc │ ├── hmalloc.3 │ ├── hmalloc.md │ ├── hmalloc_usable_size.3 │ ├── hmalloc_usable_size.md │ ├── hmctl.8 │ ├── hmctl.md │ ├── hmmap.3 │ ├── hmmap.md │ ├── hposix_memalign.3 │ └── hposix_memalign.md ├── include │ └── hmalloc.h ├── src │ ├── env.c │ ├── env.h │ ├── hmalloc.c │ └── hmctl.c └── test │ ├── .gitignore │ ├── CMakeLists.txt │ ├── example.c │ ├── hmalloc_test.cpp │ └── main.cpp └── tools ├── bwactl.py ├── gen_config.py ├── gen_migpol.py ├── iw_ratio.py └── mlc.py /.codespell_ignore: -------------------------------------------------------------------------------- 1 | damon 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.patch 2 | *.diff 3 | *.orig 4 | __pycache__ 5 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "linux"] 2 | path = linux 3 | url = https://github.com/skhynix/linux.git 4 | [submodule "numactl"] 5 | path = numactl 6 | url = https://github.com/skhynix/numactl.git 7 | [submodule "damo"] 8 | path = damo 9 | url = https://github.com/skhynix/damo.git 10 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/pre-commit/pre-commit-hooks 3 | rev: v4.0.1 4 | hooks: 5 | - id: check-yaml 6 | - id: end-of-file-fixer 7 | - id: trailing-whitespace 8 | - repo: https://github.com/pre-commit/mirrors-clang-format 9 | rev: v14.0.6 10 | hooks: 11 | - id: clang-format 12 | - repo: https://github.com/codespell-project/codespell 13 | rev: v2.2.4 14 | hooks: 15 | - id: codespell 16 | args: ['-I', '.codespell_ignore'] 17 | - repo: https://github.com/PyCQA/isort 18 | rev: 5.11.5 19 | hooks: 20 | - id: isort 21 | - repo: https://github.com/psf/black 22 | rev: 22.10.0 23 | hooks: 24 | - id: black 25 | - repo: https://github.com/cheshirekow/cmake-format-precommit 26 | rev: v0.6.13 27 | hooks: 28 | - id: cmake-format 29 | - id: cmake-lint 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 2-Clause License 2 | 3 | Copyright (c) 2022, SK hynix, Inc. All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # HMSDK 2 | 3 | HMSDK stands for Heterogeneous Memory Software Development Kit and it is 4 | especially designed to support CXL memory, which is a new promising memory 5 | system based on a CXL(Compute Express Link) open industry standard. 6 | 7 | The more explanation can be found at the [wiki page](https://github.com/skhynix/hmsdk/wiki). 8 | 9 | ## Download 10 | 11 | HMSDK consists of multiple git submodules so please download it as follows. 12 | 13 | $ git clone --recursive --shallow-submodules https://github.com/skhynix/hmsdk.git 14 | 15 | ## News 16 | 17 | - 2024-09-16: HMSDK v3.0 is released 18 | - Fully aligned with various open source projects, which allows us drop local patches 19 | - 2024-07-03: HMSDK v2.0 kernel patches have landed into upstream Linux kernel (available from v6.11) 20 | - 7 commits including [migration core logic](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=b51820ebea656be3b48bb16dcdc5ad3f203c4fd7) - [cover letter](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=a00ce85af2a1be494d3b0c9457e8e81cdcce2a89) 21 | - 2024-04-19: numactl supports [--weighted-interleave](https://github.com/numactl/numactl/commit/b67fb88e77b3c200b0e300e2e0edc4f66c1d9ea5) option 22 | - 2024-02-22: HMSDK v1.1 kernel patches have landed into upstream Linux kernel (available from v6.9) 23 | - [MPOL_WEIGHTED_INTERLEAVE](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=fa3bea4e1f8202d787709b7e3654eb0a99aed758) memory policy and its [sysfs interface](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=dce41f5ae2539d1c20ae8de4e039630aec3c3f3c) (co-developed by [MemVerge](http://www.memverge.com)) 24 | - 2023-12-27: HMSDK v2.0 released - support [DAMON](https://sjp38.github.io/post/damon) based 2-tier memory management 25 | - 2023-05-09: HMSDK v1.1 released - support bandwidth aware interleaving, user library and tools 26 | 27 | ## License 28 | 29 | The HMSDK is released under BSD 2-Clause license. 30 | Please see [LICENSE file](LICENSE) for details. 31 | -------------------------------------------------------------------------------- /hmalloc/.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: LLVM 2 | IndentWidth: 4 3 | ColumnLimit: 100 4 | AllowShortFunctionsOnASingleLine: Empty 5 | AlwaysBreakTemplateDeclarations: Yes 6 | SortIncludes: true 7 | -------------------------------------------------------------------------------- /hmalloc/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2024 SK hynix, Inc. 3 | # 4 | # SPDX-License-Identifier: BSD 2-Clause 5 | # 6 | 7 | cmake_minimum_required(VERSION 3.14) 8 | 9 | project(hmalloc) 10 | 11 | if(NOT CMAKE_BUILD_TYPE) 12 | set(CMAKE_BUILD_TYPE 13 | RelWithDebInfo 14 | CACHE STRING "Build type" FORCE) 15 | set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" 16 | "RelWithDebInfo") 17 | endif() 18 | 19 | add_compile_options(-Wall -Wextra -pedantic) 20 | 21 | option(HMALLOC_MANUAL "hmalloc: man" OFF) 22 | 23 | option(HMALLOC_TEST "hmalloc: test" OFF) 24 | 25 | option(HMALLOC_PG_BUILD "hmalloc: -pg" OFF) 26 | if(HMALLOC_PG_BUILD) 27 | add_compile_options(-pg) 28 | endif() 29 | 30 | option(HMALLOC_ASAN_BUILD "hmalloc: -fsanitize=address" OFF) 31 | if(HMALLOC_ASAN_BUILD) 32 | add_compile_options(-fsanitize=address) 33 | add_link_options(-fsanitize=address) 34 | endif() 35 | 36 | set(CMAKE_EXPORT_COMPILE_COMMANDS ON) 37 | 38 | set(HMCTL hmctl) 39 | set(HMCTL_SOURCES src/hmctl.c) 40 | add_executable(${HMCTL} ${HMCTL_SOURCES}) 41 | 42 | set(HMALLOC hmalloc) 43 | set(HMALLOC_SOURCES src/hmalloc.c src/env.c) 44 | 45 | find_library(JEMALLOC jemalloc) 46 | if(NOT JEMALLOC) 47 | message(FATAL_ERROR "jemalloc library not found!") 48 | endif() 49 | 50 | find_library(NUMA numa) 51 | if(NOT NUMA) 52 | message(FATAL_ERROR "numa library not found!") 53 | endif() 54 | 55 | add_library(${HMALLOC} SHARED ${HMALLOC_SOURCES}) 56 | 57 | target_include_directories( 58 | hmalloc 59 | PUBLIC include 60 | PRIVATE src) 61 | 62 | target_link_libraries(${HMCTL} PRIVATE ${NUMA}) 63 | target_link_libraries(${HMALLOC} PRIVATE ${JEMALLOC} ${NUMA}) 64 | 65 | if(HMALLOC_TEST) 66 | add_subdirectory(test) 67 | endif() 68 | 69 | if(HMALLOC_MANUAL) 70 | add_custom_target( 71 | man ALL 72 | COMMAND pandoc -s ${CMAKE_CURRENT_SOURCE_DIR}/doc/hmctl.md -t man -o 73 | ${CMAKE_CURRENT_SOURCE_DIR}/doc/hmctl.8 74 | COMMAND pandoc -s ${CMAKE_CURRENT_SOURCE_DIR}/doc/hmalloc.md -t man -o 75 | ${CMAKE_CURRENT_SOURCE_DIR}/doc/hmalloc.3 76 | COMMAND pandoc -s ${CMAKE_CURRENT_SOURCE_DIR}/doc/hmalloc_usable_size.md -t 77 | man -o ${CMAKE_CURRENT_SOURCE_DIR}/doc/hmalloc_usable_size.3 78 | COMMAND pandoc -s ${CMAKE_CURRENT_SOURCE_DIR}/doc/hposix_memalign.md -t man 79 | -o ${CMAKE_CURRENT_SOURCE_DIR}/doc/hposix_memalign.3 80 | COMMAND pandoc -s ${CMAKE_CURRENT_SOURCE_DIR}/doc/hmmap.md -t man -o 81 | ${CMAKE_CURRENT_SOURCE_DIR}/doc/hmmap.3 82 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} 83 | COMMENT "Generating man page") 84 | endif() 85 | 86 | install(TARGETS ${HMCTL} DESTINATION bin) 87 | install(TARGETS ${HMALLOC} DESTINATION lib) 88 | install(FILES include/hmalloc.h DESTINATION include) 89 | install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/doc/hmctl.8 90 | DESTINATION share/man/man8) 91 | install( 92 | FILES ${CMAKE_CURRENT_SOURCE_DIR}/doc/hmalloc.3 93 | ${CMAKE_CURRENT_SOURCE_DIR}/doc/hmalloc_usable_size.3 94 | ${CMAKE_CURRENT_SOURCE_DIR}/doc/hposix_memalign.3 95 | ${CMAKE_CURRENT_SOURCE_DIR}/doc/hmmap.3 96 | DESTINATION share/man/man3) 97 | -------------------------------------------------------------------------------- /hmalloc/doc/hmalloc.3: -------------------------------------------------------------------------------- 1 | .\" Automatically generated by Pandoc 2.9.2.1 2 | .\" 3 | .TH "HMALLOC" "3" "Apr, 2024" "HMSDK Programmer\[cq]s Manuals" "" 4 | .hy 5 | .SH NAME 6 | .PP 7 | hmalloc, hfree, hcalloc, hrealloc - allocate and free heterogeneous 8 | dynamic memory 9 | .SH SYNOPSIS 10 | .PP 11 | \f[B]#include \f[R] 12 | .PP 13 | \f[B]void *hmalloc(size_t \f[BI]size\f[B]);\f[R] 14 | .PD 0 15 | .P 16 | .PD 17 | \f[B]void hfree(void \f[BI]*ptr\f[B]);\f[R] 18 | .PD 0 19 | .P 20 | .PD 21 | \f[B]void *hcalloc(size_t \f[BI]nmemb\f[B], size_t 22 | \f[BI]size\f[B]);\f[R] 23 | .PD 0 24 | .P 25 | .PD 26 | \f[B]void *hrealloc(void \f[BI]*ptr\f[B], size_t \f[BI]size\f[B]);\f[R] 27 | .SH DESCRIPTION 28 | .PP 29 | The \f[B]hmalloc\f[R]() function allocates dynamic memory managed in 30 | \f[B]hmalloc pool\f[R] that can be optionally controlled by 31 | \f[B]hmctl\f[R](8) tool and this can make those dynamic memory be 32 | allocated on a specific NUMA node. 33 | Except for this, hmalloc works exactly same as \f[B]malloc\f[R](3). 34 | .PP 35 | Likewise, \f[B]hcalloc\f[R]() and \f[B]hrealloc\f[R]() work exactly same 36 | as \f[B]calloc\f[R](3) and \f[B]realloc\f[R](3), but the allocated 37 | memory can be optionally managed by \f[B]hmctl\f[R](8) tool just like 38 | \f[B]hmalloc\f[R](). 39 | .PP 40 | All those dynamic memory allocated by \f[B]hmalloc\f[R](), 41 | \f[B]hcalloc\f[R](), and \f[B]hrealloc\f[R]() should be freed by 42 | \f[B]hfree\f[R](). 43 | .SH GLOSSARY 44 | .SS HMALLOC APIS 45 | .PP 46 | The \f[B]hmalloc APIs\f[R] are heterogeneous memory allocation APIs 47 | provided by \f[B]libhmalloc.so\f[R] such as \f[B]hmalloc\f[R](3), 48 | \f[B]hcalloc\f[R](3), \f[B]hposix_memalign\f[R](3), \f[B]hmmap\f[R](3), 49 | etc. 50 | All the APIs defined in \f[B]hmalloc.h\f[R] are \f[B]hmalloc APIs\f[R]. 51 | .SS HMALLOC POOL 52 | .PP 53 | The \f[B]hmalloc pool\f[R] is specially managed memory areas that can be 54 | optionally controlled by \f[B]hmctl\f[R](8) tool. 55 | If target programs allocate memory using \f[B]hmalloc APIs\f[R], then 56 | this area is mapped as \f[B]hmalloc pool\f[R]. 57 | This \f[B]hmalloc pool\f[R] has no effect if the target program runs 58 | without \f[B]hmctl\f[R](8), but if it runs with \f[B]hmctl\f[R](8) 59 | attached, then the memory policy of this area can be changed based on 60 | the usage of \f[B]hmctl\f[R](8). 61 | .SS HMCTL 62 | .PP 63 | The \f[B]hmctl\f[R](8) is a tool that controls heterogeneous memory 64 | allocation policy. 65 | That means it can change the memory policy of \f[B]hmalloc pool\f[R] 66 | allocated by \f[B]hmalloc APIs\f[R] internally using \f[B]mmap\f[R](2) 67 | and \f[B]mbind\f[R](2). 68 | If \f[B]hmctl\f[R](8) is attached and 69 | \f[B]-m\f[R]/\f[B]\[en]membind\f[R] or 70 | \f[B]-p\f[R]/\f[B]\[en]preferred\f[R] option is given with a valid NUMA 71 | node ID, then the \f[B]hmalloc pool\f[R] memory is allocated from the 72 | target node with the given memory policy based on the usage of 73 | \f[B]hmctl\f[R](8). 74 | .SH RETURN VALUE 75 | .PP 76 | The return values of \f[B]hmalloc\f[R], \f[B]hcalloc\f[R], and 77 | \f[B]hrealloc\f[R] are same as \f[B]malloc\f[R](3), \f[B]calloc\f[R](3), 78 | and \f[B]hrealloc\f[R](3). 79 | .SH ERRORS 80 | .PP 81 | Likewise, \f[B]hmalloc\f[R](), \f[B]hcalloc\f[R](), and 82 | \f[B]hrealloc\f[R]() can fail with the following error: 83 | .PP 84 | \f[B]ENOMEM\f[R] Out of memory. 85 | Possibly, the application hit the \f[B]RLIMIT_AS\f[R] or 86 | \f[B]RLIMIT_DATA\f[R] limit described in \f[B]getrlimit\f[R](2). 87 | .SH SEE ALSO 88 | .PP 89 | \f[B]hmctl\f[R](8), \f[B]malloc\f[R](3), \f[B]free\f[R](3), 90 | \f[B]calloc\f[R](3), \f[B]realloc\f[R](3) 91 | .SH AUTHORS 92 | Honggyu Kim , Yunjeong Mun . 93 | -------------------------------------------------------------------------------- /hmalloc/doc/hmalloc.md: -------------------------------------------------------------------------------- 1 | % HMALLOC(3) HMSDK Programmer's Manuals 2 | % Honggyu Kim , Yunjeong Mun 3 | % Apr, 2024 4 | 5 | NAME 6 | ==== 7 | hmalloc, hfree, hcalloc, hrealloc - allocate and free heterogeneous dynamic memory 8 | 9 | 10 | SYNOPSIS 11 | ======== 12 | **#include ** 13 | 14 | **void \*hmalloc(size_t _size_);** \ 15 | **void hfree(void _\*ptr_);** \ 16 | **void \*hcalloc(size_t _nmemb_, size_t _size_);** \ 17 | **void \*hrealloc(void _\*ptr_, size_t _size_);** 18 | 19 | 20 | DESCRIPTION 21 | =========== 22 | The **hmalloc**() function allocates dynamic memory managed in **hmalloc pool** 23 | that can be optionally controlled by **hmctl**(8) tool and this can make those 24 | dynamic memory be allocated on a specific NUMA node. Except for this, hmalloc 25 | works exactly same as **malloc**(3). 26 | 27 | Likewise, **hcalloc**() and **hrealloc**() work exactly same as **calloc**(3) 28 | and **realloc**(3), but the allocated memory can be optionally managed by 29 | **hmctl**(8) tool just like **hmalloc**(). 30 | 31 | All those dynamic memory allocated by **hmalloc**(), **hcalloc**(), and 32 | **hrealloc**() should be freed by **hfree**(). 33 | 34 | 35 | GLOSSARY 36 | ======== 37 | HMALLOC APIS 38 | ------------ 39 | The **hmalloc APIs** are heterogeneous memory allocation APIs provided by 40 | **libhmalloc.so** such as **hmalloc**(3), **hcalloc**(3), 41 | **hposix_memalign**(3), **hmmap**(3), etc. All the APIs defined in 42 | **hmalloc.h** are **hmalloc APIs**. 43 | 44 | HMALLOC POOL 45 | ------------ 46 | The **hmalloc pool** is specially managed memory areas that can be optionally 47 | controlled by **hmctl**(8) tool. 48 | If target programs allocate memory using **hmalloc APIs**, then this area is 49 | mapped as **hmalloc pool**. This **hmalloc pool** has no effect if the target 50 | program runs without **hmctl**(8), but if it runs with **hmctl**(8) attached, 51 | then the memory policy of this area can be changed based on the usage of 52 | **hmctl**(8). 53 | 54 | HMCTL 55 | ----- 56 | The **hmctl**(8) is a tool that controls heterogeneous memory allocation policy. 57 | That means it can change the memory policy of **hmalloc pool** allocated by 58 | **hmalloc APIs** internally using **mmap**(2) and **mbind**(2). 59 | If **hmctl**(8) is attached and **-m**/**--membind** or **-p**/**--preferred** 60 | option is given with a valid NUMA node ID, then the **hmalloc pool** memory is 61 | allocated from the target node with the given memory policy based on the usage 62 | of **hmctl**(8). 63 | 64 | 65 | RETURN VALUE 66 | ============ 67 | The return values of **hmalloc**, **hcalloc**, and **hrealloc** are same as 68 | **malloc**(3), **calloc**(3), and **hrealloc**(3). 69 | 70 | 71 | ERRORS 72 | ====== 73 | Likewise, **hmalloc**(), **hcalloc**(), and **hrealloc**() can fail with the 74 | following error: 75 | 76 | **ENOMEM** Out of memory. Possibly, the application hit the **RLIMIT_AS** or 77 | **RLIMIT_DATA** limit described in **getrlimit**(2). 78 | 79 | 80 | SEE ALSO 81 | ======== 82 | **hmctl**(8), **malloc**(3), **free**(3), **calloc**(3), **realloc**(3) 83 | -------------------------------------------------------------------------------- /hmalloc/doc/hmalloc_usable_size.3: -------------------------------------------------------------------------------- 1 | .\" Automatically generated by Pandoc 2.9.2.1 2 | .\" 3 | .TH "HMALLOC_USABLE_SIZE" "3" "Apr, 2024" "HMSDK Programmer\[cq]s Manuals" "" 4 | .hy 5 | .SH NAME 6 | .PP 7 | hmalloc_usable_size - obtain size of block of memory allocated from 8 | hmalloc pool 9 | .SH SYNOPSIS 10 | .PP 11 | \f[B]#include \f[R] 12 | .PP 13 | \f[B]size_t hmalloc_usable_size (void *\f[BI]ptr\f[B]);\f[R] 14 | .SH DESCRIPTION 15 | .PP 16 | The \f[B]hmalloc_usable_size\f[R]() function returns the number of 17 | usable bytes in the block pointed to by \f[I]ptr\f[R], a pointer to a 18 | block of memory allocated by \f[B]hmalloc APIs\f[R] such as 19 | \f[B]hmalloc\f[R](3). 20 | .SH GLOSSARY 21 | .SS HMALLOC APIS 22 | .PP 23 | The \f[B]hmalloc APIs\f[R] are heterogeneous memory allocation APIs 24 | provided by \f[B]libhmalloc.so\f[R] such as \f[B]hmalloc\f[R](3), 25 | \f[B]hcalloc\f[R](3), \f[B]hposix_memalign\f[R](3), \f[B]hmmap\f[R](3), 26 | etc. 27 | All the APIs defined in \f[B]hmalloc.h\f[R] are \f[B]hmalloc APIs\f[R]. 28 | .SS HMALLOC POOL 29 | .PP 30 | The \f[B]hmalloc pool\f[R] is specially managed memory areas that can be 31 | optionally controlled by \f[B]hmctl\f[R](8) tool. 32 | If target programs allocate memory using \f[B]hmalloc APIs\f[R], then 33 | this area is mapped as \f[B]hmalloc pool\f[R]. 34 | This \f[B]hmalloc pool\f[R] has no effect if the target program runs 35 | without \f[B]hmctl\f[R](8), but if it runs with \f[B]hmctl\f[R](8) 36 | attached, then the memory policy of this area can be changed based on 37 | the usage of \f[B]hmctl\f[R](8). 38 | .SS HMCTL 39 | .PP 40 | The \f[B]hmctl\f[R](8) is a tool that controls heterogeneous memory 41 | allocation policy. 42 | That means it can change the memory policy of \f[B]hmalloc pool\f[R] 43 | allocated by \f[B]hmalloc APIs\f[R] internally using \f[B]mmap\f[R](2) 44 | and \f[B]mbind\f[R](2). 45 | If \f[B]hmctl\f[R](8) is attached and 46 | \f[B]-m\f[R]/\f[B]\[en]membind\f[R] or 47 | \f[B]-p\f[R]/\f[B]\[en]preferred\f[R] option is given with a valid NUMA 48 | node ID, then the \f[B]hmalloc pool\f[R] memory is allocated from the 49 | target node with the given memory policy based on the usage of 50 | \f[B]hmctl\f[R](8). 51 | .SH RETURN VALUE 52 | .PP 53 | \f[B]hmalloc_usable_size\f[R]() returns the number of usable bytes in 54 | the block of allocated memory pointed to by \f[I]ptr\f[R]. 55 | If \f[I]ptr\f[R] is NULL, 0 is returned. 56 | .SH SEE ALSO 57 | .PP 58 | \f[B]hmctl\f[R](8), \f[B]hmalloc\f[R](3), 59 | \f[B]malloc_usable_size\f[R](3) 60 | .SH AUTHORS 61 | Honggyu Kim , Yunjeong Mun . 62 | -------------------------------------------------------------------------------- /hmalloc/doc/hmalloc_usable_size.md: -------------------------------------------------------------------------------- 1 | % HMALLOC_USABLE_SIZE(3) HMSDK Programmer's Manuals 2 | % Honggyu Kim , Yunjeong Mun 3 | % Apr, 2024 4 | 5 | NAME 6 | ==== 7 | hmalloc_usable_size - obtain size of block of memory allocated from hmalloc pool 8 | 9 | 10 | SYNOPSIS 11 | ======== 12 | **#include ** 13 | 14 | **size_t hmalloc_usable_size (void \*_ptr_);** 15 | 16 | 17 | DESCRIPTION 18 | =========== 19 | The **hmalloc_usable_size**() function returns the number of usable bytes in the 20 | block pointed to by _ptr_, a pointer to a block of memory allocated by 21 | **hmalloc APIs** such as **hmalloc**(3). 22 | 23 | 24 | GLOSSARY 25 | ======== 26 | HMALLOC APIS 27 | ------------ 28 | The **hmalloc APIs** are heterogeneous memory allocation APIs provided by 29 | **libhmalloc.so** such as **hmalloc**(3), **hcalloc**(3), 30 | **hposix_memalign**(3), **hmmap**(3), etc. All the APIs defined in 31 | **hmalloc.h** are **hmalloc APIs**. 32 | 33 | HMALLOC POOL 34 | ------------ 35 | The **hmalloc pool** is specially managed memory areas that can be optionally 36 | controlled by **hmctl**(8) tool. 37 | If target programs allocate memory using **hmalloc APIs**, then this area is 38 | mapped as **hmalloc pool**. This **hmalloc pool** has no effect if the target 39 | program runs without **hmctl**(8), but if it runs with **hmctl**(8) attached, 40 | then the memory policy of this area can be changed based on the usage of 41 | **hmctl**(8). 42 | 43 | HMCTL 44 | ----- 45 | The **hmctl**(8) is a tool that controls heterogeneous memory allocation policy. 46 | That means it can change the memory policy of **hmalloc pool** allocated by 47 | **hmalloc APIs** internally using **mmap**(2) and **mbind**(2). 48 | If **hmctl**(8) is attached and **-m**/**--membind** or **-p**/**--preferred** 49 | option is given with a valid NUMA node ID, then the **hmalloc pool** memory is 50 | allocated from the target node with the given memory policy based on the usage 51 | of **hmctl**(8). 52 | 53 | 54 | RETURN VALUE 55 | ============ 56 | **hmalloc_usable_size**() returns the number of usable bytes in the block of 57 | allocated memory pointed to by _ptr_. If _ptr_ is NULL, 0 is returned. 58 | 59 | 60 | SEE ALSO 61 | ======== 62 | **hmctl**(8), **hmalloc**(3), **malloc_usable_size**(3) 63 | -------------------------------------------------------------------------------- /hmalloc/doc/hmctl.8: -------------------------------------------------------------------------------- 1 | .\" Automatically generated by Pandoc 2.9.2.1 2 | .\" 3 | .TH "HMCTL" "8" "Apr, 2024" "Hmctl User Manuals" "" 4 | .hy 5 | .SH NAME 6 | .PP 7 | hmctl - Control heterogeneous memory allocation policy 8 | .SH SYNOPSIS 9 | .PP 10 | hmctl [\f[I]options\f[R]] COMMAND [\f[I]command-options\f[R]] 11 | .SH DESCRIPTION 12 | .PP 13 | The \f[B]hmctl\f[R] tool is to control heterogeneous memory allocation 14 | policy. 15 | It changes memory policy only for the area allocated by \f[B]hmalloc 16 | APIs\f[R], which is called as \f[B]hmalloc pool\f[R] then executes the 17 | given \f[C]COMMAND\f[R] program. 18 | The rest of memory regions other than \f[B]hmalloc pool\f[R] are not 19 | affected by this tool. 20 | .PP 21 | \f[I]nodes\f[R] may be specified as N,N,N or N-N or N,N-N or N-N,N-N and 22 | so forth. 23 | Please see its annotation details at \f[B]numactl\f[R](8). 24 | .SH OPTIONS 25 | .TP 26 | -m \f[I]nodes\f[R], --membind=\f[I]nodes\f[R] 27 | Allocate memory only from \f[I]nodes\f[R] for hmalloc family 28 | allocations. 29 | .TP 30 | -p \f[I]node\f[R], --preferred=\f[I]node\f[R] 31 | Preferably allocate memory on \f[I]node\f[R] for hmalloc family 32 | allocations. 33 | .TP 34 | -P \f[I]nodes\f[R], --preferred-many=\f[I]nodes\f[R] 35 | Preferably allocate memory on \f[I]nodes\f[R] for hmalloc family 36 | allocations. 37 | \f[I]nodes\f[R] are a mask of preferred nodes where the closest node to 38 | local is considered most preferred. 39 | .TP 40 | -i \f[I]nodes\f[R], --interleave=\f[I]nodes\f[R] 41 | Set a memory interleave policy. 42 | Memory will be allocated using round robin on \f[I]nodes\f[R]. 43 | .TP 44 | -w \f[I]nodes\f[R], --weighted-interleave=\f[I]nodes\f[R] 45 | Set a weighted memory interleave policy. 46 | Memory will be allocated using the weighted ratio for each node, which 47 | can be read from /sys/kernel/mm/mempolicy/weighted_interleave/node*. 48 | .TP 49 | -?, --help 50 | Print help message and list of options with description 51 | .TP 52 | --usage 53 | Print usage string 54 | .SH EXAMPLES 55 | .PP 56 | Let\[cq]s say if the target test program allocates 512 MiB using 57 | \f[B]hmalloc\f[R](3), then this memory area can be allocated to the 58 | intended node as follows. 59 | .IP 60 | .nf 61 | \f[C] 62 | # Allocate hmalloc area to node 2 with MPOL_BIND policy. 63 | $ hmctl -m 2 ./prog 64 | 65 | # Allocate hmalloc area to node 1 with MPOL_PREFERRED policy. 66 | $ hmctl -p 1 ./prog 67 | \f[R] 68 | .fi 69 | .PP 70 | If you want to use a different memory policy with global policy, then 71 | hmctl can be used along with numactl as follows. 72 | .IP 73 | .nf 74 | \f[C] 75 | # Set global policy to MPOL_PREFERRED for node 1, 76 | # but use MPOL_BIND for node 2 only to hmalloc area. 77 | $ numactl -p 1 hmctl -m 2 ./prog 78 | 79 | # Set global policy to MPOL_INTERLEAVE for node 1 and 2, 80 | # but use MPOL_PREFERRED for node 3 only to hmalloc area. 81 | $ numactl -i 1,2 hmctl -p 3 ./prog 82 | \f[R] 83 | .fi 84 | .SH GLOSSARY 85 | .SS HMALLOC APIS 86 | .PP 87 | The \f[B]hmalloc APIs\f[R] are heterogeneous memory allocation APIs 88 | provided by \f[B]libhmalloc.so\f[R] such as \f[B]hmalloc\f[R](3), 89 | \f[B]hcalloc\f[R](3), \f[B]hposix_memalign\f[R](3), \f[B]hmmap\f[R](3), 90 | etc. 91 | All the APIs defined in \f[B]hmalloc.h\f[R] are \f[B]hmalloc APIs\f[R]. 92 | .SS HMALLOC POOL 93 | .PP 94 | The \f[B]hmalloc pool\f[R] is specially managed memory areas that can be 95 | optionally controlled by \f[B]hmctl\f[R](8) tool. 96 | If target programs allocate memory using \f[B]hmalloc APIs\f[R], then 97 | this area is mapped as \f[B]hmalloc pool\f[R]. 98 | This \f[B]hmalloc pool\f[R] has no effect if the target program runs 99 | without \f[B]hmctl\f[R](8), but if it runs with \f[B]hmctl\f[R](8) 100 | attached, then the memory policy of this area can be changed based on 101 | the usage of \f[B]hmctl\f[R](8). 102 | .SS HMCTL 103 | .PP 104 | The \f[B]hmctl\f[R](8) is a tool that controls heterogeneous memory 105 | allocation policy. 106 | That means it can change the memory policy of \f[B]hmalloc pool\f[R] 107 | allocated by \f[B]hmalloc APIs\f[R] internally using \f[B]mmap\f[R](2) 108 | and \f[B]mbind\f[R](2). 109 | If \f[B]hmctl\f[R](8) is attached and one of memory policy option is 110 | given with valid \f[I]node\f[R] or \f[I]nodes\f[R] argument, then the 111 | \f[B]hmalloc pool\f[R] memory is allocated from the target node with the 112 | given memory policy based on the usage of \f[B]hmctl\f[R](8). 113 | .SH SEE ALSO 114 | .PP 115 | \f[B]numactl\f[R](8), \f[B]hmalloc\f[R](3) 116 | .SH AUTHORS 117 | Honggyu Kim . 118 | -------------------------------------------------------------------------------- /hmalloc/doc/hmctl.md: -------------------------------------------------------------------------------- 1 | % HMCTL(8) Hmctl User Manuals 2 | % Honggyu Kim 3 | % Apr, 2024 4 | 5 | NAME 6 | ==== 7 | hmctl - Control heterogeneous memory allocation policy 8 | 9 | 10 | SYNOPSIS 11 | ======== 12 | hmctl [_options_] COMMAND [_command-options_] 13 | 14 | 15 | DESCRIPTION 16 | =========== 17 | The **hmctl** tool is to control heterogeneous memory allocation policy. It 18 | changes memory policy only for the area allocated by **hmalloc APIs**, which is 19 | called as **hmalloc pool** then executes the given `COMMAND` program. The rest 20 | of memory regions other than **hmalloc pool** are not affected by this tool. 21 | 22 | _nodes_ may be specified as N,N,N or N-N or N,N-N or N-N,N-N and so forth. 23 | Please see its annotation details at **numactl**(8). 24 | 25 | 26 | OPTIONS 27 | ======= 28 | -m _nodes_, \--membind=_nodes_ 29 | : Allocate memory only from _nodes_ for hmalloc family allocations. 30 | 31 | -p _node_, \--preferred=_node_ 32 | : Preferably allocate memory on _node_ for hmalloc family allocations. 33 | 34 | -P _nodes_, \--preferred-many=_nodes_ 35 | : Preferably allocate memory on _nodes_ for hmalloc family allocations. 36 | _nodes_ are a mask of preferred nodes where the closest node to local is 37 | considered most preferred. 38 | 39 | -i _nodes_, \--interleave=_nodes_ 40 | : Set a memory interleave policy. Memory will be allocated using round robin 41 | on _nodes_. 42 | 43 | -w _nodes_, \--weighted-interleave=_nodes_ 44 | : Set a weighted memory interleave policy. Memory will be allocated using the 45 | weighted ratio for each node, which can be read from 46 | /sys/kernel/mm/mempolicy/weighted_interleave/node*. 47 | 48 | -?, \--help 49 | : Print help message and list of options with description 50 | 51 | \--usage 52 | : Print usage string 53 | 54 | 55 | EXAMPLES 56 | ======== 57 | Let's say if the target test program allocates 512 MiB using **hmalloc**(3), 58 | then this memory area can be allocated to the intended node as follows. 59 | 60 | # Allocate hmalloc area to node 2 with MPOL_BIND policy. 61 | $ hmctl -m 2 ./prog 62 | 63 | # Allocate hmalloc area to node 1 with MPOL_PREFERRED policy. 64 | $ hmctl -p 1 ./prog 65 | 66 | If you want to use a different memory policy with global policy, then hmctl can 67 | be used along with numactl as follows. 68 | 69 | # Set global policy to MPOL_PREFERRED for node 1, 70 | # but use MPOL_BIND for node 2 only to hmalloc area. 71 | $ numactl -p 1 hmctl -m 2 ./prog 72 | 73 | # Set global policy to MPOL_INTERLEAVE for node 1 and 2, 74 | # but use MPOL_PREFERRED for node 3 only to hmalloc area. 75 | $ numactl -i 1,2 hmctl -p 3 ./prog 76 | 77 | 78 | GLOSSARY 79 | ======== 80 | HMALLOC APIS 81 | ------------ 82 | The **hmalloc APIs** are heterogeneous memory allocation APIs provided by 83 | **libhmalloc.so** such as **hmalloc**(3), **hcalloc**(3), 84 | **hposix_memalign**(3), **hmmap**(3), etc. All the APIs defined in 85 | **hmalloc.h** are **hmalloc APIs**. 86 | 87 | HMALLOC POOL 88 | ------------ 89 | The **hmalloc pool** is specially managed memory areas that can be optionally 90 | controlled by **hmctl**(8) tool. 91 | If target programs allocate memory using **hmalloc APIs**, then this area is 92 | mapped as **hmalloc pool**. This **hmalloc pool** has no effect if the target 93 | program runs without **hmctl**(8), but if it runs with **hmctl**(8) attached, 94 | then the memory policy of this area can be changed based on the usage of 95 | **hmctl**(8). 96 | 97 | HMCTL 98 | ----- 99 | The **hmctl**(8) is a tool that controls heterogeneous memory allocation policy. 100 | That means it can change the memory policy of **hmalloc pool** allocated by 101 | **hmalloc APIs** internally using **mmap**(2) and **mbind**(2). 102 | If **hmctl**(8) is attached and one of memory policy option is given with valid 103 | _node_ or _nodes_ argument, then the **hmalloc pool** memory is allocated from 104 | the target node with the given memory policy based on the usage of **hmctl**(8). 105 | 106 | 107 | SEE ALSO 108 | ======== 109 | **numactl**(8), **hmalloc**(3) 110 | -------------------------------------------------------------------------------- /hmalloc/doc/hmmap.3: -------------------------------------------------------------------------------- 1 | .\" Automatically generated by Pandoc 2.9.2.1 2 | .\" 3 | .TH "HMMAP" "3" "Apr, 2024" "HMSDK Programmer\[cq]s Manuals" "" 4 | .hy 5 | .SH NAME 6 | .PP 7 | hmmap, hmunmap - map or unmap files or devices into heterogeneous memory 8 | .SH SYNOPSIS 9 | .PP 10 | \f[B]#include \f[R] 11 | .PP 12 | \f[B]void *hmmap(void *\f[BI]addr\f[B], size_t \f[BI]length\f[B], int 13 | \f[BI]prot\f[B], int \f[BI]flags\f[B], int \f[BI]fd\f[B], off_t 14 | \f[BI]offset\f[B]);\f[R] 15 | .PD 0 16 | .P 17 | .PD 18 | \f[B]int hmunmap(void *\f[BI]addr\f[B], size_t \f[BI]length\f[B]);\f[R] 19 | .SH DESCRIPTION 20 | .PP 21 | \f[B]hmmap\f[R]() creates a new mapping in the virtual address space of 22 | the calling process same as \f[B]mmap\f[R](2) so the meaning of each 23 | argument can simply be refer to the manual of \f[B]mmap\f[R](2). 24 | .PP 25 | However, there are some differences between \f[B]hmmap\f[R] and 26 | \f[B]mmap(2)\f[R]. 27 | The \f[B]hmmap\f[R] allocates memory in \f[B]hmalloc pool\f[R] that can 28 | be optionally controlled by \f[B]hmctl\f[R](8) tool. 29 | .SH GLOSSARY 30 | .SS HMALLOC APIS 31 | .PP 32 | The \f[B]hmalloc APIs\f[R] are heterogeneous memory allocation APIs 33 | provided by \f[B]libhmalloc.so\f[R] such as \f[B]hmalloc\f[R](3), 34 | \f[B]hcalloc\f[R](3), \f[B]hposix_memalign\f[R](3), \f[B]hmmap\f[R](3), 35 | etc. 36 | All the APIs defined in \f[B]hmalloc.h\f[R] are \f[B]hmalloc APIs\f[R]. 37 | .SS HMALLOC POOL 38 | .PP 39 | The \f[B]hmalloc pool\f[R] is specially managed memory areas that can be 40 | optionally controlled by \f[B]hmctl\f[R](8) tool. 41 | If target programs allocate memory using \f[B]hmalloc APIs\f[R], then 42 | this area is mapped as \f[B]hmalloc pool\f[R]. 43 | This \f[B]hmalloc pool\f[R] has no effect if the target program runs 44 | without \f[B]hmctl\f[R](8), but if it runs with \f[B]hmctl\f[R](8) 45 | attached, then the memory policy of this area can be changed based on 46 | the usage of \f[B]hmctl\f[R](8). 47 | .SS HMCTL 48 | .PP 49 | The \f[B]hmctl\f[R](8) is a tool that controls heterogeneous memory 50 | allocation policy. 51 | That means it can change the memory policy of \f[B]hmalloc pool\f[R] 52 | allocated by \f[B]hmalloc APIs\f[R] internally using \f[B]mmap\f[R](2) 53 | and \f[B]mbind\f[R](2). 54 | If \f[B]hmctl\f[R](8) is attached and 55 | \f[B]-m\f[R]/\f[B]\[en]membind\f[R] or 56 | \f[B]-p\f[R]/\f[B]\[en]preferred\f[R] option is given with a valid NUMA 57 | node ID, then the \f[B]hmalloc pool\f[R] memory is allocated from the 58 | target node with the given memory policy based on the usage of 59 | \f[B]hmctl\f[R](8). 60 | .SH RETURN VALUE 61 | .PP 62 | On success, \f[B]hmmap\f[R]() returns a pointer to the mapped area in 63 | \f[B]hmalloc pool\f[R]. 64 | .PP 65 | On error of \f[B]hmmap\f[R]() due to the internal \f[B]mmap\f[R](2) 66 | fails, the value \f[B]MAP_FAILED\f[R] (that is, \f[I](void *) -1\f[R]) 67 | is returned, and \f[I]errno\f[R] is set by \f[B]mmap\f[R](2) to indicate 68 | the cause of the error. 69 | If \f[B]mmap\f[R](2) is successful but \f[B]mbind\f[R](2) fails, then it 70 | unmaps the area again and returns \f[B]NULL\f[R] with \f[I]errno\f[R] 71 | set by \f[B]mbind\f[R](2). 72 | .PP 73 | On success, \f[B]hmunmap\f[R]() returns 0. 74 | On failure, it returns -1, and \f[I]errno\f[R] is set to indicate the 75 | cause of the error (probably to \f[B]EINVAL\f[R]). 76 | .SH ERRORS 77 | .PP 78 | See \f[B]mmap\f[R](2), \f[B]mbind\f[R](2) and \f[B]munmap\f[R](2) 79 | .SH SEE ALSO 80 | .PP 81 | \f[B]hmctl\f[R](8), \f[B]mmap\f[R](2), \f[B]mbind\f[R](2), 82 | \f[B]munmap\f[R](2) 83 | .SH AUTHORS 84 | Honggyu Kim , Yunjeong Mun . 85 | -------------------------------------------------------------------------------- /hmalloc/doc/hmmap.md: -------------------------------------------------------------------------------- 1 | % HMMAP(3) HMSDK Programmer's Manuals 2 | % Honggyu Kim , Yunjeong Mun 3 | % Apr, 2024 4 | 5 | NAME 6 | ==== 7 | hmmap, hmunmap - map or unmap files or devices into heterogeneous memory 8 | 9 | 10 | SYNOPSIS 11 | ======== 12 | **#include ** 13 | 14 | **void \*hmmap(void \*_addr_, size_t _length_, int _prot_, int _flags_, int _fd_, off_t _offset_);** \ 15 | **int hmunmap(void \*_addr_, size_t _length_);** 16 | 17 | 18 | DESCRIPTION 19 | =========== 20 | **hmmap**() creates a new mapping in the virtual address space of the calling 21 | process same as **mmap**(2) so the meaning of each argument can simply be refer 22 | to the manual of **mmap**(2). 23 | 24 | However, there are some differences between **hmmap** and **mmap(2)**. The 25 | **hmmap** allocates memory in **hmalloc pool** that can be optionally controlled 26 | by **hmctl**(8) tool. 27 | 28 | 29 | GLOSSARY 30 | ======== 31 | HMALLOC APIS 32 | ------------ 33 | The **hmalloc APIs** are heterogeneous memory allocation APIs provided by 34 | **libhmalloc.so** such as **hmalloc**(3), **hcalloc**(3), 35 | **hposix_memalign**(3), **hmmap**(3), etc. All the APIs defined in 36 | **hmalloc.h** are **hmalloc APIs**. 37 | 38 | HMALLOC POOL 39 | ------------ 40 | The **hmalloc pool** is specially managed memory areas that can be optionally 41 | controlled by **hmctl**(8) tool. 42 | If target programs allocate memory using **hmalloc APIs**, then this area is 43 | mapped as **hmalloc pool**. This **hmalloc pool** has no effect if the target 44 | program runs without **hmctl**(8), but if it runs with **hmctl**(8) attached, 45 | then the memory policy of this area can be changed based on the usage of 46 | **hmctl**(8). 47 | 48 | HMCTL 49 | ----- 50 | The **hmctl**(8) is a tool that controls heterogeneous memory allocation policy. 51 | That means it can change the memory policy of **hmalloc pool** allocated by 52 | **hmalloc APIs** internally using **mmap**(2) and **mbind**(2). 53 | If **hmctl**(8) is attached and **-m**/**--membind** or **-p**/**--preferred** 54 | option is given with a valid NUMA node ID, then the **hmalloc pool** memory is 55 | allocated from the target node with the given memory policy based on the usage 56 | of **hmctl**(8). 57 | 58 | 59 | RETURN VALUE 60 | ============ 61 | On success, **hmmap**() returns a pointer to the mapped area in **hmalloc pool**. 62 | 63 | On error of **hmmap**() due to the internal **mmap**(2) fails, the value 64 | **MAP_FAILED** (that is, _(void \*) -1_) is returned, and _errno_ is set by 65 | **mmap**(2) to indicate the cause of the error. If **mmap**(2) is successful 66 | but **mbind**(2) fails, then it unmaps the area again and returns **NULL** with 67 | _errno_ set by **mbind**(2). 68 | 69 | On success, **hmunmap**() returns 0. On failure, it returns -1, and _errno_ is 70 | set to indicate the cause of the error (probably to **EINVAL**). 71 | 72 | 73 | ERRORS 74 | ====== 75 | See **mmap**(2), **mbind**(2) and **munmap**(2) 76 | 77 | 78 | SEE ALSO 79 | ======== 80 | **hmctl**(8), **mmap**(2), **mbind**(2), **munmap**(2) 81 | -------------------------------------------------------------------------------- /hmalloc/doc/hposix_memalign.3: -------------------------------------------------------------------------------- 1 | .\" Automatically generated by Pandoc 2.9.2.1 2 | .\" 3 | .TH "HPOSIX_MEMALIGN" "3" "Apr, 2024" "HMSDK Programmer\[cq]s Manuals" "" 4 | .hy 5 | .SH NAME 6 | .PP 7 | hposix_memalign, haligned_alloc - allocate aligned heterogeneous memory 8 | .SH SYNOPSIS 9 | .PP 10 | \f[B]#include \f[R] 11 | .PP 12 | \f[B]int hposix_memalign(void **\f[BI]memptr\f[B], size_t 13 | \f[BI]alignment\f[B], size_t \f[BI]size\f[B]);\f[R] 14 | .PD 0 15 | .P 16 | .PD 17 | \f[B]void *haligned_alloc(size_t \f[BI]alignment\f[B], size_t 18 | \f[BI]size\f[B]);\f[R] 19 | .SH DESCRIPTION 20 | .PP 21 | The function \f[B]hposix_memalign\f[R]() allocates \f[I]size\f[R] bytes 22 | and places the address of the allocated memory in \f[I]*memptr\f[R] from 23 | \f[B]hmalloc pool\f[R] that can be optionally controlled by 24 | \f[B]hmctl\f[R](8) tool. 25 | .PP 26 | The behaviors of \f[B]hposix_memalign\f[R] is same as 27 | \f[B]posix_memalign\f[R](3) so the address of the allocated memory will 28 | be a multiple of \f[I]alignment\f[R], which must be a power of two and a 29 | multiple of \f[I]sizeof(void *)\f[R]. 30 | This address can later be successfully passed to \f[B]hfree\f[R](3). 31 | Even if \f[I]size\f[R] is 0, then the value placed in \f[I]*memptr\f[R] 32 | is a unique pointer value that should be deallocated by 33 | \f[B]hfree\f[R](3). 34 | .PP 35 | The function \f[B]haligned_alloc\f[R]() works same as 36 | \f[B]aligned_alloc\f[R](3) so the returned pointer will be a multiple of 37 | \f[I]alignment\f[R], which must be a power of two. 38 | This pointer should be deallocated by \f[B]hfree\f[R](3) later. 39 | .SH GLOSSARY 40 | .SS HMALLOC APIS 41 | .PP 42 | The \f[B]hmalloc APIs\f[R] are heterogeneous memory allocation APIs 43 | provided by \f[B]libhmalloc.so\f[R] such as \f[B]hmalloc\f[R](3), 44 | \f[B]hcalloc\f[R](3), \f[B]hposix_memalign\f[R](3), \f[B]hmmap\f[R](3), 45 | etc. 46 | All the APIs defined in \f[B]hmalloc.h\f[R] are \f[B]hmalloc APIs\f[R]. 47 | .SS HMALLOC POOL 48 | .PP 49 | The \f[B]hmalloc pool\f[R] is specially managed memory areas that can be 50 | optionally controlled by \f[B]hmctl\f[R](8) tool. 51 | If target programs allocate memory using \f[B]hmalloc APIs\f[R], then 52 | this area is mapped as \f[B]hmalloc pool\f[R]. 53 | This \f[B]hmalloc pool\f[R] has no effect if the target program runs 54 | without \f[B]hmctl\f[R](8), but if it runs with \f[B]hmctl\f[R](8) 55 | attached, then the memory policy of this area can be changed based on 56 | the usage of \f[B]hmctl\f[R](8). 57 | .SS HMCTL 58 | .PP 59 | The \f[B]hmctl\f[R](8) is a tool that controls heterogeneous memory 60 | allocation policy. 61 | That means it can change the memory policy of \f[B]hmalloc pool\f[R] 62 | allocated by \f[B]hmalloc APIs\f[R] internally using \f[B]mmap\f[R](2) 63 | and \f[B]mbind\f[R](2). 64 | If \f[B]hmctl\f[R](8) is attached and 65 | \f[B]-m\f[R]/\f[B]\[en]membind\f[R] or 66 | \f[B]-p\f[R]/\f[B]\[en]preferred\f[R] option is given with a valid NUMA 67 | node ID, then the \f[B]hmalloc pool\f[R] memory is allocated from the 68 | target node with the given memory policy based on the usage of 69 | \f[B]hmctl\f[R](8). 70 | .SH RETURN VALUE 71 | .PP 72 | \f[B]hposix_memalign\f[R]() returns zero on success, or one of the error 73 | values listed in the \f[B]ERRORS\f[R] section on failure. 74 | The value of \f[I]errno\f[R] is not set. 75 | .PP 76 | \f[B]haligned_alloc\f[R]() returns a pointer to the allocated memory on 77 | success. 78 | On error, NULL is returned, and \f[I]errno\f[R] is set to indicate the 79 | cause of the error. 80 | .SH ERRORS 81 | .PP 82 | \f[B]EINVAL\f[R] The \f[I]alignment\f[R] argument was not a power of 83 | two, or was not a multiple of \f[I]sizeof(void *)\f[R]. 84 | .PP 85 | \f[B]ENOMEM\f[R] There was insufficient memory to fulfill the allocation 86 | request from \f[B]hmalloc pool\f[R]. 87 | .SH SEE ALSO 88 | .PP 89 | \f[B]hmctl\f[R](8), \f[B]hfree\f[R](3), \f[B]posix_memalign\f[R](3), 90 | \f[B]aligned_alloc\f[R](3) 91 | .SH AUTHORS 92 | Honggyu Kim , Yunjeong Mun . 93 | -------------------------------------------------------------------------------- /hmalloc/doc/hposix_memalign.md: -------------------------------------------------------------------------------- 1 | % HPOSIX_MEMALIGN(3) HMSDK Programmer's Manuals 2 | % Honggyu Kim , Yunjeong Mun 3 | % Apr, 2024 4 | 5 | NAME 6 | ==== 7 | hposix_memalign, haligned_alloc - allocate aligned heterogeneous memory 8 | 9 | 10 | SYNOPSIS 11 | ======== 12 | **#include ** 13 | 14 | **int hposix_memalign(void \*\*_memptr_, size_t _alignment_, size_t _size_);** \ 15 | **void \*haligned_alloc(size_t _alignment_, size_t _size_);** 16 | 17 | 18 | DESCRIPTION 19 | =========== 20 | The function **hposix_memalign**() allocates _size_ bytes and places the address 21 | of the allocated memory in _\*memptr_ from **hmalloc pool** that can be 22 | optionally controlled by **hmctl**(8) tool. 23 | 24 | The behaviors of **hposix_memalign** is same as **posix_memalign**(3) so the 25 | address of the allocated memory will be a multiple of _alignment_, 26 | which must be a power of two and a multiple of _sizeof(void \*)_. This address 27 | can later be successfully passed to **hfree**(3). Even if _size_ is 0, then the 28 | value placed in _\*memptr_ is a unique pointer value that should be deallocated 29 | by **hfree**(3). 30 | 31 | The function **haligned_alloc**() works same as **aligned_alloc**(3) so the 32 | returned pointer will be a multiple of _alignment_, which must be a power of 33 | two. This pointer should be deallocated by **hfree**(3) later. 34 | 35 | 36 | GLOSSARY 37 | ======== 38 | HMALLOC APIS 39 | ------------ 40 | The **hmalloc APIs** are heterogeneous memory allocation APIs provided by 41 | **libhmalloc.so** such as **hmalloc**(3), **hcalloc**(3), 42 | **hposix_memalign**(3), **hmmap**(3), etc. All the APIs defined in 43 | **hmalloc.h** are **hmalloc APIs**. 44 | 45 | HMALLOC POOL 46 | ------------ 47 | The **hmalloc pool** is specially managed memory areas that can be optionally 48 | controlled by **hmctl**(8) tool. 49 | If target programs allocate memory using **hmalloc APIs**, then this area is 50 | mapped as **hmalloc pool**. This **hmalloc pool** has no effect if the target 51 | program runs without **hmctl**(8), but if it runs with **hmctl**(8) attached, 52 | then the memory policy of this area can be changed based on the usage of 53 | **hmctl**(8). 54 | 55 | HMCTL 56 | ----- 57 | The **hmctl**(8) is a tool that controls heterogeneous memory allocation policy. 58 | That means it can change the memory policy of **hmalloc pool** allocated by 59 | **hmalloc APIs** internally using **mmap**(2) and **mbind**(2). 60 | If **hmctl**(8) is attached and **-m**/**--membind** or **-p**/**--preferred** 61 | option is given with a valid NUMA node ID, then the **hmalloc pool** memory is 62 | allocated from the target node with the given memory policy based on the usage 63 | of **hmctl**(8). 64 | 65 | 66 | RETURN VALUE 67 | ============ 68 | **hposix_memalign**() returns zero on success, or one of the error values listed 69 | in the **ERRORS** section on failure. The value of _errno_ is not set. 70 | 71 | **haligned_alloc**() returns a pointer to the allocated memory on success. On 72 | error, NULL is returned, and _errno_ is set to indicate the cause of the error. 73 | 74 | 75 | ERRORS 76 | ====== 77 | **EINVAL** The _alignment_ argument was not a power of two, or was not a 78 | multiple of _sizeof(void \*)_. 79 | 80 | **ENOMEM** There was insufficient memory to fulfill the allocation request from 81 | **hmalloc pool**. 82 | 83 | 84 | SEE ALSO 85 | ======== 86 | **hmctl**(8), **hfree**(3), **posix_memalign**(3), **aligned_alloc**(3) 87 | -------------------------------------------------------------------------------- /hmalloc/include/hmalloc.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2024 SK hynix, Inc. */ 2 | /* SPDX-License-Identifier: BSD 2-Clause */ 3 | 4 | #ifndef HMALLOC_H 5 | #define HMALLOC_H 6 | 7 | #include 8 | #include 9 | 10 | #ifdef __cplusplus 11 | extern "C" { 12 | #endif 13 | 14 | void *hmalloc(size_t size); 15 | void hfree(void *ptr); 16 | void *hcalloc(size_t nmemb, size_t size); 17 | void *hrealloc(void *ptr, size_t size); 18 | void *haligned_alloc(size_t alignment, size_t size); 19 | int hposix_memalign(void **memptr, size_t alignment, size_t size); 20 | void *hmmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset); 21 | int hmunmap(void *addr, size_t length); 22 | size_t hmalloc_usable_size(void *ptr); 23 | 24 | #ifdef __cplusplus 25 | } 26 | #endif 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /hmalloc/src/env.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2024 SK hynix, Inc. */ 2 | /* SPDX-License-Identifier: BSD 2-Clause */ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | bool getenv_jemalloc(void) { 10 | char *env = getenv("HMALLOC_JEMALLOC"); 11 | 12 | if (env && !strcmp(env, "1")) 13 | return true; 14 | return false; 15 | } 16 | 17 | unsigned long getenv_nodemask(void) { 18 | char *env = getenv("HMALLOC_NODEMASK"); 19 | 20 | if (!env) 21 | return 0; 22 | return atol(env); 23 | } 24 | 25 | int getenv_mpol_mode(void) { 26 | char *env = getenv("HMALLOC_MPOL_MODE"); 27 | 28 | if (!env) 29 | return MPOL_DEFAULT; 30 | return atoi(env); 31 | } 32 | -------------------------------------------------------------------------------- /hmalloc/src/env.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2024 SK hynix, Inc. */ 2 | /* SPDX-License-Identifier: BSD 2-Clause */ 3 | 4 | #include 5 | 6 | bool getenv_jemalloc(void); 7 | unsigned long getenv_nodemask(void); 8 | int getenv_mpol_mode(void); 9 | -------------------------------------------------------------------------------- /hmalloc/src/hmalloc.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2024 SK hynix, Inc. */ 2 | /* SPDX-License-Identifier: BSD 2-Clause */ 3 | 4 | #include "env.h" 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #define likely(x) __builtin_expect(!!(x), 1) 18 | #define unlikely(x) __builtin_expect(!!(x), 0) 19 | 20 | #define __unused __attribute__((unused)) 21 | 22 | #define is_pow2(val) (((val) & ((val)-1)) == 0) 23 | 24 | /* global variables set by environment variables */ 25 | static bool use_jemalloc; 26 | static unsigned long nodemask; 27 | static int mpol_mode; 28 | 29 | static unsigned arena_index; 30 | static extent_hooks_t *hooks; 31 | 32 | static int maxnode; 33 | 34 | void *hmmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset) { 35 | void *new_addr = mmap(addr, length, prot, flags, fd, offset); 36 | if (unlikely(new_addr == MAP_FAILED)) 37 | return MAP_FAILED; 38 | 39 | if (nodemask > 0) { 40 | long ret = mbind(new_addr, length, mpol_mode, &nodemask, maxnode, 0); 41 | if (unlikely(ret)) { 42 | int mbind_errno = errno; 43 | munmap(new_addr, length); 44 | errno = mbind_errno; 45 | return NULL; 46 | } 47 | } 48 | return new_addr; 49 | } 50 | 51 | int hmunmap(void *addr, size_t length) { 52 | return munmap(addr, length); 53 | } 54 | 55 | void *extent_alloc(extent_hooks_t *extent_hooks __unused, void *new_addr, size_t size, 56 | size_t alignment __unused, bool *zero __unused, bool *commit __unused, 57 | unsigned arena_ind __unused) { 58 | new_addr = hmmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, 0, 0); 59 | return new_addr; 60 | } 61 | 62 | static bool extent_dalloc(extent_hooks_t *extent_hooks __unused, void *addr, size_t size, 63 | bool committed __unused, unsigned arena_ind __unused) { 64 | return hmunmap(addr, size); 65 | } 66 | 67 | static extent_hooks_t extent_hooks = { 68 | .alloc = extent_alloc, 69 | .dalloc = extent_dalloc, 70 | }; 71 | 72 | void update_env(void) { 73 | use_jemalloc = getenv_jemalloc(); 74 | nodemask = getenv_nodemask(); 75 | mpol_mode = getenv_mpol_mode(); 76 | } 77 | 78 | __attribute__((constructor)) void hmalloc_init(void) { 79 | int err __unused; 80 | size_t unsigned_size = sizeof(unsigned); 81 | 82 | update_env(); 83 | 84 | if (use_jemalloc) { 85 | maxnode = numa_max_node() + 2; 86 | hooks = &extent_hooks; 87 | err = mallctl("arenas.create", &arena_index, &unsigned_size, (void *)&hooks, 88 | sizeof(extent_hooks_t *)); 89 | assert(!err); 90 | } 91 | } 92 | 93 | void *hmalloc(size_t size) { 94 | void *ptr; 95 | 96 | if (!use_jemalloc) 97 | return malloc(size); 98 | 99 | ptr = mallocx(size, MALLOCX_ARENA(arena_index) | MALLOCX_TCACHE_NONE); 100 | if (errno == ENOMEM) 101 | return NULL; 102 | return ptr; 103 | } 104 | 105 | void hfree(void *ptr) { 106 | if (unlikely(ptr == NULL)) 107 | return; 108 | if (!use_jemalloc) { 109 | free(ptr); 110 | return; 111 | } 112 | dallocx(ptr, MALLOCX_ARENA(arena_index) | MALLOCX_TCACHE_NONE); 113 | } 114 | 115 | void *hcalloc(size_t nmemb, size_t size) { 116 | void *ptr = hmalloc(nmemb * size); 117 | 118 | if (likely(ptr)) 119 | memset(ptr, 0, nmemb * size); 120 | return ptr; 121 | } 122 | 123 | void *hrealloc(void *ptr, size_t size) { 124 | if (!use_jemalloc) 125 | return realloc(ptr, size); 126 | 127 | if (ptr == NULL) 128 | return hmalloc(size); 129 | 130 | if (size == 0) { 131 | hfree(ptr); 132 | return NULL; 133 | } 134 | return rallocx(ptr, size, MALLOCX_ARENA(arena_index) | MALLOCX_TCACHE_NONE); 135 | } 136 | 137 | void *haligned_alloc(size_t alignment, size_t size) { 138 | if (!use_jemalloc) 139 | return aligned_alloc(alignment, size); 140 | 141 | /* NOTE: ptmalloc in glibc ignores all these checks unlike jemalloc */ 142 | if (unlikely(alignment == 0 || !is_pow2(alignment))) { 143 | errno = EINVAL; 144 | return NULL; 145 | } 146 | 147 | return mallocx(size, 148 | MALLOCX_ALIGN(alignment) | MALLOCX_ARENA(arena_index) | MALLOCX_TCACHE_NONE); 149 | } 150 | 151 | int hposix_memalign(void **memptr, size_t alignment, size_t size) { 152 | int old_errno; 153 | 154 | if (!use_jemalloc) 155 | return posix_memalign(memptr, alignment, size); 156 | 157 | old_errno = errno; 158 | 159 | if (unlikely(alignment == 0 || !is_pow2(alignment))) { 160 | *memptr = NULL; 161 | return EINVAL; 162 | } 163 | 164 | *memptr = 165 | mallocx(size, MALLOCX_ALIGN(alignment) | MALLOCX_ARENA(arena_index) | MALLOCX_TCACHE_NONE); 166 | 167 | if (unlikely(*memptr == NULL)) { 168 | int ret = errno; 169 | errno = old_errno; 170 | return ret; 171 | } 172 | return 0; 173 | } 174 | 175 | size_t hmalloc_usable_size(void *ptr) { 176 | if (!use_jemalloc) 177 | return malloc_usable_size(ptr); 178 | 179 | if (unlikely(ptr == NULL)) 180 | return 0; 181 | return sallocx(ptr, 0); 182 | } 183 | -------------------------------------------------------------------------------- /hmalloc/src/hmctl.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2024 SK hynix, Inc. */ 2 | /* SPDX-License-Identifier: BSD 2-Clause */ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #ifndef MPOL_PREFERRED_MANY 14 | #define MPOL_PREFERRED_MANY 5 15 | #endif 16 | 17 | #ifndef MPOL_WEIGHTED_INTERLEAVE 18 | #define MPOL_WEIGHTED_INTERLEAVE 6 19 | #endif 20 | 21 | struct opts { 22 | int idx; 23 | char *exename; 24 | 25 | const char *membind; 26 | const char *preferred_many; 27 | const char *interleave; 28 | const char *weighted_interleave; 29 | int preferred; 30 | }; 31 | 32 | struct opts opts; 33 | 34 | /* (a part of) output in --help option (generated by argp runtime) */ 35 | const char *argp_program_bug_address = "https://github.com/skhynix/hmsdk/issues"; 36 | 37 | static struct argp_option hmctl_options[] = { 38 | {.name = "preferred", 39 | .key = 'p', 40 | .arg = "node", 41 | .doc = "Preferably allocate memory on node for hmalloc family allocations"}, 42 | {.name = "preferred-many", 43 | .key = 'P', 44 | .arg = "nodes", 45 | .doc = "Preferably allocate memory on nodes for hmalloc family allocations"}, 46 | {.name = "membind", 47 | .key = 'm', 48 | .arg = "nodes", 49 | .doc = "Only allocate memory from nodes for hmalloc family allocations"}, 50 | {.name = "interleave", 51 | .key = 'i', 52 | .arg = "nodes", 53 | .doc = "Set a memory interleave policy. Memory will be allocated using round robin on nodes"}, 54 | {.name = "weighted-interleave", 55 | .key = 'w', 56 | .arg = "nodes", 57 | .doc = "Set a weighted memory interleave policy. Memory will be allocated using the weights " 58 | "specified in its sysfs location"}, 59 | {NULL}, 60 | }; 61 | 62 | static void fail_mpol_conflict(struct argp_state *state, char key) { 63 | fprintf(stderr, "Error: '-%c' conflicts with other memory policy.\n\n", key); 64 | argp_state_help(state, state->out_stream, ARGP_HELP_STD_HELP); 65 | } 66 | 67 | static error_t parse_option(int key, char *arg, struct argp_state *state) { 68 | struct opts *opts = state->input; 69 | 70 | switch (key) { 71 | case 'm': 72 | opts->membind = arg; 73 | if (opts->preferred > 0) 74 | fail_mpol_conflict(state, key); 75 | break; 76 | 77 | case 'P': 78 | opts->preferred_many = arg; 79 | if (opts->membind) 80 | fail_mpol_conflict(state, key); 81 | break; 82 | 83 | case 'p': 84 | opts->preferred = atoi(arg); 85 | if (opts->membind) 86 | fail_mpol_conflict(state, key); 87 | break; 88 | 89 | case 'i': 90 | opts->interleave = arg; 91 | break; 92 | 93 | case 'w': 94 | opts->weighted_interleave = arg; 95 | break; 96 | 97 | case ARGP_KEY_ARG: 98 | if (state->arg_num) 99 | return ARGP_ERR_UNKNOWN; 100 | if (opts->exename == NULL) { 101 | /* remaining options will be processed in ARGP_KEY_ARGS */ 102 | return ARGP_ERR_UNKNOWN; 103 | } 104 | break; 105 | 106 | case ARGP_KEY_ARGS: 107 | /* process remaining non-option arguments */ 108 | opts->exename = state->argv[state->next]; 109 | opts->idx = state->next; 110 | break; 111 | 112 | case ARGP_KEY_NO_ARGS: 113 | case ARGP_KEY_END: 114 | if (state->arg_num < 1) 115 | argp_usage(state); 116 | break; 117 | 118 | default: 119 | return ARGP_ERR_UNKNOWN; 120 | } 121 | return 0; 122 | } 123 | 124 | static void setup_child_environ(struct opts *opts) { 125 | unsigned long nodemask = 0; 126 | char buf[4096] = "0"; 127 | struct bitmask *bm; 128 | 129 | if (opts->membind) { 130 | snprintf(buf, sizeof(buf), "%d", MPOL_BIND); 131 | setenv("HMALLOC_MPOL_MODE", buf, 1); 132 | 133 | bm = numa_parse_nodestring(opts->membind); 134 | if (bm) { 135 | snprintf(buf, sizeof(buf), "%lu", *bm->maskp); 136 | setenv("HMALLOC_NODEMASK", buf, 1); 137 | } 138 | } else if (opts->preferred_many) { 139 | snprintf(buf, sizeof(buf), "%d", MPOL_PREFERRED_MANY); 140 | setenv("HMALLOC_MPOL_MODE", buf, 1); 141 | 142 | bm = numa_parse_nodestring(opts->preferred_many); 143 | if (bm) { 144 | snprintf(buf, sizeof(buf), "%lu", *bm->maskp); 145 | setenv("HMALLOC_NODEMASK", buf, 1); 146 | } 147 | } else if (opts->preferred >= 0) { 148 | /* ignore when --membind is used */ 149 | snprintf(buf, sizeof(buf), "%d", MPOL_PREFERRED); 150 | setenv("HMALLOC_MPOL_MODE", buf, 1); 151 | 152 | nodemask = 1 << opts->preferred; 153 | snprintf(buf, sizeof(buf), "%ld", nodemask); 154 | setenv("HMALLOC_NODEMASK", buf, 1); 155 | } else if (opts->weighted_interleave) { 156 | snprintf(buf, sizeof(buf), "%d", MPOL_WEIGHTED_INTERLEAVE); 157 | setenv("HMALLOC_MPOL_MODE", buf, 1); 158 | 159 | bm = numa_parse_nodestring(opts->weighted_interleave); 160 | if (bm) { 161 | snprintf(buf, sizeof(buf), "%lu", *bm->maskp); 162 | setenv("HMALLOC_NODEMASK", buf, 1); 163 | } 164 | } else if (opts->interleave) { 165 | snprintf(buf, sizeof(buf), "%d", MPOL_INTERLEAVE); 166 | setenv("HMALLOC_MPOL_MODE", buf, 1); 167 | 168 | bm = numa_parse_nodestring(opts->interleave); 169 | if (bm) { 170 | snprintf(buf, sizeof(buf), "%lu", *bm->maskp); 171 | setenv("HMALLOC_NODEMASK", buf, 1); 172 | } 173 | } 174 | 175 | setenv("HMALLOC_JEMALLOC", "1", 1); 176 | } 177 | 178 | int main(int argc, char *argv[]) { 179 | struct argp argp = { 180 | .options = hmctl_options, 181 | .parser = parse_option, 182 | .args_doc = "[]", 183 | .doc = "hmctl -- Control heterogeneous memory allocation policy", 184 | }; 185 | 186 | /* default option values */ 187 | opts.membind = NULL; 188 | opts.preferred = -1; 189 | 190 | argp_parse(&argp, argc, argv, ARGP_IN_ORDER, NULL, &opts); 191 | 192 | /* pass only non-hmctl options to execv() */ 193 | argc -= opts.idx; 194 | argv += opts.idx; 195 | 196 | setup_child_environ(&opts); 197 | 198 | execv(opts.exename, argv); 199 | perror(opts.exename); 200 | 201 | return -1; 202 | } 203 | -------------------------------------------------------------------------------- /hmalloc/test/.gitignore: -------------------------------------------------------------------------------- 1 | catch.hpp 2 | -------------------------------------------------------------------------------- /hmalloc/test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2024 SK hynix, Inc. 3 | # 4 | # SPDX-License-Identifier: BSD 2-Clause 5 | # 6 | 7 | find_library(HMALLOC hmalloc HINTS ${PROJECT_SOURCE_DIR}) 8 | 9 | add_compile_options(-Wall -Wextra -pedantic) 10 | 11 | if(HMALLOC_PG_BUILD) 12 | add_compile_options(-pg) 13 | endif() 14 | 15 | if(HMALLOC_ASAN_BUILD) 16 | add_compile_options(-fsanitize=address) 17 | add_link_options(-fsanitize=address) 18 | endif() 19 | 20 | # cmake-lint: disable=C0301 21 | add_custom_target( 22 | catch2 ALL 23 | COMMAND 24 | curl -s -k -R -C - -o ${CMAKE_CURRENT_SOURCE_DIR}/catch.hpp 25 | https://raw.githubusercontent.com/catchorg/Catch2/v2.13.10/single_include/catch2/catch.hpp 26 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} 27 | COMMENT "Fetch catch.hpp") 28 | 29 | set(HMALLOC_TEST hmalloc_test) 30 | add_executable(${HMALLOC_TEST} hmalloc_test.cpp main.cpp) 31 | add_dependencies(${HMALLOC_TEST} catch2) 32 | 33 | add_executable(example example.c) 34 | 35 | set(CMAKE_INSTALL_RPATH "..") 36 | set(CMAKE_BUILD_WITH_INSTALL_RPATH True) 37 | 38 | target_link_libraries(hmalloc_test PUBLIC ${HMALLOC} ${NUMA}) 39 | target_link_libraries(example PUBLIC ${HMALLOC}) 40 | -------------------------------------------------------------------------------- /hmalloc/test/example.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2024 SK hynix, Inc. */ 2 | /* SPDX-License-Identifier: BSD 2-Clause */ 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #define KiB (1024UL) 11 | #define MiB (1024UL * KiB) 12 | 13 | char *p; 14 | char *hp; 15 | 16 | int main() { 17 | size_t hsz = 512; 18 | size_t sz = 256; 19 | 20 | p = malloc(sz * MiB); 21 | memset(p, 'x', sz * MiB); 22 | 23 | hp = hmalloc(hsz * MiB); 24 | memset(hp, 'x', hsz * MiB); 25 | 26 | printf("%ld MiB is allocated by malloc().\n", sz); 27 | printf("%ld MiB is allocated by hmalloc().\n", hsz); 28 | printf("Press enter to stop.\n"); 29 | getchar(); 30 | 31 | hfree(hp); 32 | free(p); 33 | 34 | return 0; 35 | } 36 | -------------------------------------------------------------------------------- /hmalloc/test/hmalloc_test.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2024 SK hynix, Inc. */ 2 | /* SPDX-License-Identifier: BSD 2-Clause */ 3 | 4 | #include "catch.hpp" 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #ifndef MPOL_PREFERRED_MANY 20 | #define MPOL_PREFERRED_MANY 5 21 | #endif 22 | 23 | #ifndef MPOL_WEIGHTED_INTERLEAVE 24 | #define MPOL_WEIGHTED_INTERLEAVE 6 25 | #endif 26 | 27 | extern "C" { 28 | void update_env(void); 29 | void hmalloc_init(void); 30 | void *extent_alloc(extent_hooks_t *extent_hooks, void *new_addr, size_t size, size_t alignment, 31 | bool *zero, bool *commit, unsigned arena_ind); 32 | } 33 | 34 | static constexpr auto kb = 1024UL; 35 | static constexpr auto mb = 1024UL * kb; 36 | static constexpr auto gb = 1024UL * mb; 37 | 38 | __attribute__((constructor)) void init() { 39 | char *env = getenv("HMALLOC_JEMALLOC"); 40 | if (!env || !strcmp(env, "1")) { 41 | setenv("HMALLOC_JEMALLOC", "1", 1); 42 | hmalloc_init(); 43 | } 44 | } 45 | 46 | static void hmalloc_test(const std::vector &sizes) { 47 | std::vector v; 48 | 49 | for (auto size : sizes) { 50 | auto *ptr = static_cast(hmalloc(size)); 51 | REQUIRE(ptr != nullptr); 52 | v.push_back(ptr); 53 | if (size > 1) { 54 | memset(ptr, 0xff, size); 55 | CHECK(ptr[0] == 0xff); 56 | CHECK(ptr[size - 1] == 0xff); 57 | } 58 | } 59 | 60 | for (auto &ptr : v) 61 | hfree(ptr); 62 | } 63 | 64 | static void mempolicy_test(int policy, unsigned long nodemask, int maxnode, void *addr) { 65 | int hpolicy; 66 | unsigned long hnodemask; 67 | 68 | CHECK(0 == get_mempolicy(&hpolicy, &hnodemask, maxnode, addr, MPOL_F_ADDR)); 69 | CHECK(policy == hpolicy); 70 | CHECK(nodemask == hnodemask); 71 | } 72 | 73 | TEST_CASE("hmalloc") { 74 | SECTION("single hmalloc") { 75 | void *ptr = hmalloc(10); 76 | REQUIRE(ptr); 77 | hfree(ptr); 78 | } 79 | SECTION("multiple hmalloc") { 80 | std::vector sizes = {0, 1, 10, 5000, 10000, 81 | 700000, 800000, 900000, 1000000, 0x1fffffffUL}; 82 | 83 | SECTION("hmalloc/hfree") { 84 | hmalloc_test(sizes); 85 | } 86 | SECTION("repeat hmalloc/hfree") { 87 | for (int i = 0; i < 3; i++) 88 | hmalloc_test(sizes); 89 | } 90 | } 91 | } 92 | 93 | TEST_CASE("hcalloc") { 94 | size_t nmemb = 1 * mb; 95 | size_t size = sizeof(char); 96 | 97 | SECTION("zeroing check") { 98 | auto *hptr = static_cast(hcalloc(nmemb, size)); 99 | REQUIRE(hptr); 100 | 101 | auto *ptr = static_cast(calloc(nmemb, size)); 102 | REQUIRE(ptr); 103 | 104 | CHECK(0 == memcmp(ptr, hptr, nmemb * size)); 105 | 106 | free(ptr); 107 | hfree(hptr); 108 | } 109 | } 110 | 111 | TEST_CASE("hmalloc_usable_size") { 112 | SECTION("legit pointer") { 113 | void *ptr = hmalloc(1024); 114 | REQUIRE(ptr); 115 | CHECK(0 < hmalloc_usable_size(ptr)); 116 | hfree(ptr); 117 | } 118 | 119 | SECTION("NULL pointer") { 120 | CHECK(0 == hmalloc_usable_size(nullptr)); 121 | } 122 | } 123 | 124 | TEST_CASE("hrealloc") { 125 | size_t old_size = 1 * mb; 126 | auto *old_ptr = static_cast(hcalloc(old_size, sizeof(char))); 127 | REQUIRE(old_ptr); 128 | 129 | SECTION("size zero") { 130 | size_t new_size = 0; 131 | CHECK(nullptr == static_cast(hrealloc(old_ptr, new_size))); 132 | } 133 | 134 | SECTION("old_size < new_size") { 135 | size_t new_size = old_size * 2; 136 | auto *new_ptr = static_cast(hrealloc(old_ptr, new_size)); 137 | REQUIRE(new_ptr); 138 | CHECK(new_size <= hmalloc_usable_size(new_ptr)); 139 | hfree(new_ptr); 140 | } 141 | 142 | SECTION("old_size > new_size") { 143 | size_t new_size = old_size / 2; 144 | auto *new_ptr = static_cast(hrealloc(old_ptr, new_size)); 145 | REQUIRE(new_ptr); 146 | CHECK(new_size <= hmalloc_usable_size(new_ptr)); 147 | CHECK(old_size > hmalloc_usable_size(new_ptr)); 148 | hfree(new_ptr); 149 | } 150 | 151 | SECTION("old_ptr is nullptr") { 152 | size_t new_size = old_size; 153 | hfree(old_ptr); 154 | old_ptr = nullptr; 155 | auto *new_ptr = static_cast(hrealloc(old_ptr, new_size)); 156 | REQUIRE(new_ptr); 157 | CHECK(new_size <= hmalloc_usable_size(new_ptr)); 158 | hfree(new_ptr); 159 | } 160 | 161 | SECTION("old_ptr is nullptr and size zero") { 162 | hfree(old_ptr); 163 | old_ptr = nullptr; 164 | size_t new_size = 0; 165 | auto *new_ptr = static_cast(hrealloc(old_ptr, new_size)); 166 | REQUIRE(new_ptr); 167 | CHECK(0 < hmalloc_usable_size(new_ptr)); 168 | hfree(new_ptr); 169 | } 170 | } 171 | 172 | TEST_CASE("haligned_alloc") { 173 | SECTION("alignment power of two") { 174 | size_t alignment = 1024; 175 | size_t size = 1 * mb; 176 | void *ptr = haligned_alloc(alignment, size); 177 | REQUIRE(ptr); 178 | CHECK(0 == reinterpret_cast(ptr) % alignment); 179 | CHECK(0 < hmalloc_usable_size(ptr)); 180 | hfree(ptr); 181 | } 182 | 183 | #if !defined(__SANITIZE_ADDRESS__) 184 | SECTION("alignment not a power of two (fails in ptmalloc)") { 185 | size_t size = 1 * mb; 186 | REQUIRE(nullptr == haligned_alloc(1024 + 1, size)); 187 | CHECK(EINVAL == errno); 188 | } 189 | 190 | SECTION("alignment zero (fails in ptmalloc)") { 191 | size_t size = 1 * mb; 192 | REQUIRE(nullptr == haligned_alloc(0, size)); 193 | CHECK(EINVAL == errno); 194 | } 195 | 196 | SECTION("size not a multiple of alignment") { 197 | size_t alignment = 1024; 198 | size_t size = 1 * kb * alignment; /* 1 mb */ 199 | void *ptr = haligned_alloc(alignment, size + 1); 200 | REQUIRE(ptr); 201 | CHECK(0 == reinterpret_cast(ptr) % alignment); 202 | CHECK(0 < hmalloc_usable_size(ptr)); 203 | hfree(ptr); 204 | } 205 | #endif 206 | 207 | SECTION("size zero") { 208 | size_t alignment = 1024; 209 | size_t size = 0; 210 | void *ptr = haligned_alloc(alignment, size); 211 | REQUIRE(ptr); 212 | CHECK(0 == reinterpret_cast(ptr) % alignment); 213 | CHECK(0 < hmalloc_usable_size(ptr)); 214 | hfree(ptr); 215 | } 216 | } 217 | 218 | TEST_CASE("hposix_memalign") { 219 | SECTION("alignment power of two") { 220 | void *ptr; 221 | size_t alignment = 1024; 222 | size_t size = 1 * mb; 223 | REQUIRE(0 == hposix_memalign(&ptr, alignment, size)); 224 | REQUIRE(ptr); 225 | CHECK(0 == reinterpret_cast(ptr) % alignment); 226 | CHECK(0 < hmalloc_usable_size(ptr)); 227 | hfree(ptr); 228 | } 229 | 230 | #if !defined(__SANITIZE_ADDRESS__) 231 | SECTION("alignment not a power of two") { 232 | void *ptr; 233 | size_t alignment = 1024; 234 | size_t size = 1 * mb; 235 | CHECK(EINVAL == hposix_memalign(&ptr, alignment + 1, size)); 236 | CHECK(nullptr == ptr); 237 | } 238 | 239 | SECTION("alignment zero") { 240 | void *ptr; 241 | size_t alignment = 0; 242 | size_t size = 1 * mb; 243 | CHECK(EINVAL == hposix_memalign(&ptr, alignment, size)); 244 | CHECK(nullptr == ptr); 245 | } 246 | #endif 247 | 248 | SECTION("size zero") { 249 | void *ptr; 250 | size_t alignment = 1024; 251 | size_t size = 0; 252 | REQUIRE(0 == hposix_memalign(&ptr, alignment, size)); 253 | REQUIRE(ptr); 254 | CHECK(0 == reinterpret_cast(ptr) % alignment); 255 | CHECK(0 < hmalloc_usable_size(ptr)); 256 | hfree(ptr); 257 | } 258 | } 259 | 260 | TEST_CASE("hfree") { 261 | SECTION("legit hfree") { 262 | void *ptr = hmalloc(1024); 263 | REQUIRE(ptr); 264 | hfree(ptr); 265 | } 266 | 267 | SECTION("nullptr hfree") { 268 | hfree(nullptr); 269 | } 270 | } 271 | 272 | TEST_CASE("hmmap/hmunmap") { 273 | SECTION("anonymous") { 274 | size_t size = 1 * mb; 275 | void *ptr = hmmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, 0, 0); 276 | REQUIRE(MAP_FAILED != ptr); 277 | CHECK(0 == hmunmap(ptr, size)); 278 | } 279 | 280 | SECTION("file map") { 281 | const char *filename = "/tmp/__hmalloc.txt"; 282 | FILE *fp = fopen(filename, "w+"); 283 | REQUIRE(fp); 284 | 285 | int fd = fileno(fp); 286 | size_t file_size = 1 * mb; 287 | 288 | REQUIRE(fd >= 0); 289 | REQUIRE(0 == ftruncate(fd, file_size)); 290 | 291 | void *ptr = hmmap(NULL, file_size, PROT_WRITE, MAP_PRIVATE, fd, 0); 292 | REQUIRE(MAP_FAILED != ptr); 293 | 294 | CHECK(0 == hmunmap(ptr, file_size)); 295 | CHECK(0 == remove(filename)); 296 | 297 | CHECK(0 == fclose(fp)); 298 | } 299 | } 300 | 301 | TEST_CASE("mbind") { 302 | /* skip this test when the system has a single numa node */ 303 | int maxnode = numa_max_node() + 2; 304 | if (maxnode < 3) 305 | return; 306 | 307 | void *new_addr = nullptr; 308 | size_t size = 0x1fffffffUL; /* 500 MiB */ 309 | 310 | struct bitmask *mask = numa_get_mems_allowed(); 311 | unsigned long nodemask = *mask->maskp; 312 | 313 | char bm[1024] = "0"; 314 | snprintf(bm, sizeof(bm), "%lu", nodemask); 315 | 316 | SECTION("MPOL_BIND") { 317 | setenv("HMALLOC_MPOL_MODE", "2", 1); /* MPOL_BIND is 2 */ 318 | setenv("HMALLOC_NODEMASK", bm, 1); 319 | update_env(); 320 | 321 | new_addr = extent_alloc(nullptr, new_addr, size, 0, nullptr, nullptr, 0); 322 | REQUIRE(new_addr); 323 | memset(new_addr, 0, size); 324 | 325 | mempolicy_test(MPOL_BIND, nodemask, maxnode, new_addr); 326 | CHECK(0 == munmap(new_addr, size)); 327 | } 328 | 329 | SECTION("MPOL_PREFERRED") { 330 | /* MPOL_PREFERRED accepts only a single node so pick lsb node only */ 331 | unsigned long testnode = 1 << (ffs(nodemask) - 1); 332 | snprintf(bm, sizeof(bm), "%lu", testnode); 333 | 334 | setenv("HMALLOC_MPOL_MODE", "1", 1); /* MPOL_PREFERRED is 1 */ 335 | setenv("HMALLOC_NODEMASK", bm, 1); 336 | update_env(); 337 | 338 | new_addr = extent_alloc(nullptr, new_addr, size, 0, nullptr, nullptr, 0); 339 | REQUIRE(new_addr); 340 | memset(new_addr, 0, size); 341 | 342 | mempolicy_test(MPOL_PREFERRED, testnode, maxnode, new_addr); 343 | CHECK(0 == munmap(new_addr, size)); 344 | } 345 | 346 | SECTION("MPOL_PREFERRED_MANY") { 347 | setenv("HMALLOC_MPOL_MODE", "5", 1); /* MPOL_PREFERRED_MANY is 5 */ 348 | setenv("HMALLOC_NODEMASK", bm, 1); 349 | update_env(); 350 | 351 | new_addr = extent_alloc(nullptr, new_addr, size, 0, nullptr, nullptr, 0); 352 | REQUIRE(new_addr); 353 | memset(new_addr, 0, size); 354 | 355 | mempolicy_test(MPOL_PREFERRED_MANY, nodemask, maxnode, new_addr); 356 | CHECK(0 == munmap(new_addr, size)); 357 | } 358 | SECTION("MPOL_INTERLEAVE") { 359 | setenv("HMALLOC_MPOL_MODE", "3", 1); /* MPOL_INTERLEAVE is 3 */ 360 | setenv("HMALLOC_NODEMASK", bm, 1); 361 | update_env(); 362 | 363 | new_addr = extent_alloc(nullptr, new_addr, size, 0, nullptr, nullptr, 0); 364 | REQUIRE(new_addr); 365 | memset(new_addr, 0, size); 366 | 367 | mempolicy_test(MPOL_INTERLEAVE, nodemask, maxnode, new_addr); 368 | CHECK(0 == munmap(new_addr, size)); 369 | } 370 | SECTION("MPOL_WEIGHTED_INTERLEAVE") { 371 | struct utsname buffer; 372 | int major, minor; 373 | 374 | if (uname(&buffer) != 0) { 375 | perror("uname"); 376 | return; 377 | } 378 | 379 | /* 380 | * MPOL_WEIGHTED_INTERLEAVE is supported from kernel v6.9 so skip this test 381 | * if kernel version is lower than v6.9. 382 | */ 383 | sscanf(buffer.release, "%d.%d", &major, &minor); 384 | if (major < 6 || (major == 6 && minor < 9)) { 385 | CHECK("SKIP: not supported kernel version"); 386 | return; 387 | } 388 | 389 | setenv("HMALLOC_MPOL_MODE", "6", 1); /* MPOL_WEIGHTED_INTERLEAVE is 6 */ 390 | setenv("HMALLOC_NODEMASK", bm, 1); 391 | update_env(); 392 | 393 | new_addr = extent_alloc(nullptr, new_addr, size, 0, nullptr, nullptr, 0); 394 | REQUIRE(new_addr); 395 | memset(new_addr, 0, size); 396 | 397 | mempolicy_test(MPOL_WEIGHTED_INTERLEAVE, nodemask, maxnode, new_addr); 398 | CHECK(0 == munmap(new_addr, size)); 399 | } 400 | } 401 | -------------------------------------------------------------------------------- /hmalloc/test/main.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2024 SK hynix, Inc. */ 2 | /* SPDX-License-Identifier: BSD 2-Clause */ 3 | 4 | #define CATCH_CONFIG_MAIN 5 | #include "catch.hpp" 6 | -------------------------------------------------------------------------------- /tools/bwactl.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Copyright (c) 2022-2024 SK hynix, Inc. 3 | # SPDX-License-Identifier: BSD 2-Clause 4 | 5 | import argparse 6 | import os 7 | import re 8 | import subprocess as sp 9 | import sys 10 | from enum import Enum, auto 11 | 12 | from iw_ratio import * 13 | from mlc import * 14 | 15 | INTERLEAVE_WEIGHT_DIR = "/sys/kernel/mm/mempolicy/weighted_interleave" 16 | HAS_CPU_NODE_PATH = "/sys/devices/system/node/has_cpu" 17 | 18 | 19 | def check_root_perm(): 20 | if os.geteuid() != 0: 21 | print("error: bwactl.py can only be executed with root permission.") 22 | sys.exit(-1) 23 | 24 | 25 | def run_lstopo(): 26 | cmd = "lstopo-no-graphics -p" 27 | err = None 28 | try: 29 | with sp.Popen(cmd.split(), stdout=sp.PIPE) as p: 30 | lines = p.communicate()[0].decode(errors="ignore") 31 | except: 32 | err = "lstopo not found!" 33 | return None, err 34 | return lines.split("\n"), None 35 | 36 | 37 | def read_lstopo(lstopo_file): 38 | with open(lstopo_file, "r") as f: 39 | lines = f.readlines() 40 | lines = list(map(lambda x: x[:-1], lines)) # remove trailing '\n' 41 | return lines, None 42 | 43 | 44 | def get_node_to_package_map(lstopo_file): 45 | if lstopo_file != None: 46 | lines, err = read_lstopo(lstopo_file) 47 | else: 48 | lines, err = run_lstopo() 49 | if err != None: 50 | print(err) 51 | sys.exit(-1) 52 | nodes = {} 53 | 54 | rp = re.compile("^ Package P#(\d+)") 55 | rn = re.compile("^ NUMANode P#(\d+)") 56 | rn2 = re.compile("^ NUMANode P#(\d+)") 57 | rmon = re.compile("^ NUMANode P#(\d+)") # memory only node 58 | 59 | current_package = None 60 | for line in lines: 61 | if "Package" in line: 62 | m = rp.match(line) 63 | current_package = int(m.group(1)) 64 | elif "NUMANode" in line: 65 | m = rn.match(line) 66 | if not m: 67 | m = rn2.match(line) 68 | if m: 69 | node = int(m.group(1)) 70 | nodes[node] = current_package 71 | else: 72 | m2 = rmon.match(line) 73 | node = int(m2.group(1)) 74 | nodes[node] = None 75 | return nodes 76 | 77 | 78 | def get_numa_nodes_has_cpu(): 79 | nodes = [] 80 | 81 | # Read possible nodes to avoid including memory only numa nodes. 82 | with open(HAS_CPU_NODE_PATH, "r") as f: 83 | has_cpu = f.read().strip().split(",") 84 | for node in has_cpu: 85 | if "-" in node: 86 | start, end = map(int, node.split("-")) 87 | nodes.extend(range(start, end + 1)) 88 | else: 89 | nodes.append(int(node)) 90 | return nodes 91 | 92 | 93 | # Parse topology information in python 2-level nested list. 94 | # e.g. If a string "[[0,2],[1,3]]" is given, 95 | # then it return [[0,2],[1,3]] in python data. 96 | def topology_to_pydata(topology): 97 | assert topology[0:2] == "[[" 98 | assert topology[-2:] == "]]" 99 | 100 | rn = re.compile("^(\d+)") 101 | pytopo = [] 102 | i = 1 103 | nested = False 104 | 105 | while i < len(topology) - 1: 106 | if topology[i] == "[": 107 | assert not nested 108 | pytopo.append([]) 109 | nested = True 110 | i += 1 111 | elif topology[i] == "]": 112 | assert nested 113 | assert len(pytopo[-1]) > 0 114 | # check if pytopo[-1] has memory only nodes. 115 | has_cpu_node_in_package = False 116 | for nid in pytopo[-1]: 117 | has_cpu_node_in_package = True 118 | assert has_cpu_node_in_package 119 | nested = False 120 | i += 1 121 | elif topology[i] == "," or topology[i] == " ": 122 | i += 1 123 | else: 124 | assert nested 125 | m = rn.match(topology[i:]) 126 | nid = m.group(1) 127 | 128 | # check if the nid is in another package 129 | for package in pytopo: 130 | for node in package: 131 | assert node != int(nid) 132 | 133 | pytopo[-1].append(int(nid)) 134 | i += len(nid) 135 | return pytopo 136 | 137 | 138 | def generate_topology(possible, args): 139 | # nodes is a dict that contains 140 | # key : node ID 141 | # value: package ID 142 | nodes = {} 143 | if args.topology: 144 | try: 145 | pytopo = topology_to_pydata(args.topology) 146 | nr_nodes = 0 147 | for pack in pytopo: 148 | nr_nodes += len(pack) 149 | system_nodes = get_system_nodes() 150 | if nr_nodes != system_nodes: 151 | print( 152 | f"error: topology has {nr_nodes} numa nodes but must have {system_nodes} nodes" 153 | ) 154 | os._exit(-1) 155 | except: 156 | print(f"error: invalid topology '{args.topology}'") 157 | sys.exit(-1) 158 | for pack_idx in range(len(pytopo)): 159 | has_cpu_node_in_package = False 160 | for nid in pytopo[pack_idx]: 161 | for cid in possible: 162 | if nid == cid: 163 | has_cpu_node_in_package = True 164 | nodes[nid] = pack_idx 165 | if not has_cpu_node_in_package: 166 | print( 167 | f"error: package '{pytopo[pack_idx]}' has no cpu numa nodes as in the file below." 168 | ) 169 | print(f" please check '{HAS_CPU_NODE_PATH}'") 170 | sys.exit(-1) 171 | else: 172 | nodes = get_node_to_package_map(args.lstopo_file) 173 | 174 | return nodes 175 | 176 | 177 | def display_multi_ratio(matrix, possible, nodes, args): 178 | print("Bandwidth ratio for all NUMA nodes") 179 | 180 | # Write interleave weight value into sysfs file. 181 | updated_files = [] 182 | for nid in possible: 183 | print(f"from node{nid} compute cores:") 184 | row = matrix[nid] 185 | nids = [] 186 | weights = [] 187 | for i in range(len(row)): 188 | if nodes == None: 189 | nids.append(i) 190 | weights.append(int(row[i])) 191 | elif nodes[i] == nodes[nid]: 192 | # Add ratio only when they are in the same package. 193 | nids.append(i) 194 | weights.append(int(row[i])) 195 | 196 | lgcd = list_gcd(weights) 197 | if lgcd != 0: 198 | weights = list(map(lambda x: int(x / lgcd), weights)) 199 | for i in range(len(weights)): 200 | if weights[i] == 0: 201 | print( 202 | f"invalid ratio {weights[i]} on node{nids[i]}: cpu node without memory is not allowed." 203 | ) 204 | sys.exit(-1) 205 | print(f" echo {weights[i]} > {INTERLEAVE_WEIGHT_DIR}/node{nids[i]}") 206 | filename = f"{INTERLEAVE_WEIGHT_DIR}/node{nids[i]}" 207 | with open(filename, "w") as f: 208 | f.write(f"{weights[i]}") 209 | updated_files.append(filename) 210 | 211 | 212 | def check_sysfs(): 213 | if not os.path.exists(f"{INTERLEAVE_WEIGHT_DIR}"): 214 | print(f"error: sysfs not found at {INTERLEAVE_WEIGHT_DIR}") 215 | sys.exit(-1) 216 | 217 | 218 | def get_system_nodes(): 219 | nr_nodes = 0 220 | sys_node_dir = "/sys/devices/system/node/" 221 | for file in os.listdir(sys_node_dir): 222 | if file[:4] == "node": 223 | nr_nodes += 1 224 | return nr_nodes 225 | 226 | 227 | def main(): 228 | parser = argparse.ArgumentParser() 229 | parser.add_argument( 230 | "--mlc-file", 231 | type=str, 232 | required=False, 233 | help="Pass mlc result from file instead of running it", 234 | ) 235 | parser.add_argument( 236 | "--lstopo-file", 237 | type=str, 238 | required=False, 239 | help="Pass lstopo result from file instead of running it", 240 | ) 241 | parser.add_argument( 242 | "--topology", 243 | type=str, 244 | required=False, 245 | help="topology structure", 246 | ) 247 | args = parser.parse_args() 248 | 249 | check_sysfs() 250 | 251 | check_root_perm() 252 | 253 | mlc = MLC() 254 | 255 | # Get number of system numa nodes 256 | numa_node_all = get_system_nodes() 257 | 258 | if not args.mlc_file: 259 | # execute mlc 260 | output = mlc.run_bandwidth_matrix() 261 | else: 262 | # read mlc result from mlc_file 263 | with open(args.mlc_file, "r") as f: 264 | output = f.read() 265 | 266 | # Save the output of mlc result for later reference. 267 | with open("mlc.out", "w") as f: 268 | f.write(output) 269 | 270 | # Parse and create bandwidth matrix using mlc log 271 | bandwidth_matrix_log = mlc.parse_bandwidth_matrix(output) 272 | bandwidth_matrix = mlc.create_bandwidth_matrix(bandwidth_matrix_log, numa_node_all) 273 | # Convert bandwidth value from float to int 274 | bandwidth_matrix = [ 275 | [int(float(i)) for i in bandwidth_matrix[j]] 276 | for j in range(len(bandwidth_matrix)) 277 | ] 278 | 279 | possible = get_numa_nodes_has_cpu() 280 | nodes = generate_topology(possible, args) 281 | 282 | # Calculate ratio_matrix based on the given bandwidth_matrix. 283 | ratio_matrix = get_iw_ratio_matrix(bandwidth_matrix, possible, nodes) 284 | 285 | # Print result to stdout and sysfs 286 | display_multi_ratio(ratio_matrix, possible, nodes, args) 287 | 288 | 289 | if __name__ == "__main__": 290 | main() 291 | -------------------------------------------------------------------------------- /tools/gen_config.py: -------------------------------------------------------------------------------- 1 | gen_migpol.py -------------------------------------------------------------------------------- /tools/gen_migpol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Copyright (c) 2023-2024 SK hynix, Inc. 3 | # SPDX-License-Identifier: BSD 2-Clause 4 | 5 | import argparse 6 | import json 7 | import os 8 | import subprocess as sp 9 | import sys 10 | 11 | import yaml 12 | 13 | # common options 14 | monitoring_intervals = "--monitoring_intervals 100ms 2s 20s" 15 | monitoring_nr_regions_range = "--monitoring_nr_regions_range 100 10000" 16 | 17 | # common damos options 18 | damos_sz_region = "--damos_sz_region 4096 max" 19 | damos_filter = "--damos_filter memcg nomatching /hmsdk" 20 | 21 | 22 | def parse_argument(): 23 | parser = argparse.ArgumentParser() 24 | parser.add_argument( 25 | "-d", 26 | "--demote", 27 | dest="migrate_cold", 28 | action="append", 29 | nargs=2, 30 | metavar=("SRC", "DEST"), 31 | default=[], 32 | help="source and destination NUMA nodes for demotion.", 33 | ) 34 | parser.add_argument( 35 | "-p", 36 | "--promote", 37 | dest="migrate_hot", 38 | action="append", 39 | nargs=2, 40 | metavar=("SRC", "DEST"), 41 | default=[], 42 | help="source and destination NUMA nodes for promotion.", 43 | ) 44 | parser.add_argument( 45 | "-g", 46 | "--global", 47 | dest="nofilter", 48 | action="store_true", 49 | help="Apply tiered migration schemes globally not limited to PIDs under 'hmsdk' cgroup.", 50 | ) 51 | parser.add_argument( 52 | "-o", "--output", dest="output", default=None, help="Set the output json file." 53 | ) 54 | return parser.parse_args() 55 | 56 | 57 | def run_command(cmd): 58 | with sp.Popen(cmd.split(), stdout=sp.PIPE, stderr=sp.PIPE) as p: 59 | stdout, stderr = p.communicate() 60 | stdout_lines = stdout.decode(errors="ignore") 61 | stderr_lines = stderr.decode(errors="ignore") 62 | if len(stderr_lines) > 0: 63 | print(stderr_lines) 64 | return stdout_lines 65 | 66 | 67 | def parent_dir_of_file(file): 68 | return os.path.dirname(os.path.dirname(os.path.abspath(file))) 69 | 70 | 71 | class CheckNodes: 72 | def __init__(self): 73 | self.handled_node = set() 74 | 75 | def __call__(self, src_node, dest_node, node_json): 76 | if src_node in self.handled_node: 77 | return f"node {src_node} cannot be used multiple times for source node" 78 | self.handled_node.add(src_node) 79 | 80 | if src_node == dest_node: 81 | return f"node {src_node} cannot be used for both SRC and DEST node" 82 | 83 | nr_regions = len( 84 | node_json["kdamonds"][0]["contexts"][0]["targets"][0]["regions"] 85 | ) 86 | if nr_regions <= 0: 87 | return f"node {src_node} has no valid regions" 88 | 89 | return None 90 | 91 | 92 | def main(): 93 | args = parse_argument() 94 | 95 | if os.geteuid() != 0: 96 | print("error: Run as root") 97 | sys.exit(1) 98 | 99 | damo = parent_dir_of_file(__file__) + "/damo/damo" 100 | node_jsons = [] 101 | 102 | common_opts = f"{monitoring_intervals} {monitoring_nr_regions_range}" 103 | common_damos_opts = f"{damos_sz_region}" 104 | if not args.nofilter: 105 | common_damos_opts += f" {damos_filter}" 106 | 107 | check_nodes = CheckNodes() 108 | 109 | for src_node, dest_node in args.migrate_cold: 110 | numa_node = f"--numa_node {src_node}" 111 | damos_action = f"--damos_action migrate_cold {dest_node}" 112 | damos_access_rate = "--damos_access_rate 0% 0%" 113 | damos_age = "--damos_age 30s max" 114 | damos_quotas = "--damos_quotas 1s 50G 20s 0 0 1%" 115 | damos_young_filter = "--damos_filter young matching" 116 | cmd = ( 117 | f"{damo} args damon --format json {numa_node} {common_opts} " 118 | f"{damos_action} {common_damos_opts} {damos_young_filter} " 119 | f"{damos_access_rate} {damos_age} {damos_quotas}" 120 | ) 121 | json_str = run_command(cmd) 122 | node_json = json.loads(json_str) 123 | node_jsons.append(node_json) 124 | err = check_nodes(src_node, dest_node, node_json) 125 | if err: 126 | print(f"error: {err}") 127 | sys.exit(1) 128 | 129 | for src_node, dest_node in args.migrate_hot: 130 | numa_node = f"--numa_node {src_node}" 131 | damos_action = f"--damos_action migrate_hot {dest_node}" 132 | damos_access_rate = "--damos_access_rate 5% 100%" 133 | damos_age = "--damos_age 0 max" 134 | damos_quotas = "--damos_quotas 2s 50G 20s 0 0 1%" 135 | damos_young_filter = "--damos_filter young nomatching" 136 | cmd = ( 137 | f"{damo} args damon --format json {numa_node} {common_opts} " 138 | f"{damos_action} {common_damos_opts} {damos_young_filter} " 139 | f"{damos_access_rate} {damos_age} {damos_quotas}" 140 | ) 141 | json_str = run_command(cmd) 142 | node_json = json.loads(json_str) 143 | node_jsons.append(node_json) 144 | err = check_nodes(src_node, dest_node, node_json) 145 | if err: 146 | print(f"error: {err}") 147 | sys.exit(1) 148 | 149 | nodes = {"kdamonds": []} 150 | 151 | for node_json in node_jsons: 152 | nodes["kdamonds"].append(node_json["kdamonds"][0]) 153 | 154 | config = yaml.dump(nodes, default_flow_style=False, sort_keys=False) 155 | if args.output: 156 | with open(args.output, "w") as f: 157 | f.write(config + "\n") 158 | else: 159 | print(config) 160 | 161 | return 0 162 | 163 | 164 | if __name__ == "__main__": 165 | sys.exit(main()) 166 | -------------------------------------------------------------------------------- /tools/iw_ratio.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Copyright (c) 2022-2023 SK hynix, Inc. 3 | # SPDX-License-Identifier: BSD 2-Clause 4 | 5 | import math 6 | 7 | SOFT_MAX_RATIO_VAL = 10 8 | 9 | 10 | def list_gcd(data): 11 | lgcd = data[0] 12 | for i in range(1, len(data)): 13 | lgcd = math.gcd(lgcd, data[i]) 14 | return lgcd 15 | 16 | 17 | def get_iw_ratio(data, nodes, nid, soft_max=SOFT_MAX_RATIO_VAL): 18 | new_data = [0] * len(data) 19 | quit = False 20 | 21 | while True: 22 | if max(data) <= soft_max: 23 | break 24 | for i in range(len(data)): 25 | # Calculate ratio when they are in the same package. 26 | if nodes[i] == nodes[nid]: 27 | new_data[i] = data[i] / 2 28 | # new_data[i] != 0 check is to handle when cpu node doesn't have memory. 29 | if new_data[i] != 0 and new_data[i] <= 1: 30 | quit = True 31 | break 32 | if quit: 33 | break 34 | data = new_data[:] 35 | 36 | data = list(map(lambda x: round(x), data)) 37 | lgcd = list_gcd(data) 38 | if lgcd != 0: 39 | data = list(map(lambda x: round(x / lgcd), data)) 40 | 41 | return data 42 | 43 | 44 | def get_iw_ratio_matrix(bw_matrix, possible, nodes): 45 | ratio_matrix = [0] * len(bw_matrix) 46 | 47 | for nid in possible: 48 | ratio_matrix[nid] = get_iw_ratio(bw_matrix[nid], nodes, nid) 49 | return ratio_matrix 50 | -------------------------------------------------------------------------------- /tools/mlc.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # Copyright (c) 2022-2023 SK hynix, Inc. 3 | # SPDX-License-Identifier: BSD 2-Clause 4 | 5 | import os 6 | import shutil 7 | import subprocess 8 | import sys 9 | 10 | 11 | def run_check_output(cmd): 12 | out = subprocess.check_output(cmd.split()) 13 | out = out.decode("utf-8") 14 | return out 15 | 16 | 17 | def run_with_shell(cmd): 18 | proc = subprocess.Popen( 19 | cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE 20 | ) 21 | out, err = proc.communicate() 22 | return out 23 | 24 | 25 | class MLC: 26 | def __init__(self): 27 | self.work_dir = os.path.dirname(os.path.realpath(__file__)) 28 | self.mlc = self.work_dir + "/mlc/Linux/mlc" 29 | if os.path.isfile(self.mlc) == False: 30 | mlc_filename = "mlc_v3.11.tgz" 31 | self.mlc_dir = self.work_dir + "/mlc" 32 | if os.path.isdir(self.mlc_dir): 33 | shutil.rmtree(self.mlc_dir) 34 | os.mkdir(self.mlc_dir) 35 | 36 | print("Download MLC...") 37 | cmd = ( 38 | f"wget -P {self.mlc_dir} " 39 | f"https://downloadmirror.intel.com/793041/{mlc_filename} " 40 | "--no-check-certificate" 41 | ) 42 | out = run_check_output(cmd) 43 | cmd = f"tar -xvf {self.mlc_dir}/{mlc_filename} -C {self.mlc_dir}" 44 | run_with_shell(cmd) 45 | 46 | def run_bandwidth_matrix(self): 47 | print("\nMeasuring Bandwidth... It takes a few minutes..") 48 | 49 | # -W2 means 2:1 read-write ratio 50 | command = f"{self.mlc} --bandwidth_matrix -W2" 51 | 52 | out = run_with_shell(command) 53 | return out.decode("utf-8") 54 | 55 | def parse_bandwidth_matrix(self, log): 56 | log_parsed = log.split("\n") 57 | while True: 58 | if "Numa node" not in log_parsed[0]: 59 | del log_parsed[0] 60 | else: 61 | break 62 | del log_parsed[0] 63 | del log_parsed[0] 64 | return log_parsed 65 | 66 | def create_bandwidth_matrix(self, log, num_nodes): 67 | bandwidth_matrix = [[0 for col in range(num_nodes)] for row in range(num_nodes)] 68 | for line in log: 69 | if len(line) == 0: 70 | continue 71 | row = int(line.split()[0]) 72 | for i in range(num_nodes): 73 | val = line.split()[i + 1] 74 | if val == "-": 75 | val = 0 76 | bandwidth_matrix[row][i] = val 77 | 78 | return bandwidth_matrix 79 | 80 | def create_bandwidth_ratio_matrix(self, matrix, num_nodes): 81 | ratio_matrix = [[0 for col in range(num_nodes)] for row in range(num_nodes)] 82 | for row in range(num_nodes): 83 | for col in range(num_nodes): 84 | baseline = matrix[row][0] 85 | if baseline == 0: 86 | break 87 | ratio_matrix[row][col] = round( 88 | float(matrix[row][col]) / float(baseline), 2 89 | ) 90 | return ratio_matrix 91 | --------------------------------------------------------------------------------