11#include "./zns.h"
2+ #include "qemu/lockable.h"
23
34#define MIN_DISCARD_GRANULARITY (4 * KiB)
45#define NVME_DEFAULT_ZONE_SIZE (128 * MiB)
@@ -370,6 +371,8 @@ static void zns_free_params(NvmeNamespace *ns)
370371 return ;
371372 }
372373
374+ qemu_mutex_destroy (& zns -> zone_lock );
375+
373376 if (zns -> ch ) {
374377 for (i = 0 ; i < zns -> num_ch ; i ++ ) {
375378 zns_free_ch (& zns -> ch [i ], zns -> num_lun , zns -> num_plane );
@@ -1217,11 +1220,13 @@ static uint16_t zns_nvme_rw(FemuCtrl *n, NvmeNamespace *ns, NvmeCmd *cmd,
12171220 uint32_t nlb = (uint32_t )le16_to_cpu (rw -> nlb ) + 1 ;
12181221 uint64_t data_size = zns_l2b (ns , nlb );
12191222 uint64_t data_offset ;
1220- uint64_t wp ;
1223+ uint64_t wp = 0 ;
12211224 uint16_t status ;
12221225
12231226 NvmeZone * zone ;
12241227 NvmeZonedResult * res = (NvmeZonedResult * )& req -> cqe ;
1228+ bool fault = false;
1229+
12251230 assert (n -> zoned );
12261231 // Fix zone append not working as expected
12271232 req -> is_write = ((rw -> opcode == NVME_CMD_WRITE ) || (rw -> opcode == NVME_CMD_ZONE_APPEND )) ? 1 : 0 ;
@@ -1238,27 +1243,35 @@ static uint16_t zns_nvme_rw(FemuCtrl *n, NvmeNamespace *ns, NvmeCmd *cmd,
12381243
12391244 if (req -> is_write )
12401245 {
1241- zone = zns_get_zone_by_slba (ns , slba );
1242- status = zns_check_zone_write (n , ns , zone , slba , nlb , append );
1243- if (status ) {
1244- femu_err ("Misao check zone write failed with status (%u)\n" ,status );
1245- goto err ;
1246- }
12471246 /*
1248- * A write to an empty or closed zone opens it, which takes an open
1249- * (and for an empty zone an active) resource; make room or refuse
1250- * before anything moves. A conventional zone has no such state.
1247+ * Placing the write is one step: an append reads the write pointer to
1248+ * decide where it lands and then moves it. Two appends on queues that
1249+ * different pollers serve otherwise read the same pointer and are
1250+ * placed on top of each other.
12511251 */
1252- if (zone -> d .zt != NVME_ZONE_TYPE_CONVENTIONAL ) {
1253- status = zns_auto_open_zone (ns , zone );
1254- if (status ) {
1255- goto err ;
1252+ WITH_QEMU_LOCK_GUARD (& ns -> zns -> zone_lock ) {
1253+ zone = zns_get_zone_by_slba (ns , slba );
1254+ status = zns_check_zone_write (n , ns , zone , slba , nlb , append );
1255+ /*
1256+ * A write to an empty or closed zone opens it, which takes an open
1257+ * (and for an empty zone an active) resource; make room or refuse
1258+ * before anything moves. A conventional zone has no such state.
1259+ */
1260+ if (!status && zone -> d .zt != NVME_ZONE_TYPE_CONVENTIONAL ) {
1261+ status = zns_auto_open_zone (ns , zone );
1262+ }
1263+ if (!status ) {
1264+ if (append ) {
1265+ slba = zone -> w_ptr ;
1266+ }
1267+ wp = zns_advance_zone_wp (ns , zone , nlb );
12561268 }
12571269 }
1258- if (append ) {
1259- slba = zone -> w_ptr ;
1270+ if (status ) {
1271+ femu_err ("Misao check zone write failed with status (%u)\n" ,
1272+ status );
1273+ goto err ;
12601274 }
1261- wp = zns_advance_zone_wp (ns , zone , nlb );
12621275 /* only an append reports where it landed; DW0/1 are reserved otherwise */
12631276 if (append ) {
12641277 res -> slba = cpu_to_le64 (wp );
@@ -1316,35 +1329,44 @@ static uint16_t zns_nvme_rw(FemuCtrl *n, NvmeNamespace *ns, NvmeCmd *cmd,
13161329 * be reported for any block, written or not.
13171330 */
13181331 nvme_mark_written (ns , slba , nlb );
1319- zns_finalize_zoned_write (ns , req , false);
13201332
1321- /*
1322- * Take the zone read only on every Nth write. A real controller does
1323- * this when it can no longer program the zone; here it is the only
1324- * change a host does not cause, so it is what gives the Changed Zone
1325- * List and its notice something to carry. The failing write itself is
1326- * reported as a write fault, and later writes to the zone are refused
1327- * as read only by the state machine.
1328- */
1329- if (zns -> err_write_fail_period &&
1330- (++ zns -> err_write_counter % zns -> err_write_fail_period ) == 0 ) {
1331- NvmeZone * failed = zns_get_zone_by_slba (ns , slba );
1332-
1333- if (failed && zns_get_zone_state (failed ) !=
1334- NVME_ZONE_STATE_READ_ONLY ) {
1335- /*
1336- * Read only is the end of the line for the zone, so the open
1337- * and active resources it held have to go back. Left counted,
1338- * they exhaust the namespace's budget for zones that can never
1339- * be opened again, and the shutdown walk -- which finds the
1340- * zone on no list -- ends on an assertion that the open count
1341- * reached zero.
1342- */
1343- zns_release_zone_resources (ns , failed );
1344- zns_assign_zone_state (ns , failed , NVME_ZONE_STATE_READ_ONLY );
1345- zns_record_changed_zone (ns , failed -> d .zslba );
1346- zns -> err_write_injected ++ ;
1333+ WITH_QEMU_LOCK_GUARD (& zns -> zone_lock ) {
1334+ zns_finalize_zoned_write (ns , req , false);
1335+
1336+ /*
1337+ * Take the zone read only on every Nth write. A real controller
1338+ * does this when it can no longer program the zone; here it is
1339+ * the only change a host does not cause, so it is what gives the
1340+ * Changed Zone List and its notice something to carry. The
1341+ * failing write itself is reported as a write fault, and later
1342+ * writes to the zone are refused as read only by the state
1343+ * machine.
1344+ */
1345+ if (zns -> err_write_fail_period &&
1346+ (++ zns -> err_write_counter % zns -> err_write_fail_period ) == 0 ) {
1347+ NvmeZone * failed = zns_get_zone_by_slba (ns , slba );
1348+
1349+ if (failed && zns_get_zone_state (failed ) !=
1350+ NVME_ZONE_STATE_READ_ONLY ) {
1351+ /*
1352+ * Read only is the end of the line for the zone, so the
1353+ * open and active resources it held have to go back. Left
1354+ * counted, they exhaust the namespace's budget for zones
1355+ * that can never be opened again, and the shutdown walk
1356+ * -- which finds the zone on no list -- ends on an
1357+ * assertion that the open count reached zero.
1358+ */
1359+ zns_release_zone_resources (ns , failed );
1360+ zns_assign_zone_state (ns , failed ,
1361+ NVME_ZONE_STATE_READ_ONLY );
1362+ zns_record_changed_zone (ns , failed -> d .zslba );
1363+ zns -> err_write_injected ++ ;
1364+ }
1365+ fault = true;
13471366 }
1367+ }
1368+
1369+ if (fault ) {
13481370 return NVME_WRITE_FAULT | NVME_DNR ;
13491371 }
13501372 }
@@ -1377,6 +1399,9 @@ static uint16_t zns_zone_mgmt_send(FemuCtrl *n, NvmeRequest *req)
13771399
13781400 req -> status = NVME_SUCCESS ;
13791401
1402+ /* every action here moves zones between the state lists */
1403+ QEMU_LOCK_GUARD (& ns -> zns -> zone_lock );
1404+
13801405 if (!all ) {
13811406 status = zns_get_mgmt_zone_slba_idx (n , ns , cmd , & slba , & zone_idx );
13821407 if (status ) {
@@ -1582,6 +1607,9 @@ static uint16_t zns_zone_mgmt_recv(FemuCtrl *n, NvmeRequest *req)
15821607
15831608 req -> status = NVME_SUCCESS ;
15841609
1610+ /* the report is a snapshot, so no write may land in the middle of it */
1611+ QEMU_LOCK_GUARD (& ns -> zns -> zone_lock );
1612+
15851613 status = zns_get_mgmt_zone_slba_idx (n , ns , cmd , & slba , & zone_idx );
15861614 if (status ) {
15871615 return status ;
@@ -1911,6 +1939,7 @@ static void zns_init_params(FemuCtrl *n, NvmeNamespace *ns)
19111939 zns_nand_media_init (id_zns );
19121940
19131941 ns -> zns = id_zns ;
1942+ qemu_mutex_init (& id_zns -> zone_lock );
19141943}
19151944
19161945static int zns_init_zone_cap (FemuCtrl * n , NvmeNamespace * ns )
@@ -2130,6 +2159,8 @@ static uint16_t zns_changed_zone_list(FemuCtrl *n, NvmeNamespace *ns,
21302159 * emitter run past the end of a page that holds exactly as many entries as
21312160 * the array does.
21322161 */
2162+ QEMU_LOCK_GUARD (& zns -> zone_lock );
2163+
21332164 nr = zns -> nr_changed_zones ;
21342165 if (nr > ARRAY_SIZE (zns -> changed_zones )) {
21352166 nr = ARRAY_SIZE (zns -> changed_zones );
0 commit comments