⚠ Switch to EXCALIDRAW VIEW in the MORE OPTIONS menu of this document. ⚠ You can decompress Drawing data with the command palette: 'Decompress current Excalidraw file'. For more info check in plugin settings under 'Saving'

Excalidraw Data

Text Elements

ftl_writer_run

close_full_bands

get_band

ftl_band_rq_write

  1. 修改rq的success, io.band, io.addr

  2. 调用 ftl_band_rq_bdev_write(rq)

  3. 记录 band->queue_depth, dev->stat

  4. 判断band是否被写满,如果写满了
    就执行状态机转换

spdk_bdev_write_blocks

(base, cb: write_rq_end)

ENOMEM

  1. io_waite 的cb设置为ftl_band_rq_bdev_write
  2. spdk_bdev_queue_io_wait

这里的 rq 应该是连续的一段

write_rq_end

  1. ftl_stats_bdev_io_completed:
    这里会按照 io 的类型对 io 进行一次统计

  2. spdk_bdev_free_io

  3. ftl_p2l_ckpt_issue(rq)

rq->owner.cb(rq)

user_writer->rq_queue

rq

ftl_writer_queue_rq(rq)

compaction_process_finish_read

  1. for entry in rq:
    if entry is invalid:
    compaction_process_invalidate_entry

  2. 只要有一个有效的entry,就调用:

compaction_process_pin_lba

valid

invalid

ftl_l2p_pin_skip

ftl_l2p_pin

compaction_process_pin_lba_cb

compaction_process_read_entry

compaction_process_read_entry_cb

compaction_process_read

compaction_process_start

compaction_process

ftl_nv_cache_process

compactor_list

compactor_alloc

  1. calloc(compator)

  2. compactor->rq =
    ftl_rq_new()

  3. compactor->rq->
    owner.cb =
    compaction_process_ftl_done;

for entry in rq:

update L2P table

user_writer->rq_queue

rq

ftl_writer_queue_rq(rq)

compaction_process_finish_read

  1. for entry in rq:
    if entry is invalid:
    compaction_process_invalidate_entry

  2. 只要有一个有效的entry,就调用:

compaction_process_pin_lba

valid

invalid

ftl_l2p_pin_skip

ftl_l2p_pin

compaction_process_pin_lba_cb

compaction_process_read_entry

compaction_process_read_entry_cb

compaction_process_read

compaction_process_start

compaction_process

ftl_nv_cache_process

compactor_list

compactor_alloc

  1. calloc(compator)

  2. compactor->rq =
    ftl_rq_new()

  3. compactor->rq->
    owner.cb =
    compaction_process_ftl_done;

for entry in rq:

update L2P table

for rq_entry in rq

entry

ftl_io

tfl->wr_sq

user io

compaction io

update nvc l2p

update base l2p

new ftl_io

io->seq_id = ftl_get_next_seq_id

ftl_rq

struct spdk_ftl_dev *dev;
TAILQ_ENTRY(ftl_rq) qentry;

/* Number of block within the request */
uint64_t num_blocks;

/* Extended metadata for IO. Its size is io_md_size * num_blocks */
void *io_md;

/* Size of extended metadata size for one entry */
uint64_t io_md_size;

/* Payload for IO */
void *io_payload;

/* Request result status */
bool success;

/* Fields for owner of this request /
struct {
/
End request callback */
void (*cb)(struct ftl_rq *rq);

/* IO error request callback */
void (*error)(struct ftl_rq *rq, struct ftl_band *band,
uint64_t idx, uint64_t count);

/* Owner context */
void *priv;

/* This is compaction IO */
bool compaction;
} owner;

/* Iterator fields for processing state of the request */
struct {
uint32_t idx;

uint32_t count;

/* Queue depth on this request */
uint32_t qd;

uint32_t remaining;
int status;
} iter;

/* Private fields for issuing IO /
struct {
/
Request physical address, on IO completion set for append device */
ftl_addr addr;

/* Band to which IO is issued */
struct ftl_band *band;

struct spdk_bdev_io_wait_entry bdev_io_wait;
} io;

/* For writing P2L metadata */
struct ftl_md_io_entry_ctx md_persist_entry_ctx;

/* 一个entry是一个block,entry是不连续的,甚至可以跨chunk */

struct ftl_rq_entry entries[];

ftl_rq_new: 初始化为dev->xfer_size(4096/(8+8))=256

ftl_rq

num_blocks

entry

MEMORY

ftl_rq_new: 以DMA方式在内存中申请FTL_BLOCK_SIZE * 256的空间

ftl_rq_new: 初始化为nv_cache.md_size(64B)

ftl_rq_new: 以DMA方式在内存中申请io_md_size * 256的空间

/* Data payload of single entry (block) */
void *io_payload;

void *io_md;

/*
* Physical address of block described by ftl_rq_entry.
* Valid after write command is completed (due to potential append reordering)
*/
ftl_addr addr;

/* Logical block address */
uint64_t lba;

/* Sequence id of original chunk where this user data was written to */
uint64_t seq_id;

/* Index of this entry within FTL request */
const uint64_t index;

struct {
void *priv;
} owner;

/* If request issued in iterative way, it contains IO information */
struct {
struct ftl_band *band;
} io;

/* For l2p pinning */
struct ftl_l2p_pin_ctx l2p_pin_ctx;

struct {
uint64_t offset_blocks;
uint64_t num_blocks;
struct spdk_bdev_io_wait_entry wait_entry;
} bdev_io;

ftl_rq_entry

ftl_rq_new: 初始化为该ftl_rq的第几个entry

ftl_rq_new: 初始化为FTL_ADDR_INVALID

ftl_rq_new: 初始化为FTL_LBA_INVALID

ftl_rq_new: 初始化为ftl_rq申请到的io_payload该entry对应的位置

ftl_rq_new: 初始化为0

ftl_rq_new: 初始化为ftl_rq申请到的io_md该entry对应的位置

compactor_alloc: 初始化为该rq所属的compactor

compactor_alloc: 初始化为compaction_process_ftl_done

compactor_alloc: 通过compaction_process_invalidate_entry设置为NULL

is_compaction_required?

compaction_entry_read_pos

for every entry:

compaction_process_pad: 如果没有需要compaction的chunk,

就把本次rq后面的entry全部设为无效

get_chunk_for_compaction

compaction_chunk_read_pos: 找到第一个valid的block

full Chunk(To be compacted)

...

block

tail md

(l2p)

invalid

valid

skip

write_pointer

read_pointer

compaction_entry_read_pos: 赋值为找到的第一个有效的block在nvc上的addr

compaction_entry_read_pos: 赋值为回收的有效block所在的chunk

compaction_process_start: 初始化为成功获取到回收有效数据地址的entry的个数

compaction_process_start

compaction_process_read: 初始化为rq->iter.count

compaction_process_read: 初始化为 1

compaction_process_read: 初始化为对应的addr在nvc上的offset(ftl_addr_to_nvc_offset)

compaction_process_read: 依次检查连续的entry,如果addr上连续,则num_blocks++,合并entry为一个io去读

compaction_process_read_entry_cb: 减去成功读上来的blocks数

compaction_process_pin_lba: 减到0之后在pin_lba里又重新赋值为count

compaction_process_pin_lba: 初始化为0

ftl_l2p_pin_skip: 初始化为INVALID / ftl_l2p_pin: 初始化为该entry读上来的block的lba/count=1/cb=compaction_process_pin_lba_cb

compaction_process_pin_lba_cb: 每次减小1直到为0

compaction_process_finish_read: 如果entry对应的addr和目前在内存中L2P表的读上来的lba对应的addr匹配,就赋值