Skip to content

Commit b3272c0

Browse files
authored
Implement zone reset with erase latency (#178)
* Implement zone reset functionality in ZNS SSD with erase latency * Replace femu_log with ftl_debug for read/write operations
1 parent 2aabb37 commit b3272c0

File tree

3 files changed

+146
-24
lines changed

3 files changed

+146
-24
lines changed

hw/femu/zns/zftl.c

Lines changed: 123 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,124 @@ static int zns_get_wcidx(struct zns_ssd* zns)
167167
return -1;
168168
}
169169

170+
static void zns_invalidate_zone_cache(struct zns_ssd *zns, uint32_t zone_idx)
171+
{
172+
int i;
173+
174+
for (i = 0; i < zns->cache.num_wc; i++) {
175+
if (zns->cache.write_cache[i].sblk == zone_idx) {
176+
#ifdef FEMU_DEBUG_ZFTL
177+
uint64_t discarded_entries = zns->cache.write_cache[i].used;
178+
#endif
179+
180+
zns->cache.write_cache[i].used = 0;
181+
zns->cache.write_cache[i].sblk = INVALID_SBLK;
182+
183+
ftl_debug("Invalidated write_cache[%d] for zone %u (%lu entries discarded)\n",
184+
i, zone_idx, discarded_entries);
185+
return;
186+
}
187+
}
188+
}
189+
190+
static void zns_invalidate_zone_mappings(struct zns_ssd *zns, uint32_t zone_idx,
191+
uint64_t zone_size_lbas, uint64_t lbasz)
192+
{
193+
uint64_t zone_start_lba = zone_idx * zone_size_lbas;
194+
uint64_t zone_end_lba = (zone_idx + 1) * zone_size_lbas;
195+
uint64_t secs_per_pg = LOGICAL_PAGE_SIZE / lbasz;
196+
uint64_t start_lpn = zone_start_lba / secs_per_pg;
197+
uint64_t end_lpn = zone_end_lba / secs_per_pg;
198+
uint64_t lpn;
199+
uint64_t invalidated_count = 0;
200+
201+
for (lpn = start_lpn; lpn < end_lpn && lpn < zns->l2p_sz; lpn++) {
202+
if (zns->maptbl[lpn].ppa != UNMAPPED_PPA) {
203+
zns->maptbl[lpn].ppa = UNMAPPED_PPA;
204+
invalidated_count++;
205+
}
206+
}
207+
208+
ftl_debug("Invalidated %lu LPN mappings for zone %u (LPN %lu-%lu)\n",
209+
invalidated_count, zone_idx, start_lpn, end_lpn - 1);
210+
}
211+
212+
static void zns_reset_block_state(struct zns_ssd *zns, uint32_t zone_idx)
213+
{
214+
int ch, lun, pl;
215+
struct ppa ppa;
216+
struct zns_blk *blk;
217+
218+
for (ch = 0; ch < zns->num_ch; ch++) {
219+
for (lun = 0; lun < zns->num_lun; lun++) {
220+
for (pl = 0; pl < zns->num_plane; pl++) {
221+
ppa.g.ch = ch;
222+
ppa.g.fc = lun;
223+
ppa.g.pl = pl;
224+
ppa.g.blk = zone_idx;
225+
ppa.g.pg = 0;
226+
ppa.g.spg = 0;
227+
228+
blk = get_blk(zns, &ppa);
229+
blk->page_wp = 0;
230+
}
231+
}
232+
}
233+
234+
ftl_debug("Reset block state for zone %u (all page_wp = 0)\n", zone_idx);
235+
}
236+
237+
uint64_t zns_zone_reset(struct zns_ssd *zns, uint32_t zone_idx,
238+
uint64_t zone_size_lbas, uint64_t lbasz, uint64_t stime)
239+
{
240+
int ch, lun, pl;
241+
struct ppa ppa;
242+
struct nand_cmd erase_cmd;
243+
uint64_t sublat, maxlat = 0;
244+
uint64_t total_blocks_erased = 0;
245+
246+
ftl_debug("=== Zone Reset Started for Zone %u ===\n", zone_idx);
247+
248+
/* Step 1: Invalidate write cache (instant) */
249+
zns_invalidate_zone_cache(zns, zone_idx);
250+
251+
/* Step 2: Invalidate L2P mappings (instant) */
252+
zns_invalidate_zone_mappings(zns, zone_idx, zone_size_lbas, lbasz);
253+
254+
/* Step 3: Reset block state (instant) */
255+
zns_reset_block_state(zns, zone_idx);
256+
257+
/* Step 4: Simulate physical erase across all channels/LUNs/planes (parallel) */
258+
erase_cmd.type = USER_IO;
259+
erase_cmd.cmd = NAND_ERASE;
260+
erase_cmd.stime = stime;
261+
262+
for (ch = 0; ch < zns->num_ch; ch++) {
263+
for (lun = 0; lun < zns->num_lun; lun++) {
264+
for (pl = 0; pl < zns->num_plane; pl++) {
265+
ppa.ppa = 0;
266+
ppa.g.ch = ch;
267+
ppa.g.fc = lun;
268+
ppa.g.pl = pl;
269+
ppa.g.blk = zone_idx;
270+
ppa.g.pg = 0;
271+
ppa.g.spg = 0;
272+
273+
sublat = zns_advance_status(zns, &ppa, &erase_cmd);
274+
maxlat = (sublat > maxlat) ? sublat : maxlat;
275+
total_blocks_erased++;
276+
}
277+
}
278+
}
279+
280+
ftl_debug("Zone %u reset complete: erased %lu blocks across %d ch * %d lun * %d planes\n",
281+
zone_idx, total_blocks_erased, (int)zns->num_ch, (int)zns->num_lun, (int)zns->num_plane);
282+
ftl_debug("Maximum erase latency: %lu ns (%.2f ms)\n", maxlat, maxlat / 1000000.0);
283+
ftl_debug("=== Zone Reset Finished ===\n\n");
284+
285+
return maxlat;
286+
}
287+
170288
static uint64_t zns_read(struct zns_ssd *zns, NvmeRequest *req)
171289
{
172290
uint64_t lba = req->slba;
@@ -192,7 +310,7 @@ static uint64_t zns_read(struct zns_ssd *zns, NvmeRequest *req)
192310
srd.stime = req->stime;
193311

194312
sublat = zns_advance_status(zns, &ppa, &srd);
195-
femu_log("[R] lpn:\t%lu\t<--ch:\t%u\tlun:\t%u\tpl:\t%u\tblk:\t%u\tpg:\t%u\tsubpg:\t%u\tlat\t%lu\n",lpn,ppa.g.ch,ppa.g.fc,ppa.g.pl,ppa.g.blk,ppa.g.pg,ppa.g.spg,sublat);
313+
ftl_debug("[R] lpn:\t%lu\t<--ch:\t%u\tlun:\t%u\tpl:\t%u\tblk:\t%u\tpg:\t%u\tsubpg:\t%u\tlat\t%lu\n",lpn,ppa.g.ch,ppa.g.fc,ppa.g.pl,ppa.g.blk,ppa.g.pg,ppa.g.spg,sublat);
196314
maxlat = (sublat > maxlat) ? sublat : maxlat;
197315
}
198316

