From d565f549b969a890cadfbbde55e897fb96933a27 Mon Sep 17 00:00:00 2001 From: Daniel Verkamp Date: Tue, 19 Sep 2017 10:59:58 -0700 Subject: [PATCH] bdev/nvme: support large (> 2TB) unmap requests spdk_bdev_unmap_blocks() accepts a 64-bit number of blocks, which can exceed the NVMe Dataset Management range's 32-bit number of blocks, which can represent up to 2 TB with 512-byte blocks. We can support up to 0.5 PB unmap requests by using the maximum number of descriptors in a single Dataset Management command, which should be sufficient for now. Change-Id: I0a4ee77a9be148355991e1a081007ffa020a3ee5 Signed-off-by: Daniel Verkamp Reviewed-on: https://review.gerrithub.io/379202 Tested-by: SPDK Automated Test System Reviewed-by: Jim Harris Reviewed-by: Changpeng Liu --- include/spdk/nvme_spec.h | 5 +++++ lib/bdev/nvme/bdev_nvme.c | 40 ++++++++++++++++++++++++++++++++++----- 2 files changed, 40 insertions(+), 5 deletions(-) diff --git a/include/spdk/nvme_spec.h b/include/spdk/nvme_spec.h index caabf79b4..ac0bab5c6 100644 --- a/include/spdk/nvme_spec.h +++ b/include/spdk/nvme_spec.h @@ -67,6 +67,11 @@ extern "C" { */ #define SPDK_NVME_DATASET_MANAGEMENT_MAX_RANGES 256 +/** + * Maximum number of blocks that may be specified in a single dataset management range. + */ +#define SPDK_NVME_DATASET_MANAGEMENT_RANGE_MAX_BLOCKS 0xFFFFFFFFu + union spdk_nvme_cap_register { uint64_t raw; struct { diff --git a/lib/bdev/nvme/bdev_nvme.c b/lib/bdev/nvme/bdev_nvme.c index 54bf1310a..adb0bb1eb 100644 --- a/lib/bdev/nvme/bdev_nvme.c +++ b/lib/bdev/nvme/bdev_nvme.c @@ -44,6 +44,7 @@ #include "spdk/io_channel.h" #include "spdk/string.h" #include "spdk/likely.h" +#include "spdk/util.h" #include "spdk_internal/bdev.h" #include "spdk_internal/log.h" @@ -1267,15 +1268,44 @@ bdev_nvme_unmap(struct nvme_bdev *nbdev, struct spdk_io_channel *ch, uint64_t num_blocks) { struct nvme_io_channel *nvme_ch = spdk_io_channel_get_ctx(ch); - int rc = 0; - struct spdk_nvme_dsm_range dsm_range = {}; + struct spdk_nvme_dsm_range dsm_ranges[SPDK_NVME_DATASET_MANAGEMENT_MAX_RANGES]; + struct spdk_nvme_dsm_range *range; + uint64_t offset, remaining; + uint64_t num_ranges_u64; + uint16_t num_ranges; + int rc; - dsm_range.starting_lba = offset_blocks; - dsm_range.length = num_blocks; + num_ranges_u64 = (num_blocks + SPDK_NVME_DATASET_MANAGEMENT_RANGE_MAX_BLOCKS - 1) / + SPDK_NVME_DATASET_MANAGEMENT_RANGE_MAX_BLOCKS; + if (num_ranges_u64 > SPDK_COUNTOF(dsm_ranges)) { + SPDK_ERRLOG("Unmap request for %" PRIu64 " blocks is too large\n", num_blocks); + return -EINVAL; + } + num_ranges = (uint16_t)num_ranges_u64; + + offset = offset_blocks; + remaining = num_blocks; + range = &dsm_ranges[0]; + + /* Fill max-size ranges until the remaining blocks fit into one range */ + while (remaining > SPDK_NVME_DATASET_MANAGEMENT_RANGE_MAX_BLOCKS) { + range->attributes.raw = 0; + range->length = SPDK_NVME_DATASET_MANAGEMENT_RANGE_MAX_BLOCKS; + range->starting_lba = offset; + + offset += SPDK_NVME_DATASET_MANAGEMENT_RANGE_MAX_BLOCKS; + remaining -= SPDK_NVME_DATASET_MANAGEMENT_RANGE_MAX_BLOCKS; + range++; + } + + /* Final range describes the remaining blocks */ + range->attributes.raw = 0; + range->length = remaining; + range->starting_lba = offset; rc = spdk_nvme_ns_cmd_dataset_management(nbdev->ns, nvme_ch->qpair, SPDK_NVME_DSM_ATTR_DEALLOCATE, - &dsm_range, 1, + dsm_ranges, num_ranges, bdev_nvme_queued_done, bio); return rc;