Bug Summary

File:.build-ci/../plugins/lm/lm-nvme.c
Warning:line 480, column 11
Opened stream never closed. Potential resource leak

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 lm-nvme.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 static -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 nvme.p -I . -I .. -I src -I ../src -I ccan -I ../ccan -I libnvme/src -I ../libnvme/src -I /usr/include/json-c -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 -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-11-02-081605-642-1 -x c ../plugins/lm/lm-nvme.c
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Copyright (c) 2024 Samsung Electronics Co., LTD.
4 *
5 * Authors: Nate Thornton <n.thornton@samsung.com>
6 */
7
8#include <assert.h>
9#include <errno(*__errno_location ()).h>
10#include <fcntl.h>
11#include <inttypes.h>
12#include <stdio.h>
13#include <stdlib.h>
14#include <string.h>
15#include <sys/stat.h>
16#include <sys/types.h>
17#include <time.h>
18#include <unistd.h>
19
20#include <libnvme.h>
21
22#include <ccan/array_size/array_size.h>
23#include <shared/compiler-attributes-util.h>
24
25#include "global-ctx.h"
26#include "lm-print.h"
27#include "nvme-cmds.h"
28#include "nvme-print.h"
29#include "plugin.h"
30#include "src/cleanup.h"
31
32static inline const char * arg_str(const char * const *strings, size_t array_size, size_t idx)
33{
34 if (idx < array_size && strings[idx])
35 return strings[idx];
36 return "unrecognized";
37}
38
39#define ARGSTR(s, i)arg_str(s, (sizeof(s) / sizeof((s)[0]) + 0), i) arg_str(s, ARRAY_SIZE(s)(sizeof(s) / sizeof((s)[0]) + 0), i)
40
41static int lm_create_cdq(int argc, char **argv, struct command *acmd, struct plugin *plugin)
42{
43 const char *desc = "Create Controller Data Queue for controller of specific type and size";
44 const char *sz = "CDQ Size (in dwords)";
45 const char *cntlid = "Controller ID";
46 const char *qt = "Queue Type (default: 0 = User Data Migration Queue)";
47 const char *consent = "I consent this will not work and understand a CDQ cannot be mapped "
48 "to user space. If I proceed with the creation of a CDQ, the device "
49 "will write to invalid memory, inevitably leading to MMU faults or "
50 "worse.";
51
52 __cleanup_nvme_transport_handle__attribute__((cleanup(cleanup_nvme_transport_handle))) struct libnvme_transport_handle *hdl = NULL((void*)0);
53 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
54 struct lba_migration_queue_entry_type_0 *queue = NULL((void*)0);
55 __cleanup_huge__attribute__((cleanup(libnvme_free_huge))) struct libnvme_mem_huge mh = { 0, };
56 struct libnvme_passthru_cmd cmd;
57 int err = -1;
58
59 struct config {
60 __u32 sz;
61 __u16 cntlid;
62 __u8 qt;
63 bool_Bool consent;
64 char *file;
65 };
66
67 struct config cfg = {
68 .sz = 0,
69 .cntlid = 0,
70 .qt = 0,
71 .consent = false0,
72 .file = NULL((void*)0),
73 };
74
75 OPT_ARGS(opts)struct argconfig_commandline_options opts[] = {
76 OPT_UINT("size", 's', &cfg.sz, sz){"size", 's', "NUM", CFG_POSITIVE, _Generic(&cfg.sz, uint32_t
* : (&cfg.sz)), ARGCONFIG_ARG_REQUIRED, sz, 0, }
,
77 OPT_SHRT("cntlid", 'c', &cfg.cntlid, cntlid){"cntlid", 'c', "NUM", CFG_SHORT, _Generic(&cfg.cntlid, uint16_t
* : (&cfg.cntlid)), ARGCONFIG_ARG_REQUIRED, cntlid, 0, }
,
78 OPT_BYTE("queue-type", 'q', &cfg.qt, qt){"queue-type", 'q', "NUM", CFG_BYTE, _Generic(&cfg.qt, uint8_t
* : (&cfg.qt)), ARGCONFIG_ARG_REQUIRED, qt, 0, }
,
79 OPT_FLAG("consent", 0, &cfg.consent, consent){"consent", 0, ((void*)0), CFG_FLAG, _Generic(&cfg.consent
, _Bool * : (&cfg.consent)), ARGCONFIG_ARG_NONE, consent,
0, }
,
80 OPT_END(){ ((void*)0) }
81 };
82
83 err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts);
84 if (err)
85 return err;
86
87 if (!cfg.consent) {
88 nvme_show_error("ERROR: consent required")nvme_show_message(1, "ERROR: consent required");
89 return -EINVAL22;
90 }
91
92 // Not that it really matters, but we setup memory as if the CDQ can be held
93 // in user space regardless.
94 queue = libnvme_alloc_huge(cfg.sz << 2, &mh);
95 if (!queue) {
96 nvme_show_error("ERROR: libnvme_alloc of size %dB failed %s", cfg.sz << 2,nvme_show_message(1, "ERROR: libnvme_alloc of size %dB failed %s"
, cfg.sz << 2, libnvme_strerror((*__errno_location ()))
)
97 libnvme_strerror(errno))nvme_show_message(1, "ERROR: libnvme_alloc of size %dB failed %s"
, cfg.sz << 2, libnvme_strerror((*__errno_location ()))
)
;
98 return -ENOMEM12;
99 }
100
101 nvme_init_lm_cdq_create(&cmd, NVME_SET(cfg.qt, LM_QT)(((__u32)(cfg.qt) & NVME_LM_QT_MASK) << NVME_LM_QT_SHIFT
)
,
102 cfg.cntlid, cfg.sz, queue);
103 err = libnvme_exec_admin_passthru(hdl, &cmd);
104 if (err < 0)
105 nvme_show_error("ERROR: nvme_lm_cdq() failed: %s",nvme_show_message(1, "ERROR: nvme_lm_cdq() failed: %s", libnvme_strerror
(-err))
106 libnvme_strerror(-err))nvme_show_message(1, "ERROR: nvme_lm_cdq() failed: %s", libnvme_strerror
(-err))
;
107 else if (err)
108 nvme_show_status(err);
109 else
110 nvme_show_verbose_result("Create CDQ Successful: CDQID=0x%04x",nvme_show_verbose_message("Create CDQ Successful: CDQID=0x%04x"
, ((((__u32)cmd.result) >> NVME_LM_CREATE_CDQ_CDQID_SHIFT
) & NVME_LM_CREATE_CDQ_CDQID_MASK))
111 NVME_GET((__u32)cmd.result, LM_CREATE_CDQ_CDQID))nvme_show_verbose_message("Create CDQ Successful: CDQID=0x%04x"
, ((((__u32)cmd.result) >> NVME_LM_CREATE_CDQ_CDQID_SHIFT
) & NVME_LM_CREATE_CDQ_CDQID_MASK))
;
112
113 return err;
114}
115
116static int lm_delete_cdq(int argc, char **argv, struct command *acmd, struct plugin *plugin)
117{
118 const char *desc = "Delete Controller Data Queue";
119 const char *cdqid = "Controller Data Queue ID";
120
121 __cleanup_nvme_transport_handle__attribute__((cleanup(cleanup_nvme_transport_handle))) struct libnvme_transport_handle *hdl = NULL((void*)0);
122 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
123 struct libnvme_passthru_cmd cmd;
124 int err = -1;
125
126 struct config {
127 __u16 cdqid;
128 };
129
130 struct config cfg = {
131 .cdqid = 0
132 };
133
134 OPT_ARGS(opts)struct argconfig_commandline_options opts[] = {
135 OPT_SHRT("cdqid", 'C', &cfg.cdqid, cdqid){"cdqid", 'C', "NUM", CFG_SHORT, _Generic(&cfg.cdqid, uint16_t
* : (&cfg.cdqid)), ARGCONFIG_ARG_REQUIRED, cdqid, 0, }
,
136 OPT_END(){ ((void*)0) }
137 };
138
139 err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts);
140 if (err)
141 return err;
142
143 nvme_init_lm_cdq_delete(&cmd, 0, cfg.cdqid);
144 err = libnvme_exec_admin_passthru(hdl, &cmd);
145 if (err < 0)
146 nvme_show_error("ERROR: nvme_lm_cdq() failed: %s",nvme_show_message(1, "ERROR: nvme_lm_cdq() failed: %s", libnvme_strerror
(-err))
147 libnvme_strerror(-err))nvme_show_message(1, "ERROR: nvme_lm_cdq() failed: %s", libnvme_strerror
(-err))
;
148 else if (err > 0)
149 nvme_show_status(err);
150 else
151 nvme_show_verbose_result("Delete CDQ Successful: CDQID=0x%04x", cfg.cdqid)nvme_show_verbose_message("Delete CDQ Successful: CDQID=0x%04x"
, cfg.cdqid)
;
152
153 return err;
154}
155
156static const char * const lm_track_send_select_argstr[] = {
157 [NVME_LM_SEL_LOG_USER_DATA_CHANGES] = "Log User Data Changes",
158 [NVME_LM_SEL_TRACK_MEMORY_CHANGES] = "Track Memory Changes"
159};
160
161static int lm_track_send(int argc, char **argv, struct command *acmd, struct plugin *plugin)
162{
163 const char *desc = "Track Send command used to manage the tracking of information by a "
164 "controller";
165 const char *sel = "Type of management operation to perform\n"
166 " 0h = Log User Data Changes\n"
167 " 1h = Track Memory Changes";
168 const char *mos = "Management operation specific";
169 const char *cdqid = "Controller Data Queue ID";
170 const char *start = "Equivalent to start tracking with defaults";
171 const char *stop = "Equivalent to stop tracking with defaults";
172
173
174 __cleanup_nvme_transport_handle__attribute__((cleanup(cleanup_nvme_transport_handle))) struct libnvme_transport_handle *hdl = NULL((void*)0);
175 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
176 struct libnvme_passthru_cmd cmd;
177 int err = -1;
178
179 struct config {
180 __u8 sel;
181 __u8 mos;
182 __u16 cdqid;
183 bool_Bool start;
184 bool_Bool stop;
185 };
186
187 struct config cfg = {
188 .sel = 0,
189 .mos = 0,
190 .cdqid = 0,
191 .start = false0,
192 .stop = false0,
193 };
194
195 OPT_ARGS(opts)struct argconfig_commandline_options opts[] = {
196 OPT_BYTE("sel", 's', &cfg.sel, sel){"sel", 's', "NUM", CFG_BYTE, _Generic(&cfg.sel, uint8_t *
: (&cfg.sel)), ARGCONFIG_ARG_REQUIRED, sel, 0, }
,
197 OPT_BYTE("mos", 'm', &cfg.mos, mos){"mos", 'm', "NUM", CFG_BYTE, _Generic(&cfg.mos, uint8_t *
: (&cfg.mos)), ARGCONFIG_ARG_REQUIRED, mos, 0, }
,
198 OPT_SHRT("cdqid", 'C', &cfg.cdqid, cdqid){"cdqid", 'C', "NUM", CFG_SHORT, _Generic(&cfg.cdqid, uint16_t
* : (&cfg.cdqid)), ARGCONFIG_ARG_REQUIRED, cdqid, 0, }
,
199 OPT_FLAG("start", 0, &cfg.start, start){"start", 0, ((void*)0), CFG_FLAG, _Generic(&cfg.start, _Bool
* : (&cfg.start)), ARGCONFIG_ARG_NONE, start, 0, }
,
200 OPT_FLAG("stop", 0, &cfg.stop, stop){"stop", 0, ((void*)0), CFG_FLAG, _Generic(&cfg.stop, _Bool
* : (&cfg.stop)), ARGCONFIG_ARG_NONE, stop, 0, }
,
201 OPT_END(){ ((void*)0) }
202 };
203
204 err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts);
205 if (err)
206 return err;
207
208 if (!argconfig_given(opts, &cfg.sel)) {
209 nvme_show_error("Select field required")nvme_show_message(1, "Select field required");
210 return -EINVAL22;
211 }
212
213 if (cfg.sel != NVME_LM_SEL_LOG_USER_DATA_CHANGES) {
214 nvme_show_error("Unsupported select option %d (%s)", cfg.sel,nvme_show_message(1, "Unsupported select option %d (%s)", cfg
.sel, arg_str(lm_track_send_select_argstr, (sizeof(lm_track_send_select_argstr
) / sizeof((lm_track_send_select_argstr)[0]) + 0), cfg.sel))
215 ARGSTR(lm_track_send_select_argstr, cfg.sel))nvme_show_message(1, "Unsupported select option %d (%s)", cfg
.sel, arg_str(lm_track_send_select_argstr, (sizeof(lm_track_send_select_argstr
) / sizeof((lm_track_send_select_argstr)[0]) + 0), cfg.sel))
;
216 return -EINVAL22;
217 }
218
219 if (cfg.start && cfg.stop) {
220 nvme_show_error("Must select one of start & stop, not both")nvme_show_message(1, "Must select one of start & stop, not both"
)
;
221 return -EINVAL22;
222 } else if (cfg.sel == NVME_LM_SEL_LOG_USER_DATA_CHANGES) {
223 if (cfg.start)
224 cfg.mos = NVME_SET(NVME_LM_LACT_START_LOGGING, LM_LACT)(((__u32)(NVME_LM_LACT_START_LOGGING) & NVME_LM_LACT_MASK
) << NVME_LM_LACT_SHIFT)
;
225 else if (cfg.stop)
226 cfg.mos = NVME_SET(NVME_LM_LACT_STOP_LOGGING, LM_LACT)(((__u32)(NVME_LM_LACT_STOP_LOGGING) & NVME_LM_LACT_MASK)
<< NVME_LM_LACT_SHIFT)
;
227 }
228
229 nvme_init_lm_track_send(&cmd, cfg.sel, cfg.mos, cfg.cdqid, NULL((void*)0), 0);
230 err = libnvme_exec_admin_passthru(hdl, &cmd);
231 if (err < 0)
232 nvme_show_error("ERROR: nvme_lm_track_send() failed %s",nvme_show_message(1, "ERROR: nvme_lm_track_send() failed %s",
libnvme_strerror(-err))
233 libnvme_strerror(-err))nvme_show_message(1, "ERROR: nvme_lm_track_send() failed %s",
libnvme_strerror(-err))
;
234 else if (err)
235 nvme_show_status(err);
236 else
237 nvme_show_verbose_result("Track Send (%s) Successful",nvme_show_verbose_message("Track Send (%s) Successful", arg_str
(lm_track_send_select_argstr, (sizeof(lm_track_send_select_argstr
) / sizeof((lm_track_send_select_argstr)[0]) + 0), cfg.sel))
238 ARGSTR(lm_track_send_select_argstr, cfg.sel))nvme_show_verbose_message("Track Send (%s) Successful", arg_str
(lm_track_send_select_argstr, (sizeof(lm_track_send_select_argstr
) / sizeof((lm_track_send_select_argstr)[0]) + 0), cfg.sel))
;
239
240 return err;
241}
242
243static const char * const lm_migration_send_select_argstr[] = {
244 [NVME_LM_SEL_SUSPEND] = "Suspend",
245 [NVME_LM_SEL_RESUME] = "Resume",
246 [NVME_LM_SEL_SET_CONTROLLER_STATE] = "Set Controller State"
247};
248
249static int lm_migration_send(int argc, char **argv, struct command *acmd, struct plugin *plugin)
250{
251 const char *desc = "Migration Send command is used to manage the migration of a controller";
252 const char *sel = "Select (SEL) the type of management operation to perform "
253 "(CDW10[07:00])\n"
254 " 0h = Suspend\n"
255 " 1h = Resume\n"
256 " 2h = Set Controller State";
257 const char *cntlid = "Controller Identifier (CDW11[15:00])";
258 const char *stype = "Type of suspend (STYPE) (CDW11[23:16]\n"
259 " 0h = Suspend Notification\n"
260 " 1h = Suspend";
261 const char *dudmq = "Delete user data migration queue (DUDMQ) as part of suspend operation "
262 "(CDW11[31])";
263 const char *seqind = "Sequence Indicator (CDW10[17:16])\n"
264 " 0h = Not first not last\n"
265 " 1h = First in two or more\n"
266 " 2h = Last in two or more\n"
267 " 3h = Entire state info";
268 const char *csuuidi = "Controller State UUID Index (CSUUIDI) (CDW11[31:24])";
269 const char *csvi = "Controller State Version Index (CSVI) (CDW11[23:16])";
270 const char *uidx = "UUID Index (UIDX) (CDW14[16:00])";
271 const char *offset = "Controller State Offset";
272 const char *numd = "Number of Dwords (NUMD)";
273 const char *input = "Controller State Data input file";
274
275 __cleanup_nvme_transport_handle__attribute__((cleanup(cleanup_nvme_transport_handle))) struct libnvme_transport_handle *hdl = NULL((void*)0);
276 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
277 __cleanup_huge__attribute__((cleanup(libnvme_free_huge))) struct libnvme_mem_huge mh = { 0, };
278 __cleanup_file__attribute__((cleanup(shr_cleanup_file))) FILE *file = NULL((void*)0);
279 struct libnvme_passthru_cmd cmd;
280 void *data = NULL((void*)0);
281 int err = -1;
282
283 struct config {
284 __u8 sel;
285 __u16 cntlid;
286 __u8 stype;
287 __u8 seqind;
288 __u8 csuuidi;
289 __u8 csvi;
290 __u8 uidx;
291 __u64 offset;
292 __u32 numd;
293 char *input;
294 bool_Bool dudmq;
295 };
296
297 struct config cfg = {
298 .sel = 0,
299 .cntlid = 0,
300 .stype = 0,
301 .seqind = 0,
302 .csuuidi = 0,
303 .csvi = 0,
304 .uidx = 0,
305 .offset = 0,
306 .numd = 0,
307 .input = NULL((void*)0),
308 .dudmq = false0
309 };
310
311 OPT_ARGS(opts)struct argconfig_commandline_options opts[] = {
312 OPT_BYTE("sel", 's', &cfg.sel, sel){"sel", 's', "NUM", CFG_BYTE, _Generic(&cfg.sel, uint8_t *
: (&cfg.sel)), ARGCONFIG_ARG_REQUIRED, sel, 0, }
,
313 OPT_SHRT("cntlid", 'c', &cfg.cntlid, cntlid){"cntlid", 'c', "NUM", CFG_SHORT, _Generic(&cfg.cntlid, uint16_t
* : (&cfg.cntlid)), ARGCONFIG_ARG_REQUIRED, cntlid, 0, }
,
314 OPT_BYTE("stype", 't', &cfg.stype, stype){"stype", 't', "NUM", CFG_BYTE, _Generic(&cfg.stype, uint8_t
* : (&cfg.stype)), ARGCONFIG_ARG_REQUIRED, stype, 0, }
,
315 OPT_FLAG("dudmq", 'd', &cfg.dudmq, dudmq){"dudmq", 'd', ((void*)0), CFG_FLAG, _Generic(&cfg.dudmq,
_Bool * : (&cfg.dudmq)), ARGCONFIG_ARG_NONE, dudmq, 0, }
,
316 OPT_BYTE("seq-ind", 'S', &cfg.seqind, seqind){"seq-ind", 'S', "NUM", CFG_BYTE, _Generic(&cfg.seqind, uint8_t
* : (&cfg.seqind)), ARGCONFIG_ARG_REQUIRED, seqind, 0, }
,
317 OPT_BYTE("csuuidi", 'U', &cfg.csuuidi, csuuidi){"csuuidi", 'U', "NUM", CFG_BYTE, _Generic(&cfg.csuuidi, uint8_t
* : (&cfg.csuuidi)), ARGCONFIG_ARG_REQUIRED, csuuidi, 0,
}
,
318 OPT_BYTE("csvi", 'V', &cfg.csvi, csvi){"csvi", 'V', "NUM", CFG_BYTE, _Generic(&cfg.csvi, uint8_t
* : (&cfg.csvi)), ARGCONFIG_ARG_REQUIRED, csvi, 0, }
,
319 OPT_BYTE("uidx", 'u', &cfg.uidx, uidx){"uidx", 'u', "NUM", CFG_BYTE, _Generic(&cfg.uidx, uint8_t
* : (&cfg.uidx)), ARGCONFIG_ARG_REQUIRED, uidx, 0, }
,
320 OPT_LONG("offset", 'O', &cfg.offset, offset){"offset", 'O', "NUM", CFG_LONG, _Generic(&cfg.offset, uint64_t
* : (&cfg.offset), default : _Generic(&cfg.offset, unsigned
long long * : (&cfg.offset), default : (void)0)), ARGCONFIG_ARG_REQUIRED
, offset, 0, }
,
321 OPT_UINT("numd", 'n', &cfg.numd, numd){"numd", 'n', "NUM", CFG_POSITIVE, _Generic(&cfg.numd, uint32_t
* : (&cfg.numd)), ARGCONFIG_ARG_REQUIRED, numd, 0, }
,
322 OPT_FILE("input-file", 'f', &cfg.input, input){"input-file", 'f', "FILE", CFG_FILE, _Generic(&cfg.input
, char ** : (&cfg.input), const char ** : (&cfg.input
)), ARGCONFIG_ARG_REQUIRED, input, 0, }
,
323 OPT_END(){ ((void*)0) }
324 };
325
326 err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts);
327 if (err)
328 return err;
329
330 if (!argconfig_given(opts, &cfg.sel)) {
331 nvme_show_error("Select field required")nvme_show_message(1, "Select field required");
332 return -EINVAL22;
333 }
334
335 // Sanity check input parameters
336 if (cfg.sel == NVME_LM_SEL_SUSPEND || cfg.sel == NVME_LM_SEL_RESUME) {
337 if (cfg.csuuidi != 0 || cfg.csvi != 0) {
338 nvme_show_error("Unexpected fields for %s",nvme_show_message(1, "Unexpected fields for %s", arg_str(lm_migration_send_select_argstr
, (sizeof(lm_migration_send_select_argstr) / sizeof((lm_migration_send_select_argstr
)[0]) + 0), cfg.sel))
339 ARGSTR(lm_migration_send_select_argstr, cfg.sel))nvme_show_message(1, "Unexpected fields for %s", arg_str(lm_migration_send_select_argstr
, (sizeof(lm_migration_send_select_argstr) / sizeof((lm_migration_send_select_argstr
)[0]) + 0), cfg.sel))
;
340 return -EINVAL22;
341 }
342 } else if (cfg.sel == NVME_LM_SEL_SET_CONTROLLER_STATE) {
343 if (cfg.dudmq || cfg.stype != 0) {
344 nvme_show_error("Unexpected fields for %s",nvme_show_message(1, "Unexpected fields for %s", arg_str(lm_migration_send_select_argstr
, (sizeof(lm_migration_send_select_argstr) / sizeof((lm_migration_send_select_argstr
)[0]) + 0), cfg.sel))
345 ARGSTR(lm_migration_send_select_argstr, cfg.sel))nvme_show_message(1, "Unexpected fields for %s", arg_str(lm_migration_send_select_argstr
, (sizeof(lm_migration_send_select_argstr) / sizeof((lm_migration_send_select_argstr
)[0]) + 0), cfg.sel))
;
346 return -EINVAL22;
347 } else if (!strlen(cfg.input)) {
348 nvme_show_error("Expected file for %s",nvme_show_message(1, "Expected file for %s", arg_str(lm_migration_send_select_argstr
, (sizeof(lm_migration_send_select_argstr) / sizeof((lm_migration_send_select_argstr
)[0]) + 0), cfg.sel))
349 ARGSTR(lm_migration_send_select_argstr, cfg.sel))nvme_show_message(1, "Expected file for %s", arg_str(lm_migration_send_select_argstr
, (sizeof(lm_migration_send_select_argstr) / sizeof((lm_migration_send_select_argstr
)[0]) + 0), cfg.sel))
;
350 return -EINVAL22;
351 }
352 }
353
354 if (cfg.input && strlen(cfg.input)) {
355 file = fopen(cfg.input, "r");
356 if (file == NULL((void*)0)) {
357 nvme_show_perror(cfg.input);
358 return -EINVAL22;
359 }
360
361 data = libnvme_alloc_huge(cfg.numd << 2, &mh);
362 if (!data)
363 return -ENOMEM12;
364
365 size_t n_data = fread(data, 1, cfg.numd << 2, file);
366
367 if (n_data != (size_t)(cfg.numd << 2)) {
368 nvme_show_error("failed to read controller state data %s", libnvme_strerror(errno))nvme_show_message(1, "failed to read controller state data %s"
, libnvme_strerror((*__errno_location ())))
;
369 return -errno(*__errno_location ());
370 }
371 }
372
373 nvme_init_lm_migration_send(&cmd, cfg.sel,
374 NVME_SET(cfg.seqind, LM_SEQIND)(((__u32)(cfg.seqind) & NVME_LM_SEQIND_MASK) << NVME_LM_SEQIND_SHIFT
)
, cfg.cntlid,
375 cfg.stype, cfg.dudmq, cfg.csvi, cfg.csuuidi,
376 cfg.offset, cfg.uidx, data,
377 (cfg.numd << 2));
378 err = libnvme_exec_admin_passthru(hdl, &cmd);
379 if (err < 0)
380 nvme_show_error("ERROR: nvme_lm_migration_send() failed %s",nvme_show_message(1, "ERROR: nvme_lm_migration_send() failed %s"
, libnvme_strerror(-err))
381 libnvme_strerror(-err))nvme_show_message(1, "ERROR: nvme_lm_migration_send() failed %s"
, libnvme_strerror(-err))
;
382 else if (err > 0)
383 nvme_show_status(err);
384 else
385 nvme_show_verbose_result("Migration Send (%s) Successful",nvme_show_verbose_message("Migration Send (%s) Successful", arg_str
(lm_migration_send_select_argstr, (sizeof(lm_migration_send_select_argstr
) / sizeof((lm_migration_send_select_argstr)[0]) + 0), cfg.sel
))
386 ARGSTR(lm_migration_send_select_argstr, cfg.sel))nvme_show_verbose_message("Migration Send (%s) Successful", arg_str
(lm_migration_send_select_argstr, (sizeof(lm_migration_send_select_argstr
) / sizeof((lm_migration_send_select_argstr)[0]) + 0), cfg.sel
))
;
387
388
389 return err;
390}
391
392static int lm_migration_recv(int argc, char **argv, struct command *acmd, struct plugin *plugin)
393{
394 const char *desc = "Migration Receive command is used to obtain information used to manage "
395 " a migratable controller";
396 const char *sel = "Select (SEL) the type of management operation to perform "
397 "(CDW10[07:00])\n"
398 " 0h = Get Controller State";
399 const char *cntlid = "Controller Identifier (CDW10[31:16])";
400 const char *csuuidi = "Controller State UUID Index (CSUUIDI) (CDW11[23:16])";
401 const char *csvi = "Controller State Version Index (CSVI) (CDW11[7:0])";
402 const char *uidx = "UUID Index (UIDX) (CDW14[16:00])";
403 const char *offset = "Controller State Offset";
404 const char *numd = "Number of Dwords (NUMD)";
405 const char *output = "Controller State Data output file";
406
407 __cleanup_nvme_transport_handle__attribute__((cleanup(cleanup_nvme_transport_handle))) struct libnvme_transport_handle *hdl = NULL((void*)0);
408 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
409 __cleanup_huge__attribute__((cleanup(libnvme_free_huge))) struct libnvme_mem_huge mh = { 0, };
410 __cleanup_file__attribute__((cleanup(shr_cleanup_file))) FILE *fd = NULL((void*)0);
411 struct libnvme_passthru_cmd cmd;
412 nvme_print_flags_t flags;
413 void *data = NULL((void*)0);
414 int err = -1;
415 __u16 mos;
416
417 struct config {
418 __u8 sel;
419 __u16 cntlid;
420 __u8 csuuidi;
421 __u8 csvi;
422 __u8 uidx;
423 __u64 offset;
424 __u32 numd;
425 char *output;
426 };
427
428 struct config cfg = {
429 .sel = 0,
430 .cntlid = 0,
431 .csuuidi = 0,
432 .csvi = 0,
433 .uidx = 0,
434 .offset = 0,
435 .numd = 0,
436 .output = NULL((void*)0),
437 };
438
439 OPT_ARGS(opts)struct argconfig_commandline_options opts[] = {
440 OPT_BYTE("sel", 's', &cfg.sel, sel){"sel", 's', "NUM", CFG_BYTE, _Generic(&cfg.sel, uint8_t *
: (&cfg.sel)), ARGCONFIG_ARG_REQUIRED, sel, 0, }
,
441 OPT_SHRT("cntlid", 'c', &cfg.cntlid, cntlid){"cntlid", 'c', "NUM", CFG_SHORT, _Generic(&cfg.cntlid, uint16_t
* : (&cfg.cntlid)), ARGCONFIG_ARG_REQUIRED, cntlid, 0, }
,
442 OPT_BYTE("csuuidi", 'U', &cfg.csuuidi, csuuidi){"csuuidi", 'U', "NUM", CFG_BYTE, _Generic(&cfg.csuuidi, uint8_t
* : (&cfg.csuuidi)), ARGCONFIG_ARG_REQUIRED, csuuidi, 0,
}
,
443 OPT_BYTE("csvi", 'V', &cfg.csvi, csvi){"csvi", 'V', "NUM", CFG_BYTE, _Generic(&cfg.csvi, uint8_t
* : (&cfg.csvi)), ARGCONFIG_ARG_REQUIRED, csvi, 0, }
,
444 OPT_BYTE("uidx", 'u', &cfg.uidx, uidx){"uidx", 'u', "NUM", CFG_BYTE, _Generic(&cfg.uidx, uint8_t
* : (&cfg.uidx)), ARGCONFIG_ARG_REQUIRED, uidx, 0, }
,
445 OPT_LONG("offset", 'O', &cfg.offset, offset){"offset", 'O', "NUM", CFG_LONG, _Generic(&cfg.offset, uint64_t
* : (&cfg.offset), default : _Generic(&cfg.offset, unsigned
long long * : (&cfg.offset), default : (void)0)), ARGCONFIG_ARG_REQUIRED
, offset, 0, }
,
446 OPT_UINT("numd", 'n', &cfg.numd, numd){"numd", 'n', "NUM", CFG_POSITIVE, _Generic(&cfg.numd, uint32_t
* : (&cfg.numd)), ARGCONFIG_ARG_REQUIRED, numd, 0, }
,
447 OPT_FILE("output-file", 'f', &cfg.output, output){"output-file", 'f', "FILE", CFG_FILE, _Generic(&cfg.output
, char ** : (&cfg.output), const char ** : (&cfg.output
)), ARGCONFIG_ARG_REQUIRED, output, 0, }
,
448 OPT_END(){ ((void*)0) }
449 };
450
451 err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts);
452 if (err)
1
Assuming 'err' is 0
2
Taking false branch
453 return err;
454
455 if (!argconfig_given(opts, &cfg.sel)) {
3
Assuming the condition is false
4
Taking false branch
456 nvme_show_error("Select field required")nvme_show_message(1, "Select field required");
457 return -EINVAL22;
458 }
459
460 flags = nvme_args.output_flags;
461
462 if (nvme_args.output_format && cfg.offset != 0 && !(flags & BINARY)) {
5
Assuming field 'output_format' is null
463 nvme_show_error("cannot parse non-zero offset")nvme_show_message(1, "cannot parse non-zero offset");
464 return -EINVAL22;
465 }
466
467 if (nvme_args.verbose)
6
Assuming field 'verbose' is 0
468 flags |= VERBOSE;
469
470 if (cfg.output && strlen(cfg.output)) {
7
Assuming field 'output' is non-null
8
Assuming the condition is true
9
Taking true branch
471 fd = fopen(cfg.output, "w");
10
Stream opened here
11
Assuming that 'fopen' is successful
472 if (!fd
11.1
'fd' is non-null
) {
12
Taking false branch
473 nvme_show_perror(cfg.output);
474 return -errno(*__errno_location ());
475 }
476 }
477
478 data = libnvme_alloc_huge((cfg.numd + 1) << 2, &mh);
479 if (!data)
13
Assuming 'data' is null
14
Taking true branch
480 return -ENOMEM12;
15
Opened stream never closed. Potential resource leak
481
482 mos = NVME_SET(cfg.csvi, LM_GET_CONTROLLER_STATE_CSVI)(((__u32)(cfg.csvi) & NVME_LM_GET_CONTROLLER_STATE_CSVI_MASK
) << NVME_LM_GET_CONTROLLER_STATE_CSVI_SHIFT)
;
483 nvme_init_lm_migration_recv(&cmd, cfg.offset, mos, cfg.cntlid,
484 cfg.csuuidi, cfg.sel, cfg.uidx, 0, data,
485 (cfg.numd + 1) << 2);
486 err = libnvme_exec_admin_passthru(hdl, &cmd);
487 if (err < 0)
488 nvme_show_error("ERROR: nvme_lm_migration_recv() failed %s",nvme_show_message(1, "ERROR: nvme_lm_migration_recv() failed %s"
, libnvme_strerror(-err))
489 libnvme_strerror(-err))nvme_show_message(1, "ERROR: nvme_lm_migration_recv() failed %s"
, libnvme_strerror(-err))
;
490 else if (err)
491 nvme_show_status(err);
492 else if (cfg.sel == NVME_LM_SEL_GET_CONTROLLER_STATE) {
493 if (flags == NORMAL)
494 printf("CDW0: 0x%"PRIu64"l" "u"": Controller %sSuspended\n",
495 (uint64_t)cmd.result,
496 (cmd.result & NVME_LM_GET_CONTROLLER_STATE_CSUP) ?
497 "" : "NOT ");
498
499 if (cfg.output && strlen(cfg.output)) {
500 if (fwrite(data, 1, cfg.numd << 2, fd) != (cfg.numd << 2)) {
501 nvme_show_error("ERROR: %s: failed to write buffer to output file",nvme_show_message(1, "ERROR: %s: failed to write buffer to output file"
, libnvme_strerror((*__errno_location ())))
502 libnvme_strerror(errno))nvme_show_message(1, "ERROR: %s: failed to write buffer to output file"
, libnvme_strerror((*__errno_location ())))
;
503 err = -errno(*__errno_location ());
504 }
505 } else {
506 lm_show_controller_state_data((struct nvme_lm_controller_state_data *)data,
507 (cfg.numd + 1) << 2, cfg.offset, flags);
508 }
509 }
510
511 return err;
512}
513
514enum lm_controller_data_queue_feature_id {
515 lm_cdq_feature_id = 0x21
516};
517
518static int lm_set_cdq(int argc, char **argv, struct command *acmd, struct plugin *plugin)
519{
520 const char *desc = "This Feature allows a host to update the status of the head pointer "
521 "of a CDQ and specify the configuration of a CDQ Tail event.";
522 const char *cdqid = "Controller Data Queue ID";
523 const char *hp = "The slot of the head pointer for the specified CDQ";
524 const char *tpt = "If specified, the slot that causes the controller "
525 " to issue a CDQ Tail Pointer event";
526
527 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
528 __cleanup_nvme_transport_handle__attribute__((cleanup(cleanup_nvme_transport_handle))) struct libnvme_transport_handle *hdl = NULL((void*)0);
529 int err = -1;
530
531 struct config {
532 __u16 cdqid;
533 __u32 hp;
534 __u32 tpt;
535 };
536
537 struct config cfg = {
538 .cdqid = 0,
539 .hp = 0,
540 .tpt = 0,
541 };
542
543 OPT_ARGS(opts)struct argconfig_commandline_options opts[] = {
544 OPT_SHRT("cdqid", 'C', &cfg.cdqid, cdqid){"cdqid", 'C', "NUM", CFG_SHORT, _Generic(&cfg.cdqid, uint16_t
* : (&cfg.cdqid)), ARGCONFIG_ARG_REQUIRED, cdqid, 0, }
,
545 OPT_UINT("hp", 0, &cfg.hp, hp){"hp", 0, "NUM", CFG_POSITIVE, _Generic(&cfg.hp, uint32_t
* : (&cfg.hp)), ARGCONFIG_ARG_REQUIRED, hp, 0, }
,
546 OPT_UINT("tpt", 'T', &cfg.tpt, tpt){"tpt", 'T', "NUM", CFG_POSITIVE, _Generic(&cfg.tpt, uint32_t
* : (&cfg.tpt)), ARGCONFIG_ARG_REQUIRED, tpt, 0, }
,
547 OPT_END(){ ((void*)0) }
548 };
549
550 err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts);
551 if (err)
552 return err;
553
554 err = nvme_set_features(hdl, 0, lm_cdq_feature_id, 0, cfg.cdqid |
555 (argconfig_given(opts, &cfg.tpt) ?
556 NVME_SET(1, LM_CTRL_DATA_QUEUE_ETPT)(((__u32)(1) & NVME_LM_CTRL_DATA_QUEUE_ETPT_MASK) <<
NVME_LM_CTRL_DATA_QUEUE_ETPT_SHIFT)
: 0),
557 cfg.hp, cfg.tpt, 0, 0, NULL((void*)0), 0, NULL((void*)0));
558 if (err < 0)
559 nvme_show_error("ERROR: nvme_set_features() failed %s",nvme_show_message(1, "ERROR: nvme_set_features() failed %s", libnvme_strerror
(-err))
560 libnvme_strerror(-err))nvme_show_message(1, "ERROR: nvme_set_features() failed %s", libnvme_strerror
(-err))
;
561 else if (err)
562 nvme_show_status(err);
563 else
564 nvme_show_verbose_result("Success. Head Pointer: %d", cfg.hp)nvme_show_verbose_message("Success. Head Pointer: %d", cfg.hp
)
;
565
566 return err;
567}
568
569static int lm_get_cdq(int argc, char **argv, struct command *acmd, struct plugin *plugin)
570{
571 const char *desc = "This Feature allows a host to retrieve the status of the head pointer "
572 "of a CDQ and specify the configuration of a CDQ Tail event.";
573 const char *cdqid = "Controller Data Queue ID";
574
575 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
576 __cleanup_nvme_transport_handle__attribute__((cleanup(cleanup_nvme_transport_handle))) struct libnvme_transport_handle *hdl = NULL((void*)0);
577 nvme_print_flags_t flags;
578 int err = -1;
579
580 struct config {
581 __u16 cdqid;
582 };
583
584 struct config cfg = {
585 .cdqid = 0,
586 };
587
588 OPT_ARGS(opts)struct argconfig_commandline_options opts[] = {
589 OPT_SHRT("cdqid", 'C', &cfg.cdqid, cdqid){"cdqid", 'C', "NUM", CFG_SHORT, _Generic(&cfg.cdqid, uint16_t
* : (&cfg.cdqid)), ARGCONFIG_ARG_REQUIRED, cdqid, 0, }
,
590 OPT_END(){ ((void*)0) }
591 };
592
593 err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts);
594 if (err)
595 return err;
596
597 flags = nvme_args.output_flags;
598
599 struct nvme_lm_ctrl_data_queue_fid_data data;
600
601 err = nvme_get_features(hdl, 0, lm_cdq_feature_id, 0, cfg.cdqid, 0,
602 &data, sizeof(data), NULL((void*)0));
603 if (err < 0)
604 nvme_show_error("ERROR: nvme_get_features() failed %s",nvme_show_message(1, "ERROR: nvme_get_features() failed %s", libnvme_strerror
(-err))
605 libnvme_strerror(-err))nvme_show_message(1, "ERROR: nvme_get_features() failed %s", libnvme_strerror
(-err))
;
606 else if (err)
607 nvme_show_status(err);
608 else
609 lm_show_controller_data_queue(&data, flags);
610
611 return err;
612}
613
614static struct command lm_create_cdq_cmd = {
615 .name = "create-cdq",
616 .help = "Create Controller Data Queue",
617 .fn = lm_create_cdq,
618};
619
620static struct command lm_delete_cdq_cmd = {
621 .name = "delete-cdq",
622 .help = "Delete Controller Data Queue",
623 .fn = lm_delete_cdq,
624};
625
626static struct command lm_track_send_cmd = {
627 .name = "track-send",
628 .help = "Track Send Command",
629 .fn = lm_track_send,
630};
631
632static struct command lm_migration_send_cmd = {
633 .name = "migration-send",
634 .help = "Migration Send",
635 .fn = lm_migration_send,
636};
637
638static struct command lm_migration_recv_cmd = {
639 .name = "migration-recv",
640 .help = "Migration Receive",
641 .fn = lm_migration_recv,
642};
643
644static struct command lm_set_cdq_cmd = {
645 .name = "set-cdq",
646 .help = "Set Feature - Controller Data Queue (FID 21h)",
647 .fn = lm_set_cdq,
648};
649
650static struct command lm_get_cdq_cmd = {
651 .name = "get-cdq",
652 .help = "Get Feature - Controller Data Queue (FID 21h)",
653 .fn = lm_get_cdq,
654};
655
656static struct command *commands[] = {
657 &lm_create_cdq_cmd,
658 &lm_delete_cdq_cmd,
659 &lm_track_send_cmd,
660 &lm_migration_send_cmd,
661 &lm_migration_recv_cmd,
662 &lm_set_cdq_cmd,
663 &lm_get_cdq_cmd,
664 NULL((void*)0),
665};
666
667static struct plugin plugin = {
668 .name = "lm",
669 .desc = "Live Migration NVMe extensions",
670 .version = NVME_VERSION"3.2",
671 .core = true1,
672 .group = "Controller Management",
673};
674
675static void __shr_constructor__attribute__((constructor)) register_plugin(void)
676{
677 plugin_add_group(&plugin, NULL((void*)0), commands);
678 register_extension(&plugin);
679}