Skip to content

Commit 901d090

Browse files
committed
femu/zns: set zone descriptor extension leaves the zone alone on failure
This is the one zone action that does not go through the walker, which is where a conventional zone is refused. Accepted, it took a random-write zone to Closed holding an active resource nothing gives back, since such a zone's writes never touch the state machine that would; a later Finish of all zones then walks the closed list and leaves it Full, after which it takes no writes at all. The transfer also landed in the extension before the state was tested, so a command that came back refused had already replaced what the host reads in an extended report. Stage it and copy on success.
1 parent 2c7062e commit 901d090

1 file changed

Lines changed: 25 additions & 4 deletions

File tree

hw/femu/zns/zns.c

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1476,21 +1476,42 @@ static uint16_t zns_zone_mgmt_send(FemuCtrl *n, NvmeRequest *req)
14761476
}
14771477
status = zns_do_zone_op(ns, zone, proc_mask, zns_offline_zone, req);
14781478
break;
1479-
case NVME_ZONE_ACTION_SET_ZD_EXT:
1479+
case NVME_ZONE_ACTION_SET_ZD_EXT: {
1480+
g_autofree uint8_t *staged = NULL;
1481+
14801482
if (all || !ns->zd_extension_size) {
14811483
return NVME_INVALID_FIELD | NVME_DNR;
14821484
}
1483-
zd_ext = zns_get_zd_extension(ns, zone_idx);
1484-
status = dma_write_prp(n, (uint8_t *)zd_ext, ns->zd_extension_size, prp1,
1485-
prp2);
1485+
/*
1486+
* This is the one action that does not go through zns_do_zone_op(),
1487+
* which is where a conventional zone is refused. Accepted, it took the
1488+
* zone to Closed holding an active resource nothing gives back: a
1489+
* conventional zone's writes never touch the state machine that would,
1490+
* so it stays Closed, and a later Finish of all zones walks that list
1491+
* and leaves it Full, after which it takes no writes at all.
1492+
*/
1493+
if (zone->d.zt == NVME_ZONE_TYPE_CONVENTIONAL) {
1494+
return NVME_ZONE_INVAL_TRANSITION | NVME_DNR;
1495+
}
1496+
/*
1497+
* Into a buffer of its own first. The transfer used to land in the
1498+
* extension itself before the state was tested, so a command that came
1499+
* back refused had already replaced what the host reads in an extended
1500+
* report.
1501+
*/
1502+
staged = g_malloc0(ns->zd_extension_size);
1503+
status = dma_write_prp(n, staged, ns->zd_extension_size, prp1, prp2);
14861504
if (status) {
14871505
return status;
14881506
}
14891507
status = zns_set_zd_ext(ns, zone);
14901508
if (status == NVME_SUCCESS) {
1509+
zd_ext = zns_get_zd_extension(ns, zone_idx);
1510+
memcpy(zd_ext, staged, ns->zd_extension_size);
14911511
return status;
14921512
}
14931513
break;
1514+
}
14941515
default:
14951516
status = NVME_INVALID_FIELD;
14961517
}

0 commit comments

Comments
 (0)