Skip to content

Commit b73c4a4

Browse files
committed
ioctl: add hooks into submission path
Don't expose the nvme_submit_passthru completely. Instead just provide hooks for printing or deciding if a command needs to be retried. This is similar to the MI interface. Signed-off-by: Daniel Wagner <[email protected]>
1 parent a5dcd68 commit b73c4a4

File tree

8 files changed

+217
-57
lines changed

8 files changed

+217
-57
lines changed

libnvme/src/libnvme.map

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,7 @@ LIBNVME_2_0 {
240240
nvme_scan_topology;
241241
nvme_set_application;
242242
nvme_set_debug;
243+
nvme_set_dry_run;
243244
nvme_set_keyring;
244245
nvme_set_property;
245246
nvme_set_root;
@@ -273,6 +274,9 @@ LIBNVME_2_0 {
273274
nvme_transport_handle_is_chardev;
274275
nvme_transport_handle_is_direct;
275276
nvme_transport_handle_is_mi;
277+
nvme_transport_handle_set_decide_retry;
278+
nvme_transport_handle_set_submit_entry;
279+
nvme_transport_handle_set_submit_exit;
276280
nvme_unlink_ctrl;
277281
nvme_update_config;
278282
nvme_update_key;

libnvme/src/nvme/ioctl.c

Lines changed: 49 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,6 @@
2727
#include <ccan/endian/endian.h>
2828

2929
#include "ioctl.h"
30-
#include "util.h"
31-
#include "log.h"
3230
#include "private.h"
3331

3432
static int nvme_verify_chr(struct nvme_transport_handle *hdl)
@@ -99,18 +97,21 @@ int nvme_get_nsid(struct nvme_transport_handle *hdl, __u32 *nsid)
9997
return 0;
10098
}
10199

102-
__attribute__((weak))
103-
int nvme_submit_passthru(struct nvme_transport_handle *hdl,
104-
unsigned long ioctl_cmd, struct nvme_passthru_cmd *cmd,
105-
__u64 *result)
100+
void *__nvme_submit_entry(struct nvme_transport_handle *hdl,
101+
struct nvme_passthru_cmd *cmd)
106102
{
107-
int err = ioctl(hdl->fd, ioctl_cmd, cmd);
103+
return NULL;
104+
}
108105

109-
if (err >= 0 && result)
110-
*result = cmd->result;
111-
if (err < 0)
112-
return -errno;
113-
return err;
106+
void __nvme_submit_exit(struct nvme_transport_handle *hdl,
107+
struct nvme_passthru_cmd *cmd, int err, void *user_data)
108+
{
109+
}
110+
111+
bool __nvme_decide_retry(struct nvme_transport_handle *hdl,
112+
struct nvme_passthru_cmd *cmd, int err)
113+
{
114+
return false;
114115
}
115116

116117
/*
@@ -122,17 +123,29 @@ static int nvme_submit_passthru32(struct nvme_transport_handle *hdl,
122123
__u64 *result)
123124
{
124125
struct linux_passthru_cmd32 cmd32;
125-
int err;
126+
void *user_data;
127+
int err = 0;
128+
129+
user_data = hdl->submit_entry(hdl, cmd);
130+
if (hdl->ctx->dry_run)
131+
goto out;
126132

127133
memcpy(&cmd32, cmd, offsetof(struct linux_passthru_cmd32, result));
128134
cmd32.result = 0;
129135

130-
err = ioctl(hdl->fd, ioctl_cmd, &cmd32);
136+
do {
137+
err = ioctl(hdl->fd, ioctl_cmd, &cmd32);
138+
if (err >= 0)
139+
break;
140+
err = -errno;
141+
} while (hdl->decide_retry(hdl, cmd, err));
142+
143+
out:
131144
cmd->result = cmd32.result;
132145
if (err >= 0 && result)
133146
*result = cmd->result;
134-
if (err < 0)
135-
return -errno;
147+
148+
hdl->submit_exit(hdl, cmd, err, user_data);
136149
return err;
137150
}
138151

@@ -144,13 +157,29 @@ static int nvme_submit_passthru64(struct nvme_transport_handle *hdl,
144157
unsigned long ioctl_cmd, struct nvme_passthru_cmd *cmd,
145158
__u64 *result)
146159
{
147-
int err;
160+
void *user_data;
161+
int err = 0;
162+
163+
user_data = hdl->submit_entry(hdl, cmd);
164+
if (hdl->ctx->dry_run)
165+
goto out;
166+
167+
do {
168+
/*
169+
* struct nvme_passtrhu_cmd is identically to struct
170+
* linux_passthru_cmd64, thus just pass it in directly.
171+
*/
172+
err = ioctl(hdl->fd, ioctl_cmd, cmd);
173+
if (err >= 0)
174+
break;
175+
err = -errno;
176+
} while (hdl->decide_retry(hdl, cmd, err));
148177

