From: Daniel Wagner Date: Fri, 17 Nov 2023 08:01:35 +0000 (+0100) Subject: nvme: replace libhugetlbfs with mmap and madvise X-Git-Tag: v2.7~30 X-Git-Url: https://www.infradead.org/git/?a=commitdiff_plain;h=51e68f773bc16e6a0b0bfa954b9c27e5784b4da5;p=users%2Fsagi%2Fnvme-cli.git nvme: replace libhugetlbfs with mmap and madvise Instead depending on libhugetlbfs for large memory allocation mapping just use mmap and madvise for trying to allocate contiguous memory. While at it also introduce an auto cleanup helper. Signed-off-by: Daniel Wagner --- diff --git a/meson.build b/meson.build index 864ed0d4..9a45b27c 100644 --- a/meson.build +++ b/meson.build @@ -68,15 +68,6 @@ else endif conf.set('CONFIG_JSONC', json_c_dep.found(), description: 'Is json-c available?') -# Check for libhugetlbfs availability (optional) -if not get_option('libhugetlbfs').disabled() and cc.has_header('hugetlbfs.h') - libhugetlbfs_dep = cc.find_library('hugetlbfs', - required : false) -else - libhugetlbfs_dep = dependency('', required: false) -endif -conf.set('CONFIG_LIBHUGETLBFS', libhugetlbfs_dep.found(), description: 'Is libhugetlbfs available?') - # Set the nvme-cli version conf.set('NVME_VERSION', '"' + meson.project_version() + '"') @@ -286,8 +277,7 @@ subdir('Documentation') executable( 'nvme', sources, - dependencies: [ libnvme_dep, libnvme_mi_dep, json_c_dep, - libhugetlbfs_dep ], + dependencies: [ libnvme_dep, libnvme_mi_dep, json_c_dep ], link_args: '-ldl', include_directories: incdir, install: true, @@ -336,7 +326,6 @@ if meson.version().version_compare('>=0.53.0') summary(path_dict, section: 'Paths') dep_dict = { 'json-c': json_c_dep.found(), - 'libhugetlbfs': libhugetlbfs_dep.found(), } summary(dep_dict, section: 'Dependencies') conf_dict = { diff --git a/meson_options.txt b/meson_options.txt index f0920179..c61dae0f 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -70,10 +70,3 @@ option( type : 'string', description : 'override the git version string' ) - -option( - 'libhugetlbfs', - type: 'feature', - value: 'auto', - description : 'Use libhugetlbfs if library is exist when this option is not disabled' -) diff --git a/nvme.c b/nvme.c index 40277660..4f946a27 100644 --- a/nvme.c +++ b/nvme.c @@ -42,10 +42,6 @@ #include #include -#ifdef CONFIG_LIBHUGETLBFS -#include -#endif - #include #include @@ -1411,10 +1407,10 @@ static int get_persistent_event_log(int argc, char **argv, _cleanup_free_ struct nvme_persistent_event_log *pevent_collected = NULL; _cleanup_free_ struct nvme_persistent_event_log *pevent = NULL; + _cleanup_huge_ struct nvme_mem_huge mh = { 0, }; _cleanup_nvme_dev_ struct nvme_dev *dev = NULL; enum nvme_print_flags flags; void *pevent_log_info; - bool huge; int err; struct config { @@ -1483,9 +1479,10 @@ static int get_persistent_event_log(int argc, char **argv, if (cfg.action == NVME_PEVENT_LOG_EST_CTX_AND_READ) cfg.action = NVME_PEVENT_LOG_READ; - pevent_log_info = nvme_alloc_huge(cfg.log_len, &huge); + pevent_log_info = nvme_alloc_huge(cfg.log_len, &mh); if (!pevent_log_info) return -ENOMEM; + err = nvme_cli_get_log_persistent_event(dev, cfg.action, cfg.log_len, pevent_log_info); if (!err) { @@ -1494,17 +1491,16 @@ static int get_persistent_event_log(int argc, char **argv, pevent); if (err < 0) { nvme_show_error("persistent event log: %s", nvme_strerror(errno)); - goto free; + return err; } else if (err) { nvme_show_status(err); - goto free; + return err; } pevent_collected = pevent_log_info; if (pevent_collected->gen_number != pevent->gen_number) { printf("Collected Persistent Event Log may be invalid,\n" "Re-read the log is required\n"); - err = -EINVAL; - goto free; + return -EINVAL; } nvme_show_persistent_event_log(pevent_log_info, cfg.action, @@ -1515,8 +1511,6 @@ static int get_persistent_event_log(int argc, char **argv, nvme_show_error("persistent event log: %s", nvme_strerror(errno)); } -free: - nvme_free_huge(pevent_log_info, huge); return err; } @@ -4756,12 +4750,12 @@ static int fw_download(int argc, char **argv, struct command *cmd, struct plugin const char *ignore_ovr = "ignore overwrite errors"; _cleanup_nvme_dev_ struct nvme_dev *dev = NULL; + _cleanup_huge_ struct nvme_mem_huge mh = { 0, }; _cleanup_file_ int fw_fd = -1; unsigned int fw_size, pos; int err; struct stat sb; void *fw_buf; - bool huge; struct nvme_id_ctrl ctrl; struct config { @@ -4823,14 +4817,14 @@ static int fw_download(int argc, char **argv, struct command *cmd, struct plugin } else if (cfg.xfer % 4096) cfg.xfer = 4096; - fw_buf = nvme_alloc_huge(fw_size, &huge); + fw_buf = nvme_alloc_huge(fw_size, &mh); if (!fw_buf) return -ENOMEM; if (read(fw_fd, fw_buf, fw_size) != ((ssize_t)(fw_size))) { err = -errno; nvme_show_error("read :%s :%s", cfg.fw, strerror(errno)); - goto free; + return err; } for (pos = 0; pos < fw_size; pos += cfg.xfer) { @@ -4850,9 +4844,6 @@ static int fw_download(int argc, char **argv, struct command *cmd, struct plugin printf("Firmware download success\n"); } -free: - nvme_free_huge(fw_buf, huge); - return err; } @@ -7040,7 +7031,7 @@ static int submit_io(int opcode, char *command, const char *desc, int argc, char __u32 dsmgmt = 0; unsigned int logical_block_size = 0; unsigned long long buffer_size = 0, mbuffer_size = 0; - bool huge; + _cleanup_huge_ struct nvme_mem_huge mh = { 0, }; _cleanup_nvme_dev_ struct nvme_dev *dev = NULL; _cleanup_free_ struct nvme_nvm_id_ns *nvm_ns = NULL; _cleanup_free_ struct nvme_id_ns *ns = NULL; @@ -7257,15 +7248,13 @@ static int submit_io(int opcode, char *command, const char *desc, int argc, char buffer_size = ((unsigned long long)nblocks + 1) * logical_block_size; } - buffer = nvme_alloc_huge(buffer_size, &huge); + buffer = nvme_alloc_huge(buffer_size, &mh); if (!buffer) return -ENOMEM; nvm_ns = nvme_alloc(sizeof(*nvm_ns)); - if (!nvm_ns) { - err = -ENOMEM; - goto free_buffer; - } + if (!nvm_ns) + return -ENOMEM; if (cfg.metadata_size) { err = nvme_identify_ns_csi(dev_fd(dev), 1, 0, NVME_CSI_NVM, nvm_ns); @@ -7282,24 +7271,20 @@ static int submit_io(int opcode, char *command, const char *desc, int argc, char mbuffer_size = cfg.metadata_size; mbuffer = malloc(mbuffer_size); - if (!mbuffer) { - err = -ENOMEM; - goto free_buffer; - } + if (!mbuffer) + return -ENOMEM; memset(mbuffer, 0, mbuffer_size); } - if (invalid_tags(cfg.storage_tag, cfg.ref_tag, sts, pif)) { - err = -EINVAL; - goto free_buffer; - } + if (invalid_tags(cfg.storage_tag, cfg.ref_tag, sts, pif)) + return -EINVAL; if (opcode & 1) { err = read(dfd, (void *)buffer, cfg.data_size); if (err < 0) { err = -errno; nvme_show_error("failed to read data buffer from input file %s", strerror(errno)); - goto free_buffer; + return err; } } @@ -7308,7 +7293,7 @@ static int submit_io(int opcode, char *command, const char *desc, int argc, char if (err < 0) { err = -errno; nvme_show_error("failed to read meta-data buffer from input file %s", strerror(errno)); - goto free_buffer; + return err; } } @@ -7331,7 +7316,7 @@ static int submit_io(int opcode, char *command, const char *desc, int argc, char printf("sts : %02x\n", sts); } if (cfg.dry_run) - goto free_buffer; + return 0; struct nvme_io_args args = { .args_size = sizeof(args), @@ -7380,9 +7365,6 @@ static int submit_io(int opcode, char *command, const char *desc, int argc, char } } -free_buffer: - nvme_free_huge(buffer, huge); - return err; } @@ -8054,6 +8036,7 @@ static int passthru(int argc, char **argv, bool admin, const char *wr = "set dataflow direction to send"; const char *prefill = "prefill buffers with known byte-value, default 0"; + _cleanup_huge_ struct nvme_mem_huge mh = { 0, }; _cleanup_nvme_dev_ struct nvme_dev *dev = NULL; _cleanup_file_ int dfd = -1, mfd = -1; int flags; @@ -8062,7 +8045,6 @@ static int passthru(int argc, char **argv, bool admin, _cleanup_free_ void *mdata = NULL; int err = 0; __u32 result; - bool huge = false; const char *cmd_name = NULL; struct timeval start_time, end_time; @@ -8160,7 +8142,7 @@ static int passthru(int argc, char **argv, bool admin, if (read(mfd, mdata, cfg.metadata_len) < 0) { err = -errno; nvme_show_perror("failed to read metadata write buffer"); - return -errno; + return err; } } else { memset(mdata, cfg.prefill, cfg.metadata_len); @@ -8168,20 +8150,19 @@ static int passthru(int argc, char **argv, bool admin, } if (cfg.data_len) { - data = nvme_alloc_huge(cfg.data_len, &huge); + data = nvme_alloc_huge(cfg.data_len, &mh); if (!data) return -ENOMEM; memset(data, cfg.prefill, cfg.data_len); if (!cfg.read && !cfg.write) { nvme_show_error("data direction not given"); - err = -EINVAL; - goto free_data; + return -EINVAL; } else if (cfg.write) { if (read(dfd, data, cfg.data_len) < 0) { err = -errno; nvme_show_error("failed to read write buffer %s", strerror(errno)); - goto free_data; + return err; } } } @@ -8206,7 +8187,7 @@ static int passthru(int argc, char **argv, bool admin, printf("timeout_ms : %08x\n", cfg.timeout); } if (cfg.dry_run) - goto free_data; + return 0; gettimeofday(&start_time, NULL); @@ -8248,8 +8229,6 @@ static int passthru(int argc, char **argv, bool admin, if (cfg.read) passthru_print_read_output(cfg, data, dfd, mdata, mfd, err); } -free_data: - nvme_free_huge(data, huge); return err; } @@ -8923,9 +8902,9 @@ static int nvme_mi(int argc, char **argv, __u8 admin_opcode, const char *desc) bool send; _cleanup_file_ int fd = -1; int flags; + _cleanup_huge_ struct nvme_mem_huge mh = { 0, }; _cleanup_nvme_dev_ struct nvme_dev *dev = NULL; __u32 result; - bool huge = false; struct config { __u8 opcode; @@ -8979,14 +8958,15 @@ static int nvme_mi(int argc, char **argv, __u8 admin_opcode, const char *desc) } if (cfg.data_len) { - data = nvme_alloc_huge(cfg.data_len, &huge); + data = nvme_alloc_huge(cfg.data_len, &mh); if (!data) return -ENOMEM; + if (send) { if (read(fd, data, cfg.data_len) < 0) { err = -errno; nvme_show_error("failed to read write buffer %s", strerror(errno)); - goto free_data; + return err; } } } @@ -9012,9 +8992,6 @@ static int nvme_mi(int argc, char **argv, __u8 admin_opcode, const char *desc) } } -free_data: - nvme_free_huge(data, huge); - return err; } diff --git a/plugins/micron/micron-nvme.c b/plugins/micron/micron-nvme.c index c9218270..63a7a795 100644 --- a/plugins/micron/micron-nvme.c +++ b/plugins/micron/micron-nvme.c @@ -16,6 +16,7 @@ #include #include "linux/types.h" #include "nvme-print.h" +#include "util/cleanup.h" #define CREATE_CMD #include "micron-nvme.h" @@ -1767,10 +1768,10 @@ static void GetGenericLogs(int fd, const char *dir) struct nvme_firmware_slot fw_log; struct nvme_cmd_effects_log effects; struct nvme_persistent_event_log pevent_log; + _cleanup_huge_ struct nvme_mem_huge mh = { 0, }; void *pevent_log_info = NULL; __u32 log_len = 0; int err = 0; - bool huge = false; /* get self test log */ if (!nvme_get_log_device_self_test(fd, &self_test_log)) @@ -1799,17 +1800,17 @@ static void GetGenericLogs(int fd, const char *dir) } log_len = le64_to_cpu(pevent_log.tll); - pevent_log_info = nvme_alloc_huge(log_len, &huge); + pevent_log_info = nvme_alloc_huge(log_len, &mh); if (!pevent_log_info) { perror("could not alloc buffer for persistent event log page (ignored)!\n"); return; } + err = nvme_get_log_persistent_event(fd, NVME_PEVENT_LOG_READ, log_len, pevent_log_info); if (!err) WriteData((__u8 *)pevent_log_info, log_len, dir, "persistent_event_log.bin", "persistent event log"); - nvme_free_huge(pevent_log_info, huge); } static void GetNSIDDInfo(int fd, const char *dir, int nsid) diff --git a/plugins/scaleflux/sfx-nvme.c b/plugins/scaleflux/sfx-nvme.c index e74764e4..f752d5d5 100644 --- a/plugins/scaleflux/sfx-nvme.c +++ b/plugins/scaleflux/sfx-nvme.c @@ -21,6 +21,7 @@ #include "linux/types.h" #include "nvme-wrap.h" #include "nvme-print.h" +#include "util/cleanup.h" #define CREATE_CMD #include "sfx-nvme.h" @@ -1349,12 +1350,12 @@ static int nvme_dump_evtlog(struct nvme_dev *dev, __u32 namespace_id, __u32 stor { struct nvme_persistent_event_log *pevent; void *pevent_log_info; + _cleanup_huge_ struct nvme_mem_huge mh = { 0, }; __u8 lsp_base; __u32 offset = 0; __u32 length = 0; __u32 log_len; __u32 single_len; - bool huge; int err = 0; FILE *fd = NULL; struct nvme_get_log_args args = { @@ -1410,7 +1411,7 @@ static int nvme_dump_evtlog(struct nvme_dev *dev, __u32 namespace_id, __u32 stor if (log_len % 4) log_len = (log_len / 4 + 1) * 4; - pevent_log_info = nvme_alloc_huge(single_len, &huge); + pevent_log_info = nvme_alloc_huge(single_len, &mh); if (!pevent_log_info) { err = -ENOMEM; goto free_pevent; @@ -1420,7 +1421,7 @@ static int nvme_dump_evtlog(struct nvme_dev *dev, __u32 namespace_id, __u32 stor if (!fd) { fprintf(stderr, "Failed to open %s file to write\n", file); err = ENOENT; - goto free; + goto free_pevent; } args.lsp = lsp_base + NVME_PEVENT_LOG_READ; @@ -1453,8 +1454,8 @@ static int nvme_dump_evtlog(struct nvme_dev *dev, __u32 namespace_id, __u32 stor printf("\nDump-evtlog: Success\n"); if (parse) { - nvme_free_huge(pevent_log_info, huge); - pevent_log_info = nvme_alloc_huge(log_len, &huge); + nvme_free_huge(&mh); + pevent_log_info = nvme_alloc_huge(log_len, &mh); if (!pevent_log_info) { fprintf(stderr, "Failed to alloc enough memory 0x%x to parse evtlog\n", log_len); err = -ENOMEM; @@ -1466,7 +1467,7 @@ static int nvme_dump_evtlog(struct nvme_dev *dev, __u32 namespace_id, __u32 stor if (!fd) { fprintf(stderr, "Failed to open %s file to read\n", file); err = ENOENT; - goto free; + goto free_pevent; } if (fread(pevent_log_info, 1, log_len, fd) != log_len) { fprintf(stderr, "Failed to read evtlog to buffer\n"); @@ -1478,8 +1479,6 @@ static int nvme_dump_evtlog(struct nvme_dev *dev, __u32 namespace_id, __u32 stor close_fd: fclose(fd); -free: - nvme_free_huge(pevent_log_info, huge); free_pevent: free(pevent); ret: diff --git a/plugins/wdc/wdc-nvme.c b/plugins/wdc/wdc-nvme.c index 2444d36b..f81e02b5 100644 --- a/plugins/wdc/wdc-nvme.c +++ b/plugins/wdc/wdc-nvme.c @@ -36,6 +36,7 @@ #include "libnvme.h" #include "plugin.h" #include "linux/types.h" +#include "util/cleanup.h" #include "util/types.h" #include "nvme-print.h" @@ -11116,9 +11117,9 @@ static int wdc_vs_pcie_stats(int argc, char **argv, struct command *command, nvme_root_t r; __u64 capabilities = 0; int fmt = -1; + _cleanup_huge_ struct nvme_mem_huge mh = { 0, }; struct wdc_vs_pcie_stats *pcieStatsPtr = NULL; int pcie_stats_size = sizeof(struct wdc_vs_pcie_stats); - bool huge; struct config { char *output_format; @@ -11146,7 +11147,7 @@ static int wdc_vs_pcie_stats(int argc, char **argv, struct command *command, goto out; } - pcieStatsPtr = nvme_alloc_huge(pcie_stats_size, &huge); + pcieStatsPtr = nvme_alloc_huge(pcie_stats_size, &mh); if (!pcieStatsPtr) { fprintf(stderr, "ERROR: WDC: PCIE Stats alloc: %s\n", strerror(errno)); ret = -1; @@ -11176,9 +11177,6 @@ static int wdc_vs_pcie_stats(int argc, char **argv, struct command *command, } } } - - nvme_free_huge(pcieStatsPtr, huge); - out: nvme_free_tree(r); dev_close(dev); diff --git a/plugins/zns/zns.c b/plugins/zns/zns.c index bb13135a..f2c146a5 100644 --- a/plugins/zns/zns.c +++ b/plugins/zns/zns.c @@ -12,6 +12,7 @@ #include "nvme.h" #include "libnvme.h" #include "nvme-print.h" +#include "util/cleanup.h" #define CREATE_CMD #include "zns.h" @@ -833,8 +834,8 @@ static int report_zones(int argc, char **argv, struct command *cmd, struct plugi int zdes = 0, err = -1; struct nvme_dev *dev; __u32 report_size; - bool huge = false; struct nvme_zone_report *report, *buff; + _cleanup_huge_ struct nvme_mem_huge mh = { 0, }; unsigned int nr_zones_chunks = 1024, /* 1024 entries * 64 bytes per entry = 64k byte transfer */ nr_zones_retrieved = 0, @@ -949,7 +950,7 @@ static int report_zones(int argc, char **argv, struct command *cmd, struct plugi log_len = sizeof(struct nvme_zone_report) + ((sizeof(struct nvme_zns_desc) * nr_zones_chunks) + (nr_zones_chunks * zdes)); report_size = log_len; - report = nvme_alloc_huge(report_size, &huge); + report = nvme_alloc_huge(report_size, &mh); if (!report) { perror("alloc"); err = -ENOMEM; @@ -989,8 +990,6 @@ static int report_zones(int argc, char **argv, struct command *cmd, struct plugi nvme_zns_finish_zone_list(total_nr_zones, zone_list, flags); - nvme_free_huge(report, huge); - free_buff: free(buff); close_dev: diff --git a/util/cleanup.h b/util/cleanup.h index c134fe89..ad1d0cda 100644 --- a/util/cleanup.h +++ b/util/cleanup.h @@ -5,6 +5,8 @@ #include #include +#include "util/mem.h" + #define __cleanup__(fn) __attribute__((cleanup(fn))) #define DECLARE_CLEANUP_FUNC(name, type) \ @@ -25,6 +27,8 @@ static inline void freep(void *p) } #define _cleanup_free_ __cleanup__(freep) +#define _cleanup_huge_ __cleanup__(nvme_free_huge) + static inline void close_file(int *f) { if (*f > STDERR_FILENO) diff --git a/util/mem.c b/util/mem.c index 25370fef..d2be46e8 100644 --- a/util/mem.c +++ b/util/mem.c @@ -3,22 +3,24 @@ #include #include #include +#include #include "mem.h" #include "common.h" #define ROUND_UP(N, S) ((((N) + (S) - 1) / (S)) * (S)) +#define HUGE_MIN 0x80000 void *nvme_alloc(size_t len) { - size_t _len = ROUND_UP(len, 0x1000); void *p; - if (posix_memalign((void *)&p, getpagesize(), _len)) + len = ROUND_UP(len, 0x1000); + if (posix_memalign((void *)&p, getpagesize(), len)) return NULL; - memset(p, 0, _len); + memset(p, 0, len); return p; } @@ -36,68 +38,72 @@ void *nvme_realloc(void *p, size_t len) return result; } -static void *__nvme_alloc_huge(size_t len, bool *huge) +void *nvme_alloc_huge(size_t len, struct nvme_mem_huge *mh) { - void *p; - - if (!posix_memalign(&p, getpagesize(), len)) { - *huge = false; - memset(p, 0, len); - return p; + memset(mh, 0, sizeof(*mh)); + + len = ROUND_UP(len, 0x1000); + + /* + * For smaller allocation we just use posix_memalign and hope the kernel + * is able to convert to a contiguous memory region. + */ + if (len < HUGE_MIN) { + mh->p = nvme_alloc(len); + if (!mh->p) + return NULL; + mh->posix_memalign = true; + mh->len = len; + return mh->p; } - return NULL; -} -#define HUGE_MIN 0x80000 - -#ifdef CONFIG_LIBHUGETLBFS -void nvme_free_huge(void *p, bool huge) -{ - if (huge) { - if (p) - free_hugepage_region(p); - } else { - free(p); + /* + * Larger allocation will almost certainly fail with the small + * allocation approach. Instead try pre-allocating memory from the + * HugeTLB pool. + * + * https://www.kernel.org/doc/Documentation/vm/hugetlbpage.txt + */ + mh->p = mmap(NULL, len, PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_PRIVATE | MAP_HUGETLB, -1, 0); + if (mh->p != MAP_FAILED) { + mh->len = len; + return mh->p; } -} -void *nvme_alloc_huge(size_t len, bool *huge) -{ - void *p; + /* + * And if mmap fails because the pool is empty, try to use + * posix_memalign/madvise as fallback with a 2MB aligmnent in order to + * fullfil the request. This gives the kernel a chance to try to claim + * some huge pages. This might still fail though. + */ + len = ROUND_UP(len, 0x200000); + if (posix_memalign(&mh->p, 0x200000, len)) + return NULL; + mh->posix_memalign = true; + mh->len = len; - if (len < HUGE_MIN) - return __nvme_alloc_huge(len, huge); + memset(mh->p, 0, mh->len); - p = get_hugepage_region(len, GHR_DEFAULT); - if (!p) - return __nvme_alloc_huge(len, huge); + if (madvise(mh->p, mh->len, MADV_HUGEPAGE) < 0) { + nvme_free_huge(mh); + return NULL; + } - *huge = true; - return p; -} -#else -void nvme_free_huge(void *p, bool huge) -{ - free(p); + return mh->p; } -void *nvme_alloc_huge(size_t len, bool *huge) -{ - return __nvme_alloc_huge(len, huge); -} -#endif +void nvme_free_huge(struct nvme_mem_huge *mh) -void *nvme_realloc_huge(void *p, size_t len, bool *huge) { - size_t old_len = malloc_usable_size(p); - bool was_huge = *huge; - - void *result = nvme_alloc_huge(len, huge); + if (!mh || mh->len == 0) + return; - if (p) { - memcpy(result, p, min(old_len, len)); - nvme_free_huge(p, was_huge); - } + if (mh->posix_memalign) + free(mh->p); + else + munmap(mh->p, mh->len); - return result; + mh->len = 0; + mh->p = NULL; } diff --git a/util/mem.h b/util/mem.h index 12c5b0eb..d13eb3a2 100644 --- a/util/mem.h +++ b/util/mem.h @@ -8,7 +8,13 @@ void *nvme_alloc(size_t len); void *nvme_realloc(void *p, size_t len); -void *nvme_alloc_huge(size_t len, bool *huge); -void nvme_free_huge(void *p, bool huge); +struct nvme_mem_huge { + size_t len; + bool posix_memalign; /* p has been allocated using posix_memalign */ + void *p; +}; + +void *nvme_alloc_huge(size_t len, struct nvme_mem_huge *mh); +void nvme_free_huge(struct nvme_mem_huge *mh); #endif /* MEM_H_ */