Commit 1243c930 authored by Konrad Sztyber's avatar Konrad Sztyber Committed by Ben Walker
Browse files

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: default avatarKonrad Sztyber <konrad.sztyber@intel.com>
Reviewed-on: https://review.gerrithub.io/c/spdk/spdk/+/458094


Tested-by: default avatarSPDK CI Jenkins <sys_sgci@intel.com>
Reviewed-by: default avatarDarek Stojaczyk <dariusz.stojaczyk@intel.com>
Reviewed-by: default avatarBen Walker <benjamin.walker@intel.com>
Reviewed-by: default avatarShuhei Matsumoto <shuhei.matsumoto.xt@hitachi.com>
Reviewed-by: default avatarMateusz Kozlowski <mateusz.kozlowski@intel.com>
parent 7e451194
Loading
Loading
Loading
Loading
+3 −6
Original line number Diff line number Diff line
@@ -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);
+16 −0
Original line number Diff line number Diff line
@@ -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 */
+122 −35
Original line number Diff line number Diff line
@@ -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)
{