@@ -234,7 +352,7 @@ static uint64_t zns_wc_flush(struct zns_ssd* zns, int wcidx, int type,uint64_t s
234352
ppa.g.spg = subpage;
235353
/* update maptbl */
236354
set_maptbl_ent(zns, lpn, &ppa);
237-
//femu_log("[F] lpn:\t%lu\t-->ch:\t%u\tlun:\t%u\tpl:\t%u\tblk:\t%u\tpg:\t%u\tsubpg:\t%u\tlat\t%lu\n",lpn,ppa.g.ch,ppa.g.fc,ppa.g.pl,ppa.g.blk,ppa.g.pg,ppa.g.spg,sublat);
355+
// ftl_debug("[F] lpn:\t%lu\t-->ch:\t%u\tlun:\t%u\tpl:\t%u\tblk:\t%u\tpg:\t%u\tsubpg:\t%u\tlat\t%lu\n",lpn,ppa.g.ch,ppa.g.fc,ppa.g.pl,ppa.g.blk,ppa.g.pg,ppa.g.spg,sublat);
238356
}
239357
i+=ZNS_PAGE_SIZE/LOGICAL_PAGE_SIZE;
240358
}
@@ -295,16 +413,16 @@ static uint64_t zns_write(struct zns_ssd *zns, NvmeRequest *req)
295413
for (lpn = start_lpn; lpn <= end_lpn; lpn++) {
296414
if(zns->cache.write_cache[wcidx].used==zns->cache.write_cache[wcidx].cap)
297415
{
298-
femu_log("[W] flush wc %d (%u/%u)\n",wcidx,(int)zns->cache.write_cache[wcidx].used,(int)zns->cache.write_cache[wcidx].cap);
416+
ftl_debug("[W] flush wc %d (%u/%u)\n",wcidx,(int)zns->cache.write_cache[wcidx].used,(int)zns->cache.write_cache[wcidx].cap);
299417
sublat = zns_wc_flush(zns,wcidx,USER_IO,req->stime);
300-
femu_log("[W] flush lat: %u\n", (int)sublat);
418+
ftl_debug("[W] flush lat: %u\n", (int)sublat);
301419
maxlat = (sublat > maxlat) ? sublat : maxlat;
302420
sublat = 0;
303421
}
304422
zns->cache.write_cache[wcidx].lpns[zns->cache.write_cache[wcidx].used++]=lpn;
305423
sublat += SRAM_WRITE_LATENCY_NS; //Simplified timing emulation
306424
maxlat = (sublat > maxlat) ? sublat : maxlat;
307-
femu_log("[W] lpn:\t%lu\t-->wc cache:%u, used:%u\n",lpn,(int)wcidx,(int)zns->cache.write_cache[wcidx].used);
425+
ftl_debug("[W] lpn:\t%lu\t-->wc cache:%u, used:%u\n",lpn,(int)wcidx,(int)zns->cache.write_cache[wcidx].used);
308426
}
309427
return maxlat;
310428
}

