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() + '"')
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,
summary(path_dict, section: 'Paths')
dep_dict = {
'json-c': json_c_dep.found(),
- 'libhugetlbfs': libhugetlbfs_dep.found(),
}
summary(dep_dict, section: 'Dependencies')
conf_dict = {
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'
-)
#include <libgen.h>
#include <signal.h>
-#ifdef CONFIG_LIBHUGETLBFS
-#include <hugetlbfs.h>
-#endif
-
#include <linux/fs.h>
#include <sys/mman.h>
_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 {
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) {
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,
nvme_show_error("persistent event log: %s", nvme_strerror(errno));
}
-free:
- nvme_free_huge(pevent_log_info, huge);
return err;
}
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 {
} 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) {
printf("Firmware download success\n");
}
-free:
- nvme_free_huge(fw_buf, huge);
-
return err;
}
__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;
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);
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;
}
}
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;
}
}
printf("sts : %02x\n", sts);
}
if (cfg.dry_run)
- goto free_buffer;
+ return 0;
struct nvme_io_args args = {
.args_size = sizeof(args),
}
}
-free_buffer:
- nvme_free_huge(buffer, huge);
-
return err;
}
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;
_cleanup_free_ void *mdata = NULL;
int err = 0;
__u32 result;
- bool huge = false;
const char *cmd_name = NULL;
struct timeval start_time, end_time;
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);
}
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;
}
}
}
printf("timeout_ms : %08x\n", cfg.timeout);
}
if (cfg.dry_run)
- goto free_data;
+ return 0;
gettimeofday(&start_time, NULL);
if (cfg.read)
passthru_print_read_output(cfg, data, dfd, mdata, mfd, err);
}
-free_data:
- nvme_free_huge(data, huge);
return err;
}
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;
}
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;
}
}
}
}
}
-free_data:
- nvme_free_huge(data, huge);
-
return err;
}
#include <limits.h>
#include "linux/types.h"
#include "nvme-print.h"
+#include "util/cleanup.h"
#define CREATE_CMD
#include "micron-nvme.h"
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))
}
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)
#include "linux/types.h"
#include "nvme-wrap.h"
#include "nvme-print.h"
+#include "util/cleanup.h"
#define CREATE_CMD
#include "sfx-nvme.h"
{
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 = {
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;
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;
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;
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");
close_fd:
fclose(fd);
-free:
- nvme_free_huge(pevent_log_info, huge);
free_pevent:
free(pevent);
ret:
#include "libnvme.h"
#include "plugin.h"
#include "linux/types.h"
+#include "util/cleanup.h"
#include "util/types.h"
#include "nvme-print.h"
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;
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;
}
}
}
-
- nvme_free_huge(pcieStatsPtr, huge);
-
out:
nvme_free_tree(r);
dev_close(dev);
#include "nvme.h"
#include "libnvme.h"
#include "nvme-print.h"
+#include "util/cleanup.h"
#define CREATE_CMD
#include "zns.h"
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,
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;
nvme_zns_finish_zone_list(total_nr_zones, zone_list, flags);
- nvme_free_huge(report, huge);
-
free_buff:
free(buff);
close_dev:
#include <unistd.h>
#include <stdlib.h>
+#include "util/mem.h"
+
#define __cleanup__(fn) __attribute__((cleanup(fn)))
#define DECLARE_CLEANUP_FUNC(name, type) \
}
#define _cleanup_free_ __cleanup__(freep)
+#define _cleanup_huge_ __cleanup__(nvme_free_huge)
+
static inline void close_file(int *f)
{
if (*f > STDERR_FILENO)
#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;
}
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;
}
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_ */