🌟 Memory Computing and Computer Architecture Lab
NVMeVirt Weekly Study
- 이전글
[NVMeVirt] implementation of DFTL #2 : read
🌟 Memory Computing and Computer Architecture LabNVMeVirt Weekly Seminar [NVMeVirt] implementation of DFTL : init & rm🌟 Memory Computing and Computer Architecture LabNVMeVirt Weekly Seminar- 논문 리뷰로 알아보는 DFTL(참고) Paper Review : DF
wifiaircat.tistory.com
- conv_write 분석



이 write 분석에 시간을 얼마나 썼는지... 이해가 안 돼서 오래 걸렸다.
DFTL은 read랑 더 연관 있는데 write 분석이 왜 이리 어렵나요?
기존 매핑이 있을 시 `mark_page_invalid`로 기존 매핑을 invalidate하고
새 valid page를 받아오고 `mark_page_valid` 하는 부분이 문제였다.
- LPN → maptbl 조회 (기존 ppa 있으면 invalid + rmap 해제)
→ new page 할당 → update maptbl and rmap
지금도 100% 클리어하게 머릿속에 들어온 건 아니지만
선배에게 설명 듣고 그린 그림처럼 이해하고 있다.
기존 lpn이 사용되어 있으면 ppa를 inv한다. rmap도 초기화한다
덮어쓸 수 없으므로 새 페이지를 받아서 새로운 ppa를 할당한다.
쓰기 페이지들을 모아뒀다가 마지막 write면 실제로 쓰기.
마음으로 받아들이기 너무 어려운 너란 존재. (●_●)–ε/̵͇̿/’̿’̿ ̿ ̿ ̿
열심히 이해하려고 했는데 이거 아니면 진짜 슬플 듯... ε/̵͇̿/’̿’̿ ̿ ̿ ̿ (●_●)
- write 흐름 그리기

역시 본격적인 구현에 앞서 흐름도를 그려본다.
별 것 아닌 그림 같지만 고민을 많이 했다는 것이에요...
- 1차 구현(POINT1)
struct cmt_entry *victim = NULL;
int victim_index = -1;
//(Omitted)
for (lpn = start_lpn; lpn <= end_lpn; lpn++) {
uint64_t local_lpn;
uint64_t nsecs_completed = 0;
struct ppa ppa;
conv_ftl = &conv_ftls[lpn % nr_parts];
local_lpn = lpn / nr_parts;
/* Check whether the given LPN has been written before */
ppa = get_cmt_ent(conv_ftl, local_lpn); // POINT1
//ppa = get_maptbl_ent(conv_ftl, local_lpn);
/* get_cmt_ent is including a logic of miss control */
if (mapped_ppa(&ppa)) {
/* update old page information first */
mark_page_invalid(conv_ftl, &ppa);
set_rmap_ent(conv_ftl, INVALID_LPN, &ppa);
NVMEV_DEBUG("%s: %lld is invalid, ", __func__, ppa2pgidx(conv_ftl, &ppa));
}
// Will be continued to POINT2...
LPN이 쓰인 적 있는지 체크할 때 ppa를 `get_cmt_ent`에서 가져오도록 변경한다.
- 2차 구현(POINT2)
그리고 새로운 쓰기를 할 때 cmt에만 하도록 로직을 변경해야 한다.
/* new write */ // POINT2
/* is cmt full? */
if (conv_ftl->cmtsize == CMT_SIZE){
victim_index = select_victim_cmt_ent(conv_ftl);
NVMEV_ASSERT(victim_index >= 0 && victim_index < CMT_SIZE);
victim = &conv_ftl->cmt[victim_index];
/* write-back if dirty */
if (victim->dirty){
NVMEV_DEBUG("[DFTL] write-back victim: lpn=%llu\n", victim->lpn);
set_maptbl_ent(conv_ftl, victim->lpn, &victim->ppa);
}
/* eviction */
victim->ppa.ppa = UNMAPPED_PPA;
victim->dirty = false;
victim->valid = false;
conv_ftl->cmtsize--;
}
/* real write site after checking */
ppa = get_new_page(conv_ftl, USER_IO);
insert_cmt_ent(conv_ftl, local_lpn, &ppa);
//set_maptbl_ent(conv_ftl, local_lpn, &ppa);
NVMEV_DEBUG("%s: got new ppa %lld, ", __func__, ppa2pgidx(conv_ftl, &ppa));
/* update rmap */
set_rmap_ent(conv_ftl, local_lpn, &ppa);
mark_page_valid(conv_ftl, &ppa);
/* need to advance the write pointer here */
advance_write_pointer(conv_ftl, USER_IO);
/* Aggregate write io in flash page */
if (last_pg_in_wordline(conv_ftl, &ppa)) {
swr.ppa = &ppa;
nsecs_completed = ssd_advance_nand(conv_ftl->ssd, &swr);
nsecs_latest = max(nsecs_completed, nsecs_latest);
schedule_internal_operation(req->sq_id, nsecs_completed, wbuf,
spp->pgs_per_oneshotpg * spp->pgsz);
}
consume_write_credit(conv_ftl);
check_and_refill_write_credit(conv_ftl);
}
- read에서 했던 것처럼 miss control과 같은 로직이 필요하다
- write에서 evict되는 상황은 같다 : cmt가 full
- 새 ppa에 대해서 lpn을 연결해야 하니까 : insert
- 원래가 maptbl은 다 갖고 있으니까 insert가 필요X, 그래서 set 사용했음
- 3차 구현(수정)

