Spdk/lib/ftl/ftl_band.c
paul luse a6dbe3721e update Intel copyright notices
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
2022-11-10 08:28:53 +00:00

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);
}
}
}