hw/femu/zns/zftl.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99

1010
void zftl_init(FemuCtrl *n);
1111

12+
uint64_t zns_zone_reset(struct zns_ssd *zns, uint32_t zone_idx,
13+
uint64_t zone_size_lbas, uint64_t lbasz, uint64_t stime);
14+
1215
#ifdef FEMU_DEBUG_ZFTL
1316
#define ftl_debug(fmt, ...) \
1417
do { printf("[Misao] ZFTL-Dbg: " fmt, ## __VA_ARGS__); } while (0)

hw/femu/zns/zns.c

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -502,11 +502,14 @@ struct zns_zone_reset_ctx {
502502
NvmeZone *zone;
503503
};
504504

505-
static void zns_aio_zone_reset_cb(NvmeRequest *req, NvmeZone *zone)
505+
static uint64_t zns_aio_zone_reset_cb(NvmeRequest *req, NvmeZone *zone)
506506
{
507507
NvmeNamespace *ns = req->ns;
508+
FemuCtrl *n = ns->ctrl;
509+
struct zns_ssd *zns = n->zns;
510+
uint32_t zone_idx = zns_zone_idx(ns, zone->d.zslba);
511+
uint64_t erase_latency = 0;
508512

509-
/* FIXME, We always assume reset SUCCESS */
510513
switch (zns_get_zone_state(zone)) {
511514
case NVME_ZONE_STATE_EXPLICITLY_OPEN:
512515
/* fall through */
@@ -520,27 +523,20 @@ static void zns_aio_zone_reset_cb(NvmeRequest *req, NvmeZone *zone)
520523
zone->w_ptr = zone->d.zslba;
521524
zone->d.wp = zone->w_ptr;
522525
zns_assign_zone_state(ns, zone, NVME_ZONE_STATE_EMPTY);
526+
break;
523527
default:
524528
break;
525529
}
526530

527-
#if 0
528-
FemuCtrl *n = ns->ctrl;
529-
int ch, lun;
530-
struct zns_ssd *zns = n->zns;
531-
uint64_t num_ch = zns->num_ch;
532-
uint64_t num_lun = zns->num_lun;
533-
534-
struct ppa ppa;
535-
for (ch = 0; ch < num_ch; ch++) {
536-
for (lun = 0; lun < num_lun; lun++) {
537-
ppa.g.ch = ch;
538-
ppa.g.fc = lun;
539-
ppa.g.blk = zns_zone_idx(ns, zone->d.zslba);
540-
//FIXME: no erase
541-
}
531+
erase_latency = zns_zone_reset(zns, zone_idx, n->zone_size, zns->lbasz, req->stime);
532+
533+
/* Reset write pointer if this was the active zone */
534+
if (zns->active_zone == zone_idx) {
535+
zns->wp.ch = 0;
536+
zns->wp.lun = 0;
542537
}
543-
#endif
538+
539+
return erase_latency;
544540
}
545541

546542
typedef uint16_t (*op_handler_t)(NvmeNamespace *, NvmeZone *, NvmeZoneState,
@@ -631,6 +627,8 @@ static uint16_t zns_finish_zone(NvmeNamespace *ns, NvmeZone *zone,
631627
static uint16_t zns_reset_zone(NvmeNamespace *ns, NvmeZone *zone,
632628
NvmeZoneState state, NvmeRequest *req)
633629
{
630+
uint64_t erase_lat = 0;
631+
634632
switch (state) {
635633
case NVME_ZONE_STATE_EMPTY:
636634
return NVME_SUCCESS;
@@ -643,7 +641,10 @@ static uint16_t zns_reset_zone(NvmeNamespace *ns, NvmeZone *zone,
643641
return NVME_ZONE_INVAL_TRANSITION;
644642
}
645643

646-
zns_aio_zone_reset_cb(req, zone);
644+
erase_lat = zns_aio_zone_reset_cb(req, zone);
645+
646+
req->reqlat = erase_lat;
647+
req->expire_time += erase_lat;
647648

648649
return NVME_SUCCESS;
649650
}

0 commit comments

Comments
 (0)