Line data Source code
1 : /* SPDX-License-Identifier: BSD-3-Clause
2 : * Copyright (C) 2022 Intel Corporation.
3 : * Copyright (c) Samsung Electronics Co., Ltd.
4 : * All rights reserved.
5 : */
6 :
7 : #include "libxnvme.h"
8 :
9 : #include "bdev_xnvme.h"
10 :
11 : #include "spdk/stdinc.h"
12 :
13 : #include "spdk/barrier.h"
14 : #include "spdk/bdev.h"
15 : #include "spdk/env.h"
16 : #include "spdk/fd.h"
17 : #include "spdk/likely.h"
18 : #include "spdk/thread.h"
19 : #include "spdk/json.h"
20 : #include "spdk/util.h"
21 : #include "spdk/string.h"
22 :
23 : #include "spdk/log.h"
24 :
25 : struct bdev_xnvme_io_channel {
26 : struct xnvme_queue *queue;
27 : struct spdk_poller *poller;
28 : };
29 :
30 : struct bdev_xnvme_task {
31 : struct bdev_xnvme_io_channel *ch;
32 : TAILQ_ENTRY(bdev_xnvme_task) link;
33 : };
34 :
35 : struct bdev_xnvme {
36 : struct spdk_bdev bdev;
37 : char *filename;
38 : char *io_mechanism;
39 : struct xnvme_dev *dev;
40 : uint32_t nsid;
41 : bool conserve_cpu;
42 :
43 : TAILQ_ENTRY(bdev_xnvme) link;
44 : };
45 :
46 : static int bdev_xnvme_init(void);
47 : static void bdev_xnvme_fini(void);
48 : static void bdev_xnvme_free(struct bdev_xnvme *xnvme);
49 : static TAILQ_HEAD(, bdev_xnvme) g_xnvme_bdev_head = TAILQ_HEAD_INITIALIZER(g_xnvme_bdev_head);
50 :
51 : static int
52 0 : bdev_xnvme_get_ctx_size(void)
53 : {
54 0 : return sizeof(struct bdev_xnvme_task);
55 : }
56 :
57 : static int
58 0 : bdev_xnvme_config_json(struct spdk_json_write_ctx *w)
59 : {
60 : struct bdev_xnvme *xnvme;
61 :
62 0 : TAILQ_FOREACH(xnvme, &g_xnvme_bdev_head, link) {
63 0 : spdk_json_write_object_begin(w);
64 :
65 0 : spdk_json_write_named_string(w, "method", "bdev_xnvme_create");
66 :
67 0 : spdk_json_write_named_object_begin(w, "params");
68 0 : spdk_json_write_named_string(w, "name", xnvme->bdev.name);
69 0 : spdk_json_write_named_string(w, "filename", xnvme->filename);
70 0 : spdk_json_write_named_string(w, "io_mechanism", xnvme->io_mechanism);
71 0 : spdk_json_write_named_bool(w, "conserve_cpu", xnvme->conserve_cpu);
72 0 : spdk_json_write_object_end(w);
73 :
74 0 : spdk_json_write_object_end(w);
75 : }
76 :
77 0 : return 0;
78 : }
79 :
80 : static struct spdk_bdev_module xnvme_if = {
81 : .name = "xnvme",
82 : .module_init = bdev_xnvme_init,
83 : .module_fini = bdev_xnvme_fini,
84 : .get_ctx_size = bdev_xnvme_get_ctx_size,
85 : .config_json = bdev_xnvme_config_json,
86 : };
87 :
88 0 : SPDK_BDEV_MODULE_REGISTER(xnvme, &xnvme_if)
89 :
90 : static struct spdk_io_channel *
91 0 : bdev_xnvme_get_io_channel(void *ctx)
92 : {
93 0 : struct bdev_xnvme *xnvme = ctx;
94 :
95 0 : return spdk_get_io_channel(xnvme);
96 : }
97 :
98 : static bool
99 0 : bdev_xnvme_io_type_supported(void *ctx, enum spdk_bdev_io_type io_type)
100 : {
101 0 : struct bdev_xnvme *xnvme = ctx;
102 :
103 0 : switch (io_type) {
104 0 : case SPDK_BDEV_IO_TYPE_READ:
105 : case SPDK_BDEV_IO_TYPE_WRITE:
106 0 : return true;
107 0 : case SPDK_BDEV_IO_TYPE_WRITE_ZEROES:
108 : case SPDK_BDEV_IO_TYPE_UNMAP:
109 : /* libaio and io_uring only supports read and write */
110 0 : return !strcmp(xnvme->io_mechanism, "io_uring_cmd") &&
111 0 : xnvme_dev_get_csi(xnvme->dev) == XNVME_SPEC_CSI_NVM;
112 0 : default:
113 0 : return false;
114 : }
115 : }
116 :
117 : static void
118 0 : bdev_xnvme_destruct_cb(void *io_device)
119 : {
120 0 : struct bdev_xnvme *xnvme = io_device;
121 :
122 0 : TAILQ_REMOVE(&g_xnvme_bdev_head, xnvme, link);
123 0 : bdev_xnvme_free(xnvme);
124 0 : }
125 :
126 : static int
127 0 : bdev_xnvme_destruct(void *ctx)
128 : {
129 0 : struct bdev_xnvme *xnvme = ctx;
130 :
131 0 : spdk_io_device_unregister(xnvme, bdev_xnvme_destruct_cb);
132 :
133 0 : return 0;
134 : }
135 :
136 : static int
137 0 : bdev_xnvme_unmap(struct spdk_bdev_io *bdev_io, struct xnvme_cmd_ctx *ctx, struct bdev_xnvme *xnvme)
138 : {
139 : struct spdk_nvme_dsm_range *range;
140 : uint64_t offset, remaining;
141 : uint64_t num_ranges_u64, num_blocks, offset_blocks;
142 : uint16_t num_ranges;
143 :
144 0 : num_blocks = bdev_io->u.bdev.num_blocks;
145 0 : offset_blocks = bdev_io->u.bdev.offset_blocks;
146 :
147 0 : num_ranges_u64 = spdk_divide_round_up(num_blocks, xnvme->bdev.max_unmap);
148 0 : if (num_ranges_u64 > xnvme->bdev.max_unmap_segments) {
149 0 : SPDK_ERRLOG("Unmap request for %" PRIu64 " blocks is too large\n", num_blocks);
150 0 : return -EINVAL;
151 : }
152 0 : num_ranges = (uint16_t)num_ranges_u64;
153 :
154 0 : offset = offset_blocks;
155 0 : remaining = num_blocks;
156 :
157 0 : assert(bdev_io->u.bdev.iovcnt == 1);
158 0 : range = (struct spdk_nvme_dsm_range *) bdev_io->u.bdev.iovs->iov_base;
159 :
160 : /* Fill max-size ranges until the remaining blocks fit into one range */
161 0 : while (remaining > xnvme->bdev.max_unmap) {
162 0 : range->attributes.raw = 0;
163 0 : range->length = xnvme->bdev.max_unmap;
164 0 : range->starting_lba = offset;
165 :
166 0 : offset += xnvme->bdev.max_unmap;
167 0 : remaining -= xnvme->bdev.max_unmap;
168 0 : range++;
169 : }
170 :
171 : /* Final range describes the remaining blocks */
172 0 : range->attributes.raw = 0;
173 0 : range->length = remaining;
174 0 : range->starting_lba = offset;
175 :
176 0 : ctx->cmd.common.opcode = XNVME_SPEC_NVM_OPC_DATASET_MANAGEMENT;
177 0 : ctx->cmd.common.nsid = xnvme->nsid;
178 0 : ctx->cmd.nvm.nlb = num_blocks - 1;
179 0 : ctx->cmd.nvm.slba = offset_blocks;
180 0 : ctx->cmd.dsm.nr = num_ranges - 1;
181 0 : ctx->cmd.dsm.ad = true;
182 :
183 0 : return 0;
184 : }
185 :
186 : static void
187 0 : _xnvme_submit_request(struct spdk_io_channel *ch, struct spdk_bdev_io *bdev_io)
188 : {
189 0 : struct bdev_xnvme_task *xnvme_task = (struct bdev_xnvme_task *)bdev_io->driver_ctx;
190 0 : struct bdev_xnvme *xnvme = (struct bdev_xnvme *)bdev_io->bdev->ctxt;
191 0 : struct bdev_xnvme_io_channel *xnvme_ch = spdk_io_channel_get_ctx(ch);
192 0 : struct xnvme_cmd_ctx *ctx = xnvme_queue_get_cmd_ctx(xnvme_ch->queue);
193 : int err;
194 :
195 0 : SPDK_DEBUGLOG(xnvme, "bdev_io : %p, iov_cnt : %d, bdev_xnvme_task : %p\n",
196 : bdev_io, bdev_io->u.bdev.iovcnt, (struct bdev_xnvme_task *)bdev_io->driver_ctx);
197 :
198 0 : switch (bdev_io->type) {
199 0 : case SPDK_BDEV_IO_TYPE_READ:
200 0 : ctx->cmd.common.opcode = XNVME_SPEC_NVM_OPC_READ;
201 0 : ctx->cmd.common.nsid = xnvme->nsid;
202 0 : ctx->cmd.nvm.nlb = bdev_io->u.bdev.num_blocks - 1;
203 0 : ctx->cmd.nvm.slba = bdev_io->u.bdev.offset_blocks;
204 0 : break;
205 0 : case SPDK_BDEV_IO_TYPE_WRITE:
206 0 : ctx->cmd.common.opcode = XNVME_SPEC_NVM_OPC_WRITE;
207 0 : ctx->cmd.common.nsid = xnvme->nsid;
208 0 : ctx->cmd.nvm.nlb = bdev_io->u.bdev.num_blocks - 1;
209 0 : ctx->cmd.nvm.slba = bdev_io->u.bdev.offset_blocks;
210 0 : break;
211 0 : case SPDK_BDEV_IO_TYPE_WRITE_ZEROES:
212 0 : ctx->cmd.common.opcode = XNVME_SPEC_NVM_OPC_WRITE_ZEROES;
213 0 : ctx->cmd.common.nsid = xnvme->nsid;
214 0 : ctx->cmd.nvm.nlb = bdev_io->u.bdev.num_blocks - 1;
215 0 : ctx->cmd.nvm.slba = bdev_io->u.bdev.offset_blocks;
216 0 : break;
217 0 : case SPDK_BDEV_IO_TYPE_UNMAP:
218 0 : if (bdev_xnvme_unmap(bdev_io, ctx, xnvme)) {
219 0 : xnvme_queue_put_cmd_ctx(xnvme_ch->queue, ctx);
220 0 : spdk_bdev_io_complete(bdev_io, SPDK_BDEV_IO_STATUS_FAILED);
221 0 : return;
222 : }
223 0 : break;
224 0 : default:
225 0 : SPDK_ERRLOG("Wrong io type\n");
226 :
227 0 : xnvme_queue_put_cmd_ctx(xnvme_ch->queue, ctx);
228 0 : spdk_bdev_io_complete(bdev_io, SPDK_BDEV_IO_STATUS_FAILED);
229 0 : return;
230 : }
231 :
232 0 : xnvme_task->ch = xnvme_ch;
233 0 : ctx->async.cb_arg = xnvme_task;
234 :
235 0 : err = xnvme_cmd_passv(ctx, bdev_io->u.bdev.iovs, bdev_io->u.bdev.iovcnt,
236 0 : bdev_io->u.bdev.num_blocks * xnvme->bdev.blocklen, NULL, 0, 0);
237 :
238 0 : switch (err) {
239 : /* Submission success! */
240 0 : case 0:
241 0 : SPDK_DEBUGLOG(xnvme, "io_channel : %p, iovcnt:%d, nblks: %lu off: %#lx\n",
242 : xnvme_ch, bdev_io->u.bdev.iovcnt,
243 : bdev_io->u.bdev.num_blocks, bdev_io->u.bdev.offset_blocks);
244 0 : return;
245 :
246 : /* Submission failed: queue is full or no memory => Queue the I/O in bdev layer */
247 0 : case -EBUSY:
248 : case -EAGAIN:
249 : case -ENOMEM:
250 0 : SPDK_WARNLOG("Start to queue I/O for xnvme bdev\n");
251 :
252 0 : xnvme_queue_put_cmd_ctx(xnvme_ch->queue, ctx);
253 0 : spdk_bdev_io_complete(bdev_io, SPDK_BDEV_IO_STATUS_NOMEM);
254 0 : return;
255 :
256 : /* Submission failed: unexpected error, put the command-context back in the queue */
257 0 : default:
258 0 : SPDK_ERRLOG("bdev_xnvme_cmd_passv : Submission failed: unexpected error\n");
259 :
260 0 : xnvme_queue_put_cmd_ctx(xnvme_ch->queue, ctx);
261 0 : spdk_bdev_io_complete(bdev_io, SPDK_BDEV_IO_STATUS_FAILED);
262 0 : return;
263 : }
264 : }
265 :
266 : static void
267 0 : bdev_xnvme_get_buf_cb(struct spdk_io_channel *ch, struct spdk_bdev_io *bdev_io, bool success)
268 : {
269 0 : struct bdev_xnvme_io_channel *xnvme_ch = spdk_io_channel_get_ctx(ch);
270 :
271 0 : if (!success) {
272 0 : xnvme_queue_put_cmd_ctx(xnvme_ch->queue, xnvme_queue_get_cmd_ctx(xnvme_ch->queue));
273 0 : spdk_bdev_io_complete(bdev_io, SPDK_BDEV_IO_STATUS_FAILED);
274 0 : return;
275 : }
276 :
277 0 : _xnvme_submit_request(ch, bdev_io);
278 : }
279 :
280 : static void
281 0 : bdev_xnvme_submit_request(struct spdk_io_channel *ch, struct spdk_bdev_io *bdev_io)
282 : {
283 0 : switch (bdev_io->type) {
284 : /* Read and write operations must be performed on buffers aligned to
285 : * bdev->required_alignment. If user specified unaligned buffers,
286 : * get the aligned buffer from the pool by calling spdk_bdev_io_get_buf. */
287 0 : case SPDK_BDEV_IO_TYPE_READ:
288 : case SPDK_BDEV_IO_TYPE_WRITE:
289 0 : spdk_bdev_io_get_buf(bdev_io, bdev_xnvme_get_buf_cb,
290 0 : bdev_io->u.bdev.num_blocks * bdev_io->bdev->blocklen);
291 0 : break;
292 0 : case SPDK_BDEV_IO_TYPE_UNMAP:
293 : /* The max number of segments defined by spec is 256 and an
294 : * spdk_nvme_dsm_range structure is 16 bytes */
295 0 : spdk_bdev_io_get_buf(bdev_io, bdev_xnvme_get_buf_cb, 256 * 16);
296 0 : break;
297 0 : case SPDK_BDEV_IO_TYPE_WRITE_ZEROES:
298 0 : _xnvme_submit_request(ch, bdev_io);
299 0 : break;
300 :
301 0 : default:
302 0 : spdk_bdev_io_complete(bdev_io, SPDK_BDEV_IO_STATUS_FAILED);
303 0 : break;
304 : }
305 0 : }
306 :
307 : static const struct spdk_bdev_fn_table xnvme_fn_table = {
308 : .destruct = bdev_xnvme_destruct,
309 : .submit_request = bdev_xnvme_submit_request,
310 : .io_type_supported = bdev_xnvme_io_type_supported,
311 : .get_io_channel = bdev_xnvme_get_io_channel,
312 : };
313 :
314 : static void
315 0 : bdev_xnvme_free(struct bdev_xnvme *xnvme)
316 : {
317 0 : assert(xnvme != NULL);
318 :
319 0 : xnvme_dev_close(xnvme->dev);
320 0 : free(xnvme->io_mechanism);
321 0 : free(xnvme->filename);
322 0 : free(xnvme->bdev.name);
323 0 : free(xnvme);
324 0 : }
325 :
326 : static void
327 0 : bdev_xnvme_cmd_cb(struct xnvme_cmd_ctx *ctx, void *cb_arg)
328 : {
329 0 : struct bdev_xnvme_task *xnvme_task = ctx->async.cb_arg;
330 0 : enum spdk_bdev_io_status status = SPDK_BDEV_IO_STATUS_SUCCESS;
331 :
332 0 : SPDK_DEBUGLOG(xnvme, "xnvme_task : %p\n", xnvme_task);
333 :
334 0 : if (xnvme_cmd_ctx_cpl_status(ctx)) {
335 0 : SPDK_ERRLOG("xNVMe I/O Failed\n");
336 0 : xnvme_cmd_ctx_pr(ctx, XNVME_PR_DEF);
337 0 : status = SPDK_BDEV_IO_STATUS_FAILED;
338 : }
339 :
340 0 : spdk_bdev_io_complete(spdk_bdev_io_from_ctx(xnvme_task), status);
341 :
342 : /* Completed: Put the command- context back in the queue */
343 0 : xnvme_queue_put_cmd_ctx(ctx->async.queue, ctx);
344 0 : }
345 :
346 : static int
347 0 : bdev_xnvme_poll(void *arg)
348 : {
349 0 : struct bdev_xnvme_io_channel *ch = arg;
350 : int rc;
351 :
352 0 : rc = xnvme_queue_poke(ch->queue, 0);
353 0 : if (rc < 0) {
354 0 : SPDK_ERRLOG("xnvme_queue_poke failure rc : %d\n", rc);
355 0 : return SPDK_POLLER_BUSY;
356 : }
357 :
358 0 : return xnvme_queue_get_outstanding(ch->queue) ? SPDK_POLLER_BUSY : SPDK_POLLER_IDLE;
359 : }
360 :
361 : static int
362 0 : bdev_xnvme_queue_create_cb(void *io_device, void *ctx_buf)
363 : {
364 0 : struct bdev_xnvme *xnvme = io_device;
365 0 : struct bdev_xnvme_io_channel *ch = ctx_buf;
366 : int rc;
367 0 : int qd = 512;
368 :
369 0 : rc = xnvme_queue_init(xnvme->dev, qd, 0, &ch->queue);
370 0 : if (rc) {
371 0 : SPDK_ERRLOG("xnvme_queue_init failure: %d\n", rc);
372 0 : return 1;
373 : }
374 :
375 0 : xnvme_queue_set_cb(ch->queue, bdev_xnvme_cmd_cb, ch);
376 :
377 0 : ch->poller = SPDK_POLLER_REGISTER(bdev_xnvme_poll, ch, 0);
378 :
379 0 : return 0;
380 : }
381 :
382 : static void
383 0 : bdev_xnvme_queue_destroy_cb(void *io_device, void *ctx_buf)
384 : {
385 0 : struct bdev_xnvme_io_channel *ch = ctx_buf;
386 :
387 0 : spdk_poller_unregister(&ch->poller);
388 :
389 0 : xnvme_queue_term(ch->queue);
390 0 : }
391 :
392 : struct spdk_bdev *
393 0 : create_xnvme_bdev(const char *name, const char *filename, const char *io_mechanism,
394 : bool conserve_cpu)
395 : {
396 : struct bdev_xnvme *xnvme;
397 : const struct xnvme_spec_nvm_idfy_ctrlr *ctrlr;
398 : uint32_t block_size;
399 : uint64_t bdev_size;
400 : int rc;
401 0 : struct xnvme_opts opts = xnvme_opts_default();
402 :
403 0 : xnvme = calloc(1, sizeof(*xnvme));
404 0 : if (!xnvme) {
405 0 : SPDK_ERRLOG("Unable to allocate enough memory for xNVMe backend\n");
406 0 : return NULL;
407 : }
408 :
409 0 : opts.direct = 1;
410 0 : opts.async = io_mechanism;
411 0 : if (!opts.async) {
412 0 : goto error_return;
413 : }
414 0 : xnvme->io_mechanism = strdup(io_mechanism);
415 0 : if (!xnvme->io_mechanism) {
416 0 : goto error_return;
417 : }
418 :
419 0 : xnvme->conserve_cpu = conserve_cpu;
420 0 : if (!xnvme->conserve_cpu) {
421 0 : if (!strcmp(xnvme->io_mechanism, "libaio")) {
422 0 : opts.poll_io = 1;
423 0 : } else if (!strcmp(xnvme->io_mechanism, "io_uring")) {
424 0 : opts.poll_io = 1;
425 0 : } else if (!strcmp(xnvme->io_mechanism, "io_uring_cmd")) {
426 0 : opts.poll_io = 1;
427 : }
428 : }
429 :
430 0 : xnvme->filename = strdup(filename);
431 0 : if (!xnvme->filename) {
432 0 : goto error_return;
433 : }
434 :
435 0 : xnvme->dev = xnvme_dev_open(xnvme->filename, &opts);
436 0 : if (!xnvme->dev) {
437 0 : SPDK_ERRLOG("Unable to open xNVMe device %s\n", filename);
438 0 : goto error_return;
439 : }
440 :
441 0 : xnvme->nsid = xnvme_dev_get_nsid(xnvme->dev);
442 :
443 0 : bdev_size = xnvme_dev_get_geo(xnvme->dev)->tbytes;
444 0 : block_size = xnvme_dev_get_geo(xnvme->dev)->nbytes;
445 :
446 0 : xnvme->bdev.name = strdup(name);
447 0 : if (!xnvme->bdev.name) {
448 0 : goto error_return;
449 : }
450 :
451 0 : xnvme->bdev.product_name = "xNVMe bdev";
452 0 : xnvme->bdev.module = &xnvme_if;
453 :
454 0 : xnvme->bdev.write_cache = 0;
455 0 : xnvme->bdev.max_write_zeroes = UINT16_MAX + 1;
456 :
457 0 : if (xnvme_dev_get_csi(xnvme->dev) == XNVME_SPEC_CSI_NVM) {
458 0 : ctrlr = (struct xnvme_spec_nvm_idfy_ctrlr *) xnvme_dev_get_ctrlr_css(xnvme->dev);
459 0 : xnvme->bdev.max_unmap = ctrlr->dmrsl ? ctrlr->dmrsl : SPDK_NVME_DATASET_MANAGEMENT_RANGE_MAX_BLOCKS;
460 0 : xnvme->bdev.max_unmap_segments = ctrlr->dmrl ? ctrlr->dmrl :
461 : SPDK_NVME_DATASET_MANAGEMENT_MAX_RANGES;
462 : }
463 :
464 0 : if (block_size == 0) {
465 0 : SPDK_ERRLOG("Block size could not be auto-detected\n");
466 0 : goto error_return;
467 : }
468 :
469 0 : if (block_size < 512) {
470 0 : SPDK_ERRLOG("Invalid block size %" PRIu32 " (must be at least 512).\n", block_size);
471 0 : goto error_return;
472 : }
473 :
474 0 : if (!spdk_u32_is_pow2(block_size)) {
475 0 : SPDK_ERRLOG("Invalid block size %" PRIu32 " (must be a power of 2.)\n", block_size);
476 0 : goto error_return;
477 : }
478 :
479 0 : SPDK_DEBUGLOG(xnvme, "bdev_name : %s, bdev_size : %lu, block_size : %d\n",
480 : xnvme->bdev.name, bdev_size, block_size);
481 :
482 0 : xnvme->bdev.blocklen = block_size;
483 0 : xnvme->bdev.required_alignment = spdk_u32log2(block_size);
484 :
485 0 : if (bdev_size % xnvme->bdev.blocklen != 0) {
486 0 : SPDK_ERRLOG("Disk size %" PRIu64 " is not a multiple of block size %" PRIu32 "\n",
487 : bdev_size, xnvme->bdev.blocklen);
488 0 : goto error_return;
489 : }
490 :
491 0 : xnvme->bdev.blockcnt = bdev_size / xnvme->bdev.blocklen;
492 0 : xnvme->bdev.ctxt = xnvme;
493 :
494 0 : xnvme->bdev.fn_table = &xnvme_fn_table;
495 :
496 0 : spdk_io_device_register(xnvme, bdev_xnvme_queue_create_cb, bdev_xnvme_queue_destroy_cb,
497 : sizeof(struct bdev_xnvme_io_channel),
498 0 : xnvme->bdev.name);
499 0 : rc = spdk_bdev_register(&xnvme->bdev);
500 0 : if (rc) {
501 0 : spdk_io_device_unregister(xnvme, NULL);
502 0 : goto error_return;
503 : }
504 :
505 0 : TAILQ_INSERT_TAIL(&g_xnvme_bdev_head, xnvme, link);
506 :
507 0 : return &xnvme->bdev;
508 :
509 0 : error_return:
510 0 : bdev_xnvme_free(xnvme);
511 0 : return NULL;
512 : }
513 :
514 : void
515 0 : delete_xnvme_bdev(const char *name, spdk_bdev_unregister_cb cb_fn, void *cb_arg)
516 : {
517 : int rc;
518 :
519 0 : rc = spdk_bdev_unregister_by_name(name, &xnvme_if, cb_fn, cb_arg);
520 0 : if (rc != 0) {
521 0 : cb_fn(cb_arg, rc);
522 : }
523 0 : }
524 :
525 : static int
526 0 : bdev_xnvme_module_create_cb(void *io_device, void *ctx_buf)
527 : {
528 0 : return 0;
529 : }
530 :
531 : static void
532 0 : bdev_xnvme_module_destroy_cb(void *io_device, void *ctx_buf)
533 : {
534 0 : }
535 :
536 : static int
537 0 : bdev_xnvme_init(void)
538 : {
539 0 : spdk_io_device_register(&xnvme_if, bdev_xnvme_module_create_cb, bdev_xnvme_module_destroy_cb,
540 : 0, "xnvme_module");
541 :
542 0 : return 0;
543 : }
544 :
545 : static void
546 0 : bdev_xnvme_fini(void)
547 : {
548 0 : spdk_io_device_unregister(&xnvme_if, NULL);
549 0 : }
550 :
551 0 : SPDK_LOG_REGISTER_COMPONENT(xnvme)
|