Bug Summary

File:.build-ci/../libnvme/src/nvme/nvme-cmds.c
Warning:line 610, column 10
Potential leak of memory pointed to by 'cmd.addr'

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-redhat-linux-gnu -O3 -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name nvme-cmds.c -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=none -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/__w/nvme-cli/nvme-cli/.build-ci -fcoverage-compilation-dir=/__w/nvme-cli/nvme-cli/.build-ci -resource-dir /usr/bin/../lib/clang/22 -include /__w/nvme-cli/nvme-cli/.build-ci/nvme-config.h -I libnvme/src/libnvme3.so.1.0.0.p -I libnvme/src -I ../libnvme/src -I ccan -I ../ccan -I . -I .. -D _FILE_OFFSET_BITS=64 -D _GNU_SOURCE -U NDEBUG -internal-isystem /usr/bin/../lib/clang/22/include -internal-isystem /usr/local/include -internal-isystem /usr/bin/../lib/gcc/x86_64-redhat-linux/16/../../../../x86_64-redhat-linux/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -std=gnu11 -ferror-limit 19 -fvisibility=hidden -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -fcolor-diagnostics -vectorize-loops -vectorize-slp -analyzer-opt-analyze-headers -analyzer-output=html -faddrsig -fdwarf2-cfi-asm -o /__w/nvme-cli/nvme-cli/.build-ci/scan-results/2026-10-24-125112-639-1 -x c ../libnvme/src/nvme/nvme-cmds.c
1// SPDX-License-Identifier: LGPL-2.1-or-later
2/*
3 * This file is part of libnvme.
4 * Copyright (c) 2020 Western Digital Corporation or its affiliates.
5 *
6 * Authors: Keith Busch <keith.busch@wdc.com>
7 * Chaitanya Kulkarni <chaitanya.kulkarni@wdc.com>
8 */
9
10#include <ccan/endian/endian.h>
11#include <ccan/minmax/minmax.h>
12
13#include <shared/compiler-attributes-util.h>
14
15#include <libnvme.h>
16
17#include "cleanup.h"
18#include "private.h"
19
20static int submit_get_log_cmd(struct libnvme_transport_handle *hdl,
21 struct libnvme_passthru_cmd *cmd)
22{
23 int err;
24
25 if (hdl->type != LIBNVME_TRANSPORT_HANDLE_TYPE_DIRECT)
26 goto no_uring;
27
28 if (hdl->uring_state == LIBNVME_IO_URING_STATE_NOT_AVAILABLE)
29 goto no_uring;
30
31 err = libnvme_submit_admin_passthru(hdl, cmd, NULL((void*)0));
32 if (err && err == -ENOTSUP95)
33 goto no_uring;
34
35 return err;
36
37no_uring:
38 return libnvme_exec_admin_passthru(hdl, cmd);
39}
40
41static int wait_get_log_cmd(struct libnvme_transport_handle *hdl)
42{
43 if (hdl->type != LIBNVME_TRANSPORT_HANDLE_TYPE_DIRECT)
44 return 0;
45
46 if (hdl->uring_state == LIBNVME_IO_URING_STATE_NOT_AVAILABLE)
47 return 0;
48
49 return libnvme_wait_passthru(hdl);
50}
51
52__shr_public__attribute__((visibility("default"))) int libnvme_get_log(struct libnvme_transport_handle *hdl,
53 struct libnvme_passthru_cmd *cmd, bool_Bool rae,
54 __u32 xfer_len)
55{
56 __u64 offset = 0, xfer, data_len, start, lpo;
57 __u32 numd, cdw10, cdw11;
58 __u16 numdu, numdl;
59 bool_Bool _rae;
60 void *ptr;
61 int ret;
62
63 if (!hdl)
64 return -ENODEV19;
65 if (!cmd || xfer_len < 4)
66 return -EINVAL22;
67
68 data_len = cmd->data_len;
69 start = (__u64)cmd->cdw13 << 32 | cmd->cdw12;
70 ptr = (void *)(uintptr_t)cmd->addr;
71 cdw10 = cmd->cdw10 & (NVME_VAL(LOG_CDW10_LID)((__u32)NVME_LOG_CDW10_LID_MASK << NVME_LOG_CDW10_LID_SHIFT
)
|
72 NVME_VAL(LOG_CDW10_LSP)((__u32)NVME_LOG_CDW10_LSP_MASK << NVME_LOG_CDW10_LSP_SHIFT
)
);
73 cdw11 = cmd->cdw11 & NVME_VAL(LOG_CDW11_LSI)((__u32)NVME_LOG_CDW11_LSI_MASK << NVME_LOG_CDW11_LSI_SHIFT
)
;
74
75 if (hdl->ctx->force_4k)
76 xfer_len = NVME_LOG_PAGE_PDU_SIZE4096;
77
78 /*
79 * 4k is the smallest possible transfer unit, so restricting to 4k
80 * avoids having to check the MDTS value of the controller.
81 */
82 do {
83 if (!hdl->ctx->force_4k) {
84 xfer = data_len - offset;
85 if (xfer > xfer_len)
86 xfer = xfer_len;
87 } else {
88 xfer = NVME_LOG_PAGE_PDU_SIZE4096;
89 }
90
91 /*
92 * Always retain regardless of the RAE parameter until the very
93 * last portion of this log page so the data remains latched
94 * during the fetch sequence.
95 */
96 lpo = start + offset;
97 numd = (xfer >> 2) - 1;
98 numdu = numd >> 16;
99 numdl = numd & 0xffff;
100 _rae = offset + xfer < data_len || rae;
101
102 cmd->cdw10 = cdw10 |
103 NVME_SET(!!_rae, LOG_CDW10_RAE)(((__u32)(!!_rae) & NVME_LOG_CDW10_RAE_MASK) << NVME_LOG_CDW10_RAE_SHIFT
)
|
104 NVME_SET(numdl, LOG_CDW10_NUMDL)(((__u32)(numdl) & NVME_LOG_CDW10_NUMDL_MASK) << NVME_LOG_CDW10_NUMDL_SHIFT
)
;
105 cmd->cdw11 = cdw11 |
106 NVME_SET(numdu, LOG_CDW11_NUMDU)(((__u32)(numdu) & NVME_LOG_CDW11_NUMDU_MASK) << NVME_LOG_CDW11_NUMDU_SHIFT
)
;
107 cmd->cdw12 = lpo & 0xffffffff;
108 cmd->cdw13 = lpo >> 32;
109 cmd->data_len = xfer;
110 cmd->addr = (__u64)(uintptr_t)ptr;
111
112 ret = submit_get_log_cmd(hdl, cmd);
113 if (ret)
114 return ret;
115
116 offset += xfer;
117 ptr += xfer;
118 } while (offset < data_len);
119
120 return wait_get_log_cmd(hdl);
121}
122
123__shr_public__attribute__((visibility("default"))) int libnvme_get_log_dynamic_chunk(
124 struct libnvme_transport_handle *hdl,
125 struct libnvme_passthru_cmd *cmd, bool_Bool rae,
126 __u32 xfer_len)
127{
128 __u64 offset = 0, xfer, data_len, start, lpo;
129 __u32 numd, cdw10, cdw11;
130 __u16 numdu, numdl;
131 bool_Bool _rae;
132 void *ptr;
133 int ret;
134
135 if (!hdl)
136 return -ENODEV19;
137 if (!cmd || xfer_len < 4)
138 return -EINVAL22;
139
140 data_len = cmd->data_len;
141 start = (__u64)cmd->cdw13 << 32 | cmd->cdw12;
142 ptr = (void *)(uintptr_t)cmd->addr;
143 cdw10 = cmd->cdw10 & (NVME_VAL(LOG_CDW10_LID)((__u32)NVME_LOG_CDW10_LID_MASK << NVME_LOG_CDW10_LID_SHIFT
)
|
144 NVME_VAL(LOG_CDW10_LSP)((__u32)NVME_LOG_CDW10_LSP_MASK << NVME_LOG_CDW10_LSP_SHIFT
)
);
145 cdw11 = cmd->cdw11 & NVME_VAL(LOG_CDW11_LSI)((__u32)NVME_LOG_CDW11_LSI_MASK << NVME_LOG_CDW11_LSI_SHIFT
)
;
146
147 if (hdl->ctx->force_4k)
148 xfer_len = NVME_LOG_PAGE_PDU_SIZE4096;
149
150 do {
151 xfer = data_len - offset;
152 if (xfer > xfer_len)
153 xfer = xfer_len;
154
155 /*
156 * Always retain regardless of the RAE parameter until the very
157 * last portion of this log page so the data remains latched
158 * during the fetch sequence.
159 */
160 lpo = start + offset;
161 numd = (xfer >> 2) - 1;
162 numdu = numd >> 16;
163 numdl = numd & 0xffff;
164 _rae = offset + xfer < data_len || rae;
165
166 cmd->cdw10 = cdw10 |
167 NVME_SET(!!_rae, LOG_CDW10_RAE)(((__u32)(!!_rae) & NVME_LOG_CDW10_RAE_MASK) << NVME_LOG_CDW10_RAE_SHIFT
)
|
168 NVME_SET(numdl, LOG_CDW10_NUMDL)(((__u32)(numdl) & NVME_LOG_CDW10_NUMDL_MASK) << NVME_LOG_CDW10_NUMDL_SHIFT
)
;
169 cmd->cdw11 = cdw11 |
170 NVME_SET(numdu, LOG_CDW11_NUMDU)(((__u32)(numdu) & NVME_LOG_CDW11_NUMDU_MASK) << NVME_LOG_CDW11_NUMDU_SHIFT
)
;
171 cmd->cdw12 = lpo & 0xffffffff;
172 cmd->cdw13 = lpo >> 32;
173 cmd->data_len = xfer;
174 cmd->addr = (__u64)(uintptr_t)ptr;
175
176 ret = submit_get_log_cmd(hdl, cmd);
177 if (!ret)
178 ret = wait_get_log_cmd(hdl);
179 /*
180 * Retry with a smaller chunk on OS errors (negative errno,
181 * e.g. the kernel rejecting an oversized transfer) and on
182 * NVMe command errors (positive status) with Generic (SCT=0)
183 * or Command Specific (SCT=1) status types. Path errors
184 * (SCT=3) and media errors (SCT=2) are not recoverable by
185 * reducing the transfer size.
186 */
187 if (ret < 0 ||
188 (ret > 0 &&
189 (nvme_status_code_type(ret) <= NVME_SCT_CMD_SPECIFIC))) {
190 xfer_len = (xfer_len / 2) &
191 ~(__u32)(NVME_LOG_PAGE_PDU_SIZE4096 - 1);
192 if (xfer_len < NVME_LOG_PAGE_PDU_SIZE4096)
193 return ret;
194 continue;
195 }
196 if (ret)
197 return ret;
198
199 offset += xfer;
200 ptr += xfer;
201 } while (offset < data_len);
202
203 return 0;
204}
205
206static int read_ana_chunk(struct libnvme_transport_handle *hdl,
207 enum nvme_log_ana_lsp lsp, bool_Bool rae,
208 __u8 *log, __u8 **read, __u8 *to_read, __u8 *log_end)
209{
210 struct libnvme_passthru_cmd cmd;
211
212 if (to_read > log_end)
213 return -ENOSPC28;
214
215 while (*read < to_read) {
216 __u32 len = min_t(__u32, log_end - *read,({ __u32 _ta = (log_end - *read); __u32 _tb = (4096); ({ typeof
(_ta) _a = (_ta); typeof(_tb) _b = (_tb); do { } while (0); _a
< _b ? _a : _b; }); })
217 NVME_LOG_PAGE_PDU_SIZE)({ __u32 _ta = (log_end - *read); __u32 _tb = (4096); ({ typeof
(_ta) _a = (_ta); typeof(_tb) _b = (_tb); do { } while (0); _a
< _b ? _a : _b; }); })
;
218 int ret;
219
220 nvme_init_get_log_ana(&cmd, lsp, *read - log, *read, len);
221 ret = libnvme_get_log(hdl, &cmd, rae, NVME_LOG_PAGE_PDU_SIZE4096);
222 if (ret)
223 return ret;
224
225 *read += len;
226 }
227 return 0;
228}
229
230static int try_read_ana(struct libnvme_transport_handle *hdl,
231 enum nvme_log_ana_lsp lsp, bool_Bool rae,
232 struct nvme_ana_log *log, __u8 *log_end,
233 __u8 *read, __u8 **to_read, bool_Bool *may_retry)
234{
235 __u16 ngrps = le16_to_cpu(log->ngrps);
236
237 while (ngrps--) {
238 __u8 *group = *to_read;
239 int ret;
240 __le32 nnsids;
241
242 *to_read += sizeof(*log->descs);
243 ret = read_ana_chunk(hdl, lsp, rae,
244 (__u8 *)log, &read, *to_read, log_end);
245 if (ret) {
246 /*
247 * If the provided buffer isn't long enough,
248 * the log page may have changed while reading it
249 * and the computed length was inaccurate.
250 * Have the caller check chgcnt and retry.
251 */
252 *may_retry = ret == -ENOSPC28;
253 return ret;
254 }
255
256 /*
257 * struct nvme_ana_group_desc has 8-byte alignment
258 * but the group pointer is only 4-byte aligned.
259 * Don't dereference the misaligned pointer.
260 */
261 memcpy(&nnsids,
262 group + offsetof(struct nvme_ana_group_desc, nnsids)__builtin_offsetof(struct nvme_ana_group_desc, nnsids),
263 sizeof(nnsids));
264 /*
265 * The NSIDs must fit the buffer. Check it before moving
266 * to_read: with a 32 bit size_t, NNSIDS * 4 wraps around.
267 */
268 if (le32_to_cpu(nnsids) >
269 (size_t)(log_end - *to_read) / sizeof(__le32)) {
270 *may_retry = true1;
271 return -ENOSPC28;
272 }
273 *to_read += le32_to_cpu(nnsids) * sizeof(__le32);
274 ret = read_ana_chunk(hdl, lsp, rae,
275 (__u8 *)log, &read, *to_read, log_end);
276 if (ret) {
277 *may_retry = ret == -ENOSPC28;
278 return ret;
279 }
280 }
281
282 *may_retry = true1;
283 return 0;
284}
285
286__shr_public__attribute__((visibility("default"))) int libnvme_get_ana_log_atomic(
287 struct libnvme_transport_handle *hdl, bool_Bool rae, bool_Bool rgo,
288 struct nvme_ana_log *log, __u32 *len, unsigned int retries)
289{
290 const enum nvme_log_ana_lsp lsp =
291 rgo ? NVME_LOG_ANA_LSP_RGO_GROUPS_ONLY : 0;
292 /* Get Log Page can only fetch multiples of dwords */
293 __u8 * const log_end = (__u8 *)log + (*len & -4);
294 __u8 *read = (__u8 *)log;
295 __u8 *to_read;
296 int ret;
297
298 if (!retries)
299 return -EINVAL22;
300
301 to_read = (__u8 *)log->descs;
302 ret = read_ana_chunk(hdl, lsp, rae,
303 (__u8 *)log, &read, to_read, log_end);
304 if (ret)
305 return ret;
306
307 do {
308 bool_Bool may_retry = false0;
309 int saved_ret;
310 int saved_errno;
311 __le64 chgcnt;
312
313 saved_ret = try_read_ana(hdl, lsp, rae, log, log_end,
314 read, &to_read, &may_retry);
315 /*
316 * If the log page was read with multiple Get Log Page commands,
317 * chgcnt must be checked afterwards to ensure atomicity
318 */
319 *len = to_read - (__u8 *)log;
320 if (*len <= NVME_LOG_PAGE_PDU_SIZE4096 || !may_retry)
321 return saved_ret;
322
323 saved_errno = errno(*__errno_location ());
324 chgcnt = log->chgcnt;
325 read = (__u8 *)log;
326 to_read = (__u8 *)log->descs;
327 ret = read_ana_chunk(hdl, lsp, rae,
328 (__u8 *)log, &read, to_read, log_end);
329 if (ret)
330 return ret;
331
332 if (log->chgcnt == chgcnt) {
333 /* Log hasn't changed; return try_read_ana() result */
334 errno(*__errno_location ()) = saved_errno;
335 return saved_ret;
336 }
337 } while (--retries);
338
339 return -EAGAIN11;
340}
341
342__shr_public__attribute__((visibility("default"))) int libnvme_set_etdas(
343 struct libnvme_transport_handle *hdl, bool_Bool *changed)
344{
345 struct nvme_feat_host_behavior da4;
346 struct libnvme_passthru_cmd cmd;
347 int err;
348
349 nvme_init_get_features_host_behavior(&cmd, 0, &da4);
350 err = libnvme_exec_admin_passthru(hdl, &cmd);
351 if (err)
352 return err;
353
354 if (da4.etdas) {
355 *changed = false0;
356 return 0;
357 }
358
359 da4.etdas = 1;
360
361 nvme_init_set_features_host_behavior(&cmd, false0, &da4);
362 err = libnvme_exec_admin_passthru(hdl, &cmd);
363 if (err)
364 return err;
365
366 *changed = true1;
367 return 0;
368}
369
370__shr_public__attribute__((visibility("default"))) int libnvme_clear_etdas(
371 struct libnvme_transport_handle *hdl, bool_Bool *changed)
372{
373 struct nvme_feat_host_behavior da4;
374 struct libnvme_passthru_cmd cmd;
375 int err;
376
377 nvme_init_get_features_host_behavior(&cmd, 0, &da4);
378 err = libnvme_exec_admin_passthru(hdl, &cmd);
379 if (err)
380 return err;
381
382 if (!da4.etdas) {
383 *changed = false0;
384 return 0;
385 }
386
387 da4.etdas = 0;
388 nvme_init_set_features_host_behavior(&cmd, false0, &da4);
389 err = libnvme_exec_admin_passthru(hdl, &cmd);
390 if (err)
391 return err;
392
393 *changed = true1;
394 return 0;
395}
396
397__shr_public__attribute__((visibility("default"))) int libnvme_get_uuid_list(struct libnvme_transport_handle *hdl,
398 struct nvme_id_uuid_list *uuid_list)
399{
400 struct libnvme_passthru_cmd cmd;
401 struct nvme_id_ctrl ctrl;
402 int err;
403
404 if (!hdl)
405 return -ENODEV19;
406 if (!uuid_list)
407 return -EINVAL22;
408
409 memset(&ctrl, 0, sizeof(struct nvme_id_ctrl));
410 nvme_init_identify_ctrl(&cmd, &ctrl);
411 err = libnvme_exec_admin_passthru(hdl, &cmd);
412 if (err) {
413 libnvme_msg(hdl->ctx, LIBNVME_LOG_ERR,__libnvme_msg(hdl->ctx, LIBNVME_LOG_ERR, ((void*)0), "ERROR: nvme_identify_ctrl() failed 0x%x\n"
, err)
414 "ERROR: nvme_identify_ctrl() failed 0x%x\n", err)__libnvme_msg(hdl->ctx, LIBNVME_LOG_ERR, ((void*)0), "ERROR: nvme_identify_ctrl() failed 0x%x\n"
, err)
;
415 return err;
416 }
417
418 if (le32_to_cpu(ctrl.ctratt) & NVME_CTRL_CTRATT_UUID_LIST) {
419 nvme_init_identify_uuid_list(&cmd, uuid_list);
420 err = libnvme_exec_admin_passthru(hdl, &cmd);
421 }
422
423 return err;
424}
425
426__shr_public__attribute__((visibility("default"))) int libnvme_get_telemetry_max(
427 struct libnvme_transport_handle *hdl,
428 enum nvme_telemetry_da *da, size_t *data_tx)
429{
430 __cleanup_libnvme_free__attribute__((cleanup(libnvme_freep))) struct nvme_id_ctrl *id_ctrl = NULL((void*)0);
431 struct libnvme_passthru_cmd cmd;
432 int err;
433
434 id_ctrl = libnvme_alloc(sizeof(*id_ctrl));
435 if (!id_ctrl)
436 return -ENOMEM12;
437
438 nvme_init_identify_ctrl(&cmd, id_ctrl);
439 err = libnvme_exec_admin_passthru(hdl, &cmd);
440 if (err)
441 return err;
442
443 if (data_tx) {
444 *data_tx = id_ctrl->mdts;
445 if (id_ctrl->mdts) {
446 /*
447 * assuming CAP.MPSMIN is zero minimum Memory Page Size
448 * is at least 4096 bytes
449 */
450 *data_tx = (1 << id_ctrl->mdts) * 4096;
451 }
452 }
453 if (da) {
454 if (id_ctrl->lpa & 0x8)
455 *da = NVME_TELEMETRY_DA_3;
456 if (id_ctrl->lpa & 0x40)
457 *da = NVME_TELEMETRY_DA_4;
458
459 }
460 return err;
461}
462
463__shr_public__attribute__((visibility("default"))) int libnvme_get_telemetry_log(
464 struct libnvme_transport_handle *hdl, bool_Bool create, bool_Bool ctrl,
465 bool_Bool rae, size_t max_data_tx, enum nvme_telemetry_da da,
466 struct nvme_telemetry_log **buf, size_t *size)
467{
468 static const __u32 xfer = NVME_LOG_TELEM_BLOCK_SIZE;
469 struct nvme_telemetry_log *telem;
470 struct libnvme_passthru_cmd cmd;
471 __cleanup_libnvme_free__attribute__((cleanup(libnvme_freep))) void *log = NULL((void*)0);
472 void *tmp;
473 int err;
474 size_t dalb;
475
476 *size = 0;
477
478 log = libnvme_alloc(xfer);
479 if (!log)
480 return -ENOMEM12;
481
482 if (ctrl) {
483 nvme_init_get_log_telemetry_ctrl(&cmd, 0, log, xfer);
484 err = libnvme_get_log(hdl, &cmd, true1, xfer);
485 } else {
486 if (create) {
487 nvme_init_get_log_create_telemetry_host_mcda(&cmd,
488 da, log);
489 err = libnvme_get_log(hdl, &cmd, false0, xfer);
490 } else {
491 nvme_init_get_log_telemetry_host(&cmd, 0, log, xfer);
492 err = libnvme_get_log(hdl, &cmd, false0, xfer);
493 }
494 }
495
496 if (err)
497 return err;
498
499 telem = log;
500 if (ctrl && !telem->ctrlavail) {
501 *buf = log;
502 log = NULL((void*)0);
503 *size = xfer;
504 return 0;
505 }
506
507 switch (da) {
508 case NVME_TELEMETRY_DA_1:
509 dalb = le16_to_cpu(telem->dalb1);
510 break;
511 case NVME_TELEMETRY_DA_2:
512 dalb = le16_to_cpu(telem->dalb2);
513 break;
514 case NVME_TELEMETRY_DA_3:
515 /* dalb3 >= dalb2 >= dalb1 */
516 dalb = le16_to_cpu(telem->dalb3);
517 break;
518 case NVME_TELEMETRY_DA_4:
519 dalb = le32_to_cpu(telem->dalb4);
520 break;
521 default:
522 return -EINVAL22;
523 }
524
525 if (dalb == 0)
526 return -ENOENT2;
527
528 *size = (dalb + 1) * xfer;
529 tmp = libnvme_realloc(log, *size);
530 if (!tmp)
531 return -ENOMEM12;
532 log = tmp;
533
534 if (ctrl)
535 nvme_init_get_log_telemetry_ctrl(&cmd, 0, log, *size);
536 else
537 nvme_init_get_log_telemetry_host(&cmd, 0, log, *size);
538 err = libnvme_get_log(hdl, &cmd, rae, max_data_tx);
539 if (err)
540 return err;
541
542 *buf = log;
543 log = NULL((void*)0);
544 return 0;
545}
546
547static int nvme_check_get_telemetry_log(struct libnvme_transport_handle *hdl,
548 bool_Bool create, bool_Bool ctrl, bool_Bool rae,
549 struct nvme_telemetry_log **log, enum nvme_telemetry_da da,
550 size_t *size)
551{
552 enum nvme_telemetry_da max_da = 0;
553 int err;
554
555 err = libnvme_get_telemetry_max(hdl, &max_da, NULL((void*)0));
556 if (err)
557 return err;
558
559 if (da > max_da)
560 return -ENOENT2;
561
562 return libnvme_get_telemetry_log(hdl, create, ctrl, rae, 4096, da,
563 log, size);
564}
565
566
567__shr_public__attribute__((visibility("default"))) int libnvme_get_ctrl_telemetry(
568 struct libnvme_transport_handle *hdl, bool_Bool rae,
569 struct nvme_telemetry_log **log, enum nvme_telemetry_da da,
570 size_t *size)
571{
572 return nvme_check_get_telemetry_log(hdl, false0, true1, rae, log,
573 da, size);
574}
575
576__shr_public__attribute__((visibility("default"))) int libnvme_get_host_telemetry(
577 struct libnvme_transport_handle *hdl,
578 struct nvme_telemetry_log **log, enum nvme_telemetry_da da,
579 size_t *size)
580{
581 return nvme_check_get_telemetry_log(hdl, false0, false0, false0, log,
582 da, size);
583}
584
585__shr_public__attribute__((visibility("default"))) int libnvme_get_new_host_telemetry(
586 struct libnvme_transport_handle *hdl,
587 struct nvme_telemetry_log **log, enum nvme_telemetry_da da,
588 size_t *size)
589{
590 return nvme_check_get_telemetry_log(hdl, true1, false0, false0, log,
591 da, size);
592}
593
594int libnvme_get_lba_status_log(struct libnvme_transport_handle *hdl, bool_Bool rae,
595 struct nvme_lba_status_log **log)
596{
597 __cleanup_free__attribute__((cleanup(shr_freep))) struct nvme_lba_status_log *buf = NULL((void*)0);
598 struct libnvme_passthru_cmd cmd;
599 __u32 size;
600 void *tmp;
601 int err;
602
603 buf = malloc(sizeof(*buf));
1
Memory is allocated
604 if (!buf)
2
Assuming 'buf' is non-null
3
Taking false branch
605 return -ENOMEM12;
606
607 nvme_init_get_log_lba_status(&cmd, 0, buf, sizeof(*buf));
608 err = libnvme_get_log(hdl, &cmd, true1, sizeof(*buf));
609 if (err
3.1
'err' is -19
) {
4
Taking true branch
610 *log = NULL((void*)0);
5
Potential leak of memory pointed to by 'cmd.addr'
611 return err;
612 }
613
614 size = le32_to_cpu(buf->lslplen);
615 if (!size) {
616 *log = buf;
617 buf = NULL((void*)0);
618 return 0;
619 }
620
621 tmp = realloc(buf, size);
622 if (!tmp) {
623 *log = NULL((void*)0);
624 return -ENOMEM12;
625 }
626 buf = tmp;
627
628 nvme_init_get_log_lba_status(&cmd, 0, buf, size);
629 err = libnvme_get_log(hdl, &cmd, rae, NVME_LOG_PAGE_PDU_SIZE4096);
630 if (err) {
631 *log = NULL((void*)0);
632 return err;
633 }
634
635 *log = buf;
636 buf = NULL((void*)0);
637 return 0;
638}
639
640__shr_public__attribute__((visibility("default"))) size_t libnvme_get_ana_log_len_from_id_ctrl(
641 const struct nvme_id_ctrl *id_ctrl, bool_Bool rgo)
642{
643 __u32 nanagrpid = le32_to_cpu(id_ctrl->nanagrpid);
644 size_t size = sizeof(struct nvme_ana_log) +
645 nanagrpid * sizeof(struct nvme_ana_group_desc);
646
647 return rgo ? size : size + le32_to_cpu(id_ctrl->mnan) * sizeof(__le32);
648}
649
650__shr_public__attribute__((visibility("default"))) int libnvme_get_ana_log_len(
651 struct libnvme_transport_handle *hdl, size_t *analen)
652{
653 __cleanup_libnvme_free__attribute__((cleanup(libnvme_freep))) struct nvme_id_ctrl *ctrl = NULL((void*)0);
654 struct libnvme_passthru_cmd cmd;
655 int ret;
656
657 ctrl = libnvme_alloc(sizeof(*ctrl));
658 if (!ctrl)
659 return -ENOMEM12;
660
661 nvme_init_identify_ctrl(&cmd, ctrl);
662 ret = libnvme_exec_admin_passthru(hdl, &cmd);
663 if (ret)
664 return ret;
665
666 *analen = libnvme_get_ana_log_len_from_id_ctrl(ctrl, false0);
667 return 0;
668}
669
670__shr_public__attribute__((visibility("default"))) int libnvme_get_logical_block_size(
671 struct libnvme_transport_handle *hdl, __u32 nsid, int *blksize)
672{
673 __cleanup_libnvme_free__attribute__((cleanup(libnvme_freep))) struct nvme_id_ns *ns = NULL((void*)0);
674 struct libnvme_passthru_cmd cmd;
675 __u8 flbas;
676 int ret;
677
678 ns = libnvme_alloc(sizeof(*ns));
679 if (!ns)
680 return -ENOMEM12;
681
682 nvme_init_identify_ns(&cmd, nsid, ns);
683 ret = libnvme_exec_admin_passthru(hdl, &cmd);
684 if (ret)
685 return ret;
686
687 nvme_id_ns_flbas_to_lbaf_inuse(ns->flbas, &flbas);
688 /* an LBA data size the device reports, which has to fit an int */
689 if (ns->lbaf[flbas].ds >= 31)
690 return -EINVAL22;
691 *blksize = 1 << ns->lbaf[flbas].ds;
692
693 return 0;
694}
695
696__shr_public__attribute__((visibility("default"))) int libnvme_get_feature_length(
697 int fid, __u32 cdw11, enum nvme_data_tfr dir, __u32 *len)
698{
699 switch (fid) {
700 case NVME_FEAT_FID_LBA_RANGE:
701 *len = sizeof(struct nvme_lba_range_type);
702 break;
703 case NVME_FEAT_FID_AUTO_PST:
704 *len = sizeof(struct nvme_feat_auto_pst);
705 break;
706 case NVME_FEAT_FID_PLM_CONFIG:
707 *len = sizeof(struct nvme_plm_config);
708 break;
709 case NVME_FEAT_FID_TIMESTAMP:
710 *len = sizeof(struct nvme_timestamp);
711 break;
712 case NVME_FEAT_FID_HOST_BEHAVIOR:
713 *len = sizeof(struct nvme_feat_host_behavior);
714 break;
715 case NVME_FEAT_FID_HOST_ID:
716 *len = (cdw11 & 0x1) ? 16 : 8;
717 break;
718 case NVME_FEAT_FID_HOST_MEM_BUF:
719 if (dir == NVME_DATA_TFR_HOST_TO_CTRL) {
720 *len = 0;
721 break;
722 }
723 *len = sizeof(struct nvme_host_mem_buf_attrs);
724 break;
725 case NVME_FEAT_FID_ARBITRATION:
726 case NVME_FEAT_FID_POWER_MGMT:
727 case NVME_FEAT_FID_TEMP_THRESH:
728 case NVME_FEAT_FID_ERR_RECOVERY:
729 case NVME_FEAT_FID_VOLATILE_WC:
730 case NVME_FEAT_FID_NUM_QUEUES:
731 case NVME_FEAT_FID_IRQ_COALESCE:
732 case NVME_FEAT_FID_IRQ_CONFIG:
733 case NVME_FEAT_FID_WRITE_ATOMIC:
734 case NVME_FEAT_FID_ASYNC_EVENT:
735 case NVME_FEAT_FID_KATO:
736 case NVME_FEAT_FID_HCTM:
737 case NVME_FEAT_FID_NOPSC:
738 case NVME_FEAT_FID_RRL:
739 case NVME_FEAT_FID_PLM_WINDOW:
740 case NVME_FEAT_FID_LBA_STS_INTERVAL:
741 case NVME_FEAT_FID_SANITIZE:
742 case NVME_FEAT_FID_ENDURANCE_EVT_CFG:
743 case NVME_FEAT_FID_SW_PROGRESS:
744 case NVME_FEAT_FID_RESV_NF_MASK:
745 case NVME_FEAT_FID_RESV_PERSIST:
746 case NVME_FEAT_FID_WRITE_PROTECT:
747 case NVME_FEAT_FID_POWER_LIMIT:
748 case NVME_FEAT_FID_POWER_MEASUREMENT:
749 *len = 0;
750 break;
751 case NVME_FEAT_FID_ENH_CTRL_METADATA:
752 case NVME_FEAT_FID_CTRL_METADATA:
753 case NVME_FEAT_FID_NS_METADATA:
754 *len = sizeof(struct nvme_host_metadata);
755 break;
756 case NVME_FEAT_FID_PERF_CHARACTERISTICS:
757 *len = sizeof(struct nvme_perf_characteristics);
758 break;
759 case NVME_FEAT_FID_FDP_EVENTS:
760 *len = NVME_FEAT_FDPE_NOET_MASK *
761 sizeof(struct nvme_fdp_supported_event_desc);
762 break;
763 default:
764 return -EINVAL22;
765 }
766 return 0;
767}
768
769__shr_public__attribute__((visibility("default"))) int libnvme_get_directive_receive_length(
770 enum nvme_directive_dtype dtype,
771 enum nvme_directive_receive_doper doper, __u32 *len)
772{
773 switch (dtype) {
774 case NVME_DIRECTIVE_DTYPE_IDENTIFY:
775 switch (doper) {
776 case NVME_DIRECTIVE_RECEIVE_IDENTIFY_DOPER_PARAM:
777 *len = sizeof(struct nvme_id_directives);
778 return 0;
779 default:
780 return -EINVAL22;
781 }
782 case NVME_DIRECTIVE_DTYPE_STREAMS:
783 switch (doper) {
784 case NVME_DIRECTIVE_RECEIVE_STREAMS_DOPER_PARAM:
785 *len = sizeof(struct nvme_streams_directive_params);
786 return 0;
787 case NVME_DIRECTIVE_RECEIVE_STREAMS_DOPER_STATUS:
788 *len = (128 * 1024) * sizeof(__le16);
789 return 0;
790 case NVME_DIRECTIVE_RECEIVE_STREAMS_DOPER_RESOURCE:
791 *len = 0;
792 return 0;
793 default:
794 return -EINVAL22;
795 }
796 default:
797 return -EINVAL22;
798 }
799}