per Intel policy to include file commit date using git cmd below. The policy does not apply to non-Intel (C) notices. git log --follow -C90% --format=%ad --date default <file> | tail -1 and then pull just the 4 digit year from the result. Intel copyrights were not added to files where Intel either had no contribution ot the contribution lacked substance (ie license header updates, formatting changes, etc). Contribution date used "--follow -C95%" to get the most accurate date. Note that several files in this patch didn't end the license/(c) block with a blank comment line so these were added as the vast majority of files do have this last blank line. Simply there for consistency. Signed-off-by: paul luse <paul.e.luse@intel.com> Change-Id: Id5b7ce4f658fe87132f14139ead58d6e285c04d4 Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/15192 Tested-by: SPDK CI Jenkins <sys_sgci@intel.com> Reviewed-by: Jim Harris <james.r.harris@intel.com> Reviewed-by: Ben Walker <benjamin.walker@intel.com> Community-CI: Mellanox Build Bot
552 lines
14 KiB
C
552 lines
14 KiB
C
/* SPDX-License-Identifier: BSD-3-Clause
|
|
* Copyright (C) 2022 Intel Corporation.
|
|
* All rights reserved.
|
|
*/
|
|
|
|
#include "spdk/bdev_module.h"
|
|
#include "spdk/crc32.h"
|
|
|
|
#include "ftl_internal.h"
|
|
#include "ftl_band.h"
|
|
#include "ftl_core.h"
|
|
#include "ftl_layout.h"
|
|
#include "ftl_nv_cache_io.h"
|
|
#include "ftl_writer.h"
|
|
#include "mngt/ftl_mngt.h"
|
|
|
|
struct ftl_p2l_ckpt {
|
|
TAILQ_ENTRY(ftl_p2l_ckpt) link;
|
|
union ftl_md_vss *vss_md_page;
|
|
struct ftl_md *md;
|
|
struct ftl_layout_region *layout_region;
|
|
uint64_t num_pages;
|
|
|
|
#if defined(DEBUG)
|
|
uint64_t dbg_bmp_sz;
|
|
void *dbg_bmp;
|
|
struct ftl_bitmap *bmp;
|
|
#endif
|
|
};
|
|
|
|
static struct ftl_p2l_ckpt *
|
|
ftl_p2l_ckpt_new(struct spdk_ftl_dev *dev, int region_type)
|
|
{
|
|
struct ftl_p2l_ckpt *ckpt;
|
|
|
|
ckpt = calloc(1, sizeof(struct ftl_p2l_ckpt));
|
|
if (!ckpt) {
|
|
return NULL;
|
|
}
|
|
|
|
ckpt->vss_md_page = ftl_md_vss_buf_alloc(&dev->layout.region[region_type],
|
|
dev->layout.region[region_type].num_entries);
|
|
ckpt->layout_region = &dev->layout.region[region_type];
|
|
ckpt->md = dev->layout.md[region_type];
|
|
ckpt->num_pages = spdk_divide_round_up(ftl_get_num_blocks_in_band(dev), FTL_NUM_LBA_IN_BLOCK);
|
|
|
|
if (!ckpt->vss_md_page) {
|
|
free(ckpt);
|
|
return NULL;
|
|
}
|
|
|
|
#if defined(DEBUG)
|
|
/* The bitmap size must be a multiple of word size (8b) - round up */
|
|
ckpt->dbg_bmp_sz = spdk_divide_round_up(ckpt->num_pages, 8);
|
|
|
|
ckpt->dbg_bmp = calloc(1, ckpt->dbg_bmp_sz);
|
|
assert(ckpt->dbg_bmp);
|
|
ckpt->bmp = ftl_bitmap_create(ckpt->dbg_bmp, ckpt->dbg_bmp_sz);
|
|
assert(ckpt->bmp);
|
|
#endif
|
|
|
|
return ckpt;
|
|
}
|
|
|
|
static void
|
|
ftl_p2l_ckpt_destroy(struct ftl_p2l_ckpt *ckpt)
|
|
{
|
|
#if defined(DEBUG)
|
|
ftl_bitmap_destroy(ckpt->bmp);
|
|
free(ckpt->dbg_bmp);
|
|
#endif
|
|
spdk_dma_free(ckpt->vss_md_page);
|
|
free(ckpt);
|
|
}
|
|
|
|
int
|
|
ftl_p2l_ckpt_init(struct spdk_ftl_dev *dev)
|
|
{
|
|
int region_type;
|
|
struct ftl_p2l_ckpt *ckpt;
|
|
|
|
TAILQ_INIT(&dev->p2l_ckpt.free);
|
|
TAILQ_INIT(&dev->p2l_ckpt.inuse);
|
|
for (region_type = FTL_LAYOUT_REGION_TYPE_P2L_CKPT_MIN;
|
|
region_type <= FTL_LAYOUT_REGION_TYPE_P2L_CKPT_MAX;
|
|
region_type++) {
|
|
ckpt = ftl_p2l_ckpt_new(dev, region_type);
|
|
if (!ckpt) {
|
|
return -1;
|
|
}
|
|
TAILQ_INSERT_TAIL(&dev->p2l_ckpt.free, ckpt, link);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
ftl_p2l_ckpt_deinit(struct spdk_ftl_dev *dev)
|
|
{
|
|
struct ftl_p2l_ckpt *ckpt, *ckpt_next;
|
|
|
|
TAILQ_FOREACH_SAFE(ckpt, &dev->p2l_ckpt.free, link, ckpt_next) {
|
|
TAILQ_REMOVE(&dev->p2l_ckpt.free, ckpt, link);
|
|
ftl_p2l_ckpt_destroy(ckpt);
|
|
}
|
|
|
|
TAILQ_FOREACH_SAFE(ckpt, &dev->p2l_ckpt.inuse, link, ckpt_next) {
|
|
TAILQ_REMOVE(&dev->p2l_ckpt.inuse, ckpt, link);
|
|
ftl_p2l_ckpt_destroy(ckpt);
|
|
}
|
|
}
|
|
|
|
struct ftl_p2l_ckpt *
|
|
ftl_p2l_ckpt_acquire(struct spdk_ftl_dev *dev)
|
|
{
|
|
struct ftl_p2l_ckpt *ckpt;
|
|
|
|
ckpt = TAILQ_FIRST(&dev->p2l_ckpt.free);
|
|
assert(ckpt);
|
|
TAILQ_REMOVE(&dev->p2l_ckpt.free, ckpt, link);
|
|
TAILQ_INSERT_TAIL(&dev->p2l_ckpt.inuse, ckpt, link);
|
|
return ckpt;
|
|
}
|
|
|
|
void
|
|
ftl_p2l_ckpt_release(struct spdk_ftl_dev *dev, struct ftl_p2l_ckpt *ckpt)
|
|
{
|
|
assert(ckpt);
|
|
#if defined(DEBUG)
|
|
memset(ckpt->dbg_bmp, 0, ckpt->dbg_bmp_sz);
|
|
#endif
|
|
TAILQ_REMOVE(&dev->p2l_ckpt.inuse, ckpt, link);
|
|
TAILQ_INSERT_TAIL(&dev->p2l_ckpt.free, ckpt, link);
|
|
}
|
|
|
|
static void
|
|
ftl_p2l_ckpt_issue_end(int status, void *arg)
|
|
{
|
|
struct ftl_rq *rq = arg;
|
|
assert(rq);
|
|
|
|
if (status) {
|
|
#ifdef SPDK_FTL_RETRY_ON_ERROR
|
|
/* retry */
|
|
ftl_md_persist_entry_retry(&rq->md_persist_entry_ctx);
|
|
return;
|
|
#else
|
|
ftl_abort();
|
|
#endif
|
|
}
|
|
|
|
assert(rq->io.band->queue_depth > 0);
|
|
rq->io.band->queue_depth--;
|
|
|
|
rq->owner.cb(rq);
|
|
}
|
|
|
|
void
|
|
ftl_p2l_ckpt_issue(struct ftl_rq *rq)
|
|
{
|
|
struct ftl_rq_entry *iter = rq->entries;
|
|
ftl_addr addr = rq->io.addr;
|
|
struct ftl_p2l_ckpt *ckpt = NULL;
|
|
struct ftl_p2l_ckpt_page *map_page;
|
|
union ftl_md_vss *md_page;
|
|
struct ftl_band *band;
|
|
uint64_t band_offs, p2l_map_page_no, i;
|
|
|
|
assert(rq);
|
|
band = rq->io.band;
|
|
ckpt = band->p2l_map.p2l_ckpt;
|
|
assert(ckpt);
|
|
|
|
/* Derive the P2L map page no */
|
|
band_offs = ftl_band_block_offset_from_addr(band, rq->io.addr);
|
|
p2l_map_page_no = band_offs / FTL_NUM_LBA_IN_BLOCK;
|
|
assert((band_offs + rq->num_blocks - 1) / FTL_NUM_LBA_IN_BLOCK == p2l_map_page_no);
|
|
assert(p2l_map_page_no < ckpt->num_pages);
|
|
|
|
/* Get the corresponding P2L map page - the underlying stored data is the same as in the end metadata of band P2L (ftl_p2l_map_entry),
|
|
* however we're interested in a whole page (4KiB) worth of content
|
|
*/
|
|
map_page = ((struct ftl_p2l_ckpt_page *)band->p2l_map.band_map) + p2l_map_page_no;
|
|
assert(map_page);
|
|
|
|
/* Set up the md */
|
|
md_page = &ckpt->vss_md_page[p2l_map_page_no];
|
|
md_page->p2l_ckpt.seq_id = band->md->seq;
|
|
assert(rq->num_blocks == FTL_NUM_LBA_IN_BLOCK);
|
|
|
|
/* Update the band P2L map */
|
|
for (i = 0; i < rq->num_blocks; i++, iter++) {
|
|
if (iter->lba != FTL_LBA_INVALID) {
|
|
/* This is compaction or reloc */
|
|
assert(!ftl_addr_in_nvc(rq->dev, addr));
|
|
ftl_band_set_p2l(band, iter->lba, addr, iter->seq_id);
|
|
}
|
|
addr = ftl_band_next_addr(band, addr, 1);
|
|
}
|
|
|
|
#if defined(DEBUG)
|
|
ftl_bitmap_set(ckpt->bmp, p2l_map_page_no);
|
|
#endif
|
|
|
|
md_page->p2l_ckpt.p2l_checksum = spdk_crc32c_update(map_page,
|
|
rq->num_blocks * sizeof(struct ftl_p2l_map_entry), 0);
|
|
/* Save the P2L map entry */
|
|
ftl_md_persist_entry(ckpt->md, p2l_map_page_no, map_page, md_page, ftl_p2l_ckpt_issue_end,
|
|
rq, &rq->md_persist_entry_ctx);
|
|
}
|
|
|
|
#if defined(DEBUG)
|
|
static void
|
|
ftl_p2l_validate_pages(struct ftl_band *band, struct ftl_p2l_ckpt *ckpt,
|
|
uint64_t page_begin, uint64_t page_end, bool val)
|
|
{
|
|
uint64_t page_no;
|
|
|
|
for (page_no = page_begin; page_no < page_end; page_no++) {
|
|
assert(ftl_bitmap_get(ckpt->bmp, page_no) == val);
|
|
}
|
|
}
|
|
|
|
void
|
|
ftl_p2l_validate_ckpt(struct ftl_band *band)
|
|
{
|
|
struct ftl_p2l_ckpt *ckpt = band->p2l_map.p2l_ckpt;
|
|
uint64_t num_blks_tail_md = ftl_tail_md_num_blocks(band->dev);
|
|
uint64_t num_pages_tail_md = num_blks_tail_md / FTL_NUM_LBA_IN_BLOCK;
|
|
|
|
if (!ckpt) {
|
|
return;
|
|
}
|
|
|
|
assert(num_blks_tail_md % FTL_NUM_LBA_IN_BLOCK == 0);
|
|
|
|
/* all data pages written */
|
|
ftl_p2l_validate_pages(band, ckpt,
|
|
0, ckpt->num_pages - num_pages_tail_md, true);
|
|
|
|
/* tail md pages not written */
|
|
ftl_p2l_validate_pages(band, ckpt, ckpt->num_pages - num_pages_tail_md,
|
|
ckpt->num_pages, false);
|
|
}
|
|
#endif
|
|
|
|
static struct ftl_band *
|
|
ftl_get_band_from_region(struct spdk_ftl_dev *dev, enum ftl_layout_region_type type)
|
|
{
|
|
struct ftl_band *band = NULL;
|
|
uint64_t i;
|
|
|
|
assert(type >= FTL_LAYOUT_REGION_TYPE_P2L_CKPT_MIN);
|
|
assert(type <= FTL_LAYOUT_REGION_TYPE_P2L_CKPT_MAX);
|
|
|
|
for (i = 0; i < ftl_get_num_bands(dev); i++) {
|
|
band = &dev->bands[i];
|
|
if ((band->md->state == FTL_BAND_STATE_OPEN ||
|
|
band->md->state == FTL_BAND_STATE_FULL) &&
|
|
band->md->p2l_md_region == type) {
|
|
return band;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void ftl_mngt_persist_band_p2l(struct ftl_mngt_process *mngt, struct ftl_p2l_sync_ctx *ctx);
|
|
|
|
static void
|
|
ftl_p2l_ckpt_persist_end(int status, void *arg)
|
|
{
|
|
struct ftl_mngt_process *mngt = arg;
|
|
struct ftl_p2l_sync_ctx *ctx;
|
|
|
|
assert(mngt);
|
|
|
|
if (status) {
|
|
ftl_mngt_fail_step(mngt);
|
|
return;
|
|
}
|
|
|
|
ctx = ftl_mngt_get_step_ctx(mngt);
|
|
ctx->page_start++;
|
|
|
|
if (ctx->page_start == ctx->page_end) {
|
|
ctx->md_region++;
|
|
ftl_mngt_continue_step(mngt);
|
|
} else {
|
|
ftl_mngt_persist_band_p2l(mngt, ctx);
|
|
}
|
|
}
|
|
|
|
static void
|
|
ftl_mngt_persist_band_p2l(struct ftl_mngt_process *mngt, struct ftl_p2l_sync_ctx *ctx)
|
|
{
|
|
struct ftl_band *band = ctx->band;
|
|
union ftl_md_vss *md_page;
|
|
struct ftl_p2l_ckpt_page *map_page;
|
|
struct ftl_p2l_ckpt *ckpt;
|
|
|
|
ckpt = band->p2l_map.p2l_ckpt;
|
|
|
|
map_page = ((struct ftl_p2l_ckpt_page *)band->p2l_map.band_map) + ctx->page_start;
|
|
|
|
md_page = &ckpt->vss_md_page[ctx->page_start];
|
|
md_page->p2l_ckpt.seq_id = band->md->seq;
|
|
md_page->p2l_ckpt.p2l_checksum = spdk_crc32c_update(map_page,
|
|
FTL_NUM_LBA_IN_BLOCK * sizeof(struct ftl_p2l_map_entry), 0);
|
|
|
|
/* Save the P2L map entry */
|
|
ftl_md_persist_entry(ckpt->md, ctx->page_start, map_page, md_page,
|
|
ftl_p2l_ckpt_persist_end, mngt, &band->md_persist_entry_ctx);
|
|
}
|
|
|
|
void
|
|
ftl_mngt_persist_bands_p2l(struct ftl_mngt_process *mngt)
|
|
{
|
|
struct ftl_p2l_sync_ctx *ctx = ftl_mngt_get_step_ctx(mngt);
|
|
struct ftl_band *band;
|
|
uint64_t band_offs, p2l_map_page_no;
|
|
|
|
if (ctx->md_region > FTL_LAYOUT_REGION_TYPE_P2L_CKPT_MAX) {
|
|
ftl_mngt_next_step(mngt);
|
|
return;
|
|
}
|
|
|
|
band = ftl_get_band_from_region(ftl_mngt_get_dev(mngt), ctx->md_region);
|
|
|
|
/* No band has the md region assigned (shutdown happened before next_band was assigned) */
|
|
if (!band) {
|
|
ctx->page_start = 0;
|
|
ctx->page_end = 0;
|
|
ctx->md_region++;
|
|
ftl_mngt_continue_step(mngt);
|
|
return;
|
|
}
|
|
|
|
band_offs = ftl_band_block_offset_from_addr(band, band->md->iter.addr);
|
|
p2l_map_page_no = band_offs / FTL_NUM_LBA_IN_BLOCK;
|
|
|
|
ctx->page_start = 0;
|
|
ctx->page_end = p2l_map_page_no;
|
|
ctx->band = band;
|
|
|
|
/* Band wasn't written to - no need to sync its P2L */
|
|
if (ctx->page_end == 0) {
|
|
ctx->md_region++;
|
|
ftl_mngt_continue_step(mngt);
|
|
return;
|
|
}
|
|
|
|
ftl_mngt_persist_band_p2l(mngt, ctx);
|
|
}
|
|
|
|
int
|
|
ftl_mngt_p2l_ckpt_get_seq_id(struct spdk_ftl_dev *dev, int md_region)
|
|
{
|
|
struct ftl_layout *layout = &dev->layout;
|
|
struct ftl_md *md = layout->md[md_region];
|
|
union ftl_md_vss *page_md_buf = ftl_md_get_vss_buffer(md);
|
|
uint64_t page_no, seq_id = 0;
|
|
|
|
for (page_no = 0; page_no < layout->p2l.ckpt_pages; page_no++, page_md_buf++) {
|
|
if (seq_id < page_md_buf->p2l_ckpt.seq_id) {
|
|
seq_id = page_md_buf->p2l_ckpt.seq_id;
|
|
}
|
|
}
|
|
return seq_id;
|
|
}
|
|
|
|
int
|
|
ftl_mngt_p2l_ckpt_restore(struct ftl_band *band, uint32_t md_region, uint64_t seq_id)
|
|
{
|
|
struct ftl_layout *layout = &band->dev->layout;
|
|
struct ftl_md *md = layout->md[md_region];
|
|
union ftl_md_vss *page_md_buf = ftl_md_get_vss_buffer(md);
|
|
struct ftl_p2l_ckpt_page *page = ftl_md_get_buffer(md);
|
|
struct ftl_p2l_ckpt_page *map_page;
|
|
uint64_t page_no, page_max = 0;
|
|
bool page_found = false;
|
|
|
|
assert(band->md->p2l_md_region == md_region);
|
|
if (band->md->p2l_md_region != md_region) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
assert(band->md->seq == seq_id);
|
|
if (band->md->seq != seq_id) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
for (page_no = 0; page_no < layout->p2l.ckpt_pages; page_no++, page++, page_md_buf++) {
|
|
if (page_md_buf->p2l_ckpt.seq_id != seq_id) {
|
|
continue;
|
|
}
|
|
|
|
page_max = page_no;
|
|
page_found = true;
|
|
|
|
/* Get the corresponding P2L map page - the underlying stored data is the same as in the end metadata of band P2L (ftl_p2l_map_entry),
|
|
* however we're interested in a whole page (4KiB) worth of content
|
|
*/
|
|
map_page = ((struct ftl_p2l_ckpt_page *)band->p2l_map.band_map) + page_no;
|
|
|
|
if (page_md_buf->p2l_ckpt.p2l_checksum &&
|
|
page_md_buf->p2l_ckpt.p2l_checksum != spdk_crc32c_update(page,
|
|
FTL_NUM_LBA_IN_BLOCK * sizeof(struct ftl_p2l_map_entry), 0)) {
|
|
ftl_stats_crc_error(band->dev, FTL_STATS_TYPE_MD_NV_CACHE);
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* Restore the page from P2L checkpoint */
|
|
*map_page = *page;
|
|
}
|
|
|
|
assert(page_found);
|
|
if (!page_found) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* Restore check point in band P2L map */
|
|
band->p2l_map.p2l_ckpt = ftl_p2l_ckpt_acquire_region_type(
|
|
band->dev, md_region);
|
|
|
|
#ifdef DEBUG
|
|
/* Set check point valid map for validation */
|
|
struct ftl_p2l_ckpt *ckpt = band->p2l_map.p2l_ckpt ;
|
|
for (uint64_t i = 0; i <= page_max; i++) {
|
|
ftl_bitmap_set(ckpt->bmp, i);
|
|
}
|
|
#endif
|
|
|
|
ftl_band_iter_init(band);
|
|
ftl_band_iter_set(band, (page_max + 1) * FTL_NUM_LBA_IN_BLOCK);
|
|
|
|
return 0;
|
|
}
|
|
|
|
enum ftl_layout_region_type
|
|
ftl_p2l_ckpt_region_type(const struct ftl_p2l_ckpt *ckpt) {
|
|
return ckpt->layout_region->type;
|
|
}
|
|
|
|
struct ftl_p2l_ckpt *
|
|
ftl_p2l_ckpt_acquire_region_type(struct spdk_ftl_dev *dev, uint32_t region_type)
|
|
{
|
|
struct ftl_p2l_ckpt *ckpt = NULL;
|
|
|
|
TAILQ_FOREACH(ckpt, &dev->p2l_ckpt.free, link) {
|
|
if (ckpt->layout_region->type == region_type) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
assert(ckpt);
|
|
|
|
TAILQ_REMOVE(&dev->p2l_ckpt.free, ckpt, link);
|
|
TAILQ_INSERT_TAIL(&dev->p2l_ckpt.inuse, ckpt, link);
|
|
|
|
return ckpt;
|
|
}
|
|
|
|
int
|
|
ftl_mngt_p2l_ckpt_restore_clean(struct ftl_band *band)
|
|
{
|
|
struct spdk_ftl_dev *dev = band->dev;
|
|
struct ftl_layout *layout = &dev->layout;
|
|
struct ftl_p2l_ckpt_page *page, *map_page;
|
|
enum ftl_layout_region_type md_region = band->md->p2l_md_region;
|
|
uint64_t page_no;
|
|
uint64_t num_written_pages;
|
|
union ftl_md_vss *page_md_buf;
|
|
|
|
if (md_region < FTL_LAYOUT_REGION_TYPE_P2L_CKPT_MIN ||
|
|
md_region > FTL_LAYOUT_REGION_TYPE_P2L_CKPT_MAX) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
assert(band->md->iter.offset % FTL_NUM_LBA_IN_BLOCK == 0);
|
|
num_written_pages = band->md->iter.offset / FTL_NUM_LBA_IN_BLOCK;
|
|
|
|
/* Associate band with md region before shutdown */
|
|
if (!band->p2l_map.p2l_ckpt) {
|
|
band->p2l_map.p2l_ckpt = ftl_p2l_ckpt_acquire_region_type(dev, md_region);
|
|
}
|
|
|
|
/* Band was opened but no data was written */
|
|
if (band->md->iter.offset == 0) {
|
|
return 0;
|
|
}
|
|
|
|
page_no = 0;
|
|
|
|
/* Restore P2L map up to last written page */
|
|
page_md_buf = ftl_md_get_vss_buffer(layout->md[md_region]);
|
|
page = ftl_md_get_buffer(layout->md[md_region]);
|
|
|
|
for (; page_no < num_written_pages; page_no++, page++, page_md_buf++) {
|
|
if (page_md_buf->p2l_ckpt.seq_id != band->md->seq) {
|
|
assert(page_md_buf->p2l_ckpt.seq_id == band->md->seq);
|
|
}
|
|
|
|
/* Get the corresponding P2L map page */
|
|
map_page = ((struct ftl_p2l_ckpt_page *)band->p2l_map.band_map) + page_no;
|
|
|
|
/* Restore the page from P2L checkpoint */
|
|
*map_page = *page;
|
|
|
|
#if defined(DEBUG)
|
|
assert(ftl_bitmap_get(band->p2l_map.p2l_ckpt->bmp, page_no) == false);
|
|
ftl_bitmap_set(band->p2l_map.p2l_ckpt->bmp, page_no);
|
|
#endif
|
|
}
|
|
|
|
assert(page_md_buf->p2l_ckpt.seq_id < band->md->seq);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
ftl_mngt_p2l_ckpt_restore_shm_clean(struct ftl_band *band)
|
|
{
|
|
struct spdk_ftl_dev *dev = band->dev;
|
|
enum ftl_layout_region_type md_region = band->md->p2l_md_region;
|
|
|
|
/* Associate band with md region before shutdown */
|
|
if (!band->p2l_map.p2l_ckpt) {
|
|
band->p2l_map.p2l_ckpt = ftl_p2l_ckpt_acquire_region_type(dev, md_region);
|
|
}
|
|
|
|
#if defined(DEBUG)
|
|
uint64_t page_no;
|
|
uint64_t num_written_pages;
|
|
|
|
assert(band->md->iter.offset % FTL_NUM_LBA_IN_BLOCK == 0);
|
|
num_written_pages = band->md->iter.offset / FTL_NUM_LBA_IN_BLOCK;
|
|
|
|
/* Band was opened but no data was written */
|
|
if (band->md->iter.offset == 0) {
|
|
return;
|
|
}
|
|
|
|
/* Set page number to first data page - skip head md */
|
|
page_no = 0;
|
|
|
|
for (; page_no < num_written_pages; page_no++) {
|
|
assert(ftl_bitmap_get(band->p2l_map.p2l_ckpt->bmp, page_no) == false);
|
|
ftl_bitmap_set(band->p2l_map.p2l_ckpt->bmp, page_no);
|
|
}
|
|
#endif
|
|
}
|