Skip to content

Commit d5a5b78

Browse files
committed
femu/ocssd: charge an erase to the chips it names
The v1.2 erase path never set req->slba, and the timing model's erase branch read it: the chip whose queue the erase joined came from the freed address list the previous command on that request left behind. Hand it the list this command read, walk it, and charge every chip the erase names rather than the first. Validate those addresses against the geometry first, as read and write already do. Nothing bounded them, and the model turns the channel and lun fields into an index into a fixed-size array; axis counts that are not powers of two leave both fields able to hold values the device does not have. The read and write paths also stop leaving a freed pointer behind for the next command to find.
1 parent e9746f6 commit d5a5b78

1 file changed

Lines changed: 45 additions & 6 deletions

File tree

hw/femu/ocssd/oc12.c

Lines changed: 45 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -420,14 +420,32 @@ static int oc12_advance_status(FemuCtrl *n, NvmeNamespace *ns, NvmeCmd *cmd,
420420
uint64_t ppa;
421421
int i;
422422

423-
/* Erase */
423+
/*
424+
* Erase. The address list is the one the erase handler read from the
425+
* command, the same way read and write pass theirs; this used to read
426+
* req->slba, which erase never sets, so the chip it charged came from
427+
* whatever the previous command on this request left behind -- a freed
428+
* list pointer, whose address bits are not a chip of this device. Every
429+
* block in the list is erased, so every chip it names is busy, not just
430+
* the first.
431+
*/
424432
if (opcode == OC12_CMD_ERASE) {
425-
ppa = req->slba;
426-
lun = PPA_LUN(ln, ppa);
427-
ch = PPA_CH(ln, ppa);
428-
lunid = ch * c->num_lun + lun;
433+
uint32_t nlb = le16_to_cpu(ocrw->nlb) + 1;
434+
435+
for (i = 0; i < nlb; i++) {
436+
int64_t ts;
437+
438+
ppa = ((uint64_t *)req->slba)[i];
439+
lun = PPA_LUN(ln, ppa);
440+
ch = PPA_CH(ln, ppa);
441+
lunid = ch * c->num_lun + lun;
442+
443+
ts = advance_chip_timestamp(n, lunid, now, opcode, 0);
444+
if (ts > req->expire_time) {
445+
req->expire_time = ts;
446+
}
447+
}
429448

430-
req->expire_time = advance_chip_timestamp(n, lunid, now, opcode, 0);
431449
return 0;
432450
}
433451

@@ -574,12 +592,14 @@ static uint16_t oc12_read(FemuCtrl *n, NvmeNamespace *ns, NvmeCmd *cmd,
574592

575593
g_free(msl);
576594
g_free((void *)req->slba);
595+
req->slba = 0;
577596

578597
return NVME_SUCCESS;
579598

580599
fail_free:
581600
g_free(msl);
582601
g_free((void *)req->slba);
602+
req->slba = 0;
583603

584604
return err;
585605
}
@@ -685,12 +705,14 @@ static uint16_t oc12_write(FemuCtrl *n, NvmeNamespace *ns, NvmeCmd *cmd,
685705

686706
g_free(msl);
687707
g_free((void *)req->slba);
708+
req->slba = 0;
688709

689710
return NVME_SUCCESS;
690711

691712
fail_free:
692713
g_free(msl);
693714
g_free((void *)req->slba);
715+
req->slba = 0;
694716

695717
return err;
696718
}
@@ -895,6 +917,7 @@ static uint16_t oc12_erase_async(FemuCtrl *n, NvmeNamespace *ns, NvmeCmd *cmd,
895917
Oc12RwCmd *dm = (Oc12RwCmd *)cmd;
896918
uint32_t nlb = le16_to_cpu(dm->nlb) + 1;
897919
uint64_t *psl;
920+
uint32_t i;
898921

899922
/*
900923
* The list below holds max_sec_per_rq entries while nlb comes from the
@@ -909,9 +932,25 @@ static uint16_t oc12_erase_async(FemuCtrl *n, NvmeNamespace *ns, NvmeCmd *cmd,
909932

910933
oc12_read_ppa_list(n, dm, psl);
911934

935+
/*
936+
* Read and write check every address against the geometry; erase never
937+
* did, and the timing model turns the channel and lun fields into an
938+
* index into its per-chip array. A geometry whose axis counts are not
939+
* powers of two leaves those fields holding values the device does not
940+
* have, and the product of the two maxima can exceed the array.
941+
*/
942+
for (i = 0; i < nlb; i++) {
943+
if (!oc12_ppa_in_geometry(ln, psl[i])) {
944+
g_free(psl);
945+
return NVME_INVALID_FIELD | NVME_DNR;
946+
}
947+
}
948+
912949
oc12_meta_blk_set_erased(ns, ln, psl, nlb);
913950

951+
req->slba = (uint64_t)psl;
914952
oc12_advance_status(n, ns, cmd, req);
953+
req->slba = 0;
915954

916955
g_free(psl);
917956
return NVME_SUCCESS;

0 commit comments

Comments
 (0)