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
690 lines
16 KiB
C
690 lines
16 KiB
C
/* SPDX-License-Identifier: BSD-3-Clause
|
|
* Copyright (C) 2018 Intel Corporation.
|
|
* All rights reserved.
|
|
*/
|
|
|
|
#include "spdk/stdinc.h"
|
|
#include "spdk/crc32.h"
|
|
#include "spdk/likely.h"
|
|
#include "spdk/util.h"
|
|
#include "spdk/ftl.h"
|
|
|
|
#include "ftl_band.h"
|
|
#include "ftl_io.h"
|
|
#include "ftl_core.h"
|
|
#include "ftl_debug.h"
|
|
#include "ftl_internal.h"
|
|
#include "utils/ftl_md.h"
|
|
#include "utils/ftl_defs.h"
|
|
|
|
static uint64_t
|
|
ftl_band_tail_md_offset(const struct ftl_band *band)
|
|
{
|
|
return ftl_get_num_blocks_in_band(band->dev) -
|
|
ftl_tail_md_num_blocks(band->dev);
|
|
}
|
|
|
|
int
|
|
ftl_band_filled(struct ftl_band *band, size_t offset)
|
|
{
|
|
return offset == ftl_band_tail_md_offset(band);
|
|
}
|
|
|
|
static void
|
|
ftl_band_free_p2l_map(struct ftl_band *band)
|
|
{
|
|
struct spdk_ftl_dev *dev = band->dev;
|
|
struct ftl_p2l_map *p2l_map = &band->p2l_map;
|
|
|
|
assert(band->md->state == FTL_BAND_STATE_CLOSED ||
|
|
band->md->state == FTL_BAND_STATE_FREE);
|
|
assert(p2l_map->ref_cnt == 0);
|
|
assert(p2l_map->band_map != NULL);
|
|
|
|
band->md->df_p2l_map = FTL_DF_OBJ_ID_INVALID;
|
|
ftl_mempool_put(dev->p2l_pool, p2l_map->band_map);
|
|
p2l_map->band_map = NULL;
|
|
}
|
|
|
|
|
|
static void
|
|
ftl_band_free_md_entry(struct ftl_band *band)
|
|
{
|
|
struct spdk_ftl_dev *dev = band->dev;
|
|
struct ftl_p2l_map *p2l_map = &band->p2l_map;
|
|
|
|
assert(band->md->state == FTL_BAND_STATE_CLOSED ||
|
|
band->md->state == FTL_BAND_STATE_FREE);
|
|
assert(p2l_map->band_dma_md != NULL);
|
|
|
|
ftl_mempool_put(dev->band_md_pool, p2l_map->band_dma_md);
|
|
p2l_map->band_dma_md = NULL;
|
|
}
|
|
|
|
static void
|
|
_ftl_band_set_free(struct ftl_band *band)
|
|
{
|
|
struct spdk_ftl_dev *dev = band->dev;
|
|
|
|
/* Add the band to the free band list */
|
|
TAILQ_INSERT_TAIL(&dev->free_bands, band, queue_entry);
|
|
band->md->close_seq_id = 0;
|
|
band->reloc = false;
|
|
|
|
dev->num_free++;
|
|
ftl_apply_limits(dev);
|
|
|
|
band->md->p2l_map_checksum = 0;
|
|
}
|
|
|
|
static void
|
|
_ftl_band_set_preparing(struct ftl_band *band)
|
|
{
|
|
struct spdk_ftl_dev *dev = band->dev;
|
|
|
|
/* Remove band from free list */
|
|
TAILQ_REMOVE(&dev->free_bands, band, queue_entry);
|
|
|
|
band->md->wr_cnt++;
|
|
|
|
assert(dev->num_free > 0);
|
|
dev->num_free--;
|
|
|
|
ftl_apply_limits(dev);
|
|
}
|
|
|
|
static void
|
|
_ftl_band_set_closed_cb(struct ftl_band *band, bool valid)
|
|
{
|
|
struct spdk_ftl_dev *dev = band->dev;
|
|
|
|
assert(valid == true);
|
|
|
|
/* Set the state as free_md() checks for that */
|
|
band->md->state = FTL_BAND_STATE_CLOSED;
|
|
if (band->owner.state_change_fn) {
|
|
band->owner.state_change_fn(band);
|
|
}
|
|
|
|
ftl_p2l_validate_ckpt(band);
|
|
|
|
/* Free the P2L map if there are no outstanding IOs */
|
|
ftl_band_release_p2l_map(band);
|
|
assert(band->p2l_map.ref_cnt == 0);
|
|
|
|
TAILQ_INSERT_TAIL(&dev->shut_bands, band, queue_entry);
|
|
}
|
|
|
|
static void
|
|
_ftl_band_set_closed(struct ftl_band *band)
|
|
{
|
|
/* Verify that band's metadata is consistent with l2p */
|
|
ftl_band_validate_md(band, _ftl_band_set_closed_cb);
|
|
}
|
|
|
|
ftl_addr
|
|
ftl_band_tail_md_addr(struct ftl_band *band)
|
|
{
|
|
ftl_addr addr;
|
|
|
|
/* Metadata should be aligned to xfer size */
|
|
assert(ftl_band_tail_md_offset(band) % band->dev->xfer_size == 0);
|
|
|
|
addr = ftl_band_tail_md_offset(band) + band->start_addr;
|
|
|
|
return addr;
|
|
}
|
|
|
|
void
|
|
ftl_band_set_state(struct ftl_band *band, enum ftl_band_state state)
|
|
{
|
|
switch (state) {
|
|
case FTL_BAND_STATE_FREE:
|
|
assert(band->md->state == FTL_BAND_STATE_CLOSED);
|
|
_ftl_band_set_free(band);
|
|
break;
|
|
|
|
case FTL_BAND_STATE_PREP:
|
|
assert(band->md->state == FTL_BAND_STATE_FREE);
|
|
_ftl_band_set_preparing(band);
|
|
break;
|
|
|
|
case FTL_BAND_STATE_CLOSED:
|
|
if (band->md->state != FTL_BAND_STATE_CLOSED) {
|
|
assert(band->md->state == FTL_BAND_STATE_CLOSING);
|
|
_ftl_band_set_closed(band);
|
|
return; /* state can be changed asynchronously */
|
|
}
|
|
break;
|
|
|
|
case FTL_BAND_STATE_OPEN:
|
|
band->md->p2l_map_checksum = 0;
|
|
break;
|
|
case FTL_BAND_STATE_OPENING:
|
|
case FTL_BAND_STATE_FULL:
|
|
case FTL_BAND_STATE_CLOSING:
|
|
break;
|
|
default:
|
|
FTL_ERRLOG(band->dev, "Unknown band state, %u", state);
|
|
assert(false);
|
|
break;
|
|
}
|
|
|
|
band->md->state = state;
|
|
}
|
|
|
|
void
|
|
ftl_band_set_type(struct ftl_band *band, enum ftl_band_type type)
|
|
{
|
|
switch (type) {
|
|
case FTL_BAND_TYPE_COMPACTION:
|
|
case FTL_BAND_TYPE_GC:
|
|
band->md->type = type;
|
|
break;
|
|
default:
|
|
assert(false);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void
|
|
ftl_band_set_p2l(struct ftl_band *band, uint64_t lba, ftl_addr addr, uint64_t seq_id)
|
|
{
|
|
struct ftl_p2l_map *p2l_map = &band->p2l_map;
|
|
uint64_t offset;
|
|
|
|
offset = ftl_band_block_offset_from_addr(band, addr);
|
|
|
|
p2l_map->band_map[offset].lba = lba;
|
|
p2l_map->band_map[offset].seq_id = seq_id;
|
|
}
|
|
|
|
void
|
|
ftl_band_set_addr(struct ftl_band *band, uint64_t lba, ftl_addr addr)
|
|
{
|
|
band->p2l_map.num_valid++;
|
|
ftl_bitmap_set(band->dev->valid_map, addr);
|
|
}
|
|
|
|
size_t
|
|
ftl_band_user_blocks_left(const struct ftl_band *band, size_t offset)
|
|
{
|
|
size_t tail_md_offset = ftl_band_tail_md_offset(band);
|
|
|
|
if (spdk_unlikely(offset > tail_md_offset)) {
|
|
return 0;
|
|
}
|
|
|
|
return tail_md_offset - offset;
|
|
}
|
|
|
|
size_t
|
|
ftl_band_user_blocks(const struct ftl_band *band)
|
|
{
|
|
return ftl_get_num_blocks_in_band(band->dev) -
|
|
ftl_tail_md_num_blocks(band->dev);
|
|
}
|
|
|
|
static inline uint64_t
|
|
ftl_addr_get_band(const struct spdk_ftl_dev *dev, ftl_addr addr)
|
|
{
|
|
return (addr - dev->bands->start_addr) / ftl_get_num_blocks_in_band(dev);
|
|
}
|
|
|
|
struct ftl_band *
|
|
ftl_band_from_addr(struct spdk_ftl_dev *dev, ftl_addr addr)
|
|
{
|
|
uint64_t band_id = ftl_addr_get_band(dev, addr);
|
|
|
|
assert(band_id < ftl_get_num_bands(dev));
|
|
return &dev->bands[band_id];
|
|
}
|
|
|
|
uint64_t
|
|
ftl_band_block_offset_from_addr(struct ftl_band *band, ftl_addr addr)
|
|
{
|
|
assert(ftl_addr_get_band(band->dev, addr) == band->id);
|
|
return addr - band->start_addr;
|
|
}
|
|
|
|
ftl_addr
|
|
ftl_band_next_xfer_addr(struct ftl_band *band, ftl_addr addr, size_t num_blocks)
|
|
{
|
|
struct spdk_ftl_dev *dev = band->dev;
|
|
size_t num_xfers;
|
|
uint64_t offset;
|
|
|
|
assert(ftl_addr_get_band(dev, addr) == band->id);
|
|
|
|
offset = addr - band->start_addr;
|
|
|
|
/* In case starting address wasn't aligned to xfer_size, we'll align for consistent calculation
|
|
* purposes - the unaligned value will be preserved at the end however.
|
|
*/
|
|
num_blocks += (offset % dev->xfer_size);
|
|
offset -= (offset % dev->xfer_size);
|
|
|
|
/* Calculate offset based on xfer_size aligned writes */
|
|
num_xfers = (num_blocks / dev->xfer_size);
|
|
offset += num_xfers * dev->xfer_size;
|
|
num_blocks -= num_xfers * dev->xfer_size;
|
|
|
|
if (offset > ftl_get_num_blocks_in_band(dev)) {
|
|
return FTL_ADDR_INVALID;
|
|
}
|
|
|
|
/* If there's any unalignment (either starting addr value or num_blocks), reintroduce it to the final address
|
|
*/
|
|
if (num_blocks) {
|
|
offset += num_blocks;
|
|
if (offset > ftl_get_num_blocks_in_band(dev)) {
|
|
return FTL_ADDR_INVALID;
|
|
}
|
|
}
|
|
|
|
addr = band->start_addr + offset;
|
|
return addr;
|
|
}
|
|
|
|
ftl_addr
|
|
ftl_band_addr_from_block_offset(struct ftl_band *band, uint64_t block_off)
|
|
{
|
|
ftl_addr addr;
|
|
|
|
addr = block_off + band->start_addr;
|
|
return addr;
|
|
}
|
|
|
|
ftl_addr
|
|
ftl_band_next_addr(struct ftl_band *band, ftl_addr addr, size_t offset)
|
|
{
|
|
uint64_t block_off = ftl_band_block_offset_from_addr(band, addr);
|
|
|
|
return ftl_band_addr_from_block_offset(band, block_off + offset);
|
|
}
|
|
|
|
void
|
|
ftl_band_acquire_p2l_map(struct ftl_band *band)
|
|
{
|
|
assert(band->p2l_map.band_map != NULL);
|
|
band->p2l_map.ref_cnt++;
|
|
}
|
|
|
|
static int
|
|
ftl_band_alloc_md_entry(struct ftl_band *band)
|
|
{
|
|
struct spdk_ftl_dev *dev = band->dev;
|
|
struct ftl_p2l_map *p2l_map = &band->p2l_map;
|
|
struct ftl_layout_region *region = &dev->layout.region[FTL_LAYOUT_REGION_TYPE_BAND_MD];
|
|
|
|
p2l_map->band_dma_md = ftl_mempool_get(dev->band_md_pool);
|
|
|
|
if (!p2l_map->band_dma_md) {
|
|
return -1;
|
|
}
|
|
|
|
memset(p2l_map->band_dma_md, 0, region->entry_size * FTL_BLOCK_SIZE);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
ftl_band_alloc_p2l_map(struct ftl_band *band)
|
|
{
|
|
struct spdk_ftl_dev *dev = band->dev;
|
|
struct ftl_p2l_map *p2l_map = &band->p2l_map;
|
|
|
|
assert(p2l_map->ref_cnt == 0);
|
|
assert(p2l_map->band_map == NULL);
|
|
|
|
assert(band->md->df_p2l_map == FTL_DF_OBJ_ID_INVALID);
|
|
p2l_map->band_map = ftl_mempool_get(dev->p2l_pool);
|
|
if (!p2l_map->band_map) {
|
|
return -1;
|
|
}
|
|
|
|
if (ftl_band_alloc_md_entry(band)) {
|
|
ftl_band_free_p2l_map(band);
|
|
return -1;
|
|
}
|
|
|
|
band->md->df_p2l_map = ftl_mempool_get_df_obj_id(dev->p2l_pool, p2l_map->band_map);
|
|
|
|
/* Set the P2L to FTL_LBA_INVALID */
|
|
memset(p2l_map->band_map, -1, FTL_BLOCK_SIZE * ftl_p2l_map_num_blocks(band->dev));
|
|
|
|
ftl_band_acquire_p2l_map(band);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
ftl_band_open_p2l_map(struct ftl_band *band)
|
|
{
|
|
struct spdk_ftl_dev *dev = band->dev;
|
|
struct ftl_p2l_map *p2l_map = &band->p2l_map;
|
|
|
|
assert(p2l_map->ref_cnt == 0);
|
|
assert(p2l_map->band_map == NULL);
|
|
|
|
assert(band->md->df_p2l_map != FTL_DF_OBJ_ID_INVALID);
|
|
|
|
if (ftl_band_alloc_md_entry(band)) {
|
|
p2l_map->band_map = NULL;
|
|
return -1;
|
|
}
|
|
|
|
p2l_map->band_map = ftl_mempool_claim_df(dev->p2l_pool, band->md->df_p2l_map);
|
|
|
|
ftl_band_acquire_p2l_map(band);
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
ftl_band_release_p2l_map(struct ftl_band *band)
|
|
{
|
|
struct ftl_p2l_map *p2l_map = &band->p2l_map;
|
|
|
|
assert(p2l_map->band_map != NULL);
|
|
assert(p2l_map->ref_cnt > 0);
|
|
p2l_map->ref_cnt--;
|
|
|
|
if (p2l_map->ref_cnt == 0) {
|
|
if (p2l_map->p2l_ckpt) {
|
|
ftl_p2l_ckpt_release(band->dev, p2l_map->p2l_ckpt);
|
|
p2l_map->p2l_ckpt = NULL;
|
|
}
|
|
ftl_band_free_p2l_map(band);
|
|
ftl_band_free_md_entry(band);
|
|
}
|
|
}
|
|
|
|
ftl_addr
|
|
ftl_band_p2l_map_addr(struct ftl_band *band)
|
|
{
|
|
return band->tail_md_addr;
|
|
}
|
|
|
|
int
|
|
ftl_band_write_prep(struct ftl_band *band)
|
|
{
|
|
struct spdk_ftl_dev *dev = band->dev;
|
|
|
|
if (ftl_band_alloc_p2l_map(band)) {
|
|
return -1;
|
|
}
|
|
|
|
band->p2l_map.p2l_ckpt = ftl_p2l_ckpt_acquire(dev);
|
|
band->md->p2l_md_region = ftl_p2l_ckpt_region_type(band->p2l_map.p2l_ckpt);
|
|
ftl_band_iter_init(band);
|
|
|
|
band->md->seq = ftl_get_next_seq_id(dev);
|
|
|
|
FTL_DEBUGLOG(dev, "Band to write, id %u seq %"PRIu64"\n", band->id, band->md->seq);
|
|
return 0;
|
|
}
|
|
|
|
size_t
|
|
ftl_p2l_map_pool_elem_size(struct spdk_ftl_dev *dev)
|
|
{
|
|
/* Map pool element holds the whole tail md */
|
|
return ftl_tail_md_num_blocks(dev) * FTL_BLOCK_SIZE;
|
|
}
|
|
|
|
static double
|
|
_band_invalidity(struct ftl_band *band)
|
|
{
|
|
double valid = band->p2l_map.num_valid;
|
|
double count = ftl_band_user_blocks(band);
|
|
|
|
return 1.0 - (valid / count);
|
|
}
|
|
|
|
static void
|
|
dump_bands_under_relocation(struct spdk_ftl_dev *dev)
|
|
{
|
|
uint64_t i = dev->sb_shm->gc_info.current_band_id;
|
|
uint64_t end = dev->sb_shm->gc_info.current_band_id + dev->num_logical_bands_in_physical;
|
|
|
|
for (; i < end; i++) {
|
|
struct ftl_band *band = &dev->bands[i];
|
|
|
|
FTL_DEBUGLOG(dev, "Band, id %u, phys_is %u, wr cnt = %u, invalidity = %u%%\n",
|
|
band->id, band->phys_id, (uint32_t)band->md->wr_cnt,
|
|
(uint32_t)(_band_invalidity(band) * 100));
|
|
}
|
|
}
|
|
|
|
static bool
|
|
is_band_relocateable(struct ftl_band *band)
|
|
{
|
|
/* Can only move data from closed bands */
|
|
if (FTL_BAND_STATE_CLOSED != band->md->state) {
|
|
return false;
|
|
}
|
|
|
|
/* Band is already under relocation, skip it */
|
|
if (band->reloc) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static void
|
|
get_band_phys_info(struct spdk_ftl_dev *dev, uint64_t phys_id,
|
|
double *invalidity, double *wr_cnt)
|
|
{
|
|
struct ftl_band *band;
|
|
uint64_t band_id = phys_id * dev->num_logical_bands_in_physical;
|
|
|
|
*wr_cnt = *invalidity = 0.0L;
|
|
for (; band_id < ftl_get_num_bands(dev); band_id++) {
|
|
band = &dev->bands[band_id];
|
|
|
|
if (phys_id != band->phys_id) {
|
|
break;
|
|
}
|
|
|
|
*wr_cnt += band->md->wr_cnt;
|
|
|
|
if (!is_band_relocateable(band)) {
|
|
continue;
|
|
}
|
|
|
|
*invalidity += _band_invalidity(band);
|
|
}
|
|
|
|
*invalidity /= dev->num_logical_bands_in_physical;
|
|
*wr_cnt /= dev->num_logical_bands_in_physical;
|
|
}
|
|
|
|
static bool
|
|
band_cmp(double a_invalidity, double a_wr_cnt,
|
|
double b_invalidity, double b_wr_cnt,
|
|
uint64_t a_id, uint64_t b_id)
|
|
{
|
|
assert(a_id != FTL_BAND_PHYS_ID_INVALID);
|
|
assert(b_id != FTL_BAND_PHYS_ID_INVALID);
|
|
double diff = a_invalidity - b_invalidity;
|
|
if (diff < 0.0L) {
|
|
diff *= -1.0L;
|
|
}
|
|
|
|
/* Use the following metrics for picking bands for GC (in order):
|
|
* - relative invalidity
|
|
* - if invalidity is similar (within 10% points), then their write counts (how many times band was written to)
|
|
* - if write count is equal, then pick based on their placement on base device (lower LBAs win)
|
|
*/
|
|
if (diff > 0.1L) {
|
|
return a_invalidity > b_invalidity;
|
|
}
|
|
|
|
if (a_wr_cnt != b_wr_cnt) {
|
|
return a_wr_cnt < b_wr_cnt;
|
|
}
|
|
|
|
return a_id < b_id;
|
|
}
|
|
|
|
static void
|
|
band_start_gc(struct spdk_ftl_dev *dev, struct ftl_band *band)
|
|
{
|
|
ftl_bug(false == is_band_relocateable(band));
|
|
|
|
TAILQ_REMOVE(&dev->shut_bands, band, queue_entry);
|
|
band->reloc = true;
|
|
|
|
FTL_DEBUGLOG(dev, "Band to GC, id %u\n", band->id);
|
|
}
|
|
|
|
static struct ftl_band *
|
|
gc_high_priority_band(struct spdk_ftl_dev *dev)
|
|
{
|
|
struct ftl_band *band;
|
|
uint64_t high_prio_id = dev->sb_shm->gc_info.band_id_high_prio;
|
|
|
|
if (FTL_BAND_ID_INVALID != high_prio_id) {
|
|
ftl_bug(high_prio_id >= dev->num_bands);
|
|
|
|
band = &dev->bands[high_prio_id];
|
|
dev->sb_shm->gc_info.band_id_high_prio = FTL_BAND_ID_INVALID;
|
|
|
|
band_start_gc(dev, band);
|
|
FTL_NOTICELOG(dev, "GC takes high priority band, id %u\n", band->id);
|
|
return band;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
ftl_band_reset_gc_iter(struct spdk_ftl_dev *dev)
|
|
{
|
|
dev->sb->gc_info.is_valid = 0;
|
|
dev->sb->gc_info.current_band_id = FTL_BAND_ID_INVALID;
|
|
dev->sb->gc_info.band_id_high_prio = FTL_BAND_ID_INVALID;
|
|
dev->sb->gc_info.band_phys_id = FTL_BAND_PHYS_ID_INVALID;
|
|
|
|
dev->sb_shm->gc_info = dev->sb->gc_info;
|
|
}
|
|
|
|
struct ftl_band *
|
|
ftl_band_search_next_to_reloc(struct spdk_ftl_dev *dev)
|
|
{
|
|
double invalidity, max_invalidity = 0.0L;
|
|
double wr_cnt, max_wr_cnt = 0.0L;
|
|
uint64_t phys_id = FTL_BAND_PHYS_ID_INVALID;
|
|
struct ftl_band *band;
|
|
uint64_t i, band_count;
|
|
uint64_t phys_count;
|
|
|
|
band = gc_high_priority_band(dev);
|
|
if (spdk_unlikely(NULL != band)) {
|
|
return band;
|
|
}
|
|
|
|
phys_count = dev->num_logical_bands_in_physical;
|
|
band_count = ftl_get_num_bands(dev);
|
|
|
|
for (; dev->sb_shm->gc_info.current_band_id < band_count;) {
|
|
band = &dev->bands[dev->sb_shm->gc_info.current_band_id];
|
|
if (band->phys_id != dev->sb_shm->gc_info.band_phys_id) {
|
|
break;
|
|
}
|
|
|
|
if (false == is_band_relocateable(band)) {
|
|
dev->sb_shm->gc_info.current_band_id++;
|
|
continue;
|
|
}
|
|
|
|
band_start_gc(dev, band);
|
|
return band;
|
|
}
|
|
|
|
for (i = 0; i < band_count; i += phys_count) {
|
|
band = &dev->bands[i];
|
|
|
|
/* Calculate entire band physical group invalidity */
|
|
get_band_phys_info(dev, band->phys_id, &invalidity, &wr_cnt);
|
|
|
|
if (invalidity != 0.0L) {
|
|
if (phys_id == FTL_BAND_PHYS_ID_INVALID ||
|
|
band_cmp(invalidity, wr_cnt, max_invalidity, max_wr_cnt,
|
|
band->phys_id, phys_id)) {
|
|
max_invalidity = invalidity;
|
|
max_wr_cnt = wr_cnt;
|
|
phys_id = band->phys_id;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (FTL_BAND_PHYS_ID_INVALID != phys_id) {
|
|
FTL_DEBUGLOG(dev, "Band physical id %"PRIu64" to GC\n", phys_id);
|
|
dev->sb_shm->gc_info.is_valid = 0;
|
|
dev->sb_shm->gc_info.current_band_id = phys_id * phys_count;
|
|
dev->sb_shm->gc_info.band_phys_id = phys_id;
|
|
dev->sb_shm->gc_info.is_valid = 1;
|
|
dump_bands_under_relocation(dev);
|
|
return ftl_band_search_next_to_reloc(dev);
|
|
} else {
|
|
ftl_band_reset_gc_iter(dev);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void
|
|
ftl_band_init_gc_iter(struct spdk_ftl_dev *dev)
|
|
{
|
|
if (dev->conf.mode & SPDK_FTL_MODE_CREATE) {
|
|
ftl_band_reset_gc_iter(dev);
|
|
return;
|
|
}
|
|
|
|
if (dev->sb->clean) {
|
|
dev->sb_shm->gc_info = dev->sb->gc_info;
|
|
return;
|
|
}
|
|
|
|
if (ftl_fast_startup(dev) || ftl_fast_recovery(dev)) {
|
|
return;
|
|
}
|
|
|
|
/* We lost GC state due to dirty shutdown, reset GC state to start over */
|
|
ftl_band_reset_gc_iter(dev);
|
|
}
|
|
|
|
void
|
|
ftl_valid_map_load_state(struct spdk_ftl_dev *dev)
|
|
{
|
|
uint64_t i;
|
|
struct ftl_band *band;
|
|
|
|
for (i = 0; i < dev->num_bands; i++) {
|
|
band = &dev->bands[i];
|
|
band->p2l_map.num_valid = ftl_bitmap_count_set(band->p2l_map.valid);
|
|
}
|
|
}
|
|
|
|
void
|
|
ftl_band_initialize_free_state(struct ftl_band *band)
|
|
{
|
|
/* All bands start on the shut list during startup, removing it manually here */
|
|
TAILQ_REMOVE(&band->dev->shut_bands, band, queue_entry);
|
|
_ftl_band_set_free(band);
|
|
}
|
|
|
|
void
|
|
ftl_bands_load_state(struct spdk_ftl_dev *dev)
|
|
{
|
|
uint64_t i;
|
|
struct ftl_band *band;
|
|
|
|
for (i = 0; i < dev->num_bands; i++) {
|
|
band = &dev->bands[i];
|
|
|
|
if (band->md->state == FTL_BAND_STATE_FREE) {
|
|
ftl_band_initialize_free_state(band);
|
|
}
|
|
}
|
|
}
|