lib/ftl: prepare non-volatile cache area

When creating FTL device using non-volatile cache, zero out the
non-volatile cache and store metadata (device's UUID, size of the cache)
in the first block.

Change-Id: Id8f212aef756e86e8a215582ab7c32a635e18938
Signed-off-by: Konrad Sztyber <konrad.sztyber@intel.com>
Reviewed-on: https://review.gerrithub.io/c/spdk/spdk/+/458094
Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
Reviewed-by: Darek Stojaczyk <dariusz.stojaczyk@intel.com>
Reviewed-by: Ben Walker <benjamin.walker@intel.com>
Reviewed-by: Shuhei Matsumoto <shuhei.matsumoto.xt@hitachi.com>
Reviewed-by: Mateusz Kozlowski <mateusz.kozlowski@intel.com>
This commit is contained in:
Konrad Sztyber 2019-05-29 15:01:40 +02:00 committed by Ben Walker
parent 7e451194c8
commit 1243c9306d
3 changed files with 141 additions and 41 deletions

View File

@ -203,7 +203,6 @@ ftl_md_write_cb(struct ftl_io *io, void *arg, int status)
struct spdk_ftl_dev *dev = io->dev;
struct ftl_nv_cache *nv_cache = &dev->nv_cache;
struct ftl_wptr *wptr;
struct spdk_bdev *bdev;
wptr = ftl_wptr_from_band(io->band);
@ -215,13 +214,11 @@ ftl_md_write_cb(struct ftl_io *io, void *arg, int status)
ftl_band_set_next_state(io->band);
if (io->band->state == FTL_BAND_STATE_CLOSED) {
if (nv_cache->bdev_desc) {
bdev = spdk_bdev_desc_get_bdev(nv_cache->bdev_desc);
pthread_spin_lock(&nv_cache->lock);
nv_cache->num_available += ftl_band_user_lbks(io->band);
if (spdk_unlikely(nv_cache->num_available > spdk_bdev_get_num_blocks(bdev))) {
nv_cache->num_available = spdk_bdev_get_num_blocks(bdev);
if (spdk_unlikely(nv_cache->num_available > nv_cache->num_data_blocks)) {
nv_cache->num_available = nv_cache->num_data_blocks;
}
pthread_spin_unlock(&nv_cache->lock);
}
@ -975,7 +972,7 @@ ftl_reserve_nv_cache(struct ftl_nv_cache *nv_cache, size_t *num_lbks)
nv_cache->num_available -= *num_lbks;
if (nv_cache->current_addr == spdk_bdev_get_num_blocks(bdev)) {
nv_cache->current_addr = 0;
nv_cache->current_addr = FTL_NV_CACHE_DATA_OFFSET;
}
out:
pthread_spin_unlock(&nv_cache->lock);

View File

@ -110,6 +110,8 @@ struct ftl_nv_cache {
uint64_t current_addr;
/* Number of available blocks left */
uint64_t num_available;
/* Maximum number of blocks */
uint64_t num_data_blocks;
/* Metadata pool */
struct spdk_mempool *md_pool;
/* Cache lock */
@ -234,6 +236,17 @@ struct spdk_ftl_dev {
STAILQ_ENTRY(spdk_ftl_dev) stailq;
};
struct ftl_nv_cache_header {
/* Version of the header */
uint32_t version;
/* UUID of the FTL device */
struct spdk_uuid uuid;
/* Size of the non-volatile cache (in blocks) */
uint64_t size;
/* Checksum of the header, needs to be last element */
uint32_t checksum;
} __attribute__((packed));
typedef void (*ftl_restore_fn)(struct spdk_ftl_dev *, struct ftl_restore *, int);
void ftl_apply_limits(struct spdk_ftl_dev *dev);
@ -463,4 +476,7 @@ ftl_vld_map_size(const struct spdk_ftl_dev *dev)
return (size_t)spdk_divide_round_up(ftl_num_band_lbks(dev), CHAR_BIT);
}
#define FTL_NV_CACHE_HEADER_VERSION 1
#define FTL_NV_CACHE_DATA_OFFSET 1
#endif /* FTL_CORE_H */

View File

@ -39,6 +39,10 @@
#include "spdk/likely.h"
#include "spdk_internal/log.h"
#include "spdk/ftl.h"
#include "spdk/likely.h"
#include "spdk/string.h"
#include "spdk/crc32.h"
#include "ftl_core.h"
#include "ftl_anm.h"
#include "ftl_io.h"
@ -522,11 +526,13 @@ ftl_dev_init_nv_cache(struct spdk_ftl_dev *dev, struct spdk_bdev_desc *bdev_desc
/* The cache needs to be capable of storing at least two full bands. This requirement comes
* from the fact that cache works as a protection against power loss, so before the data
* inside the cache can be overwritten, the band it's stored on has to be closed.
* inside the cache can be overwritten, the band it's stored on has to be closed. Plus one
* extra block is needed to store the header.
*/
if (spdk_bdev_get_num_blocks(bdev) < ftl_num_band_lbks(dev) * 2) {
SPDK_ERRLOG("Insufficient number of blocks for write buffer cache(%"PRIu64"\n",
spdk_bdev_get_num_blocks(bdev));
if (spdk_bdev_get_num_blocks(bdev) < ftl_num_band_lbks(dev) * 2 + 1) {
SPDK_ERRLOG("Insufficient number of blocks for write buffer cache (available: %"
PRIu64", required: %"PRIu64")\n", spdk_bdev_get_num_blocks(bdev),
ftl_num_band_lbks(dev) * 2 + 1);
return -1;
}
@ -551,8 +557,9 @@ ftl_dev_init_nv_cache(struct spdk_ftl_dev *dev, struct spdk_bdev_desc *bdev_desc
}
nv_cache->bdev_desc = bdev_desc;
nv_cache->current_addr = 0;
nv_cache->num_available = spdk_bdev_get_num_blocks(bdev);
nv_cache->current_addr = FTL_NV_CACHE_DATA_OFFSET;
nv_cache->num_data_blocks = spdk_bdev_get_num_blocks(bdev) - 1;
nv_cache->num_available = nv_cache->num_data_blocks;
return 0;
}
@ -802,35 +809,6 @@ ftl_init_complete(struct spdk_ftl_dev *dev)
dev->init_arg = NULL;
}
static int
ftl_setup_initial_state(struct spdk_ftl_dev *dev)
{
struct spdk_ftl_conf *conf = &dev->conf;
size_t i;
spdk_uuid_generate(&dev->uuid);
dev->num_lbas = 0;
for (i = 0; i < ftl_dev_num_bands(dev); ++i) {
dev->num_lbas += ftl_band_num_usable_lbks(&dev->bands[i]);
}
dev->num_lbas = (dev->num_lbas * (100 - conf->lba_rsvd)) / 100;
if (ftl_dev_l2p_alloc(dev)) {
SPDK_ERRLOG("Unable to init l2p table\n");
return -1;
}
if (ftl_init_bands_state(dev)) {
SPDK_ERRLOG("Unable to finish the initialization\n");
return -1;
}
ftl_init_complete(dev);
return 0;
}
struct ftl_init_fail_ctx {
spdk_ftl_init_fn cb;
void *arg;
@ -866,6 +844,115 @@ ftl_init_fail(struct spdk_ftl_dev *dev)
}
}
static void
ftl_write_nv_cache_md_cb(struct spdk_bdev_io *bdev_io, bool success, void *cb_arg)
{
struct spdk_ftl_dev *dev = cb_arg;
struct iovec *iovs = NULL;
int iov_cnt = 0;
spdk_bdev_io_get_iovec(bdev_io, &iovs, &iov_cnt);
spdk_dma_free(iovs[0].iov_base);
spdk_bdev_free_io(bdev_io);
if (spdk_unlikely(!success)) {
SPDK_ERRLOG("Writing non-volatile cache's metadata header failed\n");
ftl_init_fail(dev);
return;
}
ftl_init_complete(dev);
}
static void
ftl_clear_nv_cache_cb(struct spdk_bdev_io *bdev_io, bool success, void *cb_arg)
{
struct spdk_ftl_dev *dev = cb_arg;
struct spdk_bdev *bdev;
struct ftl_nv_cache *nv_cache = &dev->nv_cache;
struct ftl_io_channel *ioch;
struct ftl_nv_cache_header *hdr;
spdk_bdev_free_io(bdev_io);
ioch = spdk_io_channel_get_ctx(dev->ioch);
bdev = spdk_bdev_desc_get_bdev(nv_cache->bdev_desc);
if (spdk_unlikely(!success)) {
SPDK_ERRLOG("Unable to clear the non-volatile cache bdev\n");
ftl_init_fail(dev);
return;
}
assert(sizeof(*hdr) <= spdk_bdev_get_block_size(bdev));
hdr = spdk_dma_zmalloc(spdk_bdev_get_block_size(bdev), spdk_bdev_get_buf_align(bdev), NULL);
if (spdk_unlikely(!hdr)) {
SPDK_ERRLOG("Memory allocation failure\n");
ftl_init_fail(dev);
return;
}
hdr->uuid = dev->uuid;
hdr->size = spdk_bdev_get_num_blocks(bdev);
hdr->version = FTL_NV_CACHE_HEADER_VERSION;
hdr->checksum = spdk_crc32c_update(hdr, offsetof(struct ftl_nv_cache_header, checksum), 0);
if (spdk_bdev_write_blocks(nv_cache->bdev_desc, ioch->cache_ioch, hdr, 0, 1,
ftl_write_nv_cache_md_cb, dev)) {
SPDK_ERRLOG("Unable to write non-volatile cache metadata header\n");
spdk_dma_free(hdr);
ftl_init_fail(dev);
}
}
static int
ftl_setup_initial_state(struct spdk_ftl_dev *dev)
{
struct spdk_ftl_conf *conf = &dev->conf;
struct ftl_nv_cache *nv_cache = &dev->nv_cache;
struct spdk_bdev *bdev;
struct ftl_io_channel *ioch;
size_t i;
int rc;
spdk_uuid_generate(&dev->uuid);
dev->num_lbas = 0;
for (i = 0; i < ftl_dev_num_bands(dev); ++i) {
dev->num_lbas += ftl_band_num_usable_lbks(&dev->bands[i]);
}
dev->num_lbas = (dev->num_lbas * (100 - conf->lba_rsvd)) / 100;
if (ftl_dev_l2p_alloc(dev)) {
SPDK_ERRLOG("Unable to init l2p table\n");
return -1;
}
if (ftl_init_bands_state(dev)) {
SPDK_ERRLOG("Unable to finish the initialization\n");
return -1;
}
if (!nv_cache->bdev_desc) {
ftl_init_complete(dev);
} else {
ioch = spdk_io_channel_get_ctx(dev->ioch);
bdev = spdk_bdev_desc_get_bdev(nv_cache->bdev_desc);
rc = spdk_bdev_write_zeroes_blocks(nv_cache->bdev_desc, ioch->cache_ioch,
1, spdk_bdev_get_num_blocks(bdev) - 1,
ftl_clear_nv_cache_cb, dev);
if (spdk_unlikely(rc != 0)) {
SPDK_ERRLOG("Unable to clear the non-volatile cache bdev: %s\n",
spdk_strerror(-rc));
return -1;
}
}
return 0;
}
static void
ftl_restore_device_cb(struct spdk_ftl_dev *dev, struct ftl_restore *restore, int status)
{