위와 같은 로직이 반복되는 곳이 여러 군데라서 miss control을 함수화 하여
코드의 가독성을 높이고 간단하게 수정하도록 한다.
- miss control 함수
cmt가 full인지 확인 → victim 대상을 고름 → ditry하면 write-back →evict
static miss_control(struct conv_ftl *conv_ftl){
struct cmt_entry *victim = NULL;
int victim_index = -1;
/* is cmt full? */
if (conv_ftl->cmtsize == CMT_SIZE){
victim_index = select_victim_cmt_ent(conv_ftl);
NVMEV_ASSERT(victim_index >= 0 && victim_index < CMT_SIZE);
victim = &conv_ftl->cmt[victim_index];
/* write-back if dirty */
if (victim->dirty){
NVMEV_DEBUG("[DFTL] write-back victim: lpn=%llu\n", victim->lpn);
set_maptbl_ent(conv_ftl, victim->lpn, &victim->ppa);
}
/* eviction */
victim->ppa.ppa = UNMAPPED_PPA;
victim->dirty = false;
victim->valid = false;
conv_ftl->cmtsize--;
}
return;
}
`miss_control` 함수에 들어가도 내부에서 cmt is full이 아니면 if문에 들어가지 않고 return을 진행한다.
같은 로직이 반복되는 `get_cmt_ent`, `gc_write_page`, `conv_write`의 miss control부를 대체한다.
`conv_write`도 이렇게 간단하게 바뀔 수 있다~!
/* real new write is after checking cmt */
miss_control(conv_ftl);
ppa = get_new_page(conv_ftl, USER_IO);
insert_cmt_ent(conv_ftl, local_lpn, &ppa);
//set_maptbl_ent(conv_ftl, local_lpn, &ppa);
NVMEV_DEBUG("%s: got new ppa %lld, ", __func__, ppa2pgidx(conv_ftl, &ppa));
- gc_write_page
이것은 어렵지 않음. write와 같은 로직으로 수정한다.
static uint64_t gc_write_page(struct conv_ftl *conv_ftl, struct ppa *old_ppa)
{
struct ssdparams *spp = &conv_ftl->ssd->sp;
struct convparams *cpp = &conv_ftl->cp;
struct ppa new_ppa;
uint64_t lpn = get_rmap_ent(conv_ftl, old_ppa);
NVMEV_ASSERT(valid_lpn(conv_ftl, lpn));
/* real new write is after checking cmt */
miss_control(conv_ftl);
new_ppa = get_new_page(conv_ftl, GC_IO);
insert_cmt_ent(conv_ftl, lpn, &new_ppa);
//set_maptbl_ent(conv_ftl, lpn, &new_ppa);
/* update rmap */
set_rmap_ent(conv_ftl, lpn, &new_ppa);
mark_page_valid(conv_ftl, &new_ppa);
//obmitted below
- 4차 구현(수정)
나중에 평가하면서 피드백을 받고 수정했던 부분을 여기에 덧붙인다.
Cache size가 커지면 hit ratio는 0%에 가까워져야 하는데 50%에 수렴하는 문제가 있었다.
멘토님께 피드백을 받은 내용은 `prev_ppa`와 `cur_ppa` 구성할 때 `get_cmt_ent`를 두 번 호출해서
같은 LPN이 두 번 들어갔고 처음에 miss가 난 후에 무조건 hit가 나서 1:1로 비슷해진다는 것이다.
그래서 `start_lpn`과 `local_lpn`이 같으면 `prev_ppa`를 바로 사용하도록 변경하였다.
/* normal IO read path */
for (lpn = start_lpn; lpn <= end_lpn; lpn += nr_parts) {
uint64_t local_lpn;
struct ppa cur_ppa;
// Here...
local_lpn = lpn / nr_parts;
if (start_lpn == local_lpn){
prev_ppa = get_cmt_ent(conv_ftl, start_lpn / nr_parts);
cur_ppa = prev_ppa;
} else {
cur_ppa =get_cmt_ent(conv_ftl, local_lpn)
}
//cur_ppa = get_cmt_ent(conv_ftl, local_lpn);
//cur_ppa = get_maptbl_ent(conv_ftl, local_lpn);


Hit ratio가 50%로 수렴하는 문제 해결 완료...!
- 헷갈리는 개념
- SSD - Channel - Die - Plane - Block(erase 단위) - Page(최소 rw 단위) - Sector(host io 단위)
- LBA(slba, nr_lab) : host가 요청하는 주소(sector 단위)
- LPN : sector 단위 → flash page 단위 변환한 것 start_lpn = lba / secs_per_pg
- PPA : real NAND 내부의 page 위치 (channel, block, page 번호 포함)



바보 흔적. 바로 maptbl 보면 왜 안 돼?
→ 그것이 DFTL이니까.... (ㅋㅋㅋㅋㅋ)
여전히 순조롭지 않았다... 하지만 이제 평가를 해야 한다.
내가 만든 DFTL 타당성까지 내가 증명해야 한다.
무를 뽑아서 칼을 썰고 있다 내가... _(:ι」∠)_
'My Laboratory' 카테고리의 다른 글
| [NVMeVirt] Implementation of DFTL : evaluation (0) | 2025.12.26 |
|---|---|
| Doc Review : 소프트웨어(s/w) 에러정정코드(ecc)를 이용한 낸드 플래시에서의 데이터 입출력 방법 및 그 방법을 이용한 임베디드 시스템 (0) | 2025.09.24 |
| [NVMeVirt] implementation of DFTL #2 : read (1) | 2025.09.10 |
| [NVMeVirt] implementation of DFTL #1 (0) | 2025.09.04 |
| Paper Review : NVMeVirt: A Versatile Software-defined Virtual NVMe Device (0) | 2025.08.08 |