149-
err = ioctl(hdl->fd, ioctl_cmd, cmd);
178+
out:
150179
if (err >= 0 && result)
151180
*result = cmd->result;
152-
if (err < 0)
153-
return -errno;
181+
182+
hdl->submit_exit(hdl, cmd, err, user_data);
154183
return err;
155184
}
156185

libnvme/src/nvme/linux.c

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,39 @@
4141
#include "base64.h"
4242
#include "crc32.h"
4343

44+
void nvme_set_dry_run(struct nvme_global_ctx *ctx, bool enable)
45+
{
46+
ctx->dry_run = enable;
47+
}
48+
49+
void nvme_transport_handle_set_submit_entry(struct nvme_transport_handle *hdl,
50+
void *(*submit_entry)(struct nvme_transport_handle *hdl,
51+
struct nvme_passthru_cmd *cmd))
52+
{
53+
hdl->submit_entry = submit_entry;
54+
if (!hdl->submit_exit)
55+
hdl->submit_exit = __nvme_submit_exit;
56+
}
57+
58+
void nvme_transport_handle_set_submit_exit(struct nvme_transport_handle *hdl,
59+
void (*submit_exit)(struct nvme_transport_handle *hdl,
60+
struct nvme_passthru_cmd *cmd,
61+
int err, void *user_data))
62+
{
63+
hdl->submit_exit = submit_exit;
64+
if (!hdl->submit_exit)
65+
hdl->submit_exit = __nvme_submit_exit;
66+
}
67+
68+
void nvme_transport_handle_set_decide_retry(struct nvme_transport_handle *hdl,
69+
bool (*decide_retry)(struct nvme_transport_handle *hdl,
70+
struct nvme_passthru_cmd *cmd, int err))
71+
{
72+
hdl->decide_retry = decide_retry;
73+
if (!hdl->decide_retry)
74+
hdl->decide_retry = __nvme_decide_retry;
75+
}
76+
4477
static int __nvme_transport_handle_open_direct(struct nvme_transport_handle *hdl, const char *devname)
4578
{
4679
_cleanup_free_ char *path = NULL;
@@ -96,6 +129,9 @@ struct nvme_transport_handle *__nvme_create_transport_handle(struct nvme_global_
96129
return NULL;
97130

98131
hdl->ctx = ctx;
132+
hdl->submit_entry = __nvme_submit_entry;
133+
hdl->submit_exit = __nvme_submit_exit;
134+
hdl->decide_retry = __nvme_decide_retry;
99135

100136
return hdl;
101137
}

libnvme/src/nvme/linux.h

Lines changed: 71 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,70 @@ bool nvme_transport_handle_is_direct(struct nvme_transport_handle *hdl);
287287
*/
288288
bool nvme_transport_handle_is_mi(struct nvme_transport_handle *hdl);
289289

290+
/**
291+
* nvme_transport_handle_set_submit_entry() - Install a submit-entry callback
292+
* @hdl: Transport handle to configure
293+
* @submit_entry: Callback invoked immediately before a passthrough command is
294+
* submitted. The function receives the command about to be issued
295+
* and may return an opaque pointer representing per-command
296+
* context. This pointer is later passed unmodified to the
297+
* submit-exit callback. Implementations typically use this hook
298+
* for logging, tracing, or allocating per-command state.
299+
*
300+
* Installs a user-defined callback that is invoked at the moment a passthrough
301+
* command enters the NVMe submission path. Passing NULL removes any previously
302+
* installed callback.
303+
*
304+
* Return: None.
305+
*/
306+
void nvme_transport_handle_set_submit_entry(struct nvme_transport_handle *hdl,
307+
void *(*submit_entry)(struct nvme_transport_handle *hdl,
308+
struct nvme_passthru_cmd *cmd));
309+
310+
/**
311+
* nvme_transport_handle_set_submit_exit() - Install a submit-exit callback
312+
* @hdl: Transport handle to configure
313+
* @submit_exit: Callback invoked after a passthrough command completes. The
314+
* function receives the command, the completion status @err
315+
* (0 for success, a negative errno, or an NVMe status value), and
316+
* the @user_data pointer returned earlier by the submit-entry
317+
* callback. Implementations typically use this hook for logging,
318+
* tracing, or freeing per-command state.
319+
*
320+
* Installs a callback that is invoked when a passthrough command leaves the
321+
* NVMe submission path. Passing NULL removes any previously installed callback.
322+
*
323+
* Return: None.
324+
*/
325+
void nvme_transport_handle_set_submit_exit(struct nvme_transport_handle *hdl,
326+
void (*submit_exit)(struct nvme_transport_handle *hdl,
327+
struct nvme_passthru_cmd *cmd,
328+
int err, void *user_data));
329+
330+
/**
331+
* nvme_transport_handle_set_decide_retry() - Install a retry-decision callback
332+
* @hdl: Transport handle to configure
333+
* @decide_retry: Callback used to determine whether a passthrough command
334+
* should be retried after an error. The function is called with
335+
* the command that failed and the error code returned by the
336+
* kernel or device. The callback should return true if the
337+
* submission path should retry the command, or false if the
338+
* error is final.
339+
*
340+
* Installs a user-provided callback to control retry behavior for
341+
* passthrough commands issued through @hdl. This allows transports or
342+
* higher-level logic to implement custom retry policies, such as retrying on
343+
* transient conditions like -EAGAIN or device-specific status codes.
344+
*
345+
* Passing NULL clears any previously installed callback and reverts to the
346+
* default behavior (no retries).
347+
*
348+
* Return: None.
349+
*/
350+
void nvme_transport_handle_set_decide_retry(struct nvme_transport_handle *hdl,
351+
bool (*decide_retry)(struct nvme_transport_handle *hdl,
352+
struct nvme_passthru_cmd *cmd, int err));
353+
290354
/**
291355
* enum nvme_hmac_alg - HMAC algorithm
292356
* @NVME_HMAC_ALG_NONE: No HMAC algorithm
@@ -636,22 +700,15 @@ int nvme_import_tls_key_versioned(const char *encoded_key,
636700
unsigned char *hmac,
637701
size_t *key_len,
638702
unsigned char **key);
703+
639704
/**
640-
* nvme_submit_passthru - Low level ioctl wrapper for passthru commands
641-
* @hdl: Transport handle
642-
* @ioctl_cmd: IOCTL command id
643-
* @cmd: Passhtru command
644-
* @result: Optional field to return the result
645-
*
646-
* This is a low level library function which should not be used directly. It is
647-
* exposed as weak symbol so that the user application is able to provide their own
648-
* implementation of this function with additional debugging or logging code.
705+
* nvme_set_dry_run() - Set global dry run state
706+
* @ctx: struct nvme_global_ctx object
707+
* @enable: Enable/disable dry run state
649708
*
650-
* Return: The value from the ioctl system call (see ioctl documentation) or
651-
* a negative error code otherwise.
709+
* When dry_run is enabled, any IOCTL commands send via the passthru
710+
* interface wont be executed.
652711
*/
653-
__attribute__((weak))
654-
int nvme_submit_passthru(struct nvme_transport_handle *hdl, unsigned long ioctl_cmd,
655-
struct nvme_passthru_cmd *cmd, __u64 *result);
712+
void nvme_set_dry_run(struct nvme_global_ctx *ctx, bool enable);
656713

657714
#endif /* _LIBNVME_LINUX_H */

libnvme/src/nvme/private.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,14 @@ struct nvme_transport_handle {
9090
enum nvme_transport_handle_type type;
9191
char *name;
9292

93+
void *(*submit_entry)(struct nvme_transport_handle *hdl,
94+
struct nvme_passthru_cmd *cmd);
95+
void (*submit_exit)(struct nvme_transport_handle *hdl,
96+
struct nvme_passthru_cmd *cmd,
97+
int err, void *user_data);
98+
bool (*decide_retry)(struct nvme_transport_handle *hdl,
99+
struct nvme_passthru_cmd *cmd, int err);
100+
93101
/* direct */
94102
int fd;
95103
struct stat stat;
@@ -260,6 +268,7 @@ struct nvme_global_ctx {
260268
bool modified;
261269
bool mi_probe_enabled;
262270
bool create_only;
271+
bool dry_run;
263272
struct nvme_fabric_options *options;
264273
};
265274

@@ -271,6 +280,13 @@ int json_update_config(struct nvme_global_ctx *ctx, const char *config_file);
271280

272281
int json_dump_tree(struct nvme_global_ctx *ctx);
273282

283+
void *__nvme_submit_entry(struct nvme_transport_handle *hdl,
284+
struct nvme_passthru_cmd *cmd);
285+
void __nvme_submit_exit(struct nvme_transport_handle *hdl,
286+
struct nvme_passthru_cmd *cmd, int err, void *user_data);
287+
bool __nvme_decide_retry(struct nvme_transport_handle *hdl,
288+
struct nvme_passthru_cmd *cmd, int err);
289+
274290
struct nvme_transport_handle *__nvme_open(struct nvme_global_ctx *ctx, const char *name);
275291
struct nvme_transport_handle *__nvme_create_transport_handle(struct nvme_global_ctx *ctx);
276292
int __nvme_transport_handle_open_mi(struct nvme_transport_handle *hdl, const char *devname);

logging.c

Lines changed: 26 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -100,38 +100,41 @@ static void nvme_log_retry(int errnum)
100100
printf("passthru command returned '%s'\n", strerror(errnum));
101101
}
102102

103-
int nvme_submit_passthru(struct nvme_transport_handle *hdl,
104-
unsigned long ioctl_cmd, struct nvme_passthru_cmd *cmd,
105-
__u64 *result)
103+
void *nvme_submit_entry(struct nvme_transport_handle *hdl,
104+
struct nvme_passthru_cmd *cmd)
106105
{
107-
struct timeval start;
108-
struct timeval end;
109-
int err = 0;
106+
memset(&sb, 0, sizeof(sb));
110107

111108
if (log_level >= LOG_DEBUG)
112-
gettimeofday(&start, NULL);
113-
114-
if (!nvme_cfg.dry_run) {
115-
retry:
116-
err = ioctl(nvme_transport_handle_get_fd(hdl), ioctl_cmd, cmd);
117-
if ((err && (errno == EAGAIN ||
118-
(errno == EINTR && !nvme_sigint_received))) &&
119-
!nvme_cfg.no_retries) {
120-
nvme_log_retry(errno);
121-
goto retry;
122-
}
123-
}
109+
gettimeofday(&sb.start, NULL);
110+
111+
return &sb;
112+
}
113+
114+
void nvme_submit_exit(struct nvme_transport_handle *hdl,
115+
struct nvme_passthru_cmd *cmd, int err, void *user_data)
116+
{
117+
struct submit_data *sb = user_data;
124118

125119
if (log_level >= LOG_DEBUG) {
126-
gettimeofday(&end, NULL);
120+
gettimeofday(&sb->end, NULL);
127121
nvme_show_command(cmd, err);
128-
nvme_show_latency(start, end);
122+
nvme_show_latency(sb->start, sb->end);
129123
}
124+
}
125+
126+
bool nvme_decide_retry(struct nvme_transport_handle *hdl,
127+
struct nvme_passthru_cmd *cmd, int err)
128+
{
129+
if (!nvme_cfg.no_retries)
130+
return false;
130131

131-
if (err >= 0 && result)
132-
*result = cmd->result;
132+
if (err != -EAGAIN ||
133+
!(err == -EINTR && !nvme_sigint_received))
134+
return false;
133135

134-
return err;
136+
nvme_log_retry(errno);
137+
return true;
135138
}
136139

137140
static void nvme_show_req_admin(const struct nvme_mi_admin_req_hdr *hdr, size_t hdr_len,

logging.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,16 @@
1919

2020
extern int log_level;
2121

22+
struct nvme_transport_handle;
23+
struct nvme_passthru_cmd;
24+
25+
void *nvme_submit_entry(struct nvme_transport_handle *hdl,
26+
struct nvme_passthru_cmd *cmd);
27+
void nvme_submit_exit(struct nvme_transport_handle *hdl,
28+
struct nvme_passthru_cmd *cmd, int err, void *user_data);
29+
bool nvme_decide_retry(struct nvme_transport_handle *hdl,
30+
struct nvme_passthru_cmd *cmd, int err);
31+
2232
int map_log_level(int verbose, bool quiet);
2333

2434
#endif // DEBUG_H_

0 commit comments

Comments
 (0)