]> www.infradead.org Git - users/sagi/nvme-cli.git/commitdiff
nvme: replace libhugetlbfs with mmap and madvise
authorDaniel Wagner <dwagner@suse.de>
Fri, 17 Nov 2023 08:01:35 +0000 (09:01 +0100)
committerDaniel Wagner <wagi@monom.org>
Fri, 1 Dec 2023 09:39:47 +0000 (10:39 +0100)
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 <dwagner@suse.de>
meson.build
meson_options.txt
nvme.c
plugins/micron/micron-nvme.c
plugins/scaleflux/sfx-nvme.c
plugins/wdc/wdc-nvme.c
plugins/zns/zns.c
util/cleanup.h
util/mem.c
util/mem.h

index 864ed0d402e315d0d550deaaeeb16602718b98a9..9a45b27c1f88e818b3ebff2d5c42a0b8d1944df6 100644 (file)
@@ -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 = {
index f092017953cdff1b05839cecb64fdfa4de3f1217..c61dae0fff5640d7e1fd1dbb7e1f4cdb22ce6b97 100644 (file)
@@ -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 4027766082fa1282909b6e96f411989f0d43b416..4f946a27288fe9d5ae09fd6e3c1955bed18c5f17 100644 (file)
--- a/nvme.c
+++ b/nvme.c
 #include <libgen.h>
 #include <signal.h>
 
-#ifdef CONFIG_LIBHUGETLBFS
-#include <hugetlbfs.h>
-#endif
-
 #include <linux/fs.h>
 
 #include <sys/mman.h>
@@ -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;
 }
 
index c921827021e08b1de87851c74d9584f618f4f4c3..63a7a7950e211189a41c124c0672a76275c39a72 100644 (file)
@@ -16,6 +16,7 @@
 #include <limits.h>
 #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)
index e74764e472fb28b3124736ed5703490d86ba2a12..f752d5d56ac0c52af2127ec012e9411af6cd4b8b 100644 (file)
@@ -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:
index 2444d36b4fd25cd4b4f50d92aa0ad5d159463861..f81e02b54c5fe58d92cfbc6bea5259285425b70b 100644 (file)
@@ -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);
index bb13135a716dc8222e54f6d92e0e1d72ce94456d..f2c146a5c5850733e76896dfa068e66626c36ae1 100644 (file)
@@ -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:
index c134fe89af9b942e157a3e02b958eb102e7d531f..ad1d0cda8b6ccb108fdafc71dc6f91c05d426aa6 100644 (file)
@@ -5,6 +5,8 @@
 #include <unistd.h>
 #include <stdlib.h>
 
+#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)
index 25370fefd542071ed9dc808629e0aa73b81d4be2..d2be46e870e824d36af33afb4624ae19750d6cae 100644 (file)
@@ -3,22 +3,24 @@
 #include <unistd.h>
 #include <malloc.h>
 #include <string.h>
+#include <sys/mman.h>
 
 #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;
 }
index 12c5b0eb15ba13aa51a091187a5d6115cc4186e6..d13eb3a2133177f5a5ad2ae1c9b0744978faddd5 100644 (file)
@@ -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_ */