Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright (C) 2019 Intel Corporation.
3 : : * All rights reserved.
4 : : */
5 : : #include "spdk/stdinc.h"
6 : : #include "spdk/string.h"
7 : : #include "spdk/config.h"
8 : : #include "spdk/fd_group.h"
9 : : #include "spdk/log.h"
10 : : #include "spdk/nvme.h"
11 : :
12 : : #define FUSE_USE_VERSION 31
13 : :
14 : : #include <fuse3/cuse_lowlevel.h>
15 : :
16 : : #include <linux/nvme_ioctl.h>
17 : : #include <linux/fs.h>
18 : :
19 : : #include "nvme_internal.h"
20 : : #include "nvme_io_msg.h"
21 : : #include "nvme_cuse.h"
22 : :
23 : : struct cuse_device {
24 : : bool force_exit;
25 : : char dev_name[128];
26 : : uint32_t index;
27 : : int claim_fd;
28 : : char lock_name[64];
29 : :
30 : : struct spdk_nvme_ctrlr *ctrlr; /**< NVMe controller */
31 : : uint32_t nsid; /**< NVMe name space id, or 0 */
32 : :
33 : : struct fuse_session *session;
34 : : int fuse_efd;
35 : :
36 : : struct cuse_device *ctrlr_device;
37 : : TAILQ_HEAD(, cuse_device) ns_devices;
38 : :
39 : : TAILQ_ENTRY(cuse_device) tailq;
40 : : TAILQ_ENTRY(cuse_device) cuse_thread_tailq;
41 : : };
42 : :
43 : : static pthread_mutex_t g_cuse_mtx = PTHREAD_MUTEX_INITIALIZER;
44 : : static TAILQ_HEAD(, cuse_device) g_ctrlr_ctx_head = TAILQ_HEAD_INITIALIZER(g_ctrlr_ctx_head);
45 : : static struct spdk_bit_array *g_ctrlr_started;
46 : :
47 : : static pthread_mutex_t g_pending_device_mtx = PTHREAD_MUTEX_INITIALIZER;
48 : : static struct spdk_fd_group *g_device_fdgrp;
49 : : static int g_cuse_thread_msg_fd;
50 : : static TAILQ_HEAD(, cuse_device) g_pending_device_head = TAILQ_HEAD_INITIALIZER(
51 : : g_pending_device_head);
52 : : static TAILQ_HEAD(, cuse_device) g_active_device_head = TAILQ_HEAD_INITIALIZER(
53 : : g_active_device_head);
54 : :
55 : : struct cuse_io_ctx {
56 : : struct spdk_nvme_cmd nvme_cmd;
57 : : enum spdk_nvme_data_transfer data_transfer;
58 : :
59 : : uint64_t lba;
60 : : uint32_t lba_count;
61 : : uint16_t apptag;
62 : : uint16_t appmask;
63 : :
64 : : void *data;
65 : : void *metadata;
66 : :
67 : : int data_len;
68 : : int metadata_len;
69 : :
70 : : fuse_req_t req;
71 : : };
72 : :
73 : : static void
74 : 68 : cuse_io_ctx_free(struct cuse_io_ctx *ctx)
75 : : {
76 [ # # # # ]: 68 : spdk_free(ctx->data);
77 [ # # # # ]: 68 : spdk_free(ctx->metadata);
78 : 68 : free(ctx);
79 : 68 : }
80 : :
81 : : #define FUSE_REPLY_CHECK_BUFFER(req, arg, out_bufsz, val) \
82 : : if (out_bufsz == 0) { \
83 : : struct iovec out_iov; \
84 : : out_iov.iov_base = (void *)arg; \
85 : : out_iov.iov_len = sizeof(val); \
86 : : fuse_reply_ioctl_retry(req, NULL, 0, &out_iov, 1); \
87 : : return; \
88 : : }
89 : :
90 : : #define FUSE_MAX_SIZE 128*1024
91 : :
92 : : static bool
93 : 182 : fuse_check_req_size(fuse_req_t req, struct iovec iov[], int iovcnt)
94 : : {
95 : 182 : int total_iov_len = 0;
96 [ + + # # ]: 447 : for (int i = 0; i < iovcnt; i++) {
97 [ # # # # : 265 : total_iov_len += iov[i].iov_len;
# # ]
98 [ - + ]: 265 : if (total_iov_len > FUSE_MAX_SIZE) {
99 : 0 : fuse_reply_err(req, ENOMEM);
100 : 0 : SPDK_ERRLOG("FUSE request cannot be larger that %d\n", FUSE_MAX_SIZE);
101 : 0 : return false;
102 : : }
103 : 0 : }
104 : 182 : return true;
105 : 0 : }
106 : :
107 : : static void
108 : 44 : cuse_nvme_passthru_cmd_cb(void *arg, const struct spdk_nvme_cpl *cpl)
109 : : {
110 : 44 : struct cuse_io_ctx *ctx = arg;
111 : 44 : struct iovec out_iov[3];
112 : 44 : struct spdk_nvme_cpl _cpl;
113 : 44 : int out_iovcnt = 0;
114 [ # # # # : 44 : uint16_t status_field = cpl->status_raw >> 1; /* Drop out phase bit */
# # # # ]
115 : :
116 [ # # # # ]: 44 : memcpy(&_cpl, cpl, sizeof(struct spdk_nvme_cpl));
117 [ # # # # : 44 : out_iov[out_iovcnt].iov_base = &_cpl.cdw0;
# # # # ]
118 [ # # # # : 44 : out_iov[out_iovcnt].iov_len = sizeof(_cpl.cdw0);
# # # # ]
119 [ # # ]: 44 : out_iovcnt += 1;
120 : :
121 [ + + # # : 44 : if (ctx->data_transfer == SPDK_NVME_DATA_CONTROLLER_TO_HOST) {
# # ]
122 [ + - # # : 36 : if (ctx->data_len > 0) {
# # ]
123 [ # # # # : 36 : out_iov[out_iovcnt].iov_base = ctx->data;
# # # # #
# # # ]
124 [ # # # # : 36 : out_iov[out_iovcnt].iov_len = ctx->data_len;
# # # # #
# # # ]
125 [ # # ]: 36 : out_iovcnt += 1;
126 : 0 : }
127 [ - + # # : 36 : if (ctx->metadata_len > 0) {
# # ]
128 [ # # # # : 0 : out_iov[out_iovcnt].iov_base = ctx->metadata;
# # # # #
# # # ]
129 [ # # # # : 0 : out_iov[out_iovcnt].iov_len = ctx->metadata_len;
# # # # #
# # # ]
130 [ # # ]: 0 : out_iovcnt += 1;
131 : 0 : }
132 : 0 : }
133 : :
134 [ # # # # ]: 44 : fuse_reply_ioctl_iov(ctx->req, status_field, out_iov, out_iovcnt);
135 : 44 : cuse_io_ctx_free(ctx);
136 : 44 : }
137 : :
138 : : static void
139 : 44 : cuse_nvme_passthru_cmd_execute(struct spdk_nvme_ctrlr *ctrlr, uint32_t nsid, void *arg)
140 : : {
141 : : int rc;
142 : 44 : struct cuse_io_ctx *ctx = arg;
143 : :
144 [ + + ]: 44 : if (nsid != 0) {
145 [ # # # # : 4 : rc = spdk_nvme_ctrlr_cmd_io_raw_with_md(ctrlr, ctrlr->external_io_msgs_qpair, &ctx->nvme_cmd,
# # ]
146 [ # # # # ]: 0 : ctx->data,
147 [ # # # # : 2 : ctx->data_len, ctx->metadata, cuse_nvme_passthru_cmd_cb, (void *)ctx);
# # # # ]
148 : 0 : } else {
149 [ # # # # : 42 : rc = spdk_nvme_ctrlr_cmd_admin_raw(ctrlr, &ctx->nvme_cmd, ctx->data, ctx->data_len,
# # # # #
# ]
150 : 0 : cuse_nvme_passthru_cmd_cb, (void *)ctx);
151 : : }
152 [ - + ]: 44 : if (rc < 0) {
153 [ # # # # ]: 0 : fuse_reply_err(ctx->req, EINVAL);
154 : 0 : cuse_io_ctx_free(ctx);
155 : 0 : }
156 : 44 : }
157 : :
158 : : static void
159 : 50 : cuse_nvme_passthru_cmd_send(fuse_req_t req, struct nvme_passthru_cmd *passthru_cmd,
160 : : const void *data, const void *metadata, int cmd)
161 : : {
162 : : struct cuse_io_ctx *ctx;
163 : 50 : struct cuse_device *cuse_device = fuse_req_userdata(req);
164 : : int rv;
165 : :
166 : 50 : ctx = (struct cuse_io_ctx *)calloc(1, sizeof(struct cuse_io_ctx));
167 [ - + ]: 50 : if (!ctx) {
168 : 0 : SPDK_ERRLOG("Cannot allocate memory for cuse_io_ctx\n");
169 : 0 : fuse_reply_err(req, ENOMEM);
170 : 0 : return;
171 : : }
172 : :
173 [ # # # # ]: 50 : ctx->req = req;
174 [ # # # # : 50 : ctx->data_transfer = spdk_nvme_opc_get_data_transfer(passthru_cmd->opcode);
# # # # ]
175 : :
176 [ - + # # ]: 50 : memset(&ctx->nvme_cmd, 0, sizeof(ctx->nvme_cmd));
177 [ # # # # : 50 : ctx->nvme_cmd.opc = passthru_cmd->opcode;
# # # # ]
178 [ # # # # : 50 : ctx->nvme_cmd.nsid = passthru_cmd->nsid;
# # # # #
# ]
179 [ # # # # : 50 : ctx->nvme_cmd.cdw10 = passthru_cmd->cdw10;
# # # # #
# # # ]
180 [ # # # # : 50 : ctx->nvme_cmd.cdw11 = passthru_cmd->cdw11;
# # # # #
# # # ]
181 [ # # # # : 50 : ctx->nvme_cmd.cdw12 = passthru_cmd->cdw12;
# # # # #
# # # ]
182 [ # # # # : 50 : ctx->nvme_cmd.cdw13 = passthru_cmd->cdw13;
# # # # #
# # # ]
183 [ # # # # : 50 : ctx->nvme_cmd.cdw14 = passthru_cmd->cdw14;
# # # # #
# ]
184 [ # # # # : 50 : ctx->nvme_cmd.cdw15 = passthru_cmd->cdw15;
# # # # #
# ]
185 : :
186 [ # # # # : 50 : ctx->data_len = passthru_cmd->data_len;
# # # # ]
187 [ # # # # : 50 : ctx->metadata_len = passthru_cmd->metadata_len;
# # # # ]
188 : :
189 [ + + # # : 50 : if (ctx->data_len > 0) {
# # ]
190 [ # # # # : 46 : ctx->data = spdk_malloc(ctx->data_len, 4096, NULL, SPDK_ENV_LCORE_ID_ANY, SPDK_MALLOC_DMA);
# # # # ]
191 [ - + # # : 46 : if (!ctx->data) {
# # ]
192 : 0 : SPDK_ERRLOG("Cannot allocate memory for data\n");
193 : 0 : fuse_reply_err(req, ENOMEM);
194 : 0 : free(ctx);
195 : 0 : return;
196 : : }
197 [ + + ]: 46 : if (data != NULL) {
198 [ - + - + : 4 : memcpy(ctx->data, data, ctx->data_len);
# # # # #
# # # ]
199 : 0 : }
200 : 0 : }
201 : :
202 [ + + # # : 50 : if (ctx->metadata_len > 0) {
# # ]
203 [ # # # # : 3 : ctx->metadata = spdk_malloc(ctx->metadata_len, 4096, NULL, SPDK_ENV_LCORE_ID_ANY, SPDK_MALLOC_DMA);
# # # # ]
204 [ - + # # : 3 : if (!ctx->metadata) {
# # ]
205 : 0 : SPDK_ERRLOG("Cannot allocate memory for metadata\n");
206 : 0 : fuse_reply_err(req, ENOMEM);
207 : 0 : cuse_io_ctx_free(ctx);
208 : 0 : return;
209 : : }
210 [ - + ]: 3 : if (metadata != NULL) {
211 [ # # # # : 0 : memcpy(ctx->metadata, metadata, ctx->metadata_len);
# # # # #
# # # ]
212 : 0 : }
213 : 0 : }
214 : :
215 [ + + # # : 50 : if ((unsigned int)cmd != NVME_IOCTL_ADMIN_CMD) {
# # # # #
# # # #
# ]
216 : : /* Send NS for IO IOCTLs */
217 [ # # # # : 8 : rv = nvme_io_msg_send(cuse_device->ctrlr, passthru_cmd->nsid, cuse_nvme_passthru_cmd_execute, ctx);
# # # # ]
218 : 0 : } else {
219 : : /* NS == 0 for Admin IOCTLs */
220 [ # # # # ]: 42 : rv = nvme_io_msg_send(cuse_device->ctrlr, 0, cuse_nvme_passthru_cmd_execute, ctx);
221 : : }
222 [ - + ]: 50 : if (rv) {
223 : 0 : SPDK_ERRLOG("Cannot send io msg to the controller\n");
224 [ # # ]: 0 : fuse_reply_err(req, -rv);
225 : 0 : cuse_io_ctx_free(ctx);
226 : 0 : return;
227 : : }
228 : 0 : }
229 : :
230 : : static void
231 : 132 : cuse_nvme_passthru_cmd(fuse_req_t req, int cmd, void *arg,
232 : : struct fuse_file_info *fi, unsigned flags,
233 : : const void *in_buf, size_t in_bufsz, size_t out_bufsz)
234 : : {
235 : : struct nvme_passthru_cmd *passthru_cmd;
236 : 132 : struct iovec in_iov[3], out_iov[3];
237 : 132 : int in_iovcnt = 0, out_iovcnt = 0;
238 : 132 : const void *dptr = NULL, *mdptr = NULL;
239 : : enum spdk_nvme_data_transfer data_transfer;
240 : :
241 [ # # # # : 132 : in_iov[in_iovcnt].iov_base = (void *)arg;
# # # # ]
242 [ # # # # : 132 : in_iov[in_iovcnt].iov_len = sizeof(*passthru_cmd);
# # # # ]
243 [ # # ]: 132 : in_iovcnt += 1;
244 [ + + ]: 132 : if (in_bufsz == 0) {
245 : 44 : fuse_reply_ioctl_retry(req, in_iov, in_iovcnt, NULL, out_iovcnt);
246 : 44 : return;
247 : : }
248 : :
249 : 88 : passthru_cmd = (struct nvme_passthru_cmd *)in_buf;
250 [ # # # # ]: 88 : data_transfer = spdk_nvme_opc_get_data_transfer(passthru_cmd->opcode);
251 : :
252 [ + + ]: 88 : if (data_transfer == SPDK_NVME_DATA_HOST_TO_CONTROLLER) {
253 : : /* Make data pointer accessible (RO) */
254 [ + + # # : 14 : if (passthru_cmd->addr != 0) {
# # ]
255 [ # # # # : 8 : in_iov[in_iovcnt].iov_base = (void *)passthru_cmd->addr;
# # # # #
# # # ]
256 [ # # # # : 8 : in_iov[in_iovcnt].iov_len = passthru_cmd->data_len;
# # # # #
# # # ]
257 [ # # ]: 8 : in_iovcnt += 1;
258 : 0 : }
259 : : /* Make metadata pointer accessible (RO) */
260 [ - + # # : 14 : if (passthru_cmd->metadata != 0) {
# # ]
261 [ # # # # : 0 : in_iov[in_iovcnt].iov_base = (void *)passthru_cmd->metadata;
# # # # #
# # # ]
262 [ # # # # : 0 : in_iov[in_iovcnt].iov_len = passthru_cmd->metadata_len;
# # # # #
# # # ]
263 [ # # ]: 0 : in_iovcnt += 1;
264 : 0 : }
265 : 0 : }
266 : :
267 [ - + ]: 88 : if (!fuse_check_req_size(req, in_iov, in_iovcnt)) {
268 : 0 : return;
269 : : }
270 : : /* Always make result field writeable regardless of data transfer bits */
271 [ # # # # : 88 : out_iov[out_iovcnt].iov_base = &((struct nvme_passthru_cmd *)arg)->result;
# # # # #
# ]
272 [ # # # # : 88 : out_iov[out_iovcnt].iov_len = sizeof(uint32_t);
# # # # ]
273 [ # # ]: 88 : out_iovcnt += 1;
274 : :
275 [ + + ]: 88 : if (data_transfer == SPDK_NVME_DATA_CONTROLLER_TO_HOST) {
276 : : /* Make data pointer accessible (WO) */
277 [ + - # # : 72 : if (passthru_cmd->data_len > 0) {
# # ]
278 [ # # # # : 72 : out_iov[out_iovcnt].iov_base = (void *)passthru_cmd->addr;
# # # # #
# # # ]
279 [ # # # # : 72 : out_iov[out_iovcnt].iov_len = passthru_cmd->data_len;
# # # # #
# # # ]
280 [ # # ]: 72 : out_iovcnt += 1;
281 : 0 : }
282 : : /* Make metadata pointer accessible (WO) */
283 [ - + # # : 72 : if (passthru_cmd->metadata_len > 0) {
# # ]
284 [ # # # # : 0 : out_iov[out_iovcnt].iov_base = (void *)passthru_cmd->metadata;
# # # # #
# # # ]
285 [ # # # # : 0 : out_iov[out_iovcnt].iov_len = passthru_cmd->metadata_len;
# # # # #
# # # ]
286 [ # # ]: 0 : out_iovcnt += 1;
287 : 0 : }
288 : 0 : }
289 : :
290 [ - + ]: 88 : if (!fuse_check_req_size(req, out_iov, out_iovcnt)) {
291 : 0 : return;
292 : : }
293 : :
294 [ + + ]: 88 : if (out_bufsz == 0) {
295 : 44 : fuse_reply_ioctl_retry(req, in_iov, in_iovcnt, out_iov, out_iovcnt);
296 : 44 : return;
297 : : }
298 : :
299 [ - + ]: 44 : if (data_transfer == SPDK_NVME_DATA_BIDIRECTIONAL) {
300 : 0 : fuse_reply_err(req, EINVAL);
301 : 0 : return;
302 : : }
303 : :
304 [ + + ]: 44 : if (data_transfer == SPDK_NVME_DATA_HOST_TO_CONTROLLER) {
305 [ + + # # : 7 : dptr = (passthru_cmd->addr == 0) ? NULL : (uint8_t *)in_buf + sizeof(*passthru_cmd);
# # # # ]
306 [ - + # # : 7 : mdptr = (passthru_cmd->metadata == 0) ? NULL : (uint8_t *)in_buf + sizeof(*passthru_cmd) +
# # # # #
# ]
307 [ # # # # ]: 0 : passthru_cmd->data_len;
308 : 0 : }
309 : :
310 : 44 : cuse_nvme_passthru_cmd_send(req, passthru_cmd, dptr, mdptr, cmd);
311 : 0 : }
312 : :
313 : : static void
314 : 3 : cuse_nvme_reset_execute(struct spdk_nvme_ctrlr *ctrlr, uint32_t nsid, void *arg)
315 : : {
316 : : int rc;
317 : 3 : fuse_req_t req = arg;
318 : :
319 : 3 : rc = spdk_nvme_ctrlr_reset(ctrlr);
320 [ - + ]: 3 : if (rc) {
321 : 0 : fuse_reply_err(req, rc);
322 : 0 : return;
323 : : }
324 : :
325 : 3 : fuse_reply_ioctl_iov(req, 0, NULL, 0);
326 : 0 : }
327 : :
328 : : static void
329 : 0 : cuse_nvme_subsys_reset_execute(struct spdk_nvme_ctrlr *ctrlr, uint32_t nsid, void *arg)
330 : : {
331 : : int rc;
332 : 0 : fuse_req_t req = arg;
333 : :
334 : 0 : rc = spdk_nvme_ctrlr_reset_subsystem(ctrlr);
335 [ # # ]: 0 : if (rc) {
336 : 0 : fuse_reply_err(req, rc);
337 : 0 : return;
338 : : }
339 : :
340 : 0 : fuse_reply_ioctl_iov(req, 0, NULL, 0);
341 : 0 : }
342 : :
343 : : static void
344 : 9 : cuse_nvme_reset(fuse_req_t req, int cmd, void *arg,
345 : : struct fuse_file_info *fi, unsigned flags,
346 : : const void *in_buf, size_t in_bufsz, size_t out_bufsz)
347 : : {
348 : : int rv;
349 : 9 : struct cuse_device *cuse_device = fuse_req_userdata(req);
350 : :
351 [ + + # # : 9 : if (cuse_device->nsid) {
# # ]
352 : 3 : SPDK_ERRLOG("Namespace reset not supported\n");
353 : 3 : fuse_reply_err(req, EINVAL);
354 : 3 : return;
355 : : }
356 : :
357 [ - + # # : 6 : if (cmd == NVME_IOCTL_SUBSYS_RESET) {
# # # # #
# # # # #
# # ]
358 [ # # # # : 0 : SPDK_DEBUGLOG(nvme_cuse, "NVME_IOCTL_SUBSYS_RESET\n");
# # ]
359 [ # # # # : 0 : rv = nvme_io_msg_send(cuse_device->ctrlr, cuse_device->nsid, cuse_nvme_subsys_reset_execute,
# # # # ]
360 : 0 : (void *)req);
361 : 0 : } else {
362 [ - + - + : 6 : SPDK_DEBUGLOG(nvme_cuse, "NVME_IOCTL_RESET\n");
# # ]
363 [ # # # # : 6 : rv = nvme_io_msg_send(cuse_device->ctrlr, cuse_device->nsid, cuse_nvme_reset_execute, (void *)req);
# # # # ]
364 : : }
365 [ - + ]: 6 : if (rv) {
366 : 0 : SPDK_ERRLOG("Cannot send reset\n");
367 : 0 : fuse_reply_err(req, EINVAL);
368 : 0 : }
369 : 0 : }
370 : :
371 : : static void
372 : 0 : cuse_nvme_rescan_execute(struct spdk_nvme_ctrlr *ctrlr, uint32_t nsid, void *arg)
373 : : {
374 : 0 : fuse_req_t req = arg;
375 : :
376 : 0 : nvme_ctrlr_update_namespaces(ctrlr);
377 : 0 : fuse_reply_ioctl_iov(req, 0, NULL, 0);
378 : 0 : }
379 : :
380 : : static void
381 : 0 : cuse_nvme_rescan(fuse_req_t req, int cmd, void *arg,
382 : : struct fuse_file_info *fi, unsigned flags,
383 : : const void *in_buf, size_t in_bufsz, size_t out_bufsz)
384 : : {
385 : : int rv;
386 : 0 : struct cuse_device *cuse_device = fuse_req_userdata(req);
387 : :
388 [ # # # # : 0 : if (cuse_device->nsid) {
# # ]
389 : 0 : SPDK_ERRLOG("Namespace rescan not supported\n");
390 : 0 : fuse_reply_err(req, EINVAL);
391 : 0 : return;
392 : : }
393 : :
394 [ # # # # : 0 : rv = nvme_io_msg_send(cuse_device->ctrlr, cuse_device->nsid, cuse_nvme_rescan_execute, (void *)req);
# # # # ]
395 [ # # ]: 0 : if (rv) {
396 : 0 : SPDK_ERRLOG("Cannot send rescan\n");
397 : 0 : fuse_reply_err(req, EINVAL);
398 : 0 : }
399 : 0 : }
400 : :
401 : : /*****************************************************************************
402 : : * Namespace IO requests
403 : : */
404 : :
405 : : static void
406 : 0 : cuse_nvme_submit_io_write_done(void *ref, const struct spdk_nvme_cpl *cpl)
407 : : {
408 : 0 : struct cuse_io_ctx *ctx = (struct cuse_io_ctx *)ref;
409 [ # # # # : 0 : uint16_t status_field = cpl->status_raw >> 1; /* Drop out phase bit */
# # # # ]
410 : :
411 [ # # # # ]: 0 : fuse_reply_ioctl_iov(ctx->req, status_field, NULL, 0);
412 : :
413 : 0 : cuse_io_ctx_free(ctx);
414 : 0 : }
415 : :
416 : : static void
417 : 0 : cuse_nvme_submit_io_write_cb(struct spdk_nvme_ctrlr *ctrlr, uint32_t nsid, void *arg)
418 : : {
419 : : int rc;
420 : 0 : struct cuse_io_ctx *ctx = arg;
421 : 0 : struct spdk_nvme_ns *ns = spdk_nvme_ctrlr_get_ns(ctrlr, nsid);
422 : :
423 [ # # # # : 0 : rc = spdk_nvme_ns_cmd_write_with_md(ns, ctrlr->external_io_msgs_qpair, ctx->data, ctx->metadata,
# # # # #
# # # ]
424 [ # # # # ]: 0 : ctx->lba, /* LBA start */
425 [ # # # # ]: 0 : ctx->lba_count, /* number of LBAs */
426 : 0 : cuse_nvme_submit_io_write_done, ctx, 0,
427 [ # # # # : 0 : ctx->appmask, ctx->apptag);
# # # # ]
428 : :
429 [ # # ]: 0 : if (rc != 0) {
430 : 0 : SPDK_ERRLOG("write failed: rc = %d\n", rc);
431 [ # # # # ]: 0 : fuse_reply_err(ctx->req, rc);
432 : 0 : cuse_io_ctx_free(ctx);
433 : 0 : return;
434 : : }
435 : 0 : }
436 : :
437 : : static void
438 : 9 : cuse_nvme_submit_io_write(struct cuse_device *cuse_device, fuse_req_t req, int cmd, void *arg,
439 : : struct fuse_file_info *fi, unsigned flags, uint32_t block_size, uint32_t md_size,
440 : : const void *in_buf, size_t in_bufsz, size_t out_bufsz)
441 : : {
442 : 9 : const struct nvme_user_io *user_io = in_buf;
443 : : struct cuse_io_ctx *ctx;
444 : : int rc;
445 : :
446 : 9 : ctx = (struct cuse_io_ctx *)calloc(1, sizeof(struct cuse_io_ctx));
447 [ - + ]: 9 : if (!ctx) {
448 : 0 : SPDK_ERRLOG("Cannot allocate memory for context\n");
449 : 0 : fuse_reply_err(req, ENOMEM);
450 : 0 : return;
451 : : }
452 : :
453 [ # # # # ]: 9 : ctx->req = req;
454 [ # # # # : 9 : ctx->lba = user_io->slba;
# # # # ]
455 [ # # # # : 9 : ctx->lba_count = user_io->nblocks + 1;
# # # # #
# ]
456 [ # # # # : 9 : ctx->data_len = ctx->lba_count * block_size;
# # # # ]
457 : :
458 [ # # # # : 9 : ctx->data = spdk_zmalloc(ctx->data_len, 0x1000, NULL, SPDK_ENV_NUMA_ID_ANY,
# # # # ]
459 : : SPDK_MALLOC_DMA);
460 [ - + # # : 9 : if (ctx->data == NULL) {
# # ]
461 : 0 : SPDK_ERRLOG("Write buffer allocation failed\n");
462 [ # # # # ]: 0 : fuse_reply_err(ctx->req, ENOMEM);
463 : 0 : free(ctx);
464 : 0 : return;
465 : : }
466 : :
467 [ - + - + : 9 : memcpy(ctx->data, (uint8_t *)in_buf + sizeof(*user_io), ctx->data_len);
# # # # #
# # # #
# ]
468 : :
469 [ + + # # : 9 : if (user_io->metadata) {
# # ]
470 [ # # # # : 3 : ctx->apptag = user_io->apptag;
# # # # ]
471 [ # # # # : 3 : ctx->appmask = user_io->appmask;
# # # # ]
472 [ # # # # : 3 : ctx->metadata_len = md_size * ctx->lba_count;
# # # # ]
473 [ # # # # : 3 : ctx->metadata = spdk_zmalloc(ctx->metadata_len, 4096, NULL, SPDK_ENV_LCORE_ID_ANY, SPDK_MALLOC_DMA);
# # # # ]
474 : :
475 [ - + # # : 3 : if (ctx->metadata == NULL) {
# # ]
476 : 0 : SPDK_ERRLOG("Cannot allocate memory for metadata\n");
477 [ # # # # : 0 : if (ctx->metadata_len == 0) {
# # ]
478 : 0 : SPDK_ERRLOG("Device format does not support metadata\n");
479 : 0 : }
480 : 0 : fuse_reply_err(req, ENOMEM);
481 : 0 : cuse_io_ctx_free(ctx);
482 : 0 : return;
483 : : }
484 : :
485 [ - + - + : 3 : memcpy(ctx->metadata, (uint8_t *)in_buf + sizeof(*user_io) + ctx->data_len,
# # # # #
# # # # #
# # ]
486 [ # # # # ]: 3 : ctx->metadata_len);
487 : 0 : }
488 : :
489 [ # # # # : 9 : rc = nvme_io_msg_send(cuse_device->ctrlr, cuse_device->nsid, cuse_nvme_submit_io_write_cb,
# # # # ]
490 : 0 : ctx);
491 [ - + ]: 9 : if (rc < 0) {
492 : 0 : SPDK_ERRLOG("Cannot send write io\n");
493 [ # # # # ]: 0 : fuse_reply_err(ctx->req, rc);
494 : 0 : cuse_io_ctx_free(ctx);
495 : 0 : }
496 : 0 : }
497 : :
498 : : static void
499 : 0 : cuse_nvme_submit_io_read_done(void *ref, const struct spdk_nvme_cpl *cpl)
500 : : {
501 : 0 : struct cuse_io_ctx *ctx = (struct cuse_io_ctx *)ref;
502 : 0 : struct iovec iov[2];
503 : 0 : int iovcnt = 0;
504 [ # # # # : 0 : uint16_t status_field = cpl->status_raw >> 1; /* Drop out phase bit */
# # # # ]
505 : :
506 [ # # # # : 0 : iov[iovcnt].iov_base = ctx->data;
# # # # #
# # # ]
507 [ # # # # : 0 : iov[iovcnt].iov_len = ctx->data_len;
# # # # #
# # # ]
508 [ # # ]: 0 : iovcnt += 1;
509 : :
510 [ # # # # : 0 : if (ctx->metadata) {
# # ]
511 [ # # # # : 0 : iov[iovcnt].iov_base = ctx->metadata;
# # # # #
# # # ]
512 [ # # # # : 0 : iov[iovcnt].iov_len = ctx->metadata_len;
# # # # #
# # # ]
513 [ # # ]: 0 : iovcnt += 1;
514 : 0 : }
515 : :
516 [ # # # # ]: 0 : fuse_reply_ioctl_iov(ctx->req, status_field, iov, iovcnt);
517 : :
518 : 0 : cuse_io_ctx_free(ctx);
519 : 0 : }
520 : :
521 : : static void
522 : 0 : cuse_nvme_submit_io_read_cb(struct spdk_nvme_ctrlr *ctrlr, uint32_t nsid, void *arg)
523 : : {
524 : : int rc;
525 : 0 : struct cuse_io_ctx *ctx = arg;
526 : 0 : struct spdk_nvme_ns *ns = spdk_nvme_ctrlr_get_ns(ctrlr, nsid);
527 : :
528 [ # # # # : 0 : rc = spdk_nvme_ns_cmd_read_with_md(ns, ctrlr->external_io_msgs_qpair, ctx->data, ctx->metadata,
# # # # #
# # # ]
529 [ # # # # ]: 0 : ctx->lba, /* LBA start */
530 [ # # # # ]: 0 : ctx->lba_count, /* number of LBAs */
531 : 0 : cuse_nvme_submit_io_read_done, ctx, 0,
532 [ # # # # : 0 : ctx->appmask, ctx->apptag);
# # # # ]
533 : :
534 [ # # ]: 0 : if (rc != 0) {
535 : 0 : SPDK_ERRLOG("read failed: rc = %d\n", rc);
536 [ # # # # ]: 0 : fuse_reply_err(ctx->req, rc);
537 : 0 : cuse_io_ctx_free(ctx);
538 : 0 : return;
539 : : }
540 : 0 : }
541 : :
542 : : static void
543 : 9 : cuse_nvme_submit_io_read(struct cuse_device *cuse_device, fuse_req_t req, int cmd, void *arg,
544 : : struct fuse_file_info *fi, unsigned flags, uint32_t block_size, uint32_t md_size,
545 : : const void *in_buf, size_t in_bufsz, size_t out_bufsz)
546 : : {
547 : : int rc;
548 : : struct cuse_io_ctx *ctx;
549 : 9 : const struct nvme_user_io *user_io = in_buf;
550 : :
551 : 9 : ctx = (struct cuse_io_ctx *)calloc(1, sizeof(struct cuse_io_ctx));
552 [ - + ]: 9 : if (!ctx) {
553 : 0 : SPDK_ERRLOG("Cannot allocate memory for context\n");
554 : 0 : fuse_reply_err(req, ENOMEM);
555 : 0 : return;
556 : : }
557 : :
558 [ # # # # ]: 9 : ctx->req = req;
559 [ # # # # : 9 : ctx->lba = user_io->slba;
# # # # ]
560 [ # # # # : 9 : ctx->lba_count = user_io->nblocks + 1;
# # # # #
# ]
561 : :
562 [ # # # # : 9 : ctx->data_len = ctx->lba_count * block_size;
# # # # ]
563 [ # # # # : 9 : ctx->data = spdk_zmalloc(ctx->data_len, 0x1000, NULL, SPDK_ENV_NUMA_ID_ANY,
# # # # ]
564 : : SPDK_MALLOC_DMA);
565 [ - + # # : 9 : if (ctx->data == NULL) {
# # ]
566 : 0 : SPDK_ERRLOG("Read buffer allocation failed\n");
567 [ # # # # ]: 0 : fuse_reply_err(ctx->req, ENOMEM);
568 : 0 : free(ctx);
569 : 0 : return;
570 : : }
571 : :
572 [ + + # # : 9 : if (user_io->metadata) {
# # ]
573 [ # # # # : 3 : ctx->apptag = user_io->apptag;
# # # # ]
574 [ # # # # : 3 : ctx->appmask = user_io->appmask;
# # # # ]
575 [ # # # # : 3 : ctx->metadata_len = md_size * ctx->lba_count;
# # # # ]
576 [ # # # # : 3 : ctx->metadata = spdk_zmalloc(ctx->metadata_len, 4096, NULL, SPDK_ENV_LCORE_ID_ANY, SPDK_MALLOC_DMA);
# # # # ]
577 : :
578 [ - + # # : 3 : if (ctx->metadata == NULL) {
# # ]
579 : 0 : SPDK_ERRLOG("Cannot allocate memory for metadata\n");
580 [ # # # # : 0 : if (ctx->metadata_len == 0) {
# # ]
581 : 0 : SPDK_ERRLOG("Device format does not support metadata\n");
582 : 0 : }
583 : 0 : fuse_reply_err(req, ENOMEM);
584 : 0 : cuse_io_ctx_free(ctx);
585 : 0 : return;
586 : : }
587 : 0 : }
588 : :
589 [ # # # # : 9 : rc = nvme_io_msg_send(cuse_device->ctrlr, cuse_device->nsid, cuse_nvme_submit_io_read_cb, ctx);
# # # # ]
590 [ - + ]: 9 : if (rc < 0) {
591 : 0 : SPDK_ERRLOG("Cannot send read io\n");
592 [ # # # # ]: 0 : fuse_reply_err(ctx->req, rc);
593 : 0 : cuse_io_ctx_free(ctx);
594 : 0 : }
595 : 0 : }
596 : :
597 : :
598 : : static void
599 : 9 : cuse_nvme_submit_io(fuse_req_t req, int cmd, void *arg,
600 : : struct fuse_file_info *fi, unsigned flags,
601 : : const void *in_buf, size_t in_bufsz, size_t out_bufsz)
602 : : {
603 : : const struct nvme_user_io *user_io;
604 : 9 : struct iovec in_iov[3], out_iov[2];
605 : 9 : int in_iovcnt = 0, out_iovcnt = 0;
606 : 9 : struct cuse_device *cuse_device = fuse_req_userdata(req);
607 : : struct spdk_nvme_ns *ns;
608 : : uint32_t block_size;
609 : : uint32_t md_size;
610 : :
611 [ # # # # : 9 : in_iov[in_iovcnt].iov_base = (void *)arg;
# # # # ]
612 [ # # # # : 9 : in_iov[in_iovcnt].iov_len = sizeof(*user_io);
# # # # ]
613 [ # # ]: 9 : in_iovcnt += 1;
614 [ - + ]: 9 : if (in_bufsz == 0) {
615 : 0 : fuse_reply_ioctl_retry(req, in_iov, in_iovcnt, NULL, 0);
616 : 0 : return;
617 : : }
618 : :
619 : 9 : user_io = in_buf;
620 : :
621 [ # # # # : 9 : ns = spdk_nvme_ctrlr_get_ns(cuse_device->ctrlr, cuse_device->nsid);
# # # # ]
622 : 9 : block_size = spdk_nvme_ns_get_sector_size(ns);
623 : 9 : md_size = spdk_nvme_ns_get_md_size(ns);
624 : :
625 [ + + + # : 9 : switch (user_io->opcode) {
# # # ]
626 : 3 : case SPDK_NVME_OPC_READ:
627 [ # # # # : 3 : out_iov[out_iovcnt].iov_base = (void *)user_io->addr;
# # # # #
# # # ]
628 [ # # # # : 3 : out_iov[out_iovcnt].iov_len = (user_io->nblocks + 1) * block_size;
# # # # #
# # # #
# ]
629 [ # # ]: 3 : out_iovcnt += 1;
630 [ - + # # : 3 : if (user_io->metadata != 0) {
# # ]
631 [ # # # # : 0 : out_iov[out_iovcnt].iov_base = (void *)user_io->metadata;
# # # # #
# # # ]
632 [ # # # # : 0 : out_iov[out_iovcnt].iov_len = (user_io->nblocks + 1) * md_size;
# # # # #
# # # #
# ]
633 [ # # ]: 0 : out_iovcnt += 1;
634 : 0 : }
635 [ - + ]: 3 : if (!fuse_check_req_size(req, out_iov, out_iovcnt)) {
636 : 0 : return;
637 : : }
638 [ - + ]: 3 : if (out_bufsz == 0) {
639 : 0 : fuse_reply_ioctl_retry(req, in_iov, in_iovcnt, out_iov, out_iovcnt);
640 : 0 : return;
641 : : }
642 : :
643 : 3 : cuse_nvme_submit_io_read(cuse_device, req, cmd, arg, fi, flags,
644 : 0 : block_size, md_size, in_buf, in_bufsz, out_bufsz);
645 : 3 : break;
646 : 3 : case SPDK_NVME_OPC_WRITE:
647 [ # # # # : 3 : in_iov[in_iovcnt].iov_base = (void *)user_io->addr;
# # # # #
# # # ]
648 [ # # # # : 3 : in_iov[in_iovcnt].iov_len = (user_io->nblocks + 1) * block_size;
# # # # #
# # # #
# ]
649 [ # # ]: 3 : in_iovcnt += 1;
650 [ - + # # : 3 : if (user_io->metadata != 0) {
# # ]
651 [ # # # # : 0 : in_iov[in_iovcnt].iov_base = (void *)user_io->metadata;
# # # # #
# # # ]
652 [ # # # # : 0 : in_iov[in_iovcnt].iov_len = (user_io->nblocks + 1) * md_size;
# # # # #
# # # #
# ]
653 [ # # ]: 0 : in_iovcnt += 1;
654 : 0 : }
655 [ - + ]: 3 : if (!fuse_check_req_size(req, in_iov, in_iovcnt)) {
656 : 0 : return;
657 : : }
658 [ - + ]: 3 : if (in_bufsz == sizeof(*user_io)) {
659 : 0 : fuse_reply_ioctl_retry(req, in_iov, in_iovcnt, NULL, out_iovcnt);
660 : 0 : return;
661 : : }
662 : :
663 : 3 : cuse_nvme_submit_io_write(cuse_device, req, cmd, arg, fi, flags,
664 : 0 : block_size, md_size, in_buf, in_bufsz, out_bufsz);
665 : 3 : break;
666 : 3 : default:
667 [ # # # # ]: 3 : SPDK_ERRLOG("SUBMIT_IO: opc:%d not valid\n", user_io->opcode);
668 : 3 : fuse_reply_err(req, EINVAL);
669 : 3 : return;
670 : : }
671 : :
672 : 0 : }
673 : :
674 : : /*****************************************************************************
675 : : * Other namespace IOCTLs
676 : : */
677 : : static void
678 : 0 : cuse_blkgetsize64(fuse_req_t req, int cmd, void *arg,
679 : : struct fuse_file_info *fi, unsigned flags,
680 : : const void *in_buf, size_t in_bufsz, size_t out_bufsz)
681 : : {
682 : 0 : uint64_t size;
683 : : struct spdk_nvme_ns *ns;
684 : 0 : struct cuse_device *cuse_device = fuse_req_userdata(req);
685 : :
686 [ # # # # ]: 0 : FUSE_REPLY_CHECK_BUFFER(req, arg, out_bufsz, size);
687 : :
688 [ # # # # : 0 : ns = spdk_nvme_ctrlr_get_ns(cuse_device->ctrlr, cuse_device->nsid);
# # # # ]
689 : 0 : size = spdk_nvme_ns_get_num_sectors(ns);
690 : 0 : fuse_reply_ioctl(req, 0, &size, sizeof(size));
691 : 0 : }
692 : :
693 : : static void
694 : 0 : cuse_blkpbszget(fuse_req_t req, int cmd, void *arg,
695 : : struct fuse_file_info *fi, unsigned flags,
696 : : const void *in_buf, size_t in_bufsz, size_t out_bufsz)
697 : : {
698 : 0 : int pbsz;
699 : : struct spdk_nvme_ns *ns;
700 : 0 : struct cuse_device *cuse_device = fuse_req_userdata(req);
701 : :
702 [ # # # # ]: 0 : FUSE_REPLY_CHECK_BUFFER(req, arg, out_bufsz, pbsz);
703 : :
704 [ # # # # : 0 : ns = spdk_nvme_ctrlr_get_ns(cuse_device->ctrlr, cuse_device->nsid);
# # # # ]
705 : 0 : pbsz = spdk_nvme_ns_get_sector_size(ns);
706 : 0 : fuse_reply_ioctl(req, 0, &pbsz, sizeof(pbsz));
707 : 0 : }
708 : :
709 : : static void
710 : 0 : cuse_blkgetsize(fuse_req_t req, int cmd, void *arg,
711 : : struct fuse_file_info *fi, unsigned flags,
712 : : const void *in_buf, size_t in_bufsz, size_t out_bufsz)
713 : : {
714 : 0 : long size;
715 : : struct spdk_nvme_ns *ns;
716 : 0 : struct cuse_device *cuse_device = fuse_req_userdata(req);
717 : :
718 [ # # # # ]: 0 : FUSE_REPLY_CHECK_BUFFER(req, arg, out_bufsz, size);
719 : :
720 [ # # # # : 0 : ns = spdk_nvme_ctrlr_get_ns(cuse_device->ctrlr, cuse_device->nsid);
# # # # ]
721 : :
722 : : /* return size in 512 bytes blocks */
723 [ # # ]: 0 : size = spdk_nvme_ns_get_num_sectors(ns) * 512 / spdk_nvme_ns_get_sector_size(ns);
724 : 0 : fuse_reply_ioctl(req, 0, &size, sizeof(size));
725 : 0 : }
726 : :
727 : : static void
728 : 0 : cuse_blkgetsectorsize(fuse_req_t req, int cmd, void *arg,
729 : : struct fuse_file_info *fi, unsigned flags,
730 : : const void *in_buf, size_t in_bufsz, size_t out_bufsz)
731 : : {
732 : 0 : int ssize;
733 : : struct spdk_nvme_ns *ns;
734 : 0 : struct cuse_device *cuse_device = fuse_req_userdata(req);
735 : :
736 [ # # # # ]: 0 : FUSE_REPLY_CHECK_BUFFER(req, arg, out_bufsz, ssize);
737 : :
738 [ # # # # : 0 : ns = spdk_nvme_ctrlr_get_ns(cuse_device->ctrlr, cuse_device->nsid);
# # # # ]
739 : 0 : ssize = spdk_nvme_ns_get_sector_size(ns);
740 : 0 : fuse_reply_ioctl(req, 0, &ssize, sizeof(ssize));
741 : 0 : }
742 : :
743 : : static void
744 : 9 : cuse_getid(fuse_req_t req, int cmd, void *arg,
745 : : struct fuse_file_info *fi, unsigned flags,
746 : : const void *in_buf, size_t in_bufsz, size_t out_bufsz)
747 : : {
748 : 9 : struct cuse_device *cuse_device = fuse_req_userdata(req);
749 : :
750 [ # # # # ]: 9 : fuse_reply_ioctl(req, cuse_device->nsid, NULL, 0);
751 : 9 : }
752 : :
753 : : struct cuse_transport {
754 : : char trstring[SPDK_NVMF_TRSTRING_MAX_LEN + 1];
755 : : char traddr[SPDK_NVMF_TRADDR_MAX_LEN + 1];
756 : : };
757 : :
758 : : #define SPDK_CUSE_GET_TRANSPORT _IOWR('n', 0x1, struct cuse_transport)
759 : :
760 : : static void
761 : 8 : cuse_get_transport(fuse_req_t req, int cmd, void *arg,
762 : : struct fuse_file_info *fi, unsigned flags,
763 : : const void *in_buf, size_t in_bufsz, size_t out_bufsz)
764 : : {
765 : 8 : struct cuse_device *cuse_device = fuse_req_userdata(req);
766 : 8 : struct cuse_transport tr = {};
767 : :
768 [ + + # # ]: 8 : FUSE_REPLY_CHECK_BUFFER(req, arg, out_bufsz, tr);
769 : :
770 [ # # # # : 4 : memcpy(tr.trstring, cuse_device->ctrlr->trid.trstring, SPDK_NVMF_TRSTRING_MAX_LEN + 1);
# # # # #
# # # ]
771 [ # # # # : 4 : memcpy(tr.traddr, cuse_device->ctrlr->trid.traddr, SPDK_NVMF_TRADDR_MAX_LEN + 1);
# # # # #
# # # ]
772 : :
773 : 4 : fuse_reply_ioctl(req, 0, &tr, sizeof(tr));
774 : 0 : }
775 : :
776 : : static void
777 : 114 : cuse_ctrlr_ioctl(fuse_req_t req, int cmd, void *arg,
778 : : struct fuse_file_info *fi, unsigned flags,
779 : : const void *in_buf, size_t in_bufsz, size_t out_bufsz)
780 : : {
781 [ - + # # : 114 : if (flags & FUSE_IOCTL_COMPAT) {
# # ]
782 : 0 : fuse_reply_err(req, ENOSYS);
783 : 0 : return;
784 : : }
785 : :
786 [ + + - + : 114 : switch ((unsigned int)cmd) {
+ - ]
787 : 96 : case NVME_IOCTL_ADMIN_CMD:
788 [ - + - + : 96 : SPDK_DEBUGLOG(nvme_cuse, "NVME_IOCTL_ADMIN_CMD\n");
# # ]
789 : 96 : cuse_nvme_passthru_cmd(req, cmd, arg, fi, flags, in_buf, in_bufsz, out_bufsz);
790 : 96 : break;
791 : :
792 : 3 : case NVME_IOCTL_RESET:
793 : : case NVME_IOCTL_SUBSYS_RESET:
794 : 3 : cuse_nvme_reset(req, cmd, arg, fi, flags, in_buf, in_bufsz, out_bufsz);
795 : 3 : break;
796 : :
797 : 0 : case NVME_IOCTL_RESCAN:
798 [ # # # # : 0 : SPDK_DEBUGLOG(nvme_cuse, "NVME_IOCTL_RESCAN\n");
# # ]
799 : 0 : cuse_nvme_rescan(req, cmd, arg, fi, flags, in_buf, in_bufsz, out_bufsz);
800 : 0 : break;
801 : :
802 : 7 : case NVME_IOCTL_ID:
803 : : /* Return error but don't ERRLOG - nvme-cli will frequently send this
804 : : * IOCTL to controller devices.
805 : : */
806 : 7 : fuse_reply_err(req, ENOTTY);
807 : 7 : break;
808 : :
809 : 8 : case SPDK_CUSE_GET_TRANSPORT:
810 [ - + - + : 8 : SPDK_DEBUGLOG(nvme_cuse, "SPDK_CUSE_GET_TRANSPORT\n");
# # ]
811 : 8 : cuse_get_transport(req, cmd, arg, fi, flags, in_buf, in_bufsz, out_bufsz);
812 : 8 : break;
813 : :
814 : 0 : default:
815 : 0 : SPDK_ERRLOG("Unsupported IOCTL 0x%X.\n", cmd);
816 : 0 : fuse_reply_err(req, ENOTTY);
817 : 0 : }
818 : 0 : }
819 : :
820 : : static void
821 : 45 : cuse_ns_ioctl(fuse_req_t req, int cmd, void *arg,
822 : : struct fuse_file_info *fi, unsigned flags,
823 : : const void *in_buf, size_t in_bufsz, size_t out_bufsz)
824 : : {
825 [ - + # # : 45 : if (flags & FUSE_IOCTL_COMPAT) {
# # ]
826 : 0 : fuse_reply_err(req, ENOSYS);
827 : 0 : return;
828 : : }
829 : :
830 [ + - + + : 45 : switch ((unsigned int)cmd) {
- - - -
- ]
831 : 30 : case NVME_IOCTL_ADMIN_CMD:
832 [ - + - + : 30 : SPDK_DEBUGLOG(nvme_cuse, "NVME_IOCTL_ADMIN_CMD\n");
# # ]
833 : 30 : cuse_nvme_passthru_cmd(req, cmd, arg, fi, flags, in_buf, in_bufsz, out_bufsz);
834 : 30 : break;
835 : :
836 : 0 : case NVME_IOCTL_SUBMIT_IO:
837 [ # # # # : 0 : SPDK_DEBUGLOG(nvme_cuse, "NVME_IOCTL_SUBMIT_IO\n");
# # ]
838 : 0 : cuse_nvme_submit_io(req, cmd, arg, fi, flags, in_buf, in_bufsz, out_bufsz);
839 : 0 : break;
840 : :
841 : 6 : case NVME_IOCTL_IO_CMD:
842 [ - + - + : 6 : SPDK_DEBUGLOG(nvme_cuse, "NVME_IOCTL_IO_CMD\n");
# # ]
843 : 6 : cuse_nvme_passthru_cmd(req, cmd, arg, fi, flags, in_buf, in_bufsz, out_bufsz);
844 : 6 : break;
845 : :
846 : 9 : case NVME_IOCTL_ID:
847 [ - + - + : 9 : SPDK_DEBUGLOG(nvme_cuse, "NVME_IOCTL_ID\n");
# # ]
848 : 9 : cuse_getid(req, cmd, arg, fi, flags, in_buf, in_bufsz, out_bufsz);
849 : 9 : break;
850 : :
851 : 0 : case BLKPBSZGET:
852 [ # # # # : 0 : SPDK_DEBUGLOG(nvme_cuse, "BLKPBSZGET\n");
# # ]
853 : 0 : cuse_blkpbszget(req, cmd, arg, fi, flags, in_buf, in_bufsz, out_bufsz);
854 : 0 : break;
855 : :
856 : 0 : case BLKSSZGET:
857 [ # # # # : 0 : SPDK_DEBUGLOG(nvme_cuse, "BLKSSZGET\n");
# # ]
858 : 0 : cuse_blkgetsectorsize(req, cmd, arg, fi, flags, in_buf, in_bufsz, out_bufsz);
859 : 0 : break;
860 : :
861 : 0 : case BLKGETSIZE:
862 [ # # # # : 0 : SPDK_DEBUGLOG(nvme_cuse, "BLKGETSIZE\n");
# # ]
863 : : /* Returns the device size as a number of 512-byte blocks (returns pointer to long) */
864 : 0 : cuse_blkgetsize(req, cmd, arg, fi, flags, in_buf, in_bufsz, out_bufsz);
865 : 0 : break;
866 : :
867 : 0 : case BLKGETSIZE64:
868 [ # # # # : 0 : SPDK_DEBUGLOG(nvme_cuse, "BLKGETSIZE64\n");
# # ]
869 : : /* Returns the device size in sectors (returns pointer to uint64_t) */
870 : 0 : cuse_blkgetsize64(req, cmd, arg, fi, flags, in_buf, in_bufsz, out_bufsz);
871 : 0 : break;
872 : :
873 : 0 : default:
874 : 0 : SPDK_ERRLOG("Unsupported IOCTL 0x%X.\n", cmd);
875 : 0 : fuse_reply_err(req, ENOTTY);
876 : 0 : }
877 : 0 : }
878 : :
879 : : /*****************************************************************************
880 : : * CUSE threads initialization.
881 : : */
882 : :
883 : : static void
884 : 37 : cuse_open(fuse_req_t req, struct fuse_file_info *fi)
885 : : {
886 : 37 : fuse_reply_open(req, fi);
887 : 37 : }
888 : :
889 : : static const struct cuse_lowlevel_ops cuse_ctrlr_clop = {
890 : : .open = cuse_open,
891 : : .ioctl = cuse_ctrlr_ioctl,
892 : : };
893 : :
894 : : static const struct cuse_lowlevel_ops cuse_ns_clop = {
895 : : .open = cuse_open,
896 : : .ioctl = cuse_ns_ioctl,
897 : : };
898 : :
899 : : static int
900 : 88 : cuse_session_create(struct cuse_device *cuse_device)
901 : : {
902 : 88 : char *cuse_argv[] = { "cuse", "-f" };
903 : 88 : int multithreaded;
904 : 88 : int cuse_argc = SPDK_COUNTOF(cuse_argv);
905 : 88 : struct cuse_info ci;
906 : 88 : char devname_arg[128 + 8];
907 : 88 : const char *dev_info_argv[] = { devname_arg };
908 : :
909 [ # # ]: 88 : snprintf(devname_arg, sizeof(devname_arg), "DEVNAME=%s", cuse_device->dev_name);
910 : :
911 [ # # ]: 88 : memset(&ci, 0, sizeof(ci));
912 [ # # ]: 88 : ci.dev_info_argc = 1;
913 [ # # ]: 88 : ci.dev_info_argv = dev_info_argv;
914 [ # # # # : 88 : ci.flags = CUSE_UNRESTRICTED_IOCTL;
# # ]
915 : :
916 [ + + # # : 88 : if (cuse_device->nsid) {
# # ]
917 [ # # # # : 68 : cuse_device->session = cuse_lowlevel_setup(cuse_argc, cuse_argv, &ci, &cuse_ns_clop,
# # ]
918 : 0 : &multithreaded, cuse_device);
919 : 0 : } else {
920 [ # # # # : 20 : cuse_device->session = cuse_lowlevel_setup(cuse_argc, cuse_argv, &ci, &cuse_ctrlr_clop,
# # ]
921 : 0 : &multithreaded, cuse_device);
922 : : }
923 : :
924 [ - + # # : 88 : if (!cuse_device->session) {
# # ]
925 : 0 : SPDK_ERRLOG("Cannot create cuse session\n");
926 : 0 : return -1;
927 : : }
928 [ # # ]: 88 : SPDK_NOTICELOG("fuse session for device %s created\n", cuse_device->dev_name);
929 [ # # # # : 88 : cuse_device->fuse_efd = fuse_session_fd(cuse_device->session);
# # # # ]
930 : :
931 [ # # ]: 88 : pthread_mutex_lock(&g_pending_device_mtx);
932 [ # # # # : 88 : TAILQ_INSERT_TAIL(&g_pending_device_head, cuse_device, cuse_thread_tailq);
# # # # #
# # # # #
# # # # #
# # # #
# ]
933 [ - + ]: 88 : if (eventfd_write(g_cuse_thread_msg_fd, 1) != 0) {
934 [ # # # # : 0 : TAILQ_REMOVE(&g_pending_device_head, cuse_device, cuse_thread_tailq);
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # ]
935 [ # # ]: 0 : pthread_mutex_unlock(&g_pending_device_mtx);
936 [ # # ]: 0 : SPDK_ERRLOG("eventfd_write failed: (%s).\n", spdk_strerror(errno));
937 [ # # # # ]: 0 : return -errno;
938 : : }
939 [ # # ]: 88 : pthread_mutex_unlock(&g_pending_device_mtx);
940 : 88 : return 0;
941 : 0 : }
942 : :
943 : : static int
944 : 309 : process_cuse_event(void *arg)
945 : : {
946 : 309 : struct fuse_session *session = arg;
947 : 309 : struct fuse_buf buf = { .mem = NULL };
948 : 309 : int rc = fuse_session_receive_buf(session, &buf);
949 : :
950 [ + - ]: 309 : if (rc > 0) {
951 : 309 : fuse_session_process_buf(session, &buf);
952 : 0 : }
953 [ # # ]: 309 : free(buf.mem);
954 : 309 : return 0;
955 : : }
956 : :
957 : : static int
958 : 44 : cuse_thread_add_session(void *arg)
959 : : {
960 : : struct cuse_device *cuse_device, *tmp;
961 : : int ret;
962 : 44 : eventfd_t val;
963 : :
964 : 44 : eventfd_read(g_cuse_thread_msg_fd, &val);
965 : :
966 [ - + ]: 44 : pthread_mutex_lock(&g_pending_device_mtx);
967 [ + + # # : 132 : TAILQ_FOREACH_SAFE(cuse_device, &g_pending_device_head, cuse_thread_tailq, tmp) {
# # # # #
# ]
968 [ # # # # ]: 88 : ret = spdk_fd_group_add(g_device_fdgrp, cuse_device->fuse_efd, process_cuse_event,
969 [ # # # # : 88 : cuse_device->session, cuse_device->dev_name);
# # ]
970 [ - + ]: 88 : if (ret < 0) {
971 [ # # # # : 0 : SPDK_ERRLOG("Failed to add fd %d: (%s).\n", cuse_device->fuse_efd,
# # ]
972 : : spdk_strerror(-ret));
973 [ # # # # : 0 : TAILQ_REMOVE(&g_pending_device_head, cuse_device, cuse_thread_tailq);
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # ]
974 : 0 : free(cuse_device);
975 [ # # ]: 0 : assert(false);
976 : : }
977 : 0 : }
978 [ + + # # : 44 : TAILQ_CONCAT(&g_active_device_head, &g_pending_device_head, cuse_thread_tailq);
# # # # #
# # # # #
# # # # #
# ]
979 [ - + ]: 44 : pthread_mutex_unlock(&g_pending_device_mtx);
980 : 44 : return 0;
981 : : }
982 : :
983 : : static void *
984 : 16 : cuse_thread(void *unused)
985 : : {
986 : : struct cuse_device *cuse_device, *tmp;
987 : 16 : int timeout_msecs = 500;
988 : : bool retry;
989 : :
990 : 16 : spdk_unaffinitize_thread();
991 : :
992 : 0 : do {
993 : 16 : retry = false;
994 : 16 : spdk_fd_group_wait(g_device_fdgrp, timeout_msecs);
995 [ + + ]: 908 : while (!TAILQ_EMPTY(&g_active_device_head)) {
996 [ + + # # : 4020 : TAILQ_FOREACH_SAFE(cuse_device, &g_active_device_head, cuse_thread_tailq, tmp) {
# # # # #
# ]
997 [ + + # # : 3122 : if (fuse_session_exited(cuse_device->session)) {
# # ]
998 [ # # # # ]: 71 : spdk_fd_group_remove(g_device_fdgrp, cuse_device->fuse_efd);
999 [ # # # # ]: 71 : fuse_session_reset(cuse_device->session);
1000 [ + + # # : 71 : TAILQ_REMOVE(&g_active_device_head, cuse_device, cuse_thread_tailq);
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # ]
1001 [ - + + - : 71 : if (cuse_device->force_exit) {
# # # # ]
1002 [ # # # # ]: 71 : cuse_lowlevel_teardown(cuse_device->session);
1003 : 71 : free(cuse_device);
1004 : 0 : }
1005 : 0 : }
1006 : 0 : }
1007 : : /* Receive and process fuse event and new cuse device addition requests. */
1008 : 898 : spdk_fd_group_wait(g_device_fdgrp, timeout_msecs);
1009 : : }
1010 [ - + ]: 10 : pthread_mutex_lock(&g_cuse_mtx);
1011 [ - + ]: 10 : if (!TAILQ_EMPTY(&g_pending_device_head)) {
1012 [ # # ]: 0 : pthread_mutex_unlock(&g_cuse_mtx);
1013 : : /* Retry as we have some cuse devices pending to be polled on. */
1014 : 0 : retry = true;
1015 : 0 : }
1016 [ - + # # ]: 10 : } while (retry);
1017 : :
1018 : 10 : spdk_fd_group_remove(g_device_fdgrp, g_cuse_thread_msg_fd);
1019 : 10 : close(g_cuse_thread_msg_fd);
1020 : 10 : spdk_fd_group_destroy(g_device_fdgrp);
1021 : 10 : g_device_fdgrp = NULL;
1022 [ - + ]: 10 : pthread_mutex_unlock(&g_cuse_mtx);
1023 : 10 : SPDK_NOTICELOG("Cuse thread exited.\n");
1024 : 10 : return NULL;
1025 : : }
1026 : :
1027 : : static struct cuse_device *nvme_cuse_get_cuse_ns_device(struct spdk_nvme_ctrlr *ctrlr,
1028 : : uint32_t nsid);
1029 : :
1030 : : /*****************************************************************************
1031 : : * CUSE devices management
1032 : : */
1033 : :
1034 : : static int
1035 : 91 : cuse_nvme_ns_start(struct cuse_device *ctrlr_device, uint32_t nsid)
1036 : : {
1037 : 91 : struct cuse_device *ns_device = NULL;
1038 : : int rv;
1039 : :
1040 [ # # # # ]: 91 : ns_device = nvme_cuse_get_cuse_ns_device(ctrlr_device->ctrlr, nsid);
1041 [ + + ]: 91 : if (ns_device != NULL) {
1042 : 23 : return 0;
1043 : : }
1044 : :
1045 : 68 : ns_device = calloc(1, sizeof(struct cuse_device));
1046 [ - + ]: 68 : if (ns_device == NULL) {
1047 : 0 : return -ENOMEM;
1048 : : }
1049 : :
1050 [ # # # # : 68 : ns_device->ctrlr = ctrlr_device->ctrlr;
# # # # ]
1051 [ # # # # ]: 68 : ns_device->ctrlr_device = ctrlr_device;
1052 [ # # # # ]: 68 : ns_device->nsid = nsid;
1053 [ - + ]: 136 : rv = snprintf(ns_device->dev_name, sizeof(ns_device->dev_name), "%sn%d",
1054 [ # # # # : 68 : ctrlr_device->dev_name, ns_device->nsid);
# # ]
1055 [ - + ]: 68 : if (rv < 0) {
1056 : 0 : SPDK_ERRLOG("Device name too long.\n");
1057 : 0 : rv = -ENAMETOOLONG;
1058 : 0 : goto free_device;
1059 : : }
1060 : :
1061 : 68 : rv = cuse_session_create(ns_device);
1062 [ - + ]: 68 : if (rv != 0) {
1063 : 0 : goto free_device;
1064 : : }
1065 : :
1066 [ # # # # : 68 : TAILQ_INSERT_TAIL(&ctrlr_device->ns_devices, ns_device, tailq);
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # ]
1067 : :
1068 : 68 : return 0;
1069 : :
1070 : 0 : free_device:
1071 : 0 : free(ns_device);
1072 : 0 : return rv;
1073 : 0 : }
1074 : :
1075 : : static void
1076 : 68 : cuse_nvme_ns_stop(struct cuse_device *ctrlr_device, struct cuse_device *ns_device)
1077 : : {
1078 [ + + # # : 68 : TAILQ_REMOVE(&ctrlr_device->ns_devices, ns_device, tailq);
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # ]
1079 : : /* ns_device will be freed by cuse_thread */
1080 [ + - # # : 68 : if (ns_device->session != NULL) {
# # ]
1081 [ # # # # ]: 68 : ns_device->force_exit = true;
1082 [ # # # # ]: 68 : fuse_session_exit(ns_device->session);
1083 : 0 : }
1084 : 68 : }
1085 : :
1086 : : static int
1087 : 20 : nvme_cuse_claim(struct cuse_device *ctrlr_device, uint32_t index)
1088 : : {
1089 : : int dev_fd;
1090 : : int pid;
1091 : : void *dev_map;
1092 : 20 : struct flock cusedev_lock = {
1093 : : .l_type = F_WRLCK,
1094 : : .l_whence = SEEK_SET,
1095 : : .l_start = 0,
1096 : : .l_len = 0,
1097 : : };
1098 : :
1099 [ # # ]: 20 : snprintf(ctrlr_device->lock_name, sizeof(ctrlr_device->lock_name),
1100 : 0 : "/var/tmp/spdk_nvme_cuse_lock_%" PRIu32, index);
1101 : :
1102 [ - + # # ]: 20 : dev_fd = open(ctrlr_device->lock_name, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
1103 [ - + ]: 20 : if (dev_fd == -1) {
1104 [ # # ]: 0 : SPDK_ERRLOG("could not open %s\n", ctrlr_device->lock_name);
1105 [ # # # # ]: 0 : return -errno;
1106 : : }
1107 : :
1108 [ - + ]: 20 : if (ftruncate(dev_fd, sizeof(int)) != 0) {
1109 [ # # ]: 0 : SPDK_ERRLOG("could not truncate %s\n", ctrlr_device->lock_name);
1110 : 0 : close(dev_fd);
1111 [ # # # # ]: 0 : return -errno;
1112 : : }
1113 : :
1114 : 20 : dev_map = mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE,
1115 : 0 : MAP_SHARED, dev_fd, 0);
1116 [ - + ]: 20 : if (dev_map == MAP_FAILED) {
1117 [ # # # # ]: 0 : SPDK_ERRLOG("could not mmap dev %s (%d)\n", ctrlr_device->lock_name, errno);
1118 : 0 : close(dev_fd);
1119 [ # # # # ]: 0 : return -errno;
1120 : : }
1121 : :
1122 [ - + ]: 20 : if (fcntl(dev_fd, F_SETLK, &cusedev_lock) != 0) {
1123 [ # # ]: 0 : pid = *(int *)dev_map;
1124 [ # # ]: 0 : SPDK_ERRLOG("Cannot create lock on device %s, probably"
1125 : : " process %d has claimed it\n", ctrlr_device->lock_name, pid);
1126 : 0 : munmap(dev_map, sizeof(int));
1127 : 0 : close(dev_fd);
1128 : : /* F_SETLK returns unspecified errnos, normalize them */
1129 : 0 : return -EACCES;
1130 : : }
1131 : :
1132 [ # # ]: 20 : *(int *)dev_map = (int)getpid();
1133 : 20 : munmap(dev_map, sizeof(int));
1134 [ # # # # ]: 20 : ctrlr_device->claim_fd = dev_fd;
1135 [ # # # # ]: 20 : ctrlr_device->index = index;
1136 : : /* Keep dev_fd open to maintain the lock. */
1137 : 20 : return 0;
1138 : 0 : }
1139 : :
1140 : : static void
1141 : 20 : nvme_cuse_unclaim(struct cuse_device *ctrlr_device)
1142 : : {
1143 [ # # # # ]: 20 : close(ctrlr_device->claim_fd);
1144 [ # # # # ]: 20 : ctrlr_device->claim_fd = -1;
1145 [ - + # # ]: 20 : unlink(ctrlr_device->lock_name);
1146 : 20 : }
1147 : :
1148 : : static void
1149 : 20 : cuse_nvme_ctrlr_stop(struct cuse_device *ctrlr_device)
1150 : : {
1151 : : struct cuse_device *ns_device, *tmp;
1152 : :
1153 [ + + # # : 56 : TAILQ_FOREACH_SAFE(ns_device, &ctrlr_device->ns_devices, tailq, tmp) {
# # # # #
# # # # #
# # ]
1154 : 36 : cuse_nvme_ns_stop(ctrlr_device, ns_device);
1155 : 0 : }
1156 : :
1157 [ - + # # : 20 : assert(TAILQ_EMPTY(&ctrlr_device->ns_devices));
# # # # #
# ]
1158 : :
1159 [ # # # # ]: 20 : spdk_bit_array_clear(g_ctrlr_started, ctrlr_device->index);
1160 [ + + ]: 20 : if (spdk_bit_array_count_set(g_ctrlr_started) == 0) {
1161 : 16 : spdk_bit_array_free(&g_ctrlr_started);
1162 : 0 : }
1163 : 20 : nvme_cuse_unclaim(ctrlr_device);
1164 : :
1165 [ + + # # : 20 : TAILQ_REMOVE(&g_ctrlr_ctx_head, ctrlr_device, tailq);
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # ]
1166 : : /* ctrlr_device will be freed by cuse_thread */
1167 [ # # # # ]: 20 : ctrlr_device->force_exit = true;
1168 [ # # # # ]: 20 : fuse_session_exit(ctrlr_device->session);
1169 : 20 : }
1170 : :
1171 : : static int
1172 : 38 : cuse_nvme_ctrlr_update_namespaces(struct cuse_device *ctrlr_device)
1173 : : {
1174 : : struct cuse_device *ns_device, *tmp;
1175 : : uint32_t nsid;
1176 : :
1177 : : /* Remove namespaces that have disappeared */
1178 [ + + # # : 93 : TAILQ_FOREACH_SAFE(ns_device, &ctrlr_device->ns_devices, tailq, tmp) {
# # # # #
# # # # #
# # ]
1179 [ + + # # : 55 : if (!spdk_nvme_ctrlr_is_active_ns(ctrlr_device->ctrlr, ns_device->nsid)) {
# # # # #
# ]
1180 : 32 : cuse_nvme_ns_stop(ctrlr_device, ns_device);
1181 : 0 : }
1182 : 0 : }
1183 : :
1184 : : /* Add new namespaces */
1185 [ # # # # ]: 38 : nsid = spdk_nvme_ctrlr_get_first_active_ns(ctrlr_device->ctrlr);
1186 [ + + ]: 129 : while (nsid != 0) {
1187 [ - + ]: 91 : if (cuse_nvme_ns_start(ctrlr_device, nsid) < 0) {
1188 : 0 : SPDK_ERRLOG("Cannot start CUSE namespace device.");
1189 : 0 : return -1;
1190 : : }
1191 : :
1192 [ # # # # ]: 91 : nsid = spdk_nvme_ctrlr_get_next_active_ns(ctrlr_device->ctrlr, nsid);
1193 : : }
1194 : :
1195 : 38 : return 0;
1196 : 0 : }
1197 : :
1198 : : #ifdef FUSE_LOG_H_
1199 : : static void
1200 : 47 : nvme_fuse_log_func(enum fuse_log_level level, const char *fmt, va_list ap)
1201 : : {
1202 : : /* fuse will unnecessarily print this log message when tearing down
1203 : : * sessions, once for every session after the first. So use this custom
1204 : : * log handler to silence that specific log message.
1205 : : */
1206 [ - + + - : 47 : if (strstr(fmt, "fuse_remove_signal_handlers: unknown session") != NULL) {
# # ]
1207 : 47 : return;
1208 : : }
1209 : :
1210 [ # # # # ]: 0 : vfprintf(stderr, fmt, ap);
1211 : 0 : }
1212 : : #endif
1213 : :
1214 : : static int
1215 : 20 : nvme_cuse_start(struct spdk_nvme_ctrlr *ctrlr)
1216 : : {
1217 : 20 : int rv = 0;
1218 : : struct cuse_device *ctrlr_device;
1219 : :
1220 : 20 : SPDK_NOTICELOG("Creating cuse device for controller\n");
1221 : :
1222 [ + + ]: 20 : if (g_ctrlr_started == NULL) {
1223 : 16 : g_ctrlr_started = spdk_bit_array_create(128);
1224 [ - + ]: 16 : if (g_ctrlr_started == NULL) {
1225 : 0 : SPDK_ERRLOG("Cannot create bit array\n");
1226 : 0 : return -ENOMEM;
1227 : : }
1228 : : #ifdef FUSE_LOG_H_
1229 : : /* Older versions of libfuse don't have fuse_set_log_func nor
1230 : : * fuse_log.h, so this is the easiest way to check for it
1231 : : * without adding a separate CONFIG flag.
1232 : : */
1233 : 16 : fuse_set_log_func(nvme_fuse_log_func);
1234 : : #endif
1235 : 0 : }
1236 : :
1237 : 20 : ctrlr_device = (struct cuse_device *)calloc(1, sizeof(struct cuse_device));
1238 [ - + ]: 20 : if (!ctrlr_device) {
1239 : 0 : SPDK_ERRLOG("Cannot allocate memory for ctrlr_device.");
1240 : 0 : rv = -ENOMEM;
1241 : 0 : goto free_device;
1242 : : }
1243 : :
1244 [ # # # # ]: 20 : ctrlr_device->ctrlr = ctrlr;
1245 : :
1246 : : /* Check if device already exists, if not increment index until success */
1247 [ # # # # ]: 20 : ctrlr_device->index = 0;
1248 : 0 : while (1) {
1249 [ # # # # : 20 : ctrlr_device->index = spdk_bit_array_find_first_clear(g_ctrlr_started, ctrlr_device->index);
# # # # ]
1250 [ - + # # : 20 : if (ctrlr_device->index == UINT32_MAX) {
# # ]
1251 : 0 : SPDK_ERRLOG("Too many registered controllers\n");
1252 : 0 : goto free_device;
1253 : : }
1254 : :
1255 [ + - # # : 20 : if (nvme_cuse_claim(ctrlr_device, ctrlr_device->index) == 0) {
# # ]
1256 : 20 : break;
1257 : : }
1258 [ # # ]: 0 : ctrlr_device->index++;
1259 : : }
1260 [ # # # # ]: 20 : spdk_bit_array_set(g_ctrlr_started, ctrlr_device->index);
1261 [ - + ]: 20 : snprintf(ctrlr_device->dev_name, sizeof(ctrlr_device->dev_name), "spdk/nvme%d",
1262 [ # # # # ]: 0 : ctrlr_device->index);
1263 : :
1264 : 20 : rv = cuse_session_create(ctrlr_device);
1265 [ - + ]: 20 : if (rv != 0) {
1266 : 0 : goto clear_and_free;
1267 : : }
1268 : :
1269 [ # # # # : 20 : TAILQ_INSERT_TAIL(&g_ctrlr_ctx_head, ctrlr_device, tailq);
# # # # #
# # # # #
# # # # #
# # # #
# ]
1270 : :
1271 [ # # # # : 20 : TAILQ_INIT(&ctrlr_device->ns_devices);
# # # # #
# # # # #
# # ]
1272 : :
1273 : : /* Start all active namespaces */
1274 [ - + ]: 20 : if (cuse_nvme_ctrlr_update_namespaces(ctrlr_device) < 0) {
1275 : 0 : SPDK_ERRLOG("Cannot start CUSE namespace devices.");
1276 : 0 : cuse_nvme_ctrlr_stop(ctrlr_device);
1277 : 0 : return -1;
1278 : : }
1279 : :
1280 : 20 : return 0;
1281 : :
1282 : 0 : clear_and_free:
1283 [ # # # # ]: 0 : spdk_bit_array_clear(g_ctrlr_started, ctrlr_device->index);
1284 : 0 : free_device:
1285 : 0 : free(ctrlr_device);
1286 [ # # ]: 0 : if (spdk_bit_array_count_set(g_ctrlr_started) == 0) {
1287 : 0 : spdk_bit_array_free(&g_ctrlr_started);
1288 : 0 : }
1289 : 0 : return rv;
1290 : 0 : }
1291 : :
1292 : : static struct cuse_device *
1293 : 2349 : nvme_cuse_get_cuse_ctrlr_device(struct spdk_nvme_ctrlr *ctrlr)
1294 : : {
1295 : 2349 : struct cuse_device *ctrlr_device = NULL;
1296 : :
1297 [ + + # # : 2368 : TAILQ_FOREACH(ctrlr_device, &g_ctrlr_ctx_head, tailq) {
# # # # ]
1298 [ + + # # : 189 : if (ctrlr_device->ctrlr == ctrlr) {
# # ]
1299 : 170 : break;
1300 : : }
1301 : 0 : }
1302 : :
1303 : 2349 : return ctrlr_device;
1304 : : }
1305 : :
1306 : : static struct cuse_device *
1307 : 1253 : nvme_cuse_get_cuse_ns_device(struct spdk_nvme_ctrlr *ctrlr, uint32_t nsid)
1308 : : {
1309 : 1253 : struct cuse_device *ctrlr_device = NULL;
1310 : : struct cuse_device *ns_device;
1311 : :
1312 : 1253 : ctrlr_device = nvme_cuse_get_cuse_ctrlr_device(ctrlr);
1313 [ + + ]: 1253 : if (!ctrlr_device) {
1314 : 1140 : return NULL;
1315 : : }
1316 : :
1317 [ + + # # : 319 : TAILQ_FOREACH(ns_device, &ctrlr_device->ns_devices, tailq) {
# # # # #
# # # #
# ]
1318 [ + + # # : 242 : if (ns_device->nsid == nsid) {
# # ]
1319 : 36 : return ns_device;
1320 : : }
1321 : 0 : }
1322 : :
1323 : 77 : return NULL;
1324 : 0 : }
1325 : :
1326 : : static void
1327 : 9 : nvme_cuse_stop(struct spdk_nvme_ctrlr *ctrlr)
1328 : : {
1329 : : struct cuse_device *ctrlr_device;
1330 : :
1331 [ - + # # ]: 9 : assert(spdk_process_is_primary());
1332 : :
1333 [ - + ]: 9 : pthread_mutex_lock(&g_cuse_mtx);
1334 : :
1335 : 9 : ctrlr_device = nvme_cuse_get_cuse_ctrlr_device(ctrlr);
1336 [ - + ]: 9 : if (!ctrlr_device) {
1337 : 0 : SPDK_ERRLOG("Cannot find associated CUSE device\n");
1338 [ # # ]: 0 : pthread_mutex_unlock(&g_cuse_mtx);
1339 : 0 : return;
1340 : : }
1341 : :
1342 : 9 : cuse_nvme_ctrlr_stop(ctrlr_device);
1343 : :
1344 [ - + ]: 9 : pthread_mutex_unlock(&g_cuse_mtx);
1345 : 0 : }
1346 : :
1347 : : static void
1348 : 18 : nvme_cuse_update(struct spdk_nvme_ctrlr *ctrlr)
1349 : : {
1350 : : struct cuse_device *ctrlr_device;
1351 : :
1352 [ - + # # ]: 18 : assert(spdk_process_is_primary());
1353 : :
1354 [ - + ]: 18 : pthread_mutex_lock(&g_cuse_mtx);
1355 : :
1356 : 18 : ctrlr_device = nvme_cuse_get_cuse_ctrlr_device(ctrlr);
1357 [ - + ]: 18 : if (!ctrlr_device) {
1358 [ # # ]: 0 : pthread_mutex_unlock(&g_cuse_mtx);
1359 : 0 : return;
1360 : : }
1361 : :
1362 : 18 : cuse_nvme_ctrlr_update_namespaces(ctrlr_device);
1363 : :
1364 [ - + ]: 18 : pthread_mutex_unlock(&g_cuse_mtx);
1365 : 0 : }
1366 : :
1367 : : static struct nvme_io_msg_producer cuse_nvme_io_msg_producer = {
1368 : : .name = "cuse",
1369 : : .stop = nvme_cuse_stop,
1370 : : .update = nvme_cuse_update,
1371 : : };
1372 : :
1373 : : static int
1374 : 16 : start_cuse_thread(void)
1375 : : {
1376 : 16 : int rc = 0;
1377 : 16 : pthread_t tid;
1378 : :
1379 : 16 : rc = spdk_fd_group_create(&g_device_fdgrp);
1380 [ - + ]: 16 : if (rc < 0) {
1381 [ # # ]: 0 : SPDK_ERRLOG("Failed to create fd group: (%s).\n", spdk_strerror(-rc));
1382 : 0 : return rc;
1383 : : }
1384 : :
1385 : 16 : g_cuse_thread_msg_fd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
1386 [ - + ]: 16 : if (g_cuse_thread_msg_fd < 0) {
1387 [ # # ]: 0 : SPDK_ERRLOG("Failed to create eventfd: (%s).\n", spdk_strerror(errno));
1388 [ # # # # ]: 0 : rc = -errno;
1389 : 0 : goto destroy_fd_group;
1390 : : }
1391 : :
1392 : 16 : rc = SPDK_FD_GROUP_ADD(g_device_fdgrp, g_cuse_thread_msg_fd,
1393 : : cuse_thread_add_session, NULL);
1394 [ - + ]: 16 : if (rc < 0) {
1395 [ # # ]: 0 : SPDK_ERRLOG("Failed to add fd %d: %s.\n", g_cuse_thread_msg_fd,
1396 : : spdk_strerror(-rc));
1397 : 0 : goto close_and_destroy_fd;
1398 : : }
1399 : :
1400 [ # # # # ]: 16 : rc = pthread_create(&tid, NULL, cuse_thread, NULL);
1401 [ - + ]: 16 : if (rc != 0) {
1402 : 0 : SPDK_ERRLOG("pthread_create failed\n");
1403 [ # # ]: 0 : rc = -rc;
1404 : 0 : goto remove_close_and_destroy_fd;
1405 : : }
1406 : 16 : pthread_detach(tid);
1407 [ # # ]: 16 : pthread_setname_np(tid, "cuse_thread");
1408 : 16 : SPDK_NOTICELOG("Successfully started cuse thread to poll for admin commands\n");
1409 : 16 : return rc;
1410 : :
1411 : 0 : remove_close_and_destroy_fd:
1412 : 0 : spdk_fd_group_remove(g_device_fdgrp, g_cuse_thread_msg_fd);
1413 : 0 : close_and_destroy_fd:
1414 : 0 : close(g_cuse_thread_msg_fd);
1415 : 0 : destroy_fd_group:
1416 : 0 : spdk_fd_group_destroy(g_device_fdgrp);
1417 : 0 : g_device_fdgrp = NULL;
1418 : 0 : return rc;
1419 : 0 : }
1420 : :
1421 : : int
1422 : 21 : spdk_nvme_cuse_register(struct spdk_nvme_ctrlr *ctrlr)
1423 : : {
1424 : : int rc;
1425 : :
1426 [ - + ]: 21 : if (!spdk_process_is_primary()) {
1427 : 0 : SPDK_ERRLOG("only allowed from primary process\n");
1428 : 0 : return -EINVAL;
1429 : : }
1430 : :
1431 : 21 : rc = nvme_io_msg_ctrlr_register(ctrlr, &cuse_nvme_io_msg_producer);
1432 [ + + ]: 21 : if (rc) {
1433 : 1 : return rc;
1434 : : }
1435 : :
1436 [ - + ]: 20 : pthread_mutex_lock(&g_cuse_mtx);
1437 : :
1438 [ + + ]: 20 : if (g_device_fdgrp == NULL) {
1439 : 16 : rc = start_cuse_thread();
1440 [ - + ]: 16 : if (rc < 0) {
1441 : 0 : SPDK_ERRLOG("Failed to start cuse thread to poll for admin commands\n");
1442 [ # # ]: 0 : pthread_mutex_unlock(&g_cuse_mtx);
1443 : 0 : return rc;
1444 : : }
1445 : 0 : }
1446 : :
1447 : 20 : rc = nvme_cuse_start(ctrlr);
1448 [ - + ]: 20 : if (rc) {
1449 : 0 : nvme_io_msg_ctrlr_unregister(ctrlr, &cuse_nvme_io_msg_producer);
1450 : 0 : }
1451 : :
1452 [ - + ]: 20 : pthread_mutex_unlock(&g_cuse_mtx);
1453 : :
1454 : 20 : return rc;
1455 : 0 : }
1456 : :
1457 : : int
1458 : 12 : spdk_nvme_cuse_unregister(struct spdk_nvme_ctrlr *ctrlr)
1459 : : {
1460 : : struct cuse_device *ctrlr_device;
1461 : :
1462 [ - + ]: 12 : if (!spdk_process_is_primary()) {
1463 : 0 : SPDK_ERRLOG("only allowed from primary process\n");
1464 : 0 : return -EINVAL;
1465 : : }
1466 : :
1467 [ - + ]: 12 : pthread_mutex_lock(&g_cuse_mtx);
1468 : :
1469 : 12 : ctrlr_device = nvme_cuse_get_cuse_ctrlr_device(ctrlr);
1470 [ + + ]: 12 : if (!ctrlr_device) {
1471 : 1 : SPDK_ERRLOG("Cannot find associated CUSE device\n");
1472 [ - + ]: 1 : pthread_mutex_unlock(&g_cuse_mtx);
1473 : 1 : return -ENODEV;
1474 : : }
1475 : :
1476 : 11 : cuse_nvme_ctrlr_stop(ctrlr_device);
1477 : :
1478 [ - + ]: 11 : pthread_mutex_unlock(&g_cuse_mtx);
1479 : :
1480 : 11 : nvme_io_msg_ctrlr_unregister(ctrlr, &cuse_nvme_io_msg_producer);
1481 : :
1482 : 11 : return 0;
1483 : 0 : }
1484 : :
1485 : : void
1486 : 0 : spdk_nvme_cuse_update_namespaces(struct spdk_nvme_ctrlr *ctrlr)
1487 : : {
1488 : 0 : nvme_cuse_update(ctrlr);
1489 : 0 : }
1490 : :
1491 : : int
1492 : 1057 : spdk_nvme_cuse_get_ctrlr_name(struct spdk_nvme_ctrlr *ctrlr, char *name, size_t *size)
1493 : : {
1494 : : struct cuse_device *ctrlr_device;
1495 : : size_t req_len;
1496 : :
1497 [ # # ]: 1057 : pthread_mutex_lock(&g_cuse_mtx);
1498 : :
1499 : 1057 : ctrlr_device = nvme_cuse_get_cuse_ctrlr_device(ctrlr);
1500 [ + + ]: 1057 : if (!ctrlr_device) {
1501 [ # # ]: 1038 : pthread_mutex_unlock(&g_cuse_mtx);
1502 : 1038 : return -ENODEV;
1503 : : }
1504 : :
1505 [ - + # # ]: 19 : req_len = strnlen(ctrlr_device->dev_name, sizeof(ctrlr_device->dev_name));
1506 [ + + # # ]: 19 : if (*size < req_len) {
1507 [ # # ]: 3 : *size = req_len;
1508 [ # # ]: 3 : pthread_mutex_unlock(&g_cuse_mtx);
1509 : 3 : return -ENOSPC;
1510 : : }
1511 [ # # ]: 16 : snprintf(name, req_len + 1, "%s", ctrlr_device->dev_name);
1512 : :
1513 [ # # ]: 16 : pthread_mutex_unlock(&g_cuse_mtx);
1514 : :
1515 : 16 : return 0;
1516 : 0 : }
1517 : :
1518 : : int
1519 : 1153 : spdk_nvme_cuse_get_ns_name(struct spdk_nvme_ctrlr *ctrlr, uint32_t nsid, char *name, size_t *size)
1520 : : {
1521 : : struct cuse_device *ns_device;
1522 : : size_t req_len;
1523 : :
1524 [ # # ]: 1153 : pthread_mutex_lock(&g_cuse_mtx);
1525 : :
1526 : 1153 : ns_device = nvme_cuse_get_cuse_ns_device(ctrlr, nsid);
1527 [ + + ]: 1153 : if (!ns_device) {
1528 [ # # ]: 1143 : pthread_mutex_unlock(&g_cuse_mtx);
1529 : 1143 : return -ENODEV;
1530 : : }
1531 : :
1532 [ - + # # ]: 10 : req_len = strnlen(ns_device->dev_name, sizeof(ns_device->dev_name));
1533 [ + + # # ]: 10 : if (*size < req_len) {
1534 [ # # ]: 3 : *size = req_len;
1535 [ # # ]: 3 : pthread_mutex_unlock(&g_cuse_mtx);
1536 : 3 : return -ENOSPC;
1537 : : }
1538 [ # # ]: 7 : snprintf(name, req_len + 1, "%s", ns_device->dev_name);
1539 : :
1540 [ # # ]: 7 : pthread_mutex_unlock(&g_cuse_mtx);
1541 : :
1542 : 7 : return 0;
1543 : 0 : }
1544 : :
1545 : 2752 : SPDK_LOG_REGISTER_COMPONENT(nvme_cuse)
|