Skip to content

Commit 4204cb3

Browse files
calebsanderigaw
authored andcommitted
fabrics: check genctr after getting discovery entries
From the NVMe base spec (version 2.0c, section 5.16.1.23): If the host reads the Discovery Log Page using multiple Get Log Page commands the host should ensure that there has not been a change in the contents of the data. The host should read the Discovery Log Page contents in order (i.e., with increasing Log Page Offset values) and then re-read the Generation Counter after the entire log page is transferred. If the Generation Counter does not match the original value read, the host should discard the log page read as the entries may be inconsistent. nvme_get_log_page() will issue multiple Get Log Page commands to fetch the discovery log page if it exceeds 4 KB. Since GENCTR is at the start of the log page, this ordering is possible: - GENCTR is read by a Get Log Page command for the first 4 KB - The log page is modified, changing GENCTR - Other Get Log Page commands read the remainder of the log page So the check that GENCTR hasn't changed will incorrectly pass, despite the log page having been modified. This can lead to inconsistent, missing, or duplicate log page entries. Ensure a GENCTR update is not missed by fetching log page header again after all entries. Also use NVME_LOG_PAGE_PDU_SIZE to match other nvme_get_log_page() calls instead of hard-coding the 4 KB max transfer length. And ensure LPO is correctly reset if the log page is read again. Signed-off-by: Caleb Sander <[email protected]>
1 parent 0c9443b commit 4204cb3

1 file changed

Lines changed: 26 additions & 6 deletions

File tree

src/nvme/fabrics.c

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1036,9 +1036,10 @@ static struct nvmf_discovery_log *nvme_discovery_log(nvme_ctrl_t c,
10361036
nvme_msg(r, LOG_DEBUG, "%s: get header (try %d/%d)\n",
10371037
name, retries, max_retries);
10381038
args->rae = true;
1039+
args->lpo = 0;
10391040
args->len = size;
10401041
args->log = log;
1041-
ret = nvme_get_log_page(fd, 4096, args);
1042+
ret = nvme_get_log_page(fd, NVME_LOG_PAGE_PDU_SIZE, args);
10421043
if (ret) {
10431044
nvme_msg(r, LOG_INFO,
10441045
"%s: discover try %d/%d failed, error %d\n",
@@ -1065,15 +1066,33 @@ static struct nvmf_discovery_log *nvme_discovery_log(nvme_ctrl_t c,
10651066
}
10661067

10671068
nvme_msg(r, LOG_DEBUG,
1068-
"%s: get header and %" PRIu64
1069+
"%s: get %" PRIu64
10691070
" records (length %d genctr %" PRIu64 ")\n",
10701071
name, numrec, size, genctr);
10711072

1073+
args->rae = true;
1074+
args->lpo = sizeof(struct nvmf_discovery_log);
1075+
args->len = size - sizeof(struct nvmf_discovery_log);
1076+
args->log = log->entries;
1077+
ret = nvme_get_log_page(fd, NVME_LOG_PAGE_PDU_SIZE, args);
1078+
if (ret) {
1079+
nvme_msg(r, LOG_INFO,
1080+
"%s: discover try %d/%d failed, error %d\n",
1081+
name, retries, max_retries, errno);
1082+
goto out_free_log;
1083+
}
1084+
1085+
/*
1086+
* If the log page was read with multiple Get Log Page commands,
1087+
* genctr must be checked afterwards to ensure atomicity
1088+
*/
1089+
nvme_msg(r, LOG_DEBUG, "%s: get header again\n", name);
1090+
10721091
args->rae = false;
1073-
args->len = size;
1092+
args->lpo = 0;
1093+
args->len = sizeof(struct nvmf_discovery_log);
10741094
args->log = log;
1075-
ret = nvme_get_log_page(fd, 4096, args);
1076-
1095+
ret = nvme_get_log_page(fd, NVME_LOG_PAGE_PDU_SIZE, args);
10771096
if (ret) {
10781097
nvme_msg(r, LOG_INFO,
10791098
"%s: discover try %d/%d failed, error %d\n",
@@ -1088,7 +1107,8 @@ static struct nvmf_discovery_log *nvme_discovery_log(nvme_ctrl_t c,
10881107
errno = EAGAIN;
10891108
} else if (numrec != le64_to_cpu(log->numrec)) {
10901109
nvme_msg(r, LOG_INFO,
1091-
"%s: could only fetch %" PRIu64 " of %" PRIu64 " records\n",
1110+
"%s: numrec changed unexpectedly "
1111+
"from %" PRIu64 " to %" PRIu64 "\n",
10921112
name, numrec, le64_to_cpu(log->numrec));
10931113
errno = EBADSLT;
10941114
} else {

0 commit comments

Comments
 (0)