diff --git a/lib/ftl/Makefile b/lib/ftl/Makefile index a1c3dfda7..08220a831 100644 --- a/lib/ftl/Makefile +++ b/lib/ftl/Makefile @@ -21,7 +21,7 @@ CFLAGS += -I. FTL_SUBDIRS := mngt utils -C_SRCS = ftl_core.c ftl_init.c ftl_layout.c ftl_debug.c ftl_io.c +C_SRCS = ftl_core.c ftl_init.c ftl_layout.c ftl_debug.c ftl_io.c ftl_sb.c C_SRCS += mngt/ftl_mngt.c mngt/ftl_mngt_bdev.c mngt/ftl_mngt_shutdown.c mngt/ftl_mngt_startup.c C_SRCS += mngt/ftl_mngt_md.c mngt/ftl_mngt_misc.c mngt/ftl_mngt_ioch.c C_SRCS += utils/ftl_conf.c utils/ftl_md.c utils/ftl_mempool.c diff --git a/lib/ftl/ftl_core.h b/lib/ftl/ftl_core.h index c95cc9642..3fa11049e 100644 --- a/lib/ftl/ftl_core.h +++ b/lib/ftl/ftl_core.h @@ -19,6 +19,7 @@ #include "ftl_internal.h" #include "ftl_io.h" #include "ftl_layout.h" +#include "ftl_sb.h" #include "utils/ftl_log.h" /* When using VSS on nvcache, FTL sometimes doesn't require the contents of metadata. @@ -35,6 +36,9 @@ struct spdk_ftl_dev { /* FTL device layout */ struct ftl_layout layout; + /* FTL superblock */ + struct ftl_superblock *sb; + /* Queue of registered IO channels */ TAILQ_HEAD(, ftl_io_channel) ioch_queue; diff --git a/lib/ftl/ftl_layout.c b/lib/ftl/ftl_layout.c index 1fa4cb0a3..3fa145cdd 100644 --- a/lib/ftl/ftl_layout.c +++ b/lib/ftl/ftl_layout.c @@ -8,6 +8,7 @@ #include "ftl_core.h" #include "ftl_utils.h" #include "ftl_layout.h" +#include "ftl_sb.h" static inline float blocks2mib(uint64_t blocks) @@ -27,7 +28,6 @@ blocks2mib(uint64_t blocks) #define FTL_LAYOUT_REGION_ALIGNMENT_BLOCKS 32ULL #define FTL_LAYOUT_REGION_ALIGNMENT_BYTES (FTL_LAYOUT_REGION_ALIGNMENT_BLOCKS * FTL_BLOCK_SIZE) -#ifdef SPDK_FTL_VSS_EMU static inline uint64_t blocks_region(uint64_t bytes) { @@ -40,7 +40,6 @@ blocks_region(uint64_t bytes) return result; } -#endif static void dump_region(struct spdk_ftl_dev *dev, struct ftl_layout_region *region) @@ -136,6 +135,14 @@ setup_layout_nvc(struct spdk_ftl_dev *dev) } #endif + /* Skip the superblock region. Already init`d in ftl_layout_setup_superblock */ + region = &layout->region[FTL_LAYOUT_REGION_TYPE_SB]; + offset += region->current.blocks; + + if (offset >= layout->nvc.total_blocks) { + goto error; + } + region = &layout->region[FTL_LAYOUT_REGION_TYPE_DATA_NVC]; region->type = FTL_LAYOUT_REGION_TYPE_DATA_NVC; region->name = "data_nvc"; @@ -157,21 +164,55 @@ error: return -1; } +static ftl_addr +layout_base_offset(struct spdk_ftl_dev *dev) +{ + ftl_addr addr; + + addr = dev->num_bands * ftl_get_num_blocks_in_band(dev); + return addr; +} + static int setup_layout_base(struct spdk_ftl_dev *dev) { + uint64_t left, offset; struct ftl_layout *layout = &dev->layout; struct ftl_layout_region *region; + /* Base device layout is following: + * - data + * - superblock + * - valid map + * + * Superblock has been already configured, its offset marks the end of the data region + */ + offset = layout->region[FTL_LAYOUT_REGION_TYPE_SB_BASE].current.offset; + /* Setup data region on base device */ region = &layout->region[FTL_LAYOUT_REGION_TYPE_DATA_BASE]; region->type = FTL_LAYOUT_REGION_TYPE_DATA_BASE; region->name = "data_btm"; region->current.version = region->prev.version = 0; region->current.offset = 0; - region->current.blocks = layout->base.total_blocks; + region->current.blocks = offset; set_region_bdev_btm(region, dev); + /* Move offset after base superblock */ + offset += layout->region[FTL_LAYOUT_REGION_TYPE_SB_BASE].current.blocks; + + /* Checking for underflow */ + left = layout->base.total_blocks - offset; + if (left > layout->base.total_blocks) { + FTL_ERRLOG(dev, "Error when setup base device layout\n"); + return -1; + } + + if (offset > layout->base.total_blocks) { + FTL_ERRLOG(dev, "Error when setup base device layout\n"); + return -1; + } + return 0; } @@ -191,6 +232,11 @@ ftl_layout_setup(struct spdk_ftl_dev *dev) /* Initialize mirrors types */ for (i = 0; i < FTL_LAYOUT_REGION_TYPE_MAX; ++i) { + if (i == FTL_LAYOUT_REGION_TYPE_SB) { + /* Super block has been already initialized */ + continue; + } + layout->region[i].mirror_type = FTL_LAYOUT_REGION_TYPE_INVALID; } @@ -201,6 +247,7 @@ ftl_layout_setup(struct spdk_ftl_dev *dev) if (dev->num_lbas == 0) { assert(dev->conf.mode & SPDK_FTL_MODE_CREATE); dev->num_lbas = num_lbas; + dev->sb->lba_cnt = num_lbas; } else if (dev->num_lbas != num_lbas) { FTL_ERRLOG(dev, "Mismatched FTL num_lbas\n"); return -EINVAL; @@ -262,6 +309,64 @@ ftl_layout_setup_vss_emu(struct spdk_ftl_dev *dev) } #endif +int +ftl_layout_setup_superblock(struct spdk_ftl_dev *dev) +{ + struct ftl_layout *layout = &dev->layout; + struct ftl_layout_region *region = &layout->region[FTL_LAYOUT_REGION_TYPE_SB]; + uint64_t total_blocks, offset, left; + + assert(layout->md[FTL_LAYOUT_REGION_TYPE_SB] == NULL); + + /* Initialize superblock region */ + region->type = FTL_LAYOUT_REGION_TYPE_SB; + region->mirror_type = FTL_LAYOUT_REGION_TYPE_SB_BASE; + region->name = "sb"; + region->current.version = FTL_METADATA_VERSION_CURRENT; + region->prev.version = FTL_METADATA_VERSION_CURRENT; + region->current.offset = 0; + + /* + * VSS region must go first in case SB to make calculating its relative size easier + */ +#ifdef SPDK_FTL_VSS_EMU + region->current.offset = layout->region[FTL_LAYOUT_REGION_TYPE_VSS].current.offset + + layout->region[FTL_LAYOUT_REGION_TYPE_VSS].current.blocks; +#endif + + region->current.blocks = blocks_region(FTL_SUPERBLOCK_SIZE); + region->vss_blksz = 0; + region->bdev_desc = dev->cache_bdev_desc; + region->ioch = dev->cache_ioch; + + assert(region->bdev_desc != NULL); + assert(region->ioch != NULL); + + region = &layout->region[FTL_LAYOUT_REGION_TYPE_SB_BASE]; + region->type = FTL_LAYOUT_REGION_TYPE_SB_BASE; + region->mirror_type = FTL_LAYOUT_REGION_TYPE_MAX; + region->name = "sb_mirror"; + region->current.version = FTL_METADATA_VERSION_CURRENT; + region->prev.version = FTL_METADATA_VERSION_CURRENT; + /* TODO: This should really be at offset 0 - think how best to upgrade between the two layouts + * This is an issue if some other metadata appears at block 0 of base device (most likely GPT or blobstore) + */ + region->current.offset = layout_base_offset(dev); + region->current.blocks = blocks_region(FTL_SUPERBLOCK_SIZE); + set_region_bdev_btm(region, dev); + + /* Check if SB can be stored at the end of base device */ + total_blocks = spdk_bdev_get_num_blocks( + spdk_bdev_desc_get_bdev(dev->base_bdev_desc)); + offset = region->current.offset + region->current.blocks; + left = total_blocks - offset; + if ((left > total_blocks) || (offset > total_blocks)) { + FTL_ERRLOG(dev, "Error when setup base device super block\n"); + return -1; + } + + return 0; +} void ftl_layout_dump(struct spdk_ftl_dev *dev) diff --git a/lib/ftl/ftl_layout.h b/lib/ftl/ftl_layout.h index 99c6d06d6..5dbcd678a 100644 --- a/lib/ftl/ftl_layout.h +++ b/lib/ftl/ftl_layout.h @@ -16,6 +16,10 @@ enum ftl_layout_region_type { /** VSS region for NV cache VSS emulation */ FTL_LAYOUT_REGION_TYPE_VSS, #endif + /* Superblock describing the basic FTL information */ + FTL_LAYOUT_REGION_TYPE_SB, + /* Mirrored instance of the superblock on the base device */ + FTL_LAYOUT_REGION_TYPE_SB_BASE, /* User data region on the nv cache device */ FTL_LAYOUT_REGION_TYPE_DATA_NVC, @@ -115,6 +119,11 @@ struct ftl_layout { */ int ftl_layout_setup(struct spdk_ftl_dev *dev); +/** + * @brief Setup FTL layout of a superblock + */ +int ftl_layout_setup_superblock(struct spdk_ftl_dev *dev); + #ifdef SPDK_FTL_VSS_EMU /** * @brief Setup FTL layout of VSS emu diff --git a/lib/ftl/ftl_sb.c b/lib/ftl/ftl_sb.c new file mode 100644 index 000000000..568273bd0 --- /dev/null +++ b/lib/ftl/ftl_sb.c @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright (c) Intel Corporation. + * All rights reserved. + */ + +#include "ftl_sb.h" +#include "ftl_core.h" +#include "ftl_layout.h" + +bool +ftl_superblock_check_magic(struct ftl_superblock *sb) +{ + return sb->header.magic == FTL_SUPERBLOCK_MAGIC; +} diff --git a/lib/ftl/ftl_sb.h b/lib/ftl/ftl_sb.h new file mode 100644 index 000000000..68a1fe15c --- /dev/null +++ b/lib/ftl/ftl_sb.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright (c) Intel Corporation. + * All rights reserved. + */ + +#ifndef FTL_SB_H +#define FTL_SB_H + +#include "spdk/uuid.h" +#include "ftl_sb_common.h" +#include "ftl_sb_current.h" + +struct spdk_ftl_dev; + +bool ftl_superblock_check_magic(struct ftl_superblock *sb); + +#endif /* FTL_SB_H */ diff --git a/lib/ftl/ftl_sb_common.h b/lib/ftl/ftl_sb_common.h new file mode 100644 index 000000000..61e54407c --- /dev/null +++ b/lib/ftl/ftl_sb_common.h @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright (c) Intel Corporation. + * All rights reserved. + */ + +#ifndef FTL_SB_COMMON_H +#define FTL_SB_COMMON_H + +#include "spdk/stdinc.h" +#include "utils/ftl_defs.h" + +/* Size of superblock on NV cache, make it bigger for future fields */ +#define FTL_SUPERBLOCK_SIZE (128ULL * KiB) + +#define FTL_MAGIC(a, b, c, d) \ + ((UINT64_C(a) << 48) | (UINT64_C(b) << 32) | (UINT64_C(c) << 16) | \ + UINT64_C(d)) + +/** + * Magic number identifies FTL superblock + */ +#define FTL_SUPERBLOCK_MAGIC FTL_MAGIC(0x1410, 0x1683, 0x1920, 0x1989) + +struct ftl_superblock_gc_info { + /* High priority band; if there's no free bands after dirty shutdown, don't restart GC from same id, or phys_id - + * pick actual lowest validity band to avoid being stuck and try to write it to the open band. + */ + uint64_t band_id_high_prio; + /* Currently relocated band (note it's just id, not seq_id ie. its actual location on disk) */ + uint64_t current_band_id; + /* Bands are grouped together into larger reclaim units; this is the band id translated to those units */ + uint64_t band_phys_id; + /* May be updating multiple fields at the same time, clearing/setting this marks the transaction */ + uint64_t is_valid; +}; + +struct ftl_superblock_header { + uint64_t magic; + uint64_t crc; + uint64_t version; +}; + +struct ftl_superblock_md_region { + uint32_t type; + uint32_t version; + uint64_t blk_offs; + uint64_t blk_sz; +}; + +#endif /* FTL_SB_COMMON_H */ diff --git a/lib/ftl/ftl_sb_current.h b/lib/ftl/ftl_sb_current.h new file mode 100644 index 000000000..5b745c543 --- /dev/null +++ b/lib/ftl/ftl_sb_current.h @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright (c) Intel Corporation. + * All rights reserved. + */ + +#ifndef FTL_SB_CURRENT_H +#define FTL_SB_CURRENT_H + +#include "spdk/uuid.h" +#include "ftl_sb_common.h" + +#define FTL_METADATA_VERSION_4 4 +#define FTL_METADATA_VERSION_CURRENT FTL_METADATA_VERSION_4 + +struct ftl_superblock { + struct ftl_superblock_header header; + + struct spdk_uuid uuid; + + /* Flag describing clean shutdown */ + uint64_t clean; + + /* Number of surfaced LBAs */ + uint64_t lba_cnt; + + /* Percentage of base device blocks not exposed to the user */ + uint64_t overprovisioning; + + /* Maximum IO depth per band relocate */ + uint64_t max_reloc_qdepth; + + /* Reserved field */ + uint64_t reserved; + + uint32_t reserved2; + + struct ftl_superblock_gc_info gc_info; + + struct ftl_superblock_md_region md_layout_head; +}; + +SPDK_STATIC_ASSERT(offsetof(struct ftl_superblock, header) == 0, + "Invalid placement of header"); + +SPDK_STATIC_ASSERT(FTL_SUPERBLOCK_SIZE >= sizeof(struct ftl_superblock), + "FTL SB metadata size is invalid"); + +#endif /* FTL_SB_CURRENT_H */ diff --git a/lib/ftl/mngt/ftl_mngt_md.c b/lib/ftl/mngt/ftl_mngt_md.c index 471d88851..bcf13641f 100644 --- a/lib/ftl/mngt/ftl_mngt_md.c +++ b/lib/ftl/mngt/ftl_mngt_md.c @@ -11,6 +11,7 @@ #include "ftl_mngt_steps.h" #include "ftl_utils.h" #include "ftl_internal.h" +#include "ftl_sb.h" void ftl_mngt_init_layout(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt) @@ -29,6 +30,8 @@ is_buffer_needed(enum ftl_layout_region_type type) #ifdef SPDK_FTL_VSS_EMU case FTL_LAYOUT_REGION_TYPE_VSS: #endif + case FTL_LAYOUT_REGION_TYPE_SB: + case FTL_LAYOUT_REGION_TYPE_SB_BASE: case FTL_LAYOUT_REGION_TYPE_DATA_NVC: case FTL_LAYOUT_REGION_TYPE_DATA_BASE: return false; @@ -83,6 +86,103 @@ ftl_mngt_deinit_md(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt) ftl_mngt_next_step(mngt); } +static void +persist_cb(struct spdk_ftl_dev *dev, struct ftl_md *md, int status) +{ + struct ftl_mngt_process *mngt = md->owner.cb_ctx; + + if (status) { + ftl_mngt_fail_step(mngt); + } else { + ftl_mngt_next_step(mngt); + } +} + +static void +persist(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt, + enum ftl_layout_region_type type) +{ + struct ftl_layout *layout = &dev->layout; + struct ftl_md *md = layout->md[type]; + + assert(type < FTL_LAYOUT_REGION_TYPE_MAX); + + if (!md) { + ftl_mngt_fail_step(mngt); + return; + } + + md->owner.cb_ctx = mngt; + md->cb = persist_cb; + ftl_md_persist(md); +} + +static uint32_t +get_sb_crc(struct ftl_superblock *sb) +{ + uint32_t crc = 0; + + /* Calculate CRC excluding CRC field in superblock */ + void *buffer = sb; + size_t offset = offsetof(struct ftl_superblock, header.crc); + size_t size = offset; + crc = spdk_crc32c_update(buffer, size, crc); + + buffer += offset + sizeof(sb->header.crc); + size = FTL_SUPERBLOCK_SIZE - offset - sizeof(sb->header.crc); + crc = spdk_crc32c_update(buffer, size, crc); + + return crc; +} + +void +ftl_mngt_init_default_sb(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt) +{ + struct ftl_superblock *sb = dev->sb; + + sb->header.magic = FTL_SUPERBLOCK_MAGIC; + sb->header.version = FTL_METADATA_VERSION_CURRENT; + sb->uuid = dev->conf.uuid; + sb->clean = 0; + + /* Max 16 IO depth per band relocate */ + sb->max_reloc_qdepth = 16; + + sb->overprovisioning = dev->conf.overprovisioning; + + /* md layout isn't initialized yet. + * empty region list => all regions in the default location */ + sb->md_layout_head.type = FTL_LAYOUT_REGION_TYPE_INVALID; + + sb->header.crc = get_sb_crc(sb); + + ftl_mngt_next_step(mngt); +} + +void +ftl_mngt_set_dirty(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt) +{ + struct ftl_superblock *sb = dev->sb; + + sb->clean = 0; + sb->header.crc = get_sb_crc(sb); + persist(dev, mngt, FTL_LAYOUT_REGION_TYPE_SB); +} + +/* + * Initializes the superblock fields during first startup of FTL + */ +static const struct ftl_mngt_process_desc desc_init_sb = { + .name = "SB initialize", + .steps = { + { + .name = "Default-initialize superblock", + .action = ftl_mngt_init_default_sb, + }, + {} + } +}; + #ifdef SPDK_FTL_VSS_EMU void ftl_mngt_md_init_vss_emu(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt) @@ -116,3 +216,69 @@ ftl_mngt_md_deinit_vss_emu(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mn ftl_mngt_next_step(mngt); } #endif + +void +ftl_mngt_superblock_init(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt) +{ + struct ftl_layout *layout = &dev->layout; + struct ftl_layout_region *region = &layout->region[FTL_LAYOUT_REGION_TYPE_SB]; + char uuid[SPDK_UUID_STRING_LEN]; + + /* Must generate UUID before MD create on SHM for the SB */ + if (dev->conf.mode & SPDK_FTL_MODE_CREATE) { + spdk_uuid_generate(&dev->conf.uuid); + spdk_uuid_fmt_lower(uuid, sizeof(uuid), &dev->conf.uuid); + FTL_NOTICELOG(dev, "Create new FTL, UUID %s\n", uuid); + } + + /* Setup the layout of a superblock */ + if (ftl_layout_setup_superblock(dev)) { + ftl_mngt_fail_step(mngt); + return; + } + + /* Allocate md buf */ + layout->md[FTL_LAYOUT_REGION_TYPE_SB] = ftl_md_create(dev, region->current.blocks, + region->vss_blksz, region->name, false, region); + if (NULL == layout->md[FTL_LAYOUT_REGION_TYPE_SB]) { + ftl_mngt_fail_step(mngt); + return; + } + + /* Link the md buf to the device */ + dev->sb = ftl_md_get_buffer(layout->md[FTL_LAYOUT_REGION_TYPE_SB]); + + /* Setup superblock mirror to QLC */ + region = &layout->region[FTL_LAYOUT_REGION_TYPE_SB_BASE]; + layout->md[FTL_LAYOUT_REGION_TYPE_SB_BASE] = ftl_md_create(dev, region->current.blocks, + region->vss_blksz, NULL, false, region); + if (NULL == layout->md[FTL_LAYOUT_REGION_TYPE_SB_BASE]) { + ftl_mngt_fail_step(mngt); + return; + } + + /* Initialize the superblock */ + if (dev->conf.mode & SPDK_FTL_MODE_CREATE) { + ftl_mngt_call_process(mngt, &desc_init_sb); + } else { + ftl_mngt_fail_step(mngt); + } +} + +void +ftl_mngt_superblock_deinit(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt) +{ + struct ftl_layout *layout = &dev->layout; + + if (layout->md[FTL_LAYOUT_REGION_TYPE_SB]) { + ftl_md_destroy(layout->md[FTL_LAYOUT_REGION_TYPE_SB]); + layout->md[FTL_LAYOUT_REGION_TYPE_SB] = NULL; + } + + if (layout->md[FTL_LAYOUT_REGION_TYPE_SB_BASE]) { + ftl_md_destroy(layout->md[FTL_LAYOUT_REGION_TYPE_SB_BASE]); + layout->md[FTL_LAYOUT_REGION_TYPE_SB_BASE] = NULL; + } + + ftl_mngt_next_step(mngt); +} diff --git a/lib/ftl/mngt/ftl_mngt_startup.c b/lib/ftl/mngt/ftl_mngt_startup.c index 2f5be12b9..6f1803920 100644 --- a/lib/ftl/mngt/ftl_mngt_startup.c +++ b/lib/ftl/mngt/ftl_mngt_startup.c @@ -49,6 +49,11 @@ static const struct ftl_mngt_process_desc desc_startup = { .cleanup = ftl_mngt_md_deinit_vss_emu }, #endif + { + .name = "Initialize superblock", + .action = ftl_mngt_superblock_init, + .cleanup = ftl_mngt_superblock_deinit + }, { .name = "Register IO device", .action = ftl_mngt_register_io_device, @@ -88,6 +93,10 @@ static const struct ftl_mngt_process_desc desc_first_start = { .name = "Scrub NV cache", .action = ftl_mngt_scrub_nv_cache, }, + { + .name = "Set FTL dirty state", + .action = ftl_mngt_set_dirty, + }, { .name = "Start core poller", .action = ftl_mngt_start_core_poller, diff --git a/lib/ftl/mngt/ftl_mngt_steps.h b/lib/ftl/mngt/ftl_mngt_steps.h index b1bcea90f..1b14e1fa2 100644 --- a/lib/ftl/mngt/ftl_mngt_steps.h +++ b/lib/ftl/mngt/ftl_mngt_steps.h @@ -20,6 +20,10 @@ void ftl_mngt_md_init_vss_emu(struct spdk_ftl_dev *dev, struct ftl_mngt_process void ftl_mngt_md_deinit_vss_emu(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt); #endif +void ftl_mngt_superblock_init(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt); + +void ftl_mngt_superblock_deinit(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt); + void ftl_mngt_open_cache_bdev(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt); void ftl_mngt_close_cache_bdev(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt); @@ -50,4 +54,8 @@ void ftl_mngt_rollback_device(struct spdk_ftl_dev *dev, struct ftl_mngt_process void ftl_mngt_dump_stats(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt); +void ftl_mngt_init_default_sb(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt); + +void ftl_mngt_set_dirty(struct spdk_ftl_dev *dev, struct ftl_mngt_process *mngt); + #endif /* FTL_MNGT_STEPS_H */ diff --git a/lib/ftl/utils/ftl_md.c b/lib/ftl/utils/ftl_md.c index 839924545..66df7a51e 100644 --- a/lib/ftl/utils/ftl_md.c +++ b/lib/ftl/utils/ftl_md.c @@ -9,7 +9,6 @@ #include "ftl_core.h" #include "ftl_md.h" #include "ftl_nv_cache_io.h" -#include "ftl_utils.h" struct ftl_md; static void io_submit(struct ftl_md *md); @@ -703,6 +702,21 @@ restore_mirror_cb(struct spdk_ftl_dev *dev, struct ftl_md *md, int status) } } +static void +restore_sync_cb(struct spdk_ftl_dev *dev, struct ftl_md *md, int status) +{ + struct ftl_md *primary = md->owner.private; + + if (status) { + /* Cannot sync the object from the primary to the mirror, mark error and fail */ + primary->io.status = -EIO; + io_done(primary); + } else { + primary->cb(dev, primary, primary->io.status); + io_cleanup(primary); + } +} + static int restore_done(struct ftl_md *md) { @@ -729,6 +743,18 @@ restore_done(struct ftl_md *md) } else { return -EIO; } + } else if (0 == md->io.status && false == md->dev->sb->clean) { + if (has_mirror(md)) { + /* There was a dirty shutdown, synchronize primary to mirror */ + + /* Set callback and context in the mirror */ + md->mirror->cb = restore_sync_cb; + md->mirror->owner.private = md; + + /* First persist the mirror */ + ftl_md_persist(md->mirror); + return -EAGAIN; + } } return md->io.status; diff --git a/test/unit/lib/ftl/ftl_io.c/ftl_io_ut.c b/test/unit/lib/ftl/ftl_io.c/ftl_io_ut.c index 3374fab6b..f035581a2 100644 --- a/test/unit/lib/ftl/ftl_io.c/ftl_io_ut.c +++ b/test/unit/lib/ftl/ftl_io.c/ftl_io_ut.c @@ -161,6 +161,7 @@ free_device(struct spdk_ftl_dev *dev) free_threads(); free(dev->ioch); + free(dev->sb); free(dev); }