Skip to content

Commit b4d2385

Browse files
committed
cmds: move nvme_get_log and related code to cmds.c
Group all generic command implementations into a single file. Signed-off-by: Daniel Wagner <[email protected]>
1 parent 6c3c6db commit b4d2385

2 files changed

Lines changed: 213 additions & 214 deletions

File tree

libnvme/src/nvme/cmds.c

Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,219 @@
1515
#include "cleanup.h"
1616
#include "private.h"
1717

18+
static bool force_4k;
19+
20+
__attribute__((constructor))
21+
static void nvme_init_env(void)
22+
{
23+
char *val;
24+
25+
val = getenv("LIBNVME_FORCE_4K");
26+
if (!val)
27+
return;
28+
if (!strcmp(val, "1") ||
29+
!strcasecmp(val, "true") ||
30+
!strncasecmp(val, "enable", 6))
31+
force_4k = true;
32+
}
33+
34+
int nvme_get_log(struct nvme_transport_handle *hdl,
35+
struct nvme_passthru_cmd *cmd, bool rae,
36+
__u32 xfer_len)
37+
{
38+
__u64 offset = 0, xfer, data_len = cmd->data_len;
39+
__u64 start = (__u64)cmd->cdw13 << 32 | cmd->cdw12;
40+
__u64 lpo;
41+
void *ptr = (void *)(uintptr_t)cmd->addr;
42+
int ret;
43+
bool _rae;
44+
__u32 numd;
45+
__u16 numdu, numdl;
46+
__u32 cdw10 = cmd->cdw10 & (NVME_VAL(LOG_CDW10_LID) |
47+
NVME_VAL(LOG_CDW10_LSP));
48+
__u32 cdw11 = cmd->cdw11 & NVME_VAL(LOG_CDW11_LSI);
49+
50+
if (force_4k)
51+
xfer_len = NVME_LOG_PAGE_PDU_SIZE;
52+
53+
/*
54+
* 4k is the smallest possible transfer unit, so restricting to 4k
55+
* avoids having to check the MDTS value of the controller.
56+
*/
57+
do {
58+
if (!force_4k) {
59+
xfer = data_len - offset;
60+
if (xfer > xfer_len)
61+
xfer = xfer_len;
62+
} else {
63+
xfer = NVME_LOG_PAGE_PDU_SIZE;
64+
}
65+
66+
/*
67+
* Always retain regardless of the RAE parameter until the very
68+
* last portion of this log page so the data remains latched
69+
* during the fetch sequence.
70+
*/
71+
lpo = start + offset;
72+
numd = (xfer >> 2) - 1;
73+
numdu = numd >> 16;
74+
numdl = numd & 0xffff;
75+
_rae = offset + xfer < data_len || rae;
76+
77+
cmd->cdw10 = cdw10 |
78+
NVME_SET(!!_rae, LOG_CDW10_RAE) |
79+
NVME_SET(numdl, LOG_CDW10_NUMDL);
80+
cmd->cdw11 = cdw11 |
81+
NVME_SET(numdu, LOG_CDW11_NUMDU);
82+
cmd->cdw12 = lpo & 0xffffffff;
83+
cmd->cdw13 = lpo >> 32;
84+
cmd->data_len = xfer;
85+
cmd->addr = (__u64)(uintptr_t)ptr;
86+
87+
if (hdl->uring_enabled)
88+
ret = nvme_submit_admin_passthru_async(hdl, cmd);
89+
else
90+
ret = nvme_submit_admin_passthru(hdl, cmd);
91+
if (ret)
92+
return ret;
93+
94+
offset += xfer;
95+
ptr += xfer;
96+
} while (offset < data_len);
97+
98+
if (hdl->uring_enabled) {
99+
ret = nvme_wait_complete_passthru(hdl);
100+
if (ret)
101+
return ret;
102+
}
103+
104+
return 0;
105+
}
106+
107+
static int read_ana_chunk(struct nvme_transport_handle *hdl, enum nvme_log_ana_lsp lsp, bool rae,
108+
__u8 *log, __u8 **read, __u8 *to_read, __u8 *log_end)
109+
{
110+
struct nvme_passthru_cmd cmd;
111+
112+
if (to_read > log_end)
113+
return -ENOSPC;
114+
115+
while (*read < to_read) {
116+
__u32 len = min_t(__u32, log_end - *read, NVME_LOG_PAGE_PDU_SIZE);
117+
int ret;
118+
119+
nvme_init_get_log_ana(&cmd, lsp, *read - log, *read, len);
120+
ret = nvme_get_log(hdl, &cmd, rae, NVME_LOG_PAGE_PDU_SIZE);
121+
if (ret)
122+
return ret;
123+
124+
*read += len;
125+
}
126+
return 0;
127+
}
128+
129+
static int try_read_ana(struct nvme_transport_handle *hdl, enum nvme_log_ana_lsp lsp, bool rae,
130+
struct nvme_ana_log *log, __u8 *log_end,
131+
__u8 *read, __u8 **to_read, bool *may_retry)
132+
{
133+
__u16 ngrps = le16_to_cpu(log->ngrps);
134+
135+
while (ngrps--) {
136+
__u8 *group = *to_read;
137+
int ret;
138+
__le32 nnsids;
139+
140+
*to_read += sizeof(*log->descs);
141+
ret = read_ana_chunk(hdl, lsp, rae,
142+
(__u8 *)log, &read, *to_read, log_end);
143+
if (ret) {
144+
/*
145+
* If the provided buffer isn't long enough,
146+
* the log page may have changed while reading it
147+
* and the computed length was inaccurate.
148+
* Have the caller check chgcnt and retry.
149+
*/
150+
*may_retry = ret == -ENOSPC;
151+
return ret;
152+
}
153+
154+
/*
155+
* struct nvme_ana_group_desc has 8-byte alignment
156+
* but the group pointer is only 4-byte aligned.
157+
* Don't dereference the misaligned pointer.
158+
*/
159+
memcpy(&nnsids,
160+
group + offsetof(struct nvme_ana_group_desc, nnsids),
161+
sizeof(nnsids));
162+
*to_read += le32_to_cpu(nnsids) * sizeof(__le32);
163+
ret = read_ana_chunk(hdl, lsp, rae,
164+
(__u8 *)log, &read, *to_read, log_end);
165+
if (ret) {
166+
*may_retry = ret == -ENOSPC;
167+
return ret;
168+
}
169+
}
170+
171+
*may_retry = true;
172+
return 0;
173+
}
174+
175+
int nvme_get_ana_log_atomic(struct nvme_transport_handle *hdl, bool rae, bool rgo,
176+
struct nvme_ana_log *log, __u32 *len,
177+
unsigned int retries)
178+
{
179+
const enum nvme_log_ana_lsp lsp =
180+
rgo ? NVME_LOG_ANA_LSP_RGO_GROUPS_ONLY : 0;
181+
/* Get Log Page can only fetch multiples of dwords */
182+
__u8 * const log_end = (__u8 *)log + (*len & -4);
183+
__u8 *read = (__u8 *)log;
184+
__u8 *to_read;
185+
int ret;
186+
187+
if (!retries)
188+
return -EINVAL;
189+
190+
to_read = (__u8 *)log->descs;
191+
ret = read_ana_chunk(hdl, lsp, rae,
192+
(__u8 *)log, &read, to_read, log_end);
193+
if (ret)
194+
return ret;
195+
196+
do {
197+
bool may_retry = false;
198+
int saved_ret;
199+
int saved_errno;
200+
__le64 chgcnt;
201+
202+
saved_ret = try_read_ana(hdl, lsp, rae, log, log_end,
203+
read, &to_read, &may_retry);
204+
/*
205+
* If the log page was read with multiple Get Log Page commands,
206+
* chgcnt must be checked afterwards to ensure atomicity
207+
*/
208+
*len = to_read - (__u8 *)log;
209+
if (*len <= NVME_LOG_PAGE_PDU_SIZE || !may_retry)
210+
return saved_ret;
211+
212+
saved_errno = errno;
213+
chgcnt = log->chgcnt;
214+
read = (__u8 *)log;
215+
to_read = (__u8 *)log->descs;
216+
ret = read_ana_chunk(hdl, lsp, rae,
217+
(__u8 *)log, &read, to_read, log_end);
218+
if (ret)
219+
return ret;
220+
221+
if (log->chgcnt == chgcnt) {
222+
/* Log hasn't changed; return try_read_ana() result */
223+
errno = saved_errno;
224+
return saved_ret;
225+
}
226+
} while (--retries);
227+
228+
return -EAGAIN;
229+
}
230+
18231
int nvme_set_etdas(struct nvme_transport_handle *hdl, bool *changed)
19232
{
20233
struct nvme_feat_host_behavior da4;

0 commit comments

Comments
 (0)