Skip to content

Commit 6819f12

Browse files
Add Delay Emulation to FEMU ZNS Mode (MoatLab#104)
- Add delay emulation for ZNS mode - Add a logical zone mapping mechanism - Update ``run-zns.sh`` script with some additional configuration knobs Co-authored-by: Hasen Idden (hansenidden@gmail.com)
1 parent 3b0cee5 commit 6819f12

File tree

5 files changed

+319
-3
lines changed

5 files changed

+319
-3
lines changed

femu-scripts/run-zns.sh

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,21 @@ if [[ ! -e "$OSIMGF" ]]; then
1818
exit
1919
fi
2020

21+
ssd_size=4096
22+
num_channels=2
23+
num_chips_per_channel=4
24+
read_latency=40000
25+
write_latency=200000
26+
27+
FEMU_OPTIONS="-device femu"
28+
FEMU_OPTIONS=${FEMU_OPTIONS}",devsz_mb=${ssd_size}"
29+
FEMU_OPTIONS=${FEMU_OPTIONS}",namespaces=1"
30+
FEMU_OPTIONS=${FEMU_OPTIONS}",zns_num_ch=${num_channels}"
31+
FEMU_OPTIONS=${FEMU_OPTIONS}",zns_num_lun=${num_chips_per_channel}"
32+
FEMU_OPTIONS=${FEMU_OPTIONS}",zns_read=${read_latency}"
33+
FEMU_OPTIONS=${FEMU_OPTIONS}",zns_write=${write_latency}"
34+
FEMU_OPTIONS=${FEMU_OPTIONS}",femu_mode=3"
35+
2136
sudo x86_64-softmmu/qemu-system-x86_64 \
2237
-name "FEMU-ZNSSD-VM" \
2338
-enable-kvm \
@@ -27,7 +42,7 @@ sudo x86_64-softmmu/qemu-system-x86_64 \
2742
-device virtio-scsi-pci,id=scsi0 \
2843
-device scsi-hd,drive=hd0 \
2944
-drive file=$OSIMGF,if=none,aio=native,cache=none,format=qcow2,id=hd0 \
30-
-device femu,devsz_mb=4096,femu_mode=3 \
45+
${FEMU_OPTIONS} \
3146
-net user,hostfwd=tcp::8080-:22 \
3247
-net nic,model=virtio \
3348
-nographic \

hw/femu/femu.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -660,6 +660,10 @@ static Property femu_props[] = {
660660
DEFINE_PROP_UINT8("lnum_lun", FemuCtrl, oc_params.num_lun, 8),
661661
DEFINE_PROP_UINT8("lnum_pln", FemuCtrl, oc_params.num_pln, 2),
662662
DEFINE_PROP_UINT16("lmetasize", FemuCtrl, oc_params.sos, 16),
663+
DEFINE_PROP_UINT8("zns_num_ch", FemuCtrl, zns_params.zns_num_ch, 2),
664+
DEFINE_PROP_UINT8("zns_num_lun", FemuCtrl, zns_params.zns_num_lun, 4),
665+
DEFINE_PROP_UINT64("zns_read", FemuCtrl, zns_params.zns_read, 40000),
666+
DEFINE_PROP_UINT64("zns_write", FemuCtrl, zns_params.zns_write, 200000),
663667
DEFINE_PROP_END_OF_LIST(),
664668
};
665669

hw/femu/nvme.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1145,6 +1145,13 @@ typedef struct NvmeParams {
11451145
#define FEMU_MAX_NUM_CHNLS (32)
11461146
#define FEMU_MAX_NUM_CHIPS (128)
11471147

1148+
typedef struct ZNSCtrlParams {
1149+
uint8_t zns_num_ch;
1150+
uint8_t zns_num_lun;
1151+
uint64_t zns_read;
1152+
uint64_t zns_write;
1153+
} ZNSCtrlParams;
1154+
11481155
typedef struct OcCtrlParams {
11491156
uint16_t sec_size;
11501157
uint8_t secs_per_pg;
@@ -1202,6 +1209,9 @@ typedef struct FemuCtrl {
12021209
int32_t nr_open_zones;
12031210
int32_t nr_active_zones;
12041211

1212+
struct zns_ssd *zns;
1213+
ZNSCtrlParams zns_params;
1214+
12051215
/* Coperd: OC2.0 FIXME */
12061216
NvmeParams params;
12071217
FemuExtCtrlOps ext_ops;

hw/femu/zns/zns.c

Lines changed: 233 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -471,6 +471,126 @@ static void zns_finalize_zoned_write(NvmeNamespace *ns, NvmeRequest *req,
471471
}
472472
}
473473

474+
// Add some function
475+
// --------------------------------
476+
static inline struct zns_ch *get_ch(struct zns_ssd *zns, struct ppa *ppa)
477+
{
478+
return &(zns->ch[ppa->g.ch]);
479+
}
480+
481+
static inline struct zns_fc *get_fc(struct zns_ssd *zns, struct ppa *ppa)
482+
{
483+
struct zns_ch *ch = get_ch(zns, ppa);
484+
return &(ch->fc[ppa->g.fc]);
485+
}
486+
487+
static inline struct zns_blk *get_blk(struct zns_ssd *zns, struct ppa *ppa)
488+
{
489+
struct zns_fc *fc = get_fc(zns, ppa);
490+
return &(fc->blk[ppa->g.blk]);
491+
}
492+
493+
static inline uint64_t zone_slba(FemuCtrl *n, uint32_t zone_idx)
494+
{
495+
return (zone_idx) * n->zone_size;
496+
}
497+
498+
static inline void check_addr(int a, int max)
499+
{
500+
assert(a >= 0 && a < max);
501+
}
502+
503+
static void advance_read_pointer(FemuCtrl *n)
504+
{
505+
struct zns_ssd *zns = n->zns;
506+
struct write_pointer *wpp = &zns->wp;
507+
uint8_t num_ch = zns->num_ch;
508+
uint8_t num_lun = zns->num_lun;
509+
510+
//printf("NUM CH: %"PRIu64"\n", wpp->ch);
511+
check_addr(wpp->ch, num_ch);
512+
wpp->ch++;
513+
if (wpp->ch == num_ch) {
514+
wpp->ch = 0;
515+
check_addr(wpp->lun, num_lun);
516+
wpp->lun++;
517+
if(wpp->lun == num_lun) {
518+
wpp->lun = 0;
519+
520+
assert(wpp->ch == 0);
521+
assert(wpp->lun == 0);
522+
}
523+
}
524+
}
525+
526+
static inline struct ppa lpn_to_ppa(FemuCtrl *n, NvmeNamespace *ns, uint64_t lpn)
527+
{
528+
529+
uint32_t zone_idx = zns_zone_idx(ns, (lpn * 4096));
530+
531+
struct zns_ssd *zns = n->zns;
532+
struct write_pointer *wpp = &zns->wp;
533+
//uint64_t num_ch = zns->num_ch;
534+
//uint64_t num_lun = zns->num_lun;
535+
struct ppa ppa = {0};
536+
537+
//printf("OFFSET: %"PRIu64"\n\n", offset);
538+
//wpp->ch,lun
539+
ppa.g.ch = wpp->ch;
540+
ppa.g.fc = wpp->lun;
541+
ppa.g.blk = zone_idx;
542+
543+
return ppa;
544+
}
545+
546+
static uint64_t zns_advance_status(FemuCtrl *n, struct nand_cmd *ncmd,
547+
struct ppa *ppa)
548+
{
549+
int c = ncmd->cmd;
550+
551+
struct zns_ssd *zns = n->zns;
552+
uint64_t nand_stime;
553+
uint64_t req_stime = (ncmd->stime == 0) ? \
554+
qemu_clock_get_ns(QEMU_CLOCK_REALTIME) : ncmd->stime;
555+
556+
struct zns_fc *fc = get_fc(zns, ppa);
557+
558+
uint64_t lat = 0;
559+
uint64_t read_delay = n->zns_params.zns_read;
560+
uint64_t write_delay = n->zns_params.zns_write;
561+
uint64_t erase_delay = 2000000;
562+
//200 us for write
563+
switch (c) {
564+
case NAND_READ:
565+
nand_stime = (fc->next_fc_avail_time < req_stime) ? req_stime : \
566+
fc->next_fc_avail_time;
567+
568+
fc->next_fc_avail_time = nand_stime + read_delay;
569+
lat = fc->next_fc_avail_time - req_stime;
570+
571+
break;
572+
case NAND_WRITE:
573+
nand_stime = (fc->next_fc_avail_time < req_stime) ? req_stime : \
574+
fc->next_fc_avail_time;
575+
fc->next_fc_avail_time = nand_stime + write_delay;
576+
lat = fc->next_fc_avail_time - req_stime;
577+
578+
break;
579+
case NAND_ERASE:
580+
nand_stime = (fc->next_fc_avail_time < req_stime) ? req_stime : \
581+
fc->next_fc_avail_time;
582+
fc->next_fc_avail_time = nand_stime + erase_delay;
583+
lat = fc->next_fc_avail_time - req_stime;
584+
585+
break;
586+
default:
587+
//lat = 0;
588+
;
589+
}
590+
return lat;
591+
}
592+
//----------------------------------
593+
474594
static uint64_t zns_advance_zone_wp(NvmeNamespace *ns, NvmeZone *zone,
475595
uint32_t nlb)
476596
{
@@ -502,6 +622,7 @@ struct zns_zone_reset_ctx {
502622
static void zns_aio_zone_reset_cb(NvmeRequest *req, NvmeZone *zone)
503623
{
504624
NvmeNamespace *ns = req->ns;
625+
FemuCtrl *n = ns->ctrl;
505626

506627
/* FIXME, We always assume reset SUCCESS */
507628
switch (zns_get_zone_state(zone)) {
@@ -520,6 +641,25 @@ static void zns_aio_zone_reset_cb(NvmeRequest *req, NvmeZone *zone)
520641
default:
521642
break;
522643
}
644+
645+
struct zns_ssd *zns = n->zns;
646+
uint64_t num_ch = zns->num_ch;
647+
uint64_t num_lun = zns->num_lun;
648+
struct ppa ppa;
649+
650+
for (int ch = 0; ch < num_ch; ch++){
651+
for (int lun = 0; lun < num_lun; lun++) {
652+
ppa.g.ch = ch;
653+
ppa.g.fc = lun;
654+
ppa.g.blk = zns_zone_idx(ns, zone->d.zslba);
655+
656+
struct nand_cmd erase;
657+
erase.cmd = NAND_ERASE;
658+
erase.stime = 0;
659+
zns_advance_status(n, &erase, &ppa);
660+
661+
}
662+
}
523663
}
524664

525665
typedef uint16_t (*op_handler_t)(NvmeNamespace *, NvmeZone *, NvmeZoneState,
@@ -1172,6 +1312,29 @@ static uint16_t zns_read(FemuCtrl *n, NvmeNamespace *ns, NvmeCmd *cmd,
11721312
data_offset = zns_l2b(ns, slba);
11731313

11741314
backend_rw(n->mbe, &req->qsg, &data_offset, req->is_write);
1315+
1316+
uint64_t slpn = (slba)/4096;
1317+
uint64_t elpn = (slba + nlb - 1)/4096;
1318+
1319+
uint64_t lpn;
1320+
struct ppa ppa;
1321+
uint64_t sublat,maxlat=0;
1322+
1323+
for (lpn = slpn; lpn <= elpn; lpn++) {
1324+
ppa = lpn_to_ppa(n, ns, lpn);
1325+
advance_read_pointer(n);
1326+
1327+
struct nand_cmd read;
1328+
read.cmd = NAND_READ;
1329+
read.stime = req->stime;
1330+
1331+
sublat = zns_advance_status(n, &read, &ppa);
1332+
maxlat = (sublat > maxlat) ? sublat : maxlat;
1333+
}
1334+
1335+
req->reqlat = maxlat;
1336+
req->expire_time += maxlat;
1337+
11751338
return NVME_SUCCESS;
11761339

11771340
err:
@@ -1223,10 +1386,31 @@ static uint16_t zns_write(FemuCtrl *n, NvmeNamespace *ns, NvmeCmd *cmd,
12231386
if (status) {
12241387
goto err;
12251388
}
1226-
1389+
12271390
backend_rw(n->mbe, &req->qsg, &data_offset, req->is_write);
12281391
zns_finalize_zoned_write(ns, req, false);
1392+
1393+
uint64_t slpn = (slba)/4096;
1394+
uint64_t elpn = (slba + nlb - 1)/4096;
1395+
1396+
uint64_t lpn;
1397+
struct ppa ppa;
1398+
uint64_t sublat,maxlat=0;
1399+
1400+
for (lpn = slpn; lpn <= elpn; lpn++) {
1401+
ppa = lpn_to_ppa(n, ns, lpn);
1402+
advance_read_pointer(n);
12291403

1404+
struct nand_cmd write;
1405+
write.cmd = NAND_WRITE;
1406+
write.stime = req->stime;
1407+
1408+
sublat = zns_advance_status(n, &write, &ppa);
1409+
maxlat = (sublat > maxlat) ? sublat : maxlat;
1410+
}
1411+
1412+
req->reqlat = maxlat;
1413+
req->expire_time += maxlat;
12301414
return NVME_SUCCESS;
12311415

12321416
err:
@@ -1271,6 +1455,51 @@ static void zns_set_ctrl(FemuCtrl *n)
12711455
pci_config_set_device_id(pci_conf, 0x5845);
12721456
}
12731457

1458+
// Add zns init ch, zns init flash and zns init block
1459+
// ----------------------------
1460+
static void zns_init_blk(struct zns_blk *blk)
1461+
{
1462+
blk->next_blk_avail_time = 0;
1463+
}
1464+
1465+
static void zns_init_fc(struct zns_fc *fc)
1466+
{
1467+
fc->blk = g_malloc0(sizeof(struct zns_blk) * 32);
1468+
for (int i = 0; i < 32; i++) {
1469+
zns_init_blk(&fc->blk[i]);
1470+
}
1471+
fc->next_fc_avail_time = 0;
1472+
}
1473+
1474+
static void zns_init_ch(struct zns_ch *ch, uint8_t num_lun)
1475+
{
1476+
ch->fc = g_malloc0(sizeof(struct zns_fc) * num_lun);
1477+
for (int i = 0; i < num_lun; i++) {
1478+
zns_init_fc(&ch->fc[i]);
1479+
}
1480+
ch->next_ch_avail_time = 0;
1481+
}
1482+
1483+
static void zns_init_params(FemuCtrl *n)
1484+
{
1485+
struct zns_ssd *id_zns;
1486+
1487+
id_zns = g_malloc0(sizeof(struct zns_ssd));
1488+
id_zns->num_ch = n->zns_params.zns_num_ch;
1489+
id_zns->num_lun = n->zns_params.zns_num_lun;
1490+
id_zns->ch = g_malloc0(sizeof(struct zns_ch) * id_zns->num_ch);
1491+
for (int i =0; i < id_zns->num_ch; i++) {
1492+
zns_init_ch(&id_zns->ch[i], id_zns->num_lun);
1493+
}
1494+
1495+
id_zns->wp.ch = 0;
1496+
id_zns->wp.lun = 0;
1497+
n->zns = id_zns;
1498+
}
1499+
1500+
// ---------------------------------------
1501+
1502+
12741503
static int zns_init_zone_cap(FemuCtrl *n)
12751504
{
12761505
n->zoned = true;
@@ -1281,7 +1510,7 @@ static int zns_init_zone_cap(FemuCtrl *n)
12811510
n->max_active_zones = 0;
12821511
n->max_open_zones = 0;
12831512
n->zd_extension_size = 0;
1284-
1513+
12851514
return 0;
12861515
}
12871516

@@ -1316,6 +1545,8 @@ static void zns_init(FemuCtrl *n, Error **errp)
13161545
}
13171546

13181547
zns_init_zone_identify(n, ns, 0);
1548+
1549+
zns_init_params(n);
13191550
}
13201551

13211552
static void zns_exit(FemuCtrl *n)

0 commit comments

Comments
 (0)