diff --git a/0001-blobstore-fix-memleak-problem-in-blob_load_cpl.patch b/0001-blobstore-fix-memleak-problem-in-blob_load_cpl.patch deleted file mode 100644 index 3d0afbe22504367281e14a82595ba7dbdef1d22a..0000000000000000000000000000000000000000 --- a/0001-blobstore-fix-memleak-problem-in-blob_load_cpl.patch +++ /dev/null @@ -1,50 +0,0 @@ -From 7c4665e485e764f4fee069e60bdeffa387b15a4b Mon Sep 17 00:00:00 2001 -From: Zhiqiang Liu -Date: Sun, 13 Jun 2021 19:53:08 +0800 -Subject: [PATCH 18/28] blobstore:fix memleak problem in blob_load_cpl() - -In blob_load_cpl(), spdk_realloc() is called to realloc -memory of ctx->pages. If spdk_realloc() return NULL, -the ctx->pages is set to NULL without being freed, -and then a memleak problem occurs. - -Signed-off-by: Zhiqiang Liu -Change-Id: Idf21b690e89beab0245ba57a5de66a4f506d54fb -Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/8308 -Tested-by: SPDK CI Jenkins -Community-CI: Mellanox Build Bot -Reviewed-by: Aleksey Marchuk -Reviewed-by: Tomasz Zawadzki ---- - lib/blob/blobstore.c | 8 +++++--- - 1 file changed, 5 insertions(+), 3 deletions(-) - -diff --git a/lib/blob/blobstore.c b/lib/blob/blobstore.c -index 08483fc..1f7d224 100644 ---- a/lib/blob/blobstore.c -+++ b/lib/blob/blobstore.c -@@ -1490,16 +1490,18 @@ blob_load_cpl(spdk_bs_sequence_t *seq, void *cb_arg, int bserrno) - } - - if (page->next != SPDK_INVALID_MD_PAGE) { -+ struct spdk_blob_md_page *tmp_pages; - uint32_t next_page = page->next; - uint64_t next_lba = bs_md_page_to_lba(blob->bs, next_page); - - /* Read the next page */ -- ctx->num_pages++; -- ctx->pages = spdk_realloc(ctx->pages, (sizeof(*page) * ctx->num_pages), 0); -- if (ctx->pages == NULL) { -+ tmp_pages = spdk_realloc(ctx->pages, (sizeof(*page) * (ctx->num_pages + 1)), 0); -+ if (tmp_pages == NULL) { - blob_load_final(ctx, -ENOMEM); - return; - } -+ ctx->num_pages++; -+ ctx->pages = tmp_pages; - - bs_sequence_read_dev(seq, &ctx->pages[ctx->num_pages - 1], - next_lba, --- -1.8.3.1 - diff --git a/0002-blobstore-fix-potential-memleak-problem-in-blob_seri.patch b/0002-blobstore-fix-potential-memleak-problem-in-blob_seri.patch deleted file mode 100644 index d237bd1f19bec01732a963839b4381bd2d0188d6..0000000000000000000000000000000000000000 --- a/0002-blobstore-fix-potential-memleak-problem-in-blob_seri.patch +++ /dev/null @@ -1,69 +0,0 @@ -From 94f83ca86169a3b5971c8edf99e3a4ff8e6d2d51 Mon Sep 17 00:00:00 2001 -From: Zhiqiang Liu -Date: Sun, 13 Jun 2021 19:42:14 +0800 -Subject: [PATCH 19/28] blobstore: fix potential memleak problem in - blob_serialize_add_page() - -In blob_serialize_add_page(), *pages is set to spdk_realloc(*pages). -If spdk_realloc() returns NULL, the *pages pointer will be -overridden, whose memory will leak. - -Here, we introduce a new var (tmp_pages) for checking the return -value of spdk_realloc(*pages). - -Signed-off-by: Zhiqiang Liu -Change-Id: Ib2ead3f3b5d5e44688d1f0568816f483aa9e101f -Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/8307 -Community-CI: Mellanox Build Bot -Tested-by: SPDK CI Jenkins -Reviewed-by: Aleksey Marchuk -Reviewed-by: Tomasz Zawadzki ---- - lib/blob/blobstore.c | 20 +++++++++++--------- - 1 file changed, 11 insertions(+), 9 deletions(-) - -diff --git a/lib/blob/blobstore.c b/lib/blob/blobstore.c -index 1f7d224..551e615 100644 ---- a/lib/blob/blobstore.c -+++ b/lib/blob/blobstore.c -@@ -874,26 +874,28 @@ blob_serialize_add_page(const struct spdk_blob *blob, - uint32_t *page_count, - struct spdk_blob_md_page **last_page) - { -- struct spdk_blob_md_page *page; -+ struct spdk_blob_md_page *page, *tmp_pages; - - assert(pages != NULL); - assert(page_count != NULL); - -+ *last_page = NULL; - if (*page_count == 0) { - assert(*pages == NULL); -- *page_count = 1; - *pages = spdk_malloc(SPDK_BS_PAGE_SIZE, 0, - NULL, SPDK_ENV_SOCKET_ID_ANY, SPDK_MALLOC_DMA); -+ if (*pages == NULL) { -+ return -ENOMEM; -+ } -+ *page_count = 1; - } else { - assert(*pages != NULL); -+ tmp_pages = spdk_realloc(*pages, SPDK_BS_PAGE_SIZE * (*page_count + 1), 0); -+ if (tmp_pages == NULL) { -+ return -ENOMEM; -+ } - (*page_count)++; -- *pages = spdk_realloc(*pages, SPDK_BS_PAGE_SIZE * (*page_count), 0); -- } -- -- if (*pages == NULL) { -- *page_count = 0; -- *last_page = NULL; -- return -ENOMEM; -+ *pages = tmp_pages; - } - - page = &(*pages)[*page_count - 1]; --- -1.8.3.1 - diff --git a/0003-idxd-fix-memleak-problem-in-spdk_idxd_configure_chan.patch b/0003-idxd-fix-memleak-problem-in-spdk_idxd_configure_chan.patch deleted file mode 100644 index 9ddb435cb89fa9675a22527981aa78df97c9d9fb..0000000000000000000000000000000000000000 --- a/0003-idxd-fix-memleak-problem-in-spdk_idxd_configure_chan.patch +++ /dev/null @@ -1,72 +0,0 @@ -From 7e571efc4d6b726b645cd7dc32bab7231bdf543c Mon Sep 17 00:00:00 2001 -From: Zhiqiang Liu -Date: Fri, 18 Jun 2021 17:11:16 +0800 -Subject: [PATCH 20/28] idxd: fix memleak problem in spdk_idxd_configure_chan() - -In spdk_idxd_configure_chan(), if memory allocation fails in -TAILQ_FOREACH() {} code range, we will goto err_user_comp and -err_user_desc tag, in which we donot free chan->completions -and confused batch->user_completions with chan->completions. -Memleak problem and double free problem may occurs. - -Signed-off-by: Zhiqiang Liu -Change-Id: I0e588a35184d97cab0ea6b6c013ca8b3342f940a -Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/8432 -Tested-by: SPDK CI Jenkins -Reviewed-by: Ziye Yang -Reviewed-by: Changpeng Liu -Reviewed-by: Jim Harris -Community-CI: Mellanox Build Bot ---- - lib/idxd/idxd.c | 16 +++++++++------- - 1 file changed, 9 insertions(+), 7 deletions(-) - -diff --git a/lib/idxd/idxd.c b/lib/idxd/idxd.c -index f240225..4f76f09 100644 ---- a/lib/idxd/idxd.c -+++ b/lib/idxd/idxd.c -@@ -194,7 +194,7 @@ spdk_idxd_configure_chan(struct spdk_idxd_io_channel *chan) - if (batch->user_desc == NULL) { - SPDK_ERRLOG("Failed to allocate batch descriptor memory\n"); - rc = -ENOMEM; -- goto err_user_desc; -+ goto err_user_desc_or_comp; - } - - batch->user_completions = spdk_zmalloc(DESC_PER_BATCH * sizeof(struct idxd_comp), -@@ -203,7 +203,7 @@ spdk_idxd_configure_chan(struct spdk_idxd_io_channel *chan) - if (batch->user_completions == NULL) { - SPDK_ERRLOG("Failed to allocate user completion memory\n"); - rc = -ENOMEM; -- goto err_user_comp; -+ goto err_user_desc_or_comp; - } - } - -@@ -212,16 +212,18 @@ spdk_idxd_configure_chan(struct spdk_idxd_io_channel *chan) - - return 0; - --err_user_comp: -+err_user_desc_or_comp: - TAILQ_FOREACH(batch, &chan->batch_pool, link) { - spdk_free(batch->user_desc); -+ batch->user_desc = NULL; -+ spdk_free(batch->user_completions); -+ batch->user_completions = NULL; - } --err_user_desc: -- TAILQ_FOREACH(batch, &chan->batch_pool, link) { -- spdk_free(chan->completions); -- } -+ spdk_free(chan->completions); -+ chan->completions = NULL; - err_comp: - spdk_free(chan->desc); -+ chan->desc = NULL; - err_desc: - spdk_bit_array_free(&chan->ring_slots); - --- -1.8.3.1 - diff --git a/0004-idxd-fix-one-memleak-problem-in-spdk_idxd_get_channe.patch b/0004-idxd-fix-one-memleak-problem-in-spdk_idxd_get_channe.patch deleted file mode 100644 index 8ae9d81a30799d901d748e47eb0bee81d0291445..0000000000000000000000000000000000000000 --- a/0004-idxd-fix-one-memleak-problem-in-spdk_idxd_get_channe.patch +++ /dev/null @@ -1,35 +0,0 @@ -From b4c40bfdf47efc027330a805947db521df8b8959 Mon Sep 17 00:00:00 2001 -From: Zhiqiang Liu -Date: Sun, 13 Jun 2021 14:53:27 +0800 -Subject: [PATCH 21/28] idxd: fix one memleak problem in - spdk_idxd_get_channel() - -In spdk_idxd_get_channel(), if chan->batch_base is allocated -faild, we should free chan before returning NULL. - -Signed-off-by: Zhiqiang Liu -Change-Id: Ia652c334aead592429c1171da73d67160879686d -Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/8301 -Community-CI: Mellanox Build Bot -Reviewed-by: Aleksey Marchuk -Reviewed-by: Changpeng Liu -Tested-by: SPDK CI Jenkins ---- - lib/idxd/idxd.c | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/lib/idxd/idxd.c b/lib/idxd/idxd.c -index 4f76f09..d2fad12 100644 ---- a/lib/idxd/idxd.c -+++ b/lib/idxd/idxd.c -@@ -121,6 +121,7 @@ spdk_idxd_get_channel(struct spdk_idxd_device *idxd) - chan->batch_base = calloc(NUM_BATCHES_PER_CHANNEL, sizeof(struct idxd_batch)); - if (chan->batch_base == NULL) { - SPDK_ERRLOG("Failed to allocate batch pool\n"); -+ free(chan); - return NULL; - } - --- -1.8.3.1 - diff --git a/0005-ioat-fix-potential-double-free-problem-in-ioat_chann.patch b/0005-ioat-fix-potential-double-free-problem-in-ioat_chann.patch deleted file mode 100644 index 11c8505fb5b90b16e25865b780cc8410a79ff970..0000000000000000000000000000000000000000 --- a/0005-ioat-fix-potential-double-free-problem-in-ioat_chann.patch +++ /dev/null @@ -1,41 +0,0 @@ -From 46dd4eea588780d082ff0ce002a1dc0ad6e3e7eb Mon Sep 17 00:00:00 2001 -From: Zhiqiang Liu -Date: Sun, 13 Jun 2021 12:23:58 +0800 -Subject: [PATCH 22/28] ioat: fix potential double free problem in - ioat_channel_start() - -In ioat_channel_start(), if spdk_vtophys(ioat->comp_update) returns -SPDK_VTOPHYS_ERROR, spdk_free is called to free ioat->comp_update, -and ioat->comp_update is not set to NULL. However, the caller -ioat_attach() will also call ioat_channel_destruct() to free -ioat->comp_update, then double-free problem occurs. - -Here, we will not free ioat->comp_update in ioat_channel_start(), -ioat_channel_destruct() will do that. - -Signed-off-by: Zhiqiang Liu -Change-Id: I3be19a3feec5c2188051ee67820bfd1e61de9b48 -Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/8300 -Community-CI: Mellanox Build Bot -Reviewed-by: Changpeng Liu -Reviewed-by: Aleksey Marchuk -Tested-by: SPDK CI Jenkins ---- - lib/ioat/ioat.c | 1 - - 1 file changed, 1 deletion(-) - -diff --git a/lib/ioat/ioat.c b/lib/ioat/ioat.c -index 27ac0a0..af83c42 100644 ---- a/lib/ioat/ioat.c -+++ b/lib/ioat/ioat.c -@@ -429,7 +429,6 @@ ioat_channel_start(struct spdk_ioat_chan *ioat) - - comp_update_bus_addr = spdk_vtophys((void *)ioat->comp_update, NULL); - if (comp_update_bus_addr == SPDK_VTOPHYS_ERROR) { -- spdk_free((void *)ioat->comp_update); - return -1; - } - --- -1.8.3.1 - diff --git a/0006-nvmf-check-return-value-of-strdup-in-spdk_nvmf_subsy.patch b/0006-nvmf-check-return-value-of-strdup-in-spdk_nvmf_subsy.patch deleted file mode 100644 index 5b054f5bd421fcef26cea0762af8a6cdd289c8b5..0000000000000000000000000000000000000000 --- a/0006-nvmf-check-return-value-of-strdup-in-spdk_nvmf_subsy.patch +++ /dev/null @@ -1,43 +0,0 @@ -From 7441bfb0394c6cc54ddcd270a86685b9dad16474 Mon Sep 17 00:00:00 2001 -From: Zhiqiang Liu -Date: Sun, 13 Jun 2021 18:37:02 +0800 -Subject: [PATCH 23/28] nvmf: check return value of strdup in - spdk_nvmf_subsystem_disconnect_host() - -In spdk_nvmf_subsystem_disconnect_host(), we should check -whether strdup() return NULL. - -Signed-off-by: Zhiqiang Liu -Change-Id: I29cb6b2499ecd2a2367001c0d21ac95da4e10e20 -Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/8304 -Community-CI: Mellanox Build Bot -Tested-by: SPDK CI Jenkins -Reviewed-by: Changpeng Liu -Reviewed-by: Aleksey Marchuk -Reviewed-by: Jim Harris ---- - lib/nvmf/subsystem.c | 7 ++++++- - 1 file changed, 6 insertions(+), 1 deletion(-) - -diff --git a/lib/nvmf/subsystem.c b/lib/nvmf/subsystem.c -index 8a3dd3b..5fc1813 100644 ---- a/lib/nvmf/subsystem.c -+++ b/lib/nvmf/subsystem.c -@@ -831,8 +831,13 @@ spdk_nvmf_subsystem_disconnect_host(struct spdk_nvmf_subsystem *subsystem, - return -ENOMEM; - } - -- ctx->subsystem = subsystem; - ctx->hostnqn = strdup(hostnqn); -+ if (ctx->hostnqn == NULL) { -+ free(ctx); -+ return -ENOMEM; -+ } -+ -+ ctx->subsystem = subsystem; - ctx->cb_fn = cb_fn; - ctx->cb_arg = cb_arg; - --- -1.8.3.1 - diff --git a/0007-nvmf-check-return-value-of-strdup-in-spdk_nvmf_subsy.patch b/0007-nvmf-check-return-value-of-strdup-in-spdk_nvmf_subsy.patch deleted file mode 100644 index 0f677733106c1998b7f39d9873b4eed5a74e4ee1..0000000000000000000000000000000000000000 --- a/0007-nvmf-check-return-value-of-strdup-in-spdk_nvmf_subsy.patch +++ /dev/null @@ -1,82 +0,0 @@ -From b367f485f83e65b76d3ae67b5ab4bc344e1a7c49 Mon Sep 17 00:00:00 2001 -From: Zhiqiang Liu -Date: Sun, 13 Jun 2021 18:59:13 +0800 -Subject: [PATCH 24/28] nvmf:check return value of strdup in - spdk_nvmf_subsystem_add_ns_ext() - -In spdk_nvmf_subsystem_add_ns_ext(), ns->ptpl_file is set to strdup(), -which may return NULL. We should deal with it. - -Signed-off-by: Zhiqiang Liu -Change-Id: If95102fe9d6d789b8ba9e846c4d7f4e22e48a93c -Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/8305 -Community-CI: Mellanox Build Bot -Reviewed-by: Changpeng Liu -Reviewed-by: Aleksey Marchuk -Reviewed-by: Jim Harris -Tested-by: SPDK CI Jenkins ---- - lib/nvmf/subsystem.c | 30 ++++++++++++++++++------------ - 1 file changed, 18 insertions(+), 12 deletions(-) - -diff --git a/lib/nvmf/subsystem.c b/lib/nvmf/subsystem.c -index 5fc1813..5729524 100644 ---- a/lib/nvmf/subsystem.c -+++ b/lib/nvmf/subsystem.c -@@ -1451,14 +1451,14 @@ spdk_nvmf_subsystem_add_ns_ext(struct spdk_nvmf_subsystem *subsystem, const char - rc = nvmf_ns_reservation_restore(ns, &info); - if (rc) { - SPDK_ERRLOG("Subsystem restore reservation failed\n"); -- subsystem->ns[opts.nsid - 1] = NULL; -- spdk_bdev_module_release_bdev(ns->bdev); -- spdk_bdev_close(ns->desc); -- free(ns); -- return 0; -+ goto err_ns_reservation_restore; - } - } - ns->ptpl_file = strdup(ptpl_file); -+ if (!ns->ptpl_file) { -+ SPDK_ERRLOG("Namespace ns->ptpl_file allocation failed\n"); -+ goto err_strdup; -+ } - } - - for (transport = spdk_nvmf_transport_get_first(subsystem->tgt); transport; -@@ -1467,13 +1467,7 @@ spdk_nvmf_subsystem_add_ns_ext(struct spdk_nvmf_subsystem *subsystem, const char - rc = transport->ops->subsystem_add_ns(transport, subsystem, ns); - if (rc) { - SPDK_ERRLOG("Namespace attachment is not allowed by %s transport\n", transport->ops->name); -- free(ns->ptpl_file); -- nvmf_ns_reservation_clear_all_registrants(ns); -- subsystem->ns[opts.nsid - 1] = NULL; -- spdk_bdev_module_release_bdev(ns->bdev); -- spdk_bdev_close(ns->desc); -- free(ns); -- return 0; -+ goto err_subsystem_add_ns; - } - } - } -@@ -1486,6 +1480,18 @@ spdk_nvmf_subsystem_add_ns_ext(struct spdk_nvmf_subsystem *subsystem, const char - nvmf_subsystem_ns_changed(subsystem, opts.nsid); - - return opts.nsid; -+ -+err_subsystem_add_ns: -+ free(ns->ptpl_file); -+err_strdup: -+ nvmf_ns_reservation_clear_all_registrants(ns); -+err_ns_reservation_restore: -+ subsystem->ns[opts.nsid - 1] = NULL; -+ spdk_bdev_module_release_bdev(ns->bdev); -+ spdk_bdev_close(ns->desc); -+ free(ns); -+ return 0; -+ - } - - uint32_t --- -1.8.3.1 - diff --git a/0008-nvmf-fix-fd-leakage-problem-in-nvmf_vfio_user_listen.patch b/0008-nvmf-fix-fd-leakage-problem-in-nvmf_vfio_user_listen.patch deleted file mode 100644 index dd1ab3b6d6e92ca3836915fb77afdcd9a2af1112..0000000000000000000000000000000000000000 --- a/0008-nvmf-fix-fd-leakage-problem-in-nvmf_vfio_user_listen.patch +++ /dev/null @@ -1,45 +0,0 @@ -From 09b368248b3337e5d7fd0ff9c4b1ce4fb0827ea1 Mon Sep 17 00:00:00 2001 -From: Zhiqiang Liu -Date: Sun, 13 Jun 2021 20:12:17 +0800 -Subject: [PATCH 25/28] nvmf: fix fd leakage problem in nvmf_vfio_user_listen() - -In nvmf_vfio_user_listen(), fd should be closed before -set it to endpoint->fd, otherwise, the fd leakage probem -occurs. - -Signed-off-by: Zhiqiang Liu -Change-Id: I3fabc65d2764926e5873475962e4362e46eb37e4 -Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/8309 -Community-CI: Mellanox Build Bot -Reviewed-by: Changpeng Liu -Reviewed-by: sunshihao -Reviewed-by: Aleksey Marchuk -Tested-by: SPDK CI Jenkins ---- - lib/nvmf/vfio_user.c | 3 +-- - 1 file changed, 1 insertion(+), 2 deletions(-) - -diff --git a/lib/nvmf/vfio_user.c b/lib/nvmf/vfio_user.c -index f5daa8d..7ec980a 100644 ---- a/lib/nvmf/vfio_user.c -+++ b/lib/nvmf/vfio_user.c -@@ -1662,6 +1662,7 @@ nvmf_vfio_user_listen(struct spdk_nvmf_transport *transport, - } - free(path); - -+ endpoint->fd = fd; - err = ftruncate(fd, NVMF_VFIO_USER_DOORBELLS_OFFSET + NVMF_VFIO_USER_DOORBELLS_SIZE); - if (err != 0) { - goto out; -@@ -1675,8 +1676,6 @@ nvmf_vfio_user_listen(struct spdk_nvmf_transport *transport, - goto out; - } - -- endpoint->fd = fd; -- - snprintf(uuid, PATH_MAX, "%s/cntrl", endpoint_id(endpoint)); - SPDK_DEBUGLOG(nvmf_vfio, "%s: doorbells %p\n", uuid, endpoint->doorbells); - --- -1.8.3.1 - diff --git a/0009-posix-set-fd-to-1-after-close-fd-in-posix_sock_creat.patch b/0009-posix-set-fd-to-1-after-close-fd-in-posix_sock_creat.patch deleted file mode 100644 index bd35c090f7893b469b3e7a858bc6686bdfc8f0fe..0000000000000000000000000000000000000000 --- a/0009-posix-set-fd-to-1-after-close-fd-in-posix_sock_creat.patch +++ /dev/null @@ -1,62 +0,0 @@ -From ab69fc61073df903970dbf00582617970f97a9ea Mon Sep 17 00:00:00 2001 -From: Zhiqiang Liu -Date: Sun, 13 Jun 2021 21:10:19 +0800 -Subject: [PATCH 26/28] posix: set fd to -1 after close(fd) in - posix_sock_create() - -In posix_sock_create(), we loops through all the addresses available. -If something is wrong, we should close(fd) and set fd to -1, and -try the next address. Only, when one fd satisfies all conditions, -we will break the loop with the useful fd. - -Signed-off-by: Zhiqiang Liu -Change-Id: Icbfc10246c92b95cacd6eb058e6e46cf8924fc4c -Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/8310 -Reviewed-by: Changpeng Liu -Reviewed-by: Aleksey Marchuk -Reviewed-by: Shuhei Matsumoto -Reviewed-by: Ziye Yang -Tested-by: SPDK CI Jenkins -Community-CI: Mellanox Build Bot ---- - module/sock/posix/posix.c | 4 ++++ - 1 file changed, 4 insertions(+) - -diff --git a/module/sock/posix/posix.c b/module/sock/posix/posix.c -index c180a16..ebafc1e 100644 ---- a/module/sock/posix/posix.c -+++ b/module/sock/posix/posix.c -@@ -468,12 +468,14 @@ retry: - rc = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof val); - if (rc != 0) { - close(fd); -+ fd = -1; - /* error */ - continue; - } - rc = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof val); - if (rc != 0) { - close(fd); -+ fd = -1; - /* error */ - continue; - } -@@ -483,6 +485,7 @@ retry: - rc = setsockopt(fd, SOL_SOCKET, SO_PRIORITY, &opts->priority, sizeof val); - if (rc != 0) { - close(fd); -+ fd = -1; - /* error */ - continue; - } -@@ -493,6 +496,7 @@ retry: - rc = setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof val); - if (rc != 0) { - close(fd); -+ fd = -1; - /* error */ - continue; - } --- -1.8.3.1 - diff --git a/0010-spdk_top-check-return-value-of-strdup-in-store_last_.patch b/0010-spdk_top-check-return-value-of-strdup-in-store_last_.patch deleted file mode 100644 index e8ee6d8e89460f03ac5ffe3020805f1b5ba8950f..0000000000000000000000000000000000000000 --- a/0010-spdk_top-check-return-value-of-strdup-in-store_last_.patch +++ /dev/null @@ -1,39 +0,0 @@ -From 9dace0d9cae727747f333f032537e873c73d9d8c Mon Sep 17 00:00:00 2001 -From: Zhiqiang Liu -Date: Sun, 13 Jun 2021 19:04:05 +0800 -Subject: [PATCH 27/28] spdk_top:check return value of strdup in - store_last_run_counter() - -In store_last_run_counter(), history->poller_name is set to -strdup(), which may return NULL. We should deal with it. - -Signed-off-by: Zhiqiang Liu -Change-Id: Ice5f27c4a7d2f9abd528b97a48ff5f92b48c8d7c -Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/8306 -Community-CI: Mellanox Build Bot -Reviewed-by: Jim Harris -Reviewed-by: Aleksey Marchuk -Tested-by: SPDK CI Jenkins ---- - app/spdk_top/spdk_top.c | 5 +++++ - 1 file changed, 5 insertions(+) - -diff --git a/app/spdk_top/spdk_top.c b/app/spdk_top/spdk_top.c -index 402c2a5..3c0a889 100644 ---- a/app/spdk_top/spdk_top.c -+++ b/app/spdk_top/spdk_top.c -@@ -1017,6 +1017,11 @@ store_last_run_counter(const char *poller_name, uint64_t thread_id, uint64_t las - return; - } - history->poller_name = strdup(poller_name); -+ if (!history->poller_name) { -+ fprintf(stderr, "Unable to allocate poller_name of a history object in store_last_run_counter.\n"); -+ free(history); -+ return; -+ } - history->thread_id = thread_id; - history->last_run_counter = last_run_counter; - --- -1.8.3.1 - diff --git a/0011-uring-set-fd-to-1-after-close-fd-in-uring_sock_creat.patch b/0011-uring-set-fd-to-1-after-close-fd-in-uring_sock_creat.patch deleted file mode 100644 index 3862ce4e6e98f5d45ba86b8d85e99a06dd26256f..0000000000000000000000000000000000000000 --- a/0011-uring-set-fd-to-1-after-close-fd-in-uring_sock_creat.patch +++ /dev/null @@ -1,62 +0,0 @@ -From b97c91b7d2480ee1cc038e70f6e2de2e2bb5d19d Mon Sep 17 00:00:00 2001 -From: Zhiqiang Liu -Date: Sun, 13 Jun 2021 21:29:33 +0800 -Subject: [PATCH 28/28] uring: set fd to -1 after close(fd) in - uring_sock_create() - -In uring_sock_create(), we loops through all the addresses available. -If something is wrong, we should close(fd) and set fd to -1, and -try the next address. Only, when one fd satisfies all conditions, -we will break the loop with the useful fd. - -Signed-off-by: Zhiqiang Liu -Change-Id: I22eada5437776fe90a6b57ab42cbad6dc4b0585c -Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/8311 -Community-CI: Mellanox Build Bot -Tested-by: SPDK CI Jenkins -Reviewed-by: Aleksey Marchuk -Reviewed-by: Changpeng Liu -Reviewed-by: Jim Harris -Reviewed-by: Ziye Yang ---- - module/sock/uring/uring.c | 4 ++++ - 1 file changed, 4 insertions(+) - -diff --git a/module/sock/uring/uring.c b/module/sock/uring/uring.c -index be76973..8f22758 100644 ---- a/module/sock/uring/uring.c -+++ b/module/sock/uring/uring.c -@@ -424,12 +424,14 @@ retry: - rc = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof val); - if (rc != 0) { - close(fd); -+ fd = -1; - /* error */ - continue; - } - rc = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof val); - if (rc != 0) { - close(fd); -+ fd = -1; - /* error */ - continue; - } -@@ -439,6 +441,7 @@ retry: - rc = setsockopt(fd, SOL_SOCKET, SO_PRIORITY, &opts->priority, sizeof val); - if (rc != 0) { - close(fd); -+ fd = -1; - /* error */ - continue; - } -@@ -448,6 +451,7 @@ retry: - rc = setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof val); - if (rc != 0) { - close(fd); -+ fd = -1; - /* error */ - continue; - } --- -1.8.3.1 - diff --git a/0012-spdk-use-fstack-protector-strong-instead-of-fstack-p.patch b/0012-spdk-use-fstack-protector-strong-instead-of-fstack-p.patch deleted file mode 100644 index d00dff6225b60a5e0f76cacbe372a8da8819c7c8..0000000000000000000000000000000000000000 --- a/0012-spdk-use-fstack-protector-strong-instead-of-fstack-p.patch +++ /dev/null @@ -1,30 +0,0 @@ -From b1959244d8178975119606e9fc1323dbee06c18f Mon Sep 17 00:00:00 2001 -From: Zhiqiang Liu -Date: Mon, 13 Sep 2021 21:36:51 +0800 -Subject: [PATCH] spdk: use -fstack-protector-strong instead of - -fstack-protector - -use -fstack-protector-strong instead of -fstack-protector for -stronger security. - -Signed-off-by: Zhiqiang Liu ---- - mk/spdk.common.mk | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/mk/spdk.common.mk b/mk/spdk.common.mk -index f3fe5c2..dc8ed69 100644 ---- a/mk/spdk.common.mk -+++ b/mk/spdk.common.mk -@@ -120,7 +120,7 @@ COMMON_CFLAGS += -D_GNU_SOURCE - COMMON_CFLAGS += -fPIC - - # Enable stack buffer overflow checking --COMMON_CFLAGS += -fstack-protector -+COMMON_CFLAGS += -fstack-protector-strong - - # Prevent accidental multiple definitions of global variables - COMMON_CFLAGS += -fno-common --- -1.8.3.1 - diff --git a/0013-lib-vhost-Fix-compilation-with-dpdk-21.11.patch b/0013-lib-vhost-Fix-compilation-with-dpdk-21.11.patch deleted file mode 100644 index a77582364bb4be7c662a38aec7a4a71b6fac790b..0000000000000000000000000000000000000000 --- a/0013-lib-vhost-Fix-compilation-with-dpdk-21.11.patch +++ /dev/null @@ -1,80 +0,0 @@ -From f72cab94dd35d7b45ec5a4f35967adf3184ca616 Mon Sep 17 00:00:00 2001 -From: Alexey Marchuk -Date: Mon, 15 Nov 2021 11:01:14 +0300 -Subject: [PATCH] lib/vhost: Fix compilation with dpdk 21.11 - -Structure vhost_device_ops was renamed to -rte_vhost_device_ops - -Signed-off-by: Alexey Marchuk -Change-Id: Ie9601099d47465536500aa37fc113aeae03a8254 -Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/10223 -Tested-by: SPDK CI Jenkins -Community-CI: Mellanox Build Bot -Community-CI: Broadcom CI -Reviewed-by: John Kariuki -Reviewed-by: Changpeng Liu -Reviewed-by: Tomasz Zawadzki ---- - lib/vhost/rte_vhost_compat.c | 5 +++++ - test/unit/lib/vhost/vhost.c/vhost_ut.c | 7 +++++++ - 2 files changed, 12 insertions(+) - -diff --git a/lib/vhost/rte_vhost_compat.c b/lib/vhost/rte_vhost_compat.c -index 3c9f691883a..08574cfad07 100644 ---- a/lib/vhost/rte_vhost_compat.c -+++ b/lib/vhost/rte_vhost_compat.c -@@ -3,6 +3,7 @@ - * - * Copyright (c) Intel Corporation. - * All rights reserved. -+ * Copyright (c) 2021 Mellanox Technologies LTD. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions -@@ -134,7 +135,11 @@ destroy_connection(int vid) - vhost_destroy_connection_cb(vid); - } - -+#if RTE_VERSION >= RTE_VERSION_NUM(21, 11, 0, 0) -+static const struct rte_vhost_device_ops g_spdk_vhost_ops = { -+#else - static const struct vhost_device_ops g_spdk_vhost_ops = { -+#endif - .new_device = start_device, - .destroy_device = stop_device, - .new_connection = new_connection, -diff --git a/test/unit/lib/vhost/vhost.c/vhost_ut.c b/test/unit/lib/vhost/vhost.c/vhost_ut.c -index df1c32d28e6..e62da334688 100644 ---- a/test/unit/lib/vhost/vhost.c/vhost_ut.c -+++ b/test/unit/lib/vhost/vhost.c/vhost_ut.c -@@ -3,6 +3,7 @@ - * - * Copyright (c) Intel Corporation. - * All rights reserved. -+ * Copyright (c) 2021 Mellanox Technologies LTD. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions -@@ -41,6 +42,7 @@ - #include "unit/lib/json_mock.c" - - #include "vhost/vhost.c" -+#include - - DEFINE_STUB(rte_vhost_set_vring_base, int, (int vid, uint16_t queue_id, - uint16_t last_avail_idx, uint16_t last_used_idx), 0); -@@ -65,8 +67,13 @@ DEFINE_STUB(rte_vhost_enable_guest_notification, int, - (int vid, uint16_t queue_id, int enable), 0); - DEFINE_STUB(rte_vhost_get_ifname, int, (int vid, char *buf, size_t len), 0); - DEFINE_STUB(rte_vhost_driver_start, int, (const char *name), 0); -+#if RTE_VERSION >= RTE_VERSION_NUM(21, 11, 0, 0) -+DEFINE_STUB(rte_vhost_driver_callback_register, int, -+ (const char *path, struct rte_vhost_device_ops const *const ops), 0); -+#else - DEFINE_STUB(rte_vhost_driver_callback_register, int, - (const char *path, struct vhost_device_ops const *const ops), 0); -+#endif - DEFINE_STUB(rte_vhost_driver_disable_features, int, (const char *path, uint64_t features), 0); - DEFINE_STUB(rte_vhost_driver_set_features, int, (const char *path, uint64_t features), 0); - DEFINE_STUB(rte_vhost_driver_register, int, (const char *path, uint64_t flags), 0); diff --git a/0014-mk-Fix-debug-build-error-on-ARM-ThunderX2-and-neoverse_N1_platform.patch b/0014-mk-Fix-debug-build-error-on-ARM-ThunderX2-and-neoverse_N1_platform.patch deleted file mode 100644 index ca6534d645170c465b9aa7f788495a72151c7947..0000000000000000000000000000000000000000 --- a/0014-mk-Fix-debug-build-error-on-ARM-ThunderX2-and-neoverse_N1_platform.patch +++ /dev/null @@ -1,47 +0,0 @@ -From de8f3a50ee33c8218ba59bc16297e953121206d7 Mon Sep 17 00:00:00 2001 -From: root -Date: Fri, 19 Mar 2021 15:38:55 +0800 -Subject: [PATCH] mk: Fix debug build error on ARM ThunderX2 and neoverse N1 - platform - -When building spdk on ARM platform like thunderx2 with --enable-debug, -there are following error: - -/tmp/ccOBb4AF.s: Assembler messages: -/tmp/ccOBb4AF.s:45: Error: selected processor does not support `casp x0,x1,x2,x3,[x4]' -/tmp/ccOBb4AF.s:77: Error: selected processor does not support `caspa x0,x1,x2,x3,[x4]' -/tmp/ccOBb4AF.s:109: Error: selected processor does not support `caspl x0,x1,x2,x3,[x4]' -/tmp/ccOBb4AF.s:141: Error: selected processor does not support `caspal x0,x1,x2,x3,[x4]' - -The reason is that DPDK is built with -march=armv8.1-a or -march=armv8.2-a+lse which -have these instructions while SPDK is built with -march=armv8-a+crc which does not support -them. Change spdk build machine to native can fix this. - -Signed-off-by: Rui Chang -Change-Id: I759d4ce2c557ce5ff73a802d7a4b6579c4ba64f7 -Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/7025 -Community-CI: Mellanox Build Bot -Reviewed-by: Ziye Yang -Reviewed-by: Changpeng Liu -Reviewed-by: Aleksey Marchuk -Tested-by: SPDK CI Jenkins ---- - mk/spdk.common.mk | 4 ---- - 1 file changed, 4 deletions(-) - -diff --git a/mk/spdk.common.mk b/mk/spdk.common.mk -index 633a05bda79..897be4d2150 100644 ---- a/mk/spdk.common.mk -+++ b/mk/spdk.common.mk -@@ -77,11 +77,7 @@ - ifneq ($(filter powerpc%,$(TARGET_MACHINE)),) - COMMON_CFLAGS += -mcpu=$(TARGET_ARCHITECTURE) - else ifeq ($(TARGET_MACHINE),aarch64) --ifeq ($(TARGET_ARCHITECTURE),native) --COMMON_CFLAGS += -march=armv8-a+crc --else - COMMON_CFLAGS += -march=$(TARGET_ARCHITECTURE) --endif - COMMON_CFLAGS += -DPAGE_SIZE=$(shell getconf PAGESIZE) - else - COMMON_CFLAGS += -march=$(TARGET_ARCHITECTURE) diff --git a/0015-configure-add-gcc-version-check-for-ARM-Neoverse-N1_platform.patch b/0015-configure-add-gcc-version-check-for-ARM-Neoverse-N1_platform.patch deleted file mode 100644 index 7b4d6ef9820257401137705e8c39e243d92f21d5..0000000000000000000000000000000000000000 --- a/0015-configure-add-gcc-version-check-for-ARM-Neoverse-N1_platform.patch +++ /dev/null @@ -1,53 +0,0 @@ -From fcc389490a4abe26c1efe6cc624dc2925ed6b670 Mon Sep 17 00:00:00 2001 -From: Rui Chang -Date: Tue, 18 May 2021 15:32:56 +0800 -Subject: [PATCH] configure: add gcc version check for ARM Neoverse-N1 platform - -When doing debug build on ARM Neoverse-N1 platform, if gcc version is -lower than 8.4.0, we may met following errors: - -/tmp/cc24qua1.s: Assembler messages: -/tmp/cc24qua1.s:53: Error: selected processor does not support `casp x0,x1,x2,x3,[x4]' -/tmp/cc24qua1.s:85: Error: selected processor does not support `caspa x0,x1,x2,x3,[x4]' -/tmp/cc24qua1.s:117: Error: selected processor does not support `caspl x0,x1,x2,x3,[x4]' -/tmp/cc24qua1.s:149: Error: selected processor does not support `caspal x0,x1,x2,x3,[x4]' - -The change also fix the problem by pass armv8.2-a+crypto as target architecture. - -Signed-off-by: Rui Chang -Change-Id: I2053b9440e06873066480d63e471802df2e69d4e -Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/7949 -Reviewed-by: Changpeng Liu -Reviewed-by: Aleksey Marchuk -Reviewed-by: Ziye Yang -Tested-by: SPDK CI Jenkins -Community-CI: Mellanox Build Bot ---- - configure | 14 ++++++++++++++ - 1 file changed, 14 insertions(+) - -diff --git a/configure b/configure -index 7d063f4ef3b..a2d0c5a62fb 100755 ---- a/configure -+++ b/configure -@@ -786,6 +786,20 @@ exit 1 - fi - fi - -+# For ARM Neoverse-N1 platform, debug build needs gcc version newer than 8.4 -+if [[ "${CONFIG[DEBUG]}" = "y" && $arch = aarch64* && "$CC_TYPE" = "gcc" ]]; then -+ GCC_VERSION=$($CC -dumpfullversion) -+ PART_NUM=$(grep -i -m 1 "CPU part" /proc/cpuinfo | awk '{print $4}') -+ -+ if [[ "$(printf '%s\n' "8.4.0" "$GCC_VERSION" | sort -V | head -n1)" != "8.4.0" ]]; then -+ if [[ $PART_NUM = 0xd0c ]]; then -+ echo "WARNING: For ARM Neoverse-N1 platform, debug build needs GCC version newer than 8.4." -+ echo " Will work around this by using armv8.2-a+crypto as target architecture for now." -+ CONFIG[ARCH]=armv8.2-a+crypto -+ fi -+ fi -+fi -+ - # We are now ready to generate final configuration. But first do sanity - # check to see if all keys in CONFIG array have its reflection in CONFIG file. - if [ $(egrep -c "^\s*CONFIG_[[:alnum:]_]+=" $rootdir/CONFIG) -ne ${#CONFIG[@]} ]; then diff --git a/0016-Enhance-security-for-share-library.patch b/0016-Enhance-security-for-share-library.patch deleted file mode 100644 index 8fa21d488157ccdd09ecc706c12fa3c41bde044c..0000000000000000000000000000000000000000 --- a/0016-Enhance-security-for-share-library.patch +++ /dev/null @@ -1,28 +0,0 @@ -From 56b3831310673beeb0b7d5121cf36b1993ebe322 Mon Sep 17 00:00:00 2001 -From: Weifeng Su -Date: Tue, 15 Mar 2022 11:25:02 +0000 -Subject: [PATCH] Enhance security for share library - -Remove rpath link option, Due to it's easy for attacher to -construct 'rpath' attacks. - -Signed-off-by: Weifeng Su ---- - mk/spdk.common.mk | 1 - - 1 file changed, 1 deletion(-) - -diff --git a/mk/spdk.common.mk b/mk/spdk.common.mk -index f9409c4..8569687 100644 ---- a/mk/spdk.common.mk -+++ b/mk/spdk.common.mk -@@ -293,7 +293,6 @@ LINK_CXX=\ - # Provide function to ease build of a shared lib - define spdk_build_realname_shared_lib - $(CC) -o $@ -shared $(CPPFLAGS) $(LDFLAGS) \ -- -Wl,-rpath=$(DESTDIR)/$(libdir) \ - -Wl,--soname,$(notdir $@) \ - -Wl,--whole-archive $(1) -Wl,--no-whole-archive \ - -Wl,--version-script=$(2) \ --- -2.27.0 - diff --git a/0017-barrier-LOONGARCH-memory-barriers.patch b/0017-barrier-LOONGARCH-memory-barriers.patch deleted file mode 100644 index 6e6cf29e5e25acc812aef8c668f1e51a1610027e..0000000000000000000000000000000000000000 --- a/0017-barrier-LOONGARCH-memory-barriers.patch +++ /dev/null @@ -1,41 +0,0 @@ -From 2d686707df37b8752f31684db16b689637ba141d Mon Sep 17 00:00:00 2001 -From: Xue Liu -Date: Thu, 1 Dec 2022 18:37:21 +0800 -Subject: [PATCH 1/3] barrier: LOONGARCH memory barriers - -Implement memory barrier for LOONGARCH platforms. - -Change-Id: I44f5e63e6eb3f8bf98e965a22fb86f94e727061d -Signed-off-by: Xue Liu -Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/16082 -Community-CI: Mellanox Build Bot -Tested-by: SPDK CI Jenkins -Reviewed-by: Jim Harris -Reviewed-by: Changpeng Liu ---- - include/spdk/barrier.h | 9 +++++++++ - 1 file changed, 9 insertions(+) - -diff --git a/include/spdk/barrier.h b/include/spdk/barrier.h -index acae360..1ee7240 100644 ---- a/include/spdk/barrier.h -+++ b/include/spdk/barrier.h -@@ -97,6 +97,15 @@ extern "C" { - #define _spdk_smp_mb() __asm volatile("lock addl $0, -128(%%esp); " ::: "memory"); - #endif - -+#elif defined(__loongarch__) -+ -+#define _spdk_rmb() __asm volatile("dbar 0" ::: "memory") -+#define _spdk_wmb() __asm volatile("dbar 0" ::: "memory") -+#define _spdk_mb() __asm volatile("dbar 0" ::: "memory") -+#define _spdk_smp_rmb() __asm volatile("dbar 0" ::: "memory") -+#define _spdk_smp_wmb() __asm volatile("dbar 0" ::: "memory") -+#define _spdk_smp_mb() __asm volatile("dbar 0" ::: "memory") -+ - #else - - #define _spdk_rmb() --- -2.20.1 - diff --git a/0018-nvme-pcie-add-memory-barrier-for-LOONGARCH.patch b/0018-nvme-pcie-add-memory-barrier-for-LOONGARCH.patch deleted file mode 100644 index 1fb9bbc652ef6091709d9261b7742b49e6d130b9..0000000000000000000000000000000000000000 --- a/0018-nvme-pcie-add-memory-barrier-for-LOONGARCH.patch +++ /dev/null @@ -1,34 +0,0 @@ -From e9a94122b8704411b79157b25c4d07580246be6c Mon Sep 17 00:00:00 2001 -From: Xue Liu -Date: Thu, 1 Dec 2022 18:38:57 +0800 -Subject: [PATCH 2/3] nvme/pcie: add memory barrier for LOONGARCH - -Add memory barrier for LOONGARCH in nvme_pcie_qpair_process_completions. - -Signed-off-by: Xue Liu -Change-Id: Icc992ef612a00dd18ff33f70ab8f54e8c5d5c5b7 -Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/16083 -Community-CI: Mellanox Build Bot -Tested-by: SPDK CI Jenkins -Reviewed-by: Jim Harris -Reviewed-by: Changpeng Liu ---- - lib/nvme/nvme_pcie_common.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/lib/nvme/nvme_pcie_common.c b/lib/nvme/nvme_pcie_common.c -index 0ef56cb..5fb6304 100644 ---- a/lib/nvme/nvme_pcie_common.c -+++ b/lib/nvme/nvme_pcie_common.c -@@ -801,7 +801,7 @@ nvme_pcie_qpair_process_completions(struct spdk_nvme_qpair *qpair, uint32_t max_ - __builtin_prefetch(&pqpair->tr[next_cpl->cid]); - } - --#ifdef __PPC64__ -+#if defined(__PPC64__) || defined(__loongarch__) - /* - * This memory barrier prevents reordering of: - * - load after store from/to tr --- -2.20.1 - diff --git a/0019-build-Specify-the-target-build-architecture-for-LOON.patch b/0019-build-Specify-the-target-build-architecture-for-LOON.patch deleted file mode 100644 index 4f37b71d0191883cdf5e72c504658e7641cff80a..0000000000000000000000000000000000000000 --- a/0019-build-Specify-the-target-build-architecture-for-LOON.patch +++ /dev/null @@ -1,36 +0,0 @@ -From d7484395ac0fd85c91365beb4af00c6e5ea39ee6 Mon Sep 17 00:00:00 2001 -From: Xue Liu -Date: Thu, 1 Dec 2022 18:39:44 +0800 -Subject: [PATCH 3/3] build: Specify the target build architecture for - LOONGARCH. - -More information about LoongArch: -- https://loongson.github.io/LoongArch-Documentation/README-EN.html - -Signed-off-by: Xue Liu -Change-Id: I24852e31b5fadef3578354da2d26252014330e83 -Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/16084 -Tested-by: SPDK CI Jenkins -Reviewed-by: Jim Harris -Reviewed-by: Changpeng Liu ---- - mk/spdk.common.mk | 3 +++ - 1 file changed, 3 insertions(+) - -diff --git a/mk/spdk.common.mk b/mk/spdk.common.mk -index 8569687..1326f83 100644 ---- a/mk/spdk.common.mk -+++ b/mk/spdk.common.mk -@@ -80,6 +80,9 @@ COMMON_CFLAGS += -mcpu=$(TARGET_ARCHITECTURE) - else ifeq ($(TARGET_MACHINE),aarch64) - COMMON_CFLAGS += -march=$(TARGET_ARCHITECTURE) - COMMON_CFLAGS += -DPAGE_SIZE=$(shell getconf PAGESIZE) -+else ifneq ($(filter loongarch%,$(TARGET_MACHINE)),) -+COMMON_CFLAGS += -march=$(TARGET_ARCHITECTURE) -+COMMON_CFLAGS += -DPAGE_SIZE=$(shell getconf PAGESIZE) - else - COMMON_CFLAGS += -march=$(TARGET_ARCHITECTURE) - endif --- -2.20.1 - diff --git a/0020-configure-add-CONFIG_HAVE_ARC4RANDOM.patch b/0020-configure-add-CONFIG_HAVE_ARC4RANDOM.patch deleted file mode 100644 index d791591803647f20fb3bc62dc13454fe030a4f9c..0000000000000000000000000000000000000000 --- a/0020-configure-add-CONFIG_HAVE_ARC4RANDOM.patch +++ /dev/null @@ -1,111 +0,0 @@ -From 1fc649fe351919c60c7e1e5044bd6fb00ce59edd Mon Sep 17 00:00:00 2001 -From: Jim Harris -Date: Fri, 12 Aug 2022 05:00:25 +0000 -Subject: [PATCH] configure: add CONFIG_HAVE_ARC4RANDOM - -glibc 2.36 added arc4random(), which breaks -the SPDK iSCSI build since it always implements its -own arc4random() implementation on non-FreeBSD OS -(meaning always on Linux). - -So instead add a CONFIG_HAVE_ARC4RANDOM and remove -the explicit FreeBSD dependency - this will work on -FreeBSD as well as Linux with >= glibc 2.36. - -Also fix check_format.sh, so that it does not -enforce spdk/stdinc.h checks on code snippets in -the configure file. - -Fixes issue #2637. - -Reported-by: Karl Bonde Torp -Signed-off-by: Jim Harris -Change-Id: Iab9da8ae30d62a56869530846372ffddf7138eed -Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/14028 -Community-CI: Mellanox Build Bot -Tested-by: SPDK CI Jenkins -Reviewed-by: Aleksey Marchuk -Reviewed-by: Changpeng Liu -Reviewed-by: Dong Yi - -Confilict: file CONFIG context is inconsistent ---- - CONFIG | 3 +++ - configure | 5 +++++ - lib/iscsi/iscsi.c | 5 ++--- - scripts/check_format.sh | 2 +- - 4 files changed, 11 insertions(+), 4 deletions(-) - -diff --git a/CONFIG b/CONFIG -index 214e59e..b8dde2e 100644 ---- a/CONFIG -+++ b/CONFIG -@@ -171,3 +171,6 @@ CONFIG_RAID5=n - - # Build with IDXD support - CONFIG_IDXD=n -+ -+# arc4random is available in stdlib.h -+CONFIG_HAVE_ARC4RANDOM=n -diff --git a/configure b/configure -index 01db27a..376019c 100755 ---- a/configure -+++ b/configure -@@ -750,6 +750,11 @@ if [[ "${CONFIG[TSAN]}" = "y" ]]; then - fi - fi - -+if echo -e '#include \nint main(void) { arc4random(); return 0; }\n' \ -+ | "${BUILD_CMD[@]}" - 2> /dev/null; then -+ CONFIG[HAVE_ARC4RANDOM]="y" -+fi -+ - if [[ "${CONFIG[OCF]}" = "y" ]]; then - # If OCF_PATH is a file, assume it is a library and use it to compile with - if [ -f ${CONFIG[OCF_PATH]} ]; then -diff --git a/lib/iscsi/iscsi.c b/lib/iscsi/iscsi.c -index 4bca616..48e548a 100644 ---- a/lib/iscsi/iscsi.c -+++ b/lib/iscsi/iscsi.c -@@ -64,7 +64,6 @@ - - #ifdef __FreeBSD__ - #define HAVE_SRANDOMDEV 1 --#define HAVE_ARC4RANDOM 1 - #endif - - struct spdk_iscsi_globals g_iscsi = { -@@ -99,7 +98,7 @@ srandomdev(void) - } - #endif /* HAVE_SRANDOMDEV */ - --#ifndef HAVE_ARC4RANDOM -+#ifndef SPDK_CONFIG_HAVE_ARC4RANDOM - static int g_arc4random_initialized = 0; - - static uint32_t -@@ -117,7 +116,7 @@ arc4random(void) - r = (r1 << 16) | r2; - return r; - } --#endif /* HAVE_ARC4RANDOM */ -+#endif /* SPDK_CONFIG_HAVE_ARC4RANDOM */ - - static void - gen_random(uint8_t *buf, size_t len) -diff --git a/scripts/check_format.sh b/scripts/check_format.sh -index b522115..8c242a7 100755 ---- a/scripts/check_format.sh -+++ b/scripts/check_format.sh -@@ -240,7 +240,7 @@ function check_posix_includes() { - local rc=0 - - echo -n "Checking for POSIX includes..." -- git grep -I -i -f scripts/posix.txt -- './*' ':!include/spdk/stdinc.h' ':!include/linux/**' ':!lib/rte_vhost*/**' ':!scripts/posix.txt' ':!*.patch' > scripts/posix.log || true -+ git grep -I -i -f scripts/posix.txt -- './*' ':!include/spdk/stdinc.h' ':!include/linux/**' ':!lib/rte_vhost*/**' ':!scripts/posix.txt' ':!*.patch' ':!configure' > scripts/posix.log || true - if [ -s scripts/posix.log ]; then - echo "POSIX includes detected. Please include spdk/stdinc.h instead." - cat scripts/posix.log --- -2.33.0 - diff --git a/0021-lib-bdev-return-error-when-failing-to-get-resource.patch b/0021-lib-bdev-return-error-when-failing-to-get-resource.patch deleted file mode 100644 index 183c0d48d239de783356d268e629d155dbcc4099..0000000000000000000000000000000000000000 --- a/0021-lib-bdev-return-error-when-failing-to-get-resource.patch +++ /dev/null @@ -1,104 +0,0 @@ -From 8afb3d003798965618a8f018bac1644204f4b6e2 Mon Sep 17 00:00:00 2001 -From: GangCao -Date: Thu, 29 Sep 2022 03:03:22 -0400 -Subject: [PATCH] lib/bdev: return error when failing to get resource - -To fix issue: 2719 - -Change-Id: I983ef607fad154608fff9bb9355645968caf0c5a -Signed-off-by: GangCao -Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/14746 -Reviewed-by: Changpeng Liu -Reviewed-by: Ben Walker -Reviewed-by: Shuhei Matsumoto -Reviewed-by: Jim Harris -Reviewed-by: Aleksey Marchuk -Tested-by: SPDK CI Jenkins -Community-CI: Mellanox Build Bot ---- - lib/bdev/bdev.c | 55 +++++++++++++++++++++++++++---------------------- - 1 file changed, 30 insertions(+), 25 deletions(-) - -diff --git a/lib/bdev/bdev.c b/lib/bdev/bdev.c -index 122f3668a..1642289ff 100644 ---- a/lib/bdev/bdev.c -+++ b/lib/bdev/bdev.c -@@ -1382,6 +1382,30 @@ spdk_bdev_subsystem_config_json(struct spdk_json_write_ctx *w) - spdk_json_write_array_end(w); - } - -+static void -+bdev_mgmt_channel_destroy(void *io_device, void *ctx_buf) -+{ -+ struct spdk_bdev_mgmt_channel *ch = ctx_buf; -+ struct spdk_bdev_io *bdev_io; -+ -+ if (!STAILQ_EMPTY(&ch->need_buf_small) || !STAILQ_EMPTY(&ch->need_buf_large)) { -+ SPDK_ERRLOG("Pending I/O list wasn't empty on mgmt channel free\n"); -+ } -+ -+ if (!TAILQ_EMPTY(&ch->shared_resources)) { -+ SPDK_ERRLOG("Module channel list wasn't empty on mgmt channel free\n"); -+ } -+ -+ while (!STAILQ_EMPTY(&ch->per_thread_cache)) { -+ bdev_io = STAILQ_FIRST(&ch->per_thread_cache); -+ STAILQ_REMOVE_HEAD(&ch->per_thread_cache, internal.buf_link); -+ ch->per_thread_cache_count--; -+ spdk_mempool_put(g_bdev_mgr.bdev_io_pool, (void *)bdev_io); -+ } -+ -+ assert(ch->per_thread_cache_count == 0); -+} -+ - static int - bdev_mgmt_channel_create(void *io_device, void *ctx_buf) - { -@@ -1399,7 +1423,12 @@ bdev_mgmt_channel_create(void *io_device, void *ctx_buf) - ch->per_thread_cache_count = 0; - for (i = 0; i < ch->bdev_io_cache_size; i++) { - bdev_io = spdk_mempool_get(g_bdev_mgr.bdev_io_pool); -- assert(bdev_io != NULL); -+ if (bdev_io == NULL) { -+ SPDK_ERRLOG("You need to increase bdev_io_pool_size using bdev_set_options RPC.\n"); -+ assert(false); -+ bdev_mgmt_channel_destroy(io_device, ctx_buf); -+ return -1; -+ } - ch->per_thread_cache_count++; - STAILQ_INSERT_HEAD(&ch->per_thread_cache, bdev_io, internal.buf_link); - } -@@ -1410,30 +1439,6 @@ bdev_mgmt_channel_create(void *io_device, void *ctx_buf) - return 0; - } - --static void --bdev_mgmt_channel_destroy(void *io_device, void *ctx_buf) --{ -- struct spdk_bdev_mgmt_channel *ch = ctx_buf; -- struct spdk_bdev_io *bdev_io; -- -- if (!STAILQ_EMPTY(&ch->need_buf_small) || !STAILQ_EMPTY(&ch->need_buf_large)) { -- SPDK_ERRLOG("Pending I/O list wasn't empty on mgmt channel free\n"); -- } -- -- if (!TAILQ_EMPTY(&ch->shared_resources)) { -- SPDK_ERRLOG("Module channel list wasn't empty on mgmt channel free\n"); -- } -- -- while (!STAILQ_EMPTY(&ch->per_thread_cache)) { -- bdev_io = STAILQ_FIRST(&ch->per_thread_cache); -- STAILQ_REMOVE_HEAD(&ch->per_thread_cache, internal.buf_link); -- ch->per_thread_cache_count--; -- spdk_mempool_put(g_bdev_mgr.bdev_io_pool, (void *)bdev_io); -- } -- -- assert(ch->per_thread_cache_count == 0); --} -- - static void - bdev_init_complete(int rc) - { --- -2.23.0 - diff --git a/0022-Fix-the-build-error-ppc64le-gnu-gcc-does-not-support.patch b/0022-Fix-the-build-error-ppc64le-gnu-gcc-does-not-support.patch deleted file mode 100644 index 9253ca6cad7a4189ad3fefac2c9d6d8d28d125c2..0000000000000000000000000000000000000000 --- a/0022-Fix-the-build-error-ppc64le-gnu-gcc-does-not-support.patch +++ /dev/null @@ -1,27 +0,0 @@ -From 219b299eb85d5626e9c2c4da6ca92e6debe8239b Mon Sep 17 00:00:00 2001 -From: Ren Zhijie -Date: Mon, 4 Mar 2024 18:03:47 +0800 -Subject: [PATCH] Fix the build error ppc64le-gnu-gcc does not support -march - option - -Signed-off-by: Ren Zhijie ---- - mk/spdk.common.mk | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/mk/spdk.common.mk b/mk/spdk.common.mk -index 1326f83..14777ae 100644 ---- a/mk/spdk.common.mk -+++ b/mk/spdk.common.mk -@@ -75,7 +75,7 @@ TARGET_MACHINE := $(firstword $(TARGET_TRIPLET_WORDS)) - - COMMON_CFLAGS = -g $(C_OPT) -Wall -Wextra -Wno-unused-parameter -Wno-missing-field-initializers -Wmissing-declarations -fno-strict-aliasing -I$(SPDK_ROOT_DIR)/include - --ifneq ($(filter powerpc%,$(TARGET_MACHINE)),) -+ifneq ($(filter powerpc% ppc%,$(TARGET_MACHINE)),) - COMMON_CFLAGS += -mcpu=$(TARGET_ARCHITECTURE) - else ifeq ($(TARGET_MACHINE),aarch64) - COMMON_CFLAGS += -march=$(TARGET_ARCHITECTURE) --- -2.33.0 - diff --git a/0023-Fix-probe-core-dump-while-admin-cmd-timeout.patch b/0023-Fix-probe-core-dump-while-admin-cmd-timeout.patch deleted file mode 100644 index ed146da73855a12fbe3aa930813611cfc82ce507..0000000000000000000000000000000000000000 --- a/0023-Fix-probe-core-dump-while-admin-cmd-timeout.patch +++ /dev/null @@ -1,34 +0,0 @@ -From c89931a6c3e7041dd7b6378438a48046cc5d5d57 Mon Sep 17 00:00:00 2001 -From: Zht-Try -Date: Tue, 5 Mar 2024 19:59:10 +0800 -Subject: [PATCH] Fix probe core dump while admin cmd timeout - -Signed-off-by: zhanghongtao ---- - module/bdev/nvme/bdev_nvme.c | 3 ++- - 1 file changed, 2 insertions(+), 1 deletion(-) - -diff --git a/module/bdev/nvme/bdev_nvme.c b/module/bdev/nvme/bdev_nvme.c -index e9d730d..425436f 100644 ---- a/module/bdev/nvme/bdev_nvme.c -+++ b/module/bdev/nvme/bdev_nvme.c -@@ -1592,6 +1592,8 @@ nvme_bdev_ctrlr_create(struct spdk_nvme_ctrlr *ctrlr, - spdk_nvme_ctrlr_register_aer_callback(ctrlr, aer_cb, nvme_bdev_ctrlr); - spdk_nvme_ctrlr_set_remove_cb(ctrlr, remove_cb, nvme_bdev_ctrlr); - -+ TAILQ_INSERT_HEAD(&nvme_bdev_ctrlr->trids, trid_entry, link); -+ - if (spdk_nvme_ctrlr_get_flags(nvme_bdev_ctrlr->ctrlr) & - SPDK_NVME_CTRLR_SECURITY_SEND_RECV_SUPPORTED) { - nvme_bdev_ctrlr->opal_dev = spdk_opal_dev_construct(nvme_bdev_ctrlr->ctrlr); -@@ -1600,7 +1602,6 @@ nvme_bdev_ctrlr_create(struct spdk_nvme_ctrlr *ctrlr, - } - } - -- TAILQ_INSERT_HEAD(&nvme_bdev_ctrlr->trids, trid_entry, link); - return 0; - - err_init_ocssd: --- -2.33.0 - diff --git a/0024-Fix-build-warning.patch b/0024-Fix-build-warning.patch deleted file mode 100644 index 278eba9b94dfe78ac1bb4152ac468e4f9d9a938f..0000000000000000000000000000000000000000 --- a/0024-Fix-build-warning.patch +++ /dev/null @@ -1,129 +0,0 @@ -From d3fdb05328531608736cf68880b9f39da6f3b9cd Mon Sep 17 00:00:00 2001 -From: wangxiaomeng -Date: Thu, 14 Mar 2024 17:19:12 +0800 -Subject: [PATCH] Fix build warning - ---- - app/spdk_top/spdk_top.c | 32 ++++++++++++++++---------------- - 1 file changed, 16 insertions(+), 16 deletions(-) - -diff --git a/app/spdk_top/spdk_top.c b/app/spdk_top/spdk_top.c -index 3c0a889..d72c26f 100644 ---- a/app/spdk_top/spdk_top.c -+++ b/app/spdk_top/spdk_top.c -@@ -674,7 +674,7 @@ print_max_len(WINDOW *win, int row, uint16_t col, uint16_t max_len, enum str_ali - snprintf(&tmp_str[max_str - DOTS_STR_LEN - 2], DOTS_STR_LEN, "%s", dots); - } - -- mvwprintw(win, row, col, tmp_str); -+ mvwprintw(win, row, col, "%s", tmp_str); - - refresh(); - wrefresh(win); -@@ -1937,19 +1937,19 @@ display_thread(struct rpc_thread_info *thread_info) - - print_left(thread_win, 3, THREAD_WIN_FIRST_COL, THREAD_WIN_WIDTH, - "Core: Idle [us]: Busy [us]:", COLOR_PAIR(5)); -- mvwprintw(thread_win, 3, THREAD_WIN_FIRST_COL + 6, "%" PRIu64, -+ mvwprintw(thread_win, 3, THREAD_WIN_FIRST_COL + 6, "%d", - thread_info->core_num); - - if (g_interval_data) { - get_time_str(g_thread_history[thread_info->id].idle, idle_time); -- mvwprintw(thread_win, 3, THREAD_WIN_FIRST_COL + 32, idle_time); -+ mvwprintw(thread_win, 3, THREAD_WIN_FIRST_COL + 32, "%s", idle_time); - get_time_str(g_thread_history[thread_info->id].busy, busy_time); -- mvwprintw(thread_win, 3, THREAD_WIN_FIRST_COL + 54, busy_time); -+ mvwprintw(thread_win, 3, THREAD_WIN_FIRST_COL + 54, "%s", busy_time); - } else { - get_time_str(thread_info->idle, idle_time); -- mvwprintw(thread_win, 3, THREAD_WIN_FIRST_COL + 32, idle_time); -+ mvwprintw(thread_win, 3, THREAD_WIN_FIRST_COL + 32, "%s", idle_time); - get_time_str(thread_info->busy, busy_time); -- mvwprintw(thread_win, 3, THREAD_WIN_FIRST_COL + 54, busy_time); -+ mvwprintw(thread_win, 3, THREAD_WIN_FIRST_COL + 54, "%s", busy_time); - } - - print_left(thread_win, 4, THREAD_WIN_FIRST_COL, THREAD_WIN_WIDTH, -@@ -1979,7 +1979,7 @@ display_thread(struct rpc_thread_info *thread_info) - mvwprintw(thread_win, current_row, THREAD_WIN_FIRST_COL, "%s", poller->name); - mvwprintw(thread_win, current_row, THREAD_WIN_FIRST_COL + 33, "Active"); - snprintf(run_count, MAX_POLLER_COUNT_STR_LEN, "%" PRIu64, poller->run_count); -- mvwprintw(thread_win, current_row, THREAD_WIN_FIRST_COL + 41, run_count); -+ mvwprintw(thread_win, current_row, THREAD_WIN_FIRST_COL + 41, "%s", run_count); - current_row++; - } - pollers = &thread->timed_pollers; -@@ -2108,20 +2108,20 @@ show_core(uint8_t current_page) - get_time_str(core_info[core_number]->idle, idle_time); - get_time_str(core_info[core_number]->busy, busy_time); - } -- mvwprintw(core_win, 3, CORE_WIN_FIRST_COL + 20, idle_time); -+ mvwprintw(core_win, 3, CORE_WIN_FIRST_COL + 20, "%s", idle_time); - - print_left(core_win, 5, 1, CORE_WIN_WIDTH, "Poller count: Busy time:", COLOR_PAIR(5)); - mvwprintw(core_win, 5, CORE_WIN_FIRST_COL, "%" PRIu64, - g_cores_history[core_number].pollers_count); - -- mvwprintw(core_win, 5, CORE_WIN_FIRST_COL + 20, busy_time); -+ mvwprintw(core_win, 5, CORE_WIN_FIRST_COL + 20, "%s", busy_time); - - mvwhline(core_win, 4, 1, ACS_HLINE, CORE_WIN_WIDTH - 2); - mvwhline(core_win, 6, 1, ACS_HLINE, CORE_WIN_WIDTH - 2); - print_left(core_win, 7, 1, CORE_WIN_WIDTH, "Threads on this core", COLOR_PAIR(5)); - - for (j = 0; j < core_info[core_number]->threads.threads_count; j++) { -- mvwprintw(core_win, j + 8, 1, core_info[core_number]->threads.thread[j].name); -+ mvwprintw(core_win, j + 8, 1, "%s", core_info[core_number]->threads.thread[j].name); - } - - refresh(); -@@ -2132,7 +2132,7 @@ show_core(uint8_t current_page) - while (!stop_loop) { - for (j = 0; j < core_info[core_number]->threads.threads_count; j++) { - if (j != current_threads_row) { -- mvwprintw(core_win, j + 8, 1, core_info[core_number]->threads.thread[j].name); -+ mvwprintw(core_win, j + 8, 1, "%s", core_info[core_number]->threads.thread[j].name); - } else { - print_left(core_win, j + 8, 1, CORE_WIN_WIDTH - 2, - core_info[core_number]->threads.thread[j].name, COLOR_PAIR(2)); -@@ -2204,9 +2204,9 @@ show_poller(uint8_t current_page) - mvwaddch(poller_win, 2, POLLER_WIN_WIDTH, ACS_RTEE); - - print_left(poller_win, 3, 2, POLLER_WIN_WIDTH, "Type: On thread:", COLOR_PAIR(5)); -- mvwprintw(poller_win, 3, POLLER_WIN_FIRST_COL, -+ mvwprintw(poller_win, 3, POLLER_WIN_FIRST_COL, "%s", - poller_type_str[pollers[poller_number]->type]); -- mvwprintw(poller_win, 3, POLLER_WIN_FIRST_COL + 23, pollers[poller_number]->thread_name); -+ mvwprintw(poller_win, 3, POLLER_WIN_FIRST_COL + 23, "%s", pollers[poller_number]->thread_name); - - print_left(poller_win, 4, 2, POLLER_WIN_WIDTH, "Run count:", COLOR_PAIR(5)); - -@@ -2221,7 +2221,7 @@ show_poller(uint8_t current_page) - if (pollers[poller_number]->period_ticks != 0) { - print_left(poller_win, 4, 28, POLLER_WIN_WIDTH, "Period:", COLOR_PAIR(5)); - get_time_str(g_pollers_history[poller_number].period_ticks, poller_period); -- mvwprintw(poller_win, 4, POLLER_WIN_FIRST_COL + 23, poller_period); -+ mvwprintw(poller_win, 4, POLLER_WIN_FIRST_COL + 23, "%s", poller_period); - } - mvwhline(poller_win, 5, 1, ACS_HLINE, POLLER_WIN_WIDTH - 2); - print_in_middle(poller_win, 6, 1, POLLER_WIN_WIDTH - 7, "Status:", COLOR_PAIR(5)); -@@ -2374,13 +2374,13 @@ show_stats(void) - time_last = time_now.tv_sec; - rc = get_data(); - if (rc) { -- mvprintw(g_max_row - 1, g_max_col - strlen(refresh_error) - 2, refresh_error); -+ mvprintw(g_max_row - 1, g_max_col - strlen(refresh_error) - 2, "%s", refresh_error); - } - - max_pages = refresh_tab(active_tab, current_page); - - snprintf(current_page_str, CURRENT_PAGE_STR_LEN - 1, "Page: %d/%d", current_page + 1, max_pages); -- mvprintw(g_max_row - 1, 1, current_page_str); -+ mvprintw(g_max_row - 1, 1, "%s", current_page_str); - - free_data(); - --- -2.33.0 - diff --git a/0025-ut-rdma-Fix-GCC-10.2.0-warning.patch b/0025-ut-rdma-Fix-GCC-10.2.0-warning.patch deleted file mode 100644 index 77f1b36a930103cec2b0f5f6d15beb5eb3571236..0000000000000000000000000000000000000000 --- a/0025-ut-rdma-Fix-GCC-10.2.0-warning.patch +++ /dev/null @@ -1,94 +0,0 @@ -From dd5d0c45dcfc51acc80f47be0367e25c10b9436f Mon Sep 17 00:00:00 2001 -From: Alexey Marchuk -Date: Wed, 14 Apr 2021 11:27:38 +0300 -Subject: [PATCH] ut/rdma: Fix GCC 10.2.0 warning -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -rdma_ut.c: In function ‘test_spdk_nvmf_rdma_request_parse_sgl_with_md’: -rdma_ut.c:1152:54: warning: array subscript 10 is outside array bounds of ‘struct spdk_nvmf_rdma_request_data[1]’ [-Warray-bounds] - 1152 | aligned_buffer = (void *)((uintptr_t)((char *)&data + NVMF_DATA_BUFFER_MASK) & - | ~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~ -rdma_ut.c:834:37: note: while referencing ‘data’ - 834 | struct spdk_nvmf_rdma_request_data data; - | ^~~~ - -The fix is to use array instead of spdk_nvmf_rdma_request_data -structure - -Change-Id: I81bd311d26037dcb9340d85abcb4ea45b20a5171 -Reported-by: G.Balaji -Signed-off-by: Alexey Marchuk -Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/7365 -Community-CI: Broadcom CI -Tested-by: SPDK CI Jenkins -Reviewed-by: -Reviewed-by: Shuhei Matsumoto -Reviewed-by: Jim Harris ---- - test/unit/lib/nvmf/rdma.c/rdma_ut.c | 23 ++++++++++++----------- - 1 file changed, 12 insertions(+), 11 deletions(-) - -diff --git a/test/unit/lib/nvmf/rdma.c/rdma_ut.c b/test/unit/lib/nvmf/rdma.c/rdma_ut.c -index 8674f52..eb46a4c 100644 ---- a/test/unit/lib/nvmf/rdma.c/rdma_ut.c -+++ b/test/unit/lib/nvmf/rdma.c/rdma_ut.c -@@ -836,7 +836,8 @@ test_spdk_nvmf_rdma_request_parse_sgl_with_md(void) - union nvmf_h2c_msg cmd; - struct spdk_nvme_sgl_descriptor *sgl; - struct spdk_nvme_sgl_descriptor sgl_desc[SPDK_NVMF_MAX_SGL_ENTRIES] = {{0}}; -- struct spdk_nvmf_rdma_request_data data; -+ char data_buffer[8192]; -+ struct spdk_nvmf_rdma_request_data *data = (struct spdk_nvmf_rdma_request_data *)data_buffer; - char data2_buffer[8192]; - struct spdk_nvmf_rdma_request_data *data2 = (struct spdk_nvmf_rdma_request_data *)data2_buffer; - const uint32_t data_bs = 512; -@@ -844,7 +845,7 @@ test_spdk_nvmf_rdma_request_parse_sgl_with_md(void) - int rc, i; - void *aligned_buffer; - -- data.wr.sg_list = data.sgl; -+ data->wr.sg_list = data->sgl; - STAILQ_INIT(&group.group.buf_cache); - group.group.buf_cache_size = 0; - group.group.buf_cache_count = 0; -@@ -1153,8 +1154,8 @@ test_spdk_nvmf_rdma_request_parse_sgl_with_md(void) - sgl->unkeyed.subtype = SPDK_NVME_SGL_SUBTYPE_OFFSET; - sgl->address = 0; - rdma_req.recv->buf = (void *)&sgl_desc; -- MOCK_SET(spdk_mempool_get, &data); -- aligned_buffer = (void *)((uintptr_t)((char *)&data + NVMF_DATA_BUFFER_MASK) & -+ MOCK_SET(spdk_mempool_get, data_buffer); -+ aligned_buffer = (void *)((uintptr_t)(data_buffer + NVMF_DATA_BUFFER_MASK) & - ~NVMF_DATA_BUFFER_MASK); - - /* part 1: 2 segments each with 1 wr. io_unit_size is aligned with data_bs + md_size */ -@@ -1190,17 +1191,17 @@ test_spdk_nvmf_rdma_request_parse_sgl_with_md(void) - - CU_ASSERT(rdma_req.data.wr.wr.rdma.rkey == 0x44); - CU_ASSERT(rdma_req.data.wr.wr.rdma.remote_addr == 0x4000); -- CU_ASSERT(rdma_req.data.wr.next == &data.wr); -- CU_ASSERT(data.wr.wr.rdma.rkey == 0x44); -- CU_ASSERT(data.wr.wr.rdma.remote_addr == 0x4000 + data_bs * 4); -- CU_ASSERT(data.wr.num_sge == 4); -+ CU_ASSERT(rdma_req.data.wr.next == &data->wr); -+ CU_ASSERT(data->wr.wr.rdma.rkey == 0x44); -+ CU_ASSERT(data->wr.wr.rdma.remote_addr == 0x4000 + data_bs * 4); -+ CU_ASSERT(data->wr.num_sge == 4); - for (i = 0; i < 4; ++i) { -- CU_ASSERT(data.wr.sg_list[i].addr == (uintptr_t)((unsigned char *)aligned_buffer) + i * -+ CU_ASSERT(data->wr.sg_list[i].addr == (uintptr_t)((unsigned char *)aligned_buffer) + i * - (data_bs + md_size)); -- CU_ASSERT(data.wr.sg_list[i].length == data_bs); -+ CU_ASSERT(data->wr.sg_list[i].length == data_bs); - } - -- CU_ASSERT(data.wr.next == &rdma_req.rsp.wr); -+ CU_ASSERT(data->wr.next == &rdma_req.rsp.wr); - } - - int main(int argc, char **argv) --- -2.33.0 - diff --git a/0026-lib-nvme-add-mutex-before-submit-admin-request.patch b/0026-lib-nvme-add-mutex-before-submit-admin-request.patch deleted file mode 100644 index 4f35967ae207e3fd6f9f28ccac9aa68a0d2dde33..0000000000000000000000000000000000000000 --- a/0026-lib-nvme-add-mutex-before-submit-admin-request.patch +++ /dev/null @@ -1,57 +0,0 @@ -From 4224fc348bc320803ee7af2d091353cfb0f5981b Mon Sep 17 00:00:00 2001 -From: Marcin Spiewak -Date: Wed, 20 Mar 2024 16:59:06 +0100 -Subject: [PATCH] lib/nvme: add mutex before submit admin request - -Conflict:NA -Reference:https://github.com/spdk/spdk/commit/4224fc348bc320803ee7af2d091353cfb0f5981b - -In nvme_ctrlr_cmd_identify(), the call to -nvme_ctrlr_submit_admin_request() shall be -preceeded by taking ctrlr->ctrlr_lock mutex, -like in other places in the code. - -Change-Id: Ibd4ef2aa02d906dac853e537df9a837974b6c358 -Signed-off-by: Marcin Spiewak -Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/22419 -Reviewed-by: Konrad Sztyber -Tested-by: SPDK CI Jenkins -Reviewed-by: Jim Harris ---- - lib/nvme/nvme_ctrlr_cmd.c | 8 +++++++- - 1 file changed, 7 insertions(+), 1 deletion(-) - -diff --git a/lib/nvme/nvme_ctrlr_cmd.c b/lib/nvme/nvme_ctrlr_cmd.c -index bcc92b29c..2f00ef83c 100644 ---- a/lib/nvme/nvme_ctrlr_cmd.c -+++ b/lib/nvme/nvme_ctrlr_cmd.c -@@ -152,11 +152,14 @@ nvme_ctrlr_cmd_identify(struct spdk_nvme_ctrlr *ctrlr, uint8_t cns, uint16_t cnt - { - struct nvme_request *req; - struct spdk_nvme_cmd *cmd; -+ int rc; - -+ nvme_robust_mutex_lock(&ctrlr->ctrlr_lock); - req = nvme_allocate_request_user_copy(ctrlr->adminq, - payload, payload_size, - cb_fn, cb_arg, false); - if (req == NULL) { -+ nvme_robust_mutex_unlock(&ctrlr->ctrlr_lock); - return -ENOMEM; - } - -@@ -167,7 +170,10 @@ nvme_ctrlr_cmd_identify(struct spdk_nvme_ctrlr *ctrlr, uint8_t cns, uint16_t cnt - cmd->cdw11_bits.identify.csi = csi; - cmd->nsid = nsid; - -- return nvme_ctrlr_submit_admin_request(ctrlr, req); -+ rc = nvme_ctrlr_submit_admin_request(ctrlr, req); -+ -+ nvme_robust_mutex_unlock(&ctrlr->ctrlr_lock); -+ return rc; - } - - int --- -2.27.0 - diff --git a/0027--nvme-cuse-Add-ctrlr_lock-for-cuse-register-and-unreg.patch b/0027--nvme-cuse-Add-ctrlr_lock-for-cuse-register-and-unreg.patch deleted file mode 100644 index 06ac0d7dd395527b342d67f6b1c9ef8ea96c8ed1..0000000000000000000000000000000000000000 --- a/0027--nvme-cuse-Add-ctrlr_lock-for-cuse-register-and-unreg.patch +++ /dev/null @@ -1,98 +0,0 @@ -From 253cca4fc3a89c38e79d2e940c5a0b7bb082afcc Mon Sep 17 00:00:00 2001 -From: Zhanghongtao2417 <651380626@qq.com> -Date: Fri, 26 Apr 2024 22:01:25 +0800 -Subject: [PATCH] nvme/cuse: Add ctrlr_lock for cuse register and unregister - -conflicts: -lib/nvme/nvme_io_msg.c nvme_io_msg_ctrlr_update - -spdk_nvme_cuse_unregister and spdk_nvme_ctrlr_process_admin_completions - running at the same time, concurrently operate external_io_msgs. -So we add locks to protect. - -Fixes #3353 - -Change-Id: Id5176975676c29a475e8e2a0d7c93e44646c00dc -Signed-off-by: Zhanghongtao2417 <651380626@qq.com> -Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/22927 -Tested-by: SPDK CI Jenkins -Community-CI: Mellanox Build Bot -Reviewed-by: Jim Harris -Reviewed-by: Konrad Sztyber ---- - lib/nvme/nvme_io_msg.c | 11 +++++++++++ - 1 file changed, 11 insertions(+) - -diff --git a/lib/nvme/nvme_io_msg.c b/lib/nvme/nvme_io_msg.c -index 94c4d071c..e11e67c85 100644 ---- a/lib/nvme/nvme_io_msg.c -+++ b/lib/nvme/nvme_io_msg.c -@@ -111,13 +111,16 @@ nvme_io_msg_ctrlr_register(struct spdk_nvme_ctrlr *ctrlr, - return -EINVAL; - } - -+ nvme_robust_mutex_lock(&ctrlr->ctrlr_lock); - if (nvme_io_msg_is_producer_registered(ctrlr, io_msg_producer)) { -+ nvme_robust_mutex_unlock(&ctrlr->ctrlr_lock); - return -EEXIST; - } - - if (!STAILQ_EMPTY(&ctrlr->io_producers) || ctrlr->is_resetting) { - /* There are registered producers - IO messaging already started */ - STAILQ_INSERT_TAIL(&ctrlr->io_producers, io_msg_producer, link); -+ nvme_robust_mutex_unlock(&ctrlr->ctrlr_lock); - return 0; - } - -@@ -129,6 +132,7 @@ nvme_io_msg_ctrlr_register(struct spdk_nvme_ctrlr *ctrlr, - ctrlr->external_io_msgs = spdk_ring_create(SPDK_RING_TYPE_MP_SC, 65536, SPDK_ENV_SOCKET_ID_ANY); - if (!ctrlr->external_io_msgs) { - SPDK_ERRLOG("Unable to allocate memory for message ring\n"); -+ nvme_robust_mutex_unlock(&ctrlr->ctrlr_lock); - return -ENOMEM; - } - -@@ -137,10 +141,12 @@ nvme_io_msg_ctrlr_register(struct spdk_nvme_ctrlr *ctrlr, - SPDK_ERRLOG("spdk_nvme_ctrlr_alloc_io_qpair() failed\n"); - spdk_ring_free(ctrlr->external_io_msgs); - ctrlr->external_io_msgs = NULL; -+ nvme_robust_mutex_unlock(&ctrlr->ctrlr_lock); - return -ENOMEM; - } - - STAILQ_INSERT_TAIL(&ctrlr->io_producers, io_msg_producer, link); -+ nvme_robust_mutex_unlock(&ctrlr->ctrlr_lock); - - return 0; - } -@@ -156,9 +162,11 @@ nvme_io_msg_ctrlr_update(struct spdk_nvme_ctrlr *ctrlr) - struct nvme_io_msg_producer *io_msg_producer; - - /* Update all producers */ -+ nvme_robust_mutex_lock(&ctrlr->ctrlr_lock); - STAILQ_FOREACH(io_msg_producer, &ctrlr->io_producers, link) { - io_msg_producer->update(ctrlr); - } -+ nvme_robust_mutex_unlock(&ctrlr->ctrlr_lock); - } - - void -@@ -195,7 +203,9 @@ nvme_io_msg_ctrlr_unregister(struct spdk_nvme_ctrlr *ctrlr, - { - assert(io_msg_producer != NULL); - -+ nvme_robust_mutex_lock(&ctrlr->ctrlr_lock); - if (!nvme_io_msg_is_producer_registered(ctrlr, io_msg_producer)) { -+ nvme_robust_mutex_unlock(&ctrlr->ctrlr_lock); - return; - } - -@@ -203,4 +213,5 @@ nvme_io_msg_ctrlr_unregister(struct spdk_nvme_ctrlr *ctrlr, - if (STAILQ_EMPTY(&ctrlr->io_producers)) { - nvme_io_msg_ctrlr_detach(ctrlr); - } -+ nvme_robust_mutex_unlock(&ctrlr->ctrlr_lock); - } --- -2.33.0 - diff --git a/0028-fixed-use-after-free-detected-by-Coverity.patch b/0028-fixed-use-after-free-detected-by-Coverity.patch deleted file mode 100644 index bed8abb2217ca61297a5de3bef8b100ad91d8cef..0000000000000000000000000000000000000000 --- a/0028-fixed-use-after-free-detected-by-Coverity.patch +++ /dev/null @@ -1,43 +0,0 @@ -From 19cfba7624a31bc5790a335158244b29657e9253 Mon Sep 17 00:00:00 2001 -From: Marcin Spiewak -Date: Fri, 19 Jan 2024 12:30:41 +0100 -Subject: [PATCH] lib/nvme: fixed use-after-free detected by Coverity - -If cuse_nvme_ctrlr_update_namespaces(ctrlr_device) fails, -the cuse_nvme_ctrlr_stop(ctrlr_device) function is called. This -function frees ctrl_device, and also clears/frees bit arrays, -so there is no need to jump to clear_and_free label, as these -operations ale already done. Just return with appropriate error -code. -If there is a jump, we will try to access already freed memory -(ctrl_device->index) in line 1213 - -Change-Id: I4217c3783a22781feabbae9735d44479c5f511d9 -Signed-off-by: Marcin Spiewak -Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/21518 -Community-CI: Mellanox Build Bot -Reviewed-by: Konrad Sztyber -Reviewed-by: Aleksey Marchuk -Tested-by: SPDK CI Jenkins - ---- - lib/nvme/nvme_cuse.c | 3 +-- - 1 file changed, 1 insertion(+), 2 deletions(-) - -diff --git a/lib/nvme/nvme_cuse.c b/lib/nvme/nvme_cuse.c -index 62d1422..0a78b8e 100644 ---- a/lib/nvme/nvme_cuse.c -+++ b/lib/nvme/nvme_cuse.c -@@ -904,8 +904,7 @@ nvme_cuse_start(struct spdk_nvme_ctrlr *ctrlr) - if (cuse_nvme_ctrlr_update_namespaces(ctrlr_device) < 0) { - SPDK_ERRLOG("Cannot start CUSE namespace devices."); - cuse_nvme_ctrlr_stop(ctrlr_device); -- rv = -1; -- goto err3; -+ return -1; - } - - return 0; --- -2.27.0 - diff --git a/patch/spdk-21.01.patch b/patch/spdk-21.01.patch new file mode 100644 index 0000000000000000000000000000000000000000..e1252307c3be3b4250d311efdc8224d8d686a5ca --- /dev/null +++ b/patch/spdk-21.01.patch @@ -0,0 +1,11122 @@ +diff --git a/CONFIG b/CONFIG +index 92b5c97..f56a956 100644 +--- a/CONFIG ++++ b/CONFIG +@@ -74,7 +74,7 @@ CONFIG_TESTS=y + CONFIG_UNIT_TESTS=y + + # Build examples +-CONFIG_EXAMPLES=y ++CONFIG_EXAMPLES=n + + # Build with Control-flow Enforcement Technology (CET) + CONFIG_CET=n +@@ -117,6 +117,9 @@ CONFIG_RBD=n + # Build vhost library. + CONFIG_VHOST=y + ++# Build ssam library. ++CONFIG_SSAM=y ++ + # Build vhost initiator (Virtio) driver. + CONFIG_VIRTIO=y + +diff --git a/app/Makefile b/app/Makefile +index 8ff318d..9850d2c 100644 +--- a/app/Makefile ++++ b/app/Makefile +@@ -42,6 +42,7 @@ DIRS-y += iscsi_tgt + DIRS-y += spdk_tgt + DIRS-y += spdk_lspci + DIRS-y += spdk_top ++DIRS-y += ssam + ifeq ($(OS),Linux) + DIRS-$(CONFIG_VHOST) += vhost + DIRS-y += spdk_dd +diff --git a/app/ssam/Makefile b/app/ssam/Makefile +new file mode 100644 +index 0000000..2b9ae3f +--- /dev/null ++++ b/app/ssam/Makefile +@@ -0,0 +1,58 @@ ++# ++# BSD LICENSE ++# ++# Copyright (c) Huawei Technologies Co., Ltd. 2021-2021. All rights reserved. ++# ++# Redistribution and use in source and binary forms, with or without ++# modification, are permitted provided that the following conditions ++# are met: ++# ++# * Redistributions of source code must retain the above copyright ++# notice, this list of conditions and the following disclaimer. ++# * Redistributions in binary form must reproduce the above copyright ++# notice, this list of conditions and the following disclaimer in ++# the documentation and/or other materials provided with the ++# distribution. ++# * Neither the name of Intel Corporation nor the names of its ++# contributors may be used to endorse or promote products derived ++# from this software without specific prior written permission. ++# ++# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ++# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ++# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ++# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ++# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ++# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ++# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ++# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ++# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ++# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++# ++ ++SPDK_ROOT_DIR := $(abspath $(CURDIR)/../..) ++include $(SPDK_ROOT_DIR)/mk/spdk.common.mk ++include $(SPDK_ROOT_DIR)/mk/spdk.modules.mk ++ ++APP = ssam ++ ++C_SRCS := ssam.c ++ ++SYS_LIBS += -ldpak_ssam -lcap ++SPDK_LIB_LIST = $(ALL_MODULES_LIST) event_ssam event ssam ssam_adapter ++ ++ifeq ($(OS),Linux) ++SPDK_LIB_LIST += event_nbd ++endif ++ ++ifeq ($(SPDK_ROOT_DIR)/lib/env_dpdk,$(CONFIG_ENV)) ++SPDK_LIB_LIST += env_dpdk_rpc ++endif ++ ++include $(SPDK_ROOT_DIR)/mk/spdk.app.mk ++ ++install: $(APP) ++ $(INSTALL_APP) ++ ++uninstall: ++ $(UNINSTALL_APP) +diff --git a/app/ssam/ssam.c b/app/ssam/ssam.c +new file mode 100644 +index 0000000..ba323f9 +--- /dev/null ++++ b/app/ssam/ssam.c +@@ -0,0 +1,86 @@ ++/*- ++ * BSD LICENSE ++ * ++ * Copyright (c) Huawei Technologies Co., Ltd. 2021-2021. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * ++ * * Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * * Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in ++ * the documentation and/or other materials provided with the ++ * distribution. ++ * * Neither the name of Intel Corporation nor the names of its ++ * contributors may be used to endorse or promote products derived ++ * from this software without specific prior written permission. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ++ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ++ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ++ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ++ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ++ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ++ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ++ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ++ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ++ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#include "spdk/ssam.h" ++#include "spdk/string.h" ++ ++#define IOVA_MODE_PA "pa" ++ ++static void ++ssam_started(void *ctx) ++{ ++ ssam_poller_start(); ++ SPDK_NOTICELOG("%s server started.\n", SSAM_SERVER_NAME); ++} ++ ++int ++main(int argc, char *argv[]) ++{ ++ struct spdk_app_opts opts = {}; ++ int rc; ++ int shm_id; ++ ++ spdk_app_opts_init(&opts, sizeof(opts)); ++ opts.name = SSAM_SERVER_NAME; ++ opts.iova_mode = IOVA_MODE_PA; ++ opts.num_entries = 0; ++ ++ spdk_ssam_user_config_init(); ++ ++ shm_id = shm_open(SSAM_SHM, O_RDWR, SSAM_SHM_PERMIT); ++ if (shm_id < 0) { ++ SPDK_NOTICELOG("ssam share memory hasn't been created.\n"); ++ } else { ++ ssam_set_shm_created(true); ++ SPDK_NOTICELOG("ssam share memory has been created.\n"); ++ } ++ ++ rc = ssam_rc_preinit(); ++ if (rc < 0) { ++ exit(rc); ++ } ++ ++ rc = spdk_app_parse_args(argc, argv, &opts, NULL, NULL, NULL, NULL); ++ if (rc != SPDK_APP_PARSE_ARGS_SUCCESS) { ++ SPDK_ERRLOG("spdk app parse args fail: %d \n", rc); ++ exit(rc); ++ } ++ ++ /* Blocks until the application is exiting */ ++ rc = spdk_app_start(&opts, ssam_started, NULL); ++ spdk_ssam_exit(); ++ ++ spdk_app_fini(); ++ SPDK_NOTICELOG("%s server exited.\n", SSAM_SERVER_NAME); ++ ++ return rc; ++} +\ No newline at end of file +diff --git a/configure b/configure +index 723bc45..e53d20c 100644 +--- a/configure ++++ b/configure +@@ -97,6 +97,7 @@ function usage() + echo " No path required." + echo " raid5 Build with bdev_raid module RAID5 support." + echo " No path required." ++ echo " ssam Support to build ssam for DPU storage accel." + echo "" + echo "Environment variables:" + echo "" +@@ -419,6 +420,15 @@ for i in "$@"; do + --without-fuse) + CONFIG[FUSE]=n + ;; ++ --with-ssam) ++ CONFIG[SSAM]=y ++ ;; ++ --without-ssam) ++ CONFIG[SSAM]=n ++ ;; ++ --with-ssam-only) ++ CONFIG[SSAM_ONLY]=y ++ ;; + --with-nvme-cuse) + CONFIG[NVME_CUSE]=y + ;; +@@ -784,6 +794,21 @@ if [[ "${CONFIG[FUSE]}" = "y" ]]; then + fi + fi + ++if [[ "${CONFIG[SSAM]}" = "y" ]]; then ++ if [[ ! -e /usr/lib64/libdpak_ssam.so ]]; then ++ echo "--with-ssam requires libdpak_ssam." ++ echo "Please install then re-run this script." ++ exit 1 ++ fi ++fi ++ ++if [[ "${CONFIG[SSAM_ONLY]}" = "y" ]]; then ++ if [[ "${CONFIG[SSAM]}" = "n" ]]; then ++ echo "--with-ssam-only requires --with-ssam." ++ exit 1 ++ fi ++fi ++ + if [ "${CONFIG[CET]}" = "y" ]; then + if ! echo -e 'int main(void) { return 0; }\n' | ${BUILD_CMD[@]} -fcf-protection - 2>/dev/null; then + echo --enable-cet requires compiler/linker that supports CET. +diff --git a/include/spdk/event.h b/include/spdk/event.h +index f757b33..cc55752 100644 +--- a/include/spdk/event.h ++++ b/include/spdk/event.h +@@ -232,6 +232,8 @@ void spdk_app_stop(int rc); + */ + int spdk_app_get_shm_id(void); + ++bool spdk_get_shutdown_sig_received(void); ++ + /** + * Convert a string containing a CPU core mask into a bitmask + * +diff --git a/include/spdk/ssam.h b/include/spdk/ssam.h +new file mode 100644 +index 0000000..f73fd08 +--- /dev/null ++++ b/include/spdk/ssam.h +@@ -0,0 +1,243 @@ ++/*- ++ * BSD LICENSE ++ * ++ * Copyright (c) Huawei Technologies Co., Ltd. 2021-2021. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * ++ * * Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * * Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in ++ * the documentation and/or other materials provided with the ++ * distribution. ++ * * Neither the name of Intel Corporation nor the names of its ++ * contributors may be used to endorse or promote products derived ++ * from this software without specific prior written permission. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ++ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ++ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ++ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ++ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ++ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ++ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ++ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ++ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ++ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#ifndef SSAM_H ++#define SSAM_H ++ ++#include ++ ++#include "spdk/stdinc.h" ++#include "spdk/cpuset.h" ++#include "spdk/json.h" ++#include "spdk/thread.h" ++#include "spdk/event.h" ++ ++#include "dpak_ssam.h" ++ ++#ifdef DEBUG ++#define ASSERT(f) assert(f) ++#else ++#define ASSERT(f) ((void)0) ++#endif ++ ++#define SPDK_INVALID_TID UINT16_MAX ++#define SPDK_SESSION_TYPE_MAX_LEN 64 ++ ++#define SPDK_SESSION_TYPE_BLK "blk" ++#define SPDK_SESSION_TYPE_SCSI "scsi" ++ ++#define SSAM_SHM "ssam_shm" ++#define SSAM_SHM_PERMIT 0640 ++#define SSAM_STORAGE_READY_FILE "/proc/sdi_storage/storage_ready" ++ ++enum virtio_type ++{ ++ VIRTIO_TYPE_UNKNOWN, ++ VIRTIO_TYPE_BLK, ++ VIRTIO_TYPE_SCSI, ++}; ++ ++/** ++ * ssam subsystem init callback ++ * ++ * \param rc The preceding processing result, ++ * 0 on success, negative errno on error. ++ */ ++typedef void (*spdk_ssam_init_cb)(int rc); ++ ++/** ++ * ssam subsystem fini callback ++ */ ++typedef void (*spdk_ssam_fini_cb)(void); ++ ++/** ++ * get ssam mempool pointer ++ */ ++ssam_mempool_t *spdk_ssam_get_mp(void); ++ ++/** ++ * ssam dump config json ++ */ ++void spdk_ssam_config_json(struct spdk_json_write_ctx *w); ++ ++/** ++ * Check if ssam support the global vf id. ++ * ++ * \param gfunc_id ssam global vf id. ++ * ++ * \return -EINVAL indicate gfunc_id invalid, -ENODEV indicate no such vf or ++ * 0 indicate gfunc_id valid. ++ */ ++int spdk_ssam_check_gfunc_id(uint16_t gfunc_id); ++ ++/** ++ * Find a ssam session by global vf id. ++ * ++ * \param gfunc_id ssam global vf id. ++ * ++ * \return ssam session or NULL indicate not find. ++ */ ++struct spdk_ssam_session *spdk_ssam_session_find(uint16_t gfunc_id); ++ ++/** ++ * Get gfunc id by controller name. ++ * ++ * \param name controller name. ++ * ++ * \return gfunc id or SPDK_INVALID_GFUNC_ID gfunc id not find. ++ */ ++uint16_t spdk_ssam_get_gfunc_id_by_name(char *name); ++ ++/** ++ * Get the next ssam device. If there's no more devices to iterate ++ * through, NULL will be returned. ++ * ++ * \param smdev ssam device. If NULL, this function will return the ++ * very first device. ++ * ++ * \return smdev ssam device or NULL indicate no more devices ++ */ ++struct spdk_ssam_dev *spdk_ssam_dev_next(const struct spdk_ssam_dev *smdev); ++ ++/** ++ * Lock the global ssam mutex synchronizing all the ssam device accesses. ++ */ ++void spdk_ssam_lock(void); ++ ++/** ++ * Lock the global ssam mutex synchronizing all the ssam device accesses. ++ * ++ * \return 0 if the mutex could be locked immediately, negative errno otherwise. ++ */ ++int spdk_ssam_trylock(void); ++ ++/** ++ * Unlock the global ssam mutex. ++ */ ++void spdk_ssam_unlock(void); ++ ++/** ++ * \param smsession ssam session. ++ * \param arg user-provided parameter. ++ * ++ * \return 0 on success, negative if failed ++ */ ++typedef int (*spdk_ssam_session_fn)(struct spdk_ssam_session *smsession, void **arg); ++ ++/** ++ * \param smsession ssam session. ++ * \param arg user-provided parameter. ++ */ ++typedef void (*spdk_ssam_session_cpl_fn)(struct spdk_ssam_session *smsession, void **arg); ++ ++/** ++ * \param arg user-provided parameter. ++ * \param rsp spdk_ssam_session_fn call back response value, 0 success, negative if failed. ++ */ ++typedef void (*spdk_ssam_session_rsp_fn)(void *arg, int rsp); ++ ++struct spdk_ssam_session_reg_info { ++ char type_name[SPDK_SESSION_TYPE_MAX_LEN]; ++ spdk_ssam_session_rsp_fn rsp_fn; ++ void *rsp_ctx; ++ uint16_t gfunc_id; ++ uint16_t tid; ++ uint16_t queues; ++ const struct spdk_ssam_session_backend *backend; ++ uint32_t session_ctx_size; ++ char *name; ++ char *dbdf; ++}; ++ ++/** ++ * Construct a ssam blk device. This will create a ssam ++ * blk device and then create a session. Creating the smdev will ++ * start an I/O poller and hog a CPU. If already exist a ssam ++ * blk device, then it will only create a session to this device. ++ * All sessions in the same device share one I/O poller and one CPU. ++ * ssam blk device is tightly associated with given SPDK bdev. ++ * Given bdev can not be changed, unless it has been hotremoved. This ++ * would result in all I/O failing with virtio VIRTIO_BLK_S_IOERR ++ * error code. ++ * ++ * This function is thread-safe. ++ * ++ * \param info session register information. ++ * \param dev_name bdev name to associate with this vhost device ++ * \param readonly if set, all writes to the device will fail with ++ * VIRTIO_BLK_S_IOERR error code. ++ * \param serial means volume id. ++ * ++ * \return 0 on success, negative errno on error. ++ */ ++int spdk_ssam_blk_construct(struct spdk_ssam_session_reg_info *info, ++ const char *dev_name, bool readonly, char *serial); ++ ++/** ++ * ssam user config init. ++ */ ++void spdk_ssam_user_config_init(void); ++ ++/** ++ * ssam get tid which has minimum device. ++ */ ++uint16_t spdk_ssam_get_tid(void); ++ ++void spdk_ssam_exit(void); ++ ++void spdk_ssam_subsystem_fini(spdk_ssam_fini_cb fini_cb); ++ ++void spdk_ssam_subsystem_init(spdk_ssam_init_cb init_cb); ++ ++int spdk_ssam_scsi_construct(struct spdk_ssam_session_reg_info *info); ++ ++int spdk_ssam_scsi_dev_add_tgt(struct spdk_ssam_session *smsession, int target_num, ++ const char *bdev_name); ++ ++int spdk_ssam_scsi_dev_remove_tgt(struct spdk_ssam_session *smsession, ++ unsigned scsi_tgt_num, spdk_ssam_session_rsp_fn cb_fn, void *cb_arg); ++ ++void ssam_set_shm_created(bool shm_created); ++ ++bool ssam_get_shm_created(void); ++ ++void ssam_poller_start(void); ++ ++void ssam_deinit_device_pcie_list(void); ++ ++int ssam_init_device_pcie_list(void); ++ ++void ssam_dump_device_pcie_list(struct spdk_json_write_ctx *w); ++ ++uint32_t ssam_get_device_pcie_list_size(void); ++ ++#endif /* SSAM_H */ +diff --git a/lib/Makefile b/lib/Makefile +index eab297e..a642833 100644 +--- a/lib/Makefile ++++ b/lib/Makefile +@@ -45,6 +45,8 @@ endif + DIRS-$(CONFIG_OCF) += env_ocf + DIRS-$(CONFIG_IDXD) += idxd + DIRS-$(CONFIG_VHOST) += vhost ++DIRS-$(CONFIG_SSAM) += ssam ++DIRS-$(CONFIG_SSAM) += ssam_adapter + DIRS-$(CONFIG_VIRTIO) += virtio + DIRS-$(CONFIG_REDUCE) += reduce + DIRS-$(CONFIG_RDMA) += rdma +diff --git a/lib/bdev/bdev.c b/lib/bdev/bdev.c +index 191520d..0f36704 100644 +--- a/lib/bdev/bdev.c ++++ b/lib/bdev/bdev.c +@@ -49,6 +49,7 @@ + #include "spdk/bdev_module.h" + #include "spdk/log.h" + #include "spdk/string.h" ++#include "spdk/event.h" + + #include "bdev_internal.h" + +@@ -2568,6 +2569,7 @@ bdev_channel_destroy_resource(struct spdk_bdev_channel *ch) + { + struct spdk_bdev_shared_resource *shared_resource; + struct lba_range *range; ++ struct spdk_bdev_io *bdev_io, *tmp; + + while (!TAILQ_EMPTY(&ch->locked_ranges)) { + range = TAILQ_FIRST(&ch->locked_ranges); +@@ -2578,6 +2580,11 @@ bdev_channel_destroy_resource(struct spdk_bdev_channel *ch) + spdk_put_io_channel(ch->channel); + + shared_resource = ch->shared_resource; ++ ch->shared_resource = NULL; ++ ++ TAILQ_FOREACH_SAFE(bdev_io, &ch->io_submitted, internal.ch_link, tmp) { ++ spdk_bdev_io_complete(bdev_io, SPDK_BDEV_IO_STATUS_ABORTED); ++ } + + assert(TAILQ_EMPTY(&ch->io_locked)); + assert(TAILQ_EMPTY(&ch->io_submitted)); +@@ -5183,6 +5190,15 @@ spdk_bdev_io_complete(struct spdk_bdev_io *bdev_io, enum spdk_bdev_io_status sta + struct spdk_bdev_channel *bdev_ch = bdev_io->internal.ch; + struct spdk_bdev_shared_resource *shared_resource = bdev_ch->shared_resource; + ++ if (spdk_unlikely(spdk_get_shutdown_sig_received())) { ++ /* ++ * In the hot restart process, when this callback is triggered, ++ * the bdev buf memory may have been released. ++ * Therefore, do not need to continue. ++ */ ++ return; ++ } ++ + bdev_io->internal.status = status; + + if (spdk_unlikely(bdev_io->type == SPDK_BDEV_IO_TYPE_RESET)) { +diff --git a/lib/event/app.c b/lib/event/app.c +index 03f9692..1df969c 100644 +--- a/lib/event/app.c ++++ b/lib/event/app.c +@@ -81,6 +81,11 @@ spdk_app_get_shm_id(void) + return g_spdk_app.shm_id; + } + ++bool spdk_get_shutdown_sig_received(void) ++{ ++ return g_shutdown_sig_received; ++} ++ + /* append one empty option to indicate the end of the array */ + static const struct option g_cmdline_options[] = { + #define CONFIG_FILE_OPT_IDX 'c' +diff --git a/lib/event/json_config.c b/lib/event/json_config.c +index 67890de..08dd044 100644 +--- a/lib/event/json_config.c ++++ b/lib/event/json_config.c +@@ -355,6 +355,15 @@ app_json_config_load_subsystem_config_entry(void *_ctx) + size_t params_len = 0; + int rc; + ++ if (spdk_get_shutdown_sig_received()) { ++ /* ++ * In the hot restart process, when this callback is triggered, ++ * rpc and thread may have been released. ++ * Therefore, dont continue. ++ */ ++ return; ++ } ++ + if (ctx->config_it == NULL) { + SPDK_DEBUG_APP_CFG("Subsystem '%.*s': configuration done.\n", ctx->subsystem_name->len, + (char *)ctx->subsystem_name->start); +diff --git a/lib/event/spdk_event.map b/lib/event/spdk_event.map +index 9a4ba56..54a6d73 100644 +--- a/lib/event/spdk_event.map ++++ b/lib/event/spdk_event.map +@@ -40,6 +40,7 @@ + spdk_subsystem_config_json; + spdk_rpc_initialize; + spdk_rpc_finish; ++ spdk_get_shutdown_sig_received; + + local: *; + }; +diff --git a/lib/scsi/lun.c b/lib/scsi/lun.c +index fef179e..7264f10 100644 +--- a/lib/scsi/lun.c ++++ b/lib/scsi/lun.c +@@ -38,6 +38,8 @@ + #include "spdk/thread.h" + #include "spdk/util.h" + #include "spdk/likely.h" ++#include "spdk/event.h" ++#include "spdk/bdev_module.h" + + static void scsi_lun_execute_tasks(struct spdk_scsi_lun *lun); + static void _scsi_lun_execute_mgmt_task(struct spdk_scsi_lun *lun); +@@ -352,6 +354,16 @@ _scsi_lun_hot_remove(void *arg1) + { + struct spdk_scsi_lun *lun = arg1; + ++ if (spdk_unlikely(spdk_get_shutdown_sig_received())) { ++ /* ++ * In the hot restart process, when this callback is triggered, ++ * the task and bdev_io memory may have been released. ++ * Therefore, outstanding task are not executed in this scenario. ++ */ ++ scsi_lun_notify_hot_remove(lun); ++ return; ++ } ++ + /* If lun->removed is set, no new task can be submitted to the LUN. + * Execute previously queued tasks, which will be immediately aborted. + */ +diff --git a/lib/ssam/Makefile b/lib/ssam/Makefile +new file mode 100644 +index 0000000..93c1ec7 +--- /dev/null ++++ b/lib/ssam/Makefile +@@ -0,0 +1,49 @@ ++# ++# BSD LICENSE ++# ++# Copyright (c) Huawei Technologies Co., Ltd. 2021-2021. All rights reserved. ++# ++# Redistribution and use in source and binary forms, with or without ++# modification, are permitted provided that the following conditions ++# are met: ++# ++# * Redistributions of source code must retain the above copyright ++# notice, this list of conditions and the following disclaimer. ++# * Redistributions in binary form must reproduce the above copyright ++# notice, this list of conditions and the following disclaimer in ++# the documentation and/or other materials provided with the ++# distribution. ++# * Neither the name of Intel Corporation nor the names of its ++# contributors may be used to endorse or promote products derived ++# from this software without specific prior written permission. ++# ++# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ++# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ++# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ++# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ++# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ++# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ++# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ++# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ++# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ++# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++# ++ ++SPDK_ROOT_DIR := $(abspath $(CURDIR)/../..) ++include $(SPDK_ROOT_DIR)/mk/spdk.common.mk ++ ++SO_VER := 1 ++SO_MINOR := 0 ++ ++CFLAGS += -I. -I../../dpdk/lib/eal/common ++CFLAGS += $(ENV_CFLAGS) ++ ++C_SRCS = ssam.c ssam_blk.c ssam_rpc.c \ ++ ssam_config.c ssam_scsi.c ssam_malloc.c ssam_device_pcie.c ++ ++LIBNAME = ssam ++ ++SPDK_MAP_FILE = $(abspath $(CURDIR)/spdk_ssam.map) ++ ++include $(SPDK_ROOT_DIR)/mk/spdk.lib.mk +diff --git a/lib/ssam/spdk_ssam.map b/lib/ssam/spdk_ssam.map +new file mode 100644 +index 0000000..ed8b8cd +--- /dev/null ++++ b/lib/ssam/spdk_ssam.map +@@ -0,0 +1,16 @@ ++{ ++ global: ++ ++ # public functions ++ spdk_ssam_user_config_init; ++ spdk_ssam_init; ++ spdk_ssam_exit; ++ spdk_ssam_subsystem_fini; ++ spdk_ssam_subsystem_init; ++ spdk_ssam_config_json; ++ ssam_set_shm_created; ++ ssam_get_shm_created; ++ ssam_poller_start; ++ ++ local: *; ++}; +diff --git a/lib/ssam/ssam.c b/lib/ssam/ssam.c +new file mode 100644 +index 0000000..d5da335 +--- /dev/null ++++ b/lib/ssam/ssam.c +@@ -0,0 +1,1718 @@ ++/*- ++ * BSD LICENSE ++ * ++ * Copyright (c) Huawei Technologies Co., Ltd. 2021-2021. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * ++ * * Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * * Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in ++ * the documentation and/or other materials provided with the ++ * distribution. ++ * * Neither the name of Intel Corporation nor the names of its ++ * contributors may be used to endorse or promote products derived ++ * from this software without specific prior written permission. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ++ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ++ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ++ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ++ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ++ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ++ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ++ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ++ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ++ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#include ++#include ++ ++#include "spdk/scsi_spec.h" ++#include "spdk/scsi.h" ++#include "spdk/stdinc.h" ++#include "spdk/env.h" ++#include "spdk/likely.h" ++#include "spdk/string.h" ++#include "spdk/util.h" ++#include "spdk/memory.h" ++#include "spdk/barrier.h" ++#include "spdk/bdev_module.h" ++#include "spdk/bdev.h" ++#include "spdk/endian.h" ++ ++#include "ssam_internal.h" ++ ++#define SSAM_PF_NUM_MAX_VAL 31 ++#define SSAM_PF_PLUS_VF_NUM_MAX_VAL 4096 ++ ++#define INQUIRY_OFFSET(field) \ ++ offsetof(struct spdk_scsi_cdb_inquiry_data, field) + \ ++ sizeof(((struct spdk_scsi_cdb_inquiry_data *)0x0)->field) ++ ++#define IO_STUCK_TIMEOUT 120 ++#define SEND_EVENT_WAIT_TIME 10 ++#define VMIO_TYPE_VIRTIO_SCSI_CTRL 4 ++#define DEVICE_READY_TIMEOUT 15 ++#define DEVICE_READY_WAIT_TIME 100000 ++ ++bool g_ssam_subsystem_exit = false; ++ ++struct ssam_event_user_ctx { ++ bool session_freed; /* true if session has been freed */ ++ bool async_done; /* true if session event done */ ++ void *ctx; /* store user context pointer */ ++}; ++ ++struct ssam_session_fn_ctx { ++ /* Device session pointer obtained before enqueuing the event */ ++ struct spdk_ssam_session *smsession; ++ ++ spdk_ssam_session_rsp_fn *rsp_fn; ++ ++ void *rsp_ctx; ++ ++ /* User provided function to be executed on session's thread. */ ++ spdk_ssam_session_fn cb_fn; ++ /** ++ * User provided function to be called on the init thread ++ * after iterating through all sessions. ++ */ ++ spdk_ssam_session_cpl_fn cpl_fn; ++ ++ /* Custom user context */ ++ struct ssam_event_user_ctx user_ctx; ++ ++ /* Session start event time */ ++ uint64_t start_tsc; ++ ++ bool need_async; ++ ++ int rsp; ++}; ++ ++/* ssam total information */ ++struct spdk_ssam_info { ++ ssam_mempool_t *mp; ++}; ++ ++static struct spdk_ssam_info g_ssam_info; ++ ++/* Thread performing all ssam management operations */ ++static struct spdk_thread *g_ssam_init_thread; ++ ++static TAILQ_HEAD(, spdk_ssam_dev) g_ssam_devices = ++ TAILQ_HEAD_INITIALIZER(g_ssam_devices); ++ ++static pthread_mutex_t g_ssam_mutex = PTHREAD_MUTEX_INITIALIZER; ++ ++/* Save cpu mask when ssam management thread started */ ++static struct spdk_cpuset g_ssam_core_mask; ++ ++/* Call back when spdk_ssam_fini complete */ ++static spdk_ssam_fini_cb g_ssam_fini_cpl_cb; ++ ++static int spdk_ssam_init(void); ++ ++static int ++ssam_sessions_init(struct spdk_ssam_session ***smsession) ++{ ++ *smsession = (struct spdk_ssam_session **)calloc( ++ SSAM_MAX_SESSION_PER_DEV, sizeof(struct spdk_ssam_session *)); ++ if (*smsession == NULL) { ++ SPDK_ERRLOG("calloc sessions failed\n"); ++ return -ENOMEM; ++ } ++ return 0; ++} ++ ++static int ++ssam_sessions_insert(struct spdk_ssam_session **smsessions, struct spdk_ssam_session *smsession) ++{ ++ uint16_t i = smsession->gfunc_id; ++ ++ if (smsessions[i] != NULL) { ++ SPDK_ERRLOG("smsessions already have such sesseion\n"); ++ return -ENOSPC; ++ } ++ ++ smsessions[i] = smsession; ++ ++ return 0; ++} ++ ++void ++ssam_sessions_remove(struct spdk_ssam_session **smsessions, struct spdk_ssam_session *smsession) ++{ ++ uint16_t i = smsession->gfunc_id; ++ ++ if (smsessions[i] == NULL) { ++ SPDK_WARNLOG("smsessions no such sesseion\n"); ++ return; ++ } ++ ++ smsessions[i] = NULL; ++ return; ++} ++ ++static struct spdk_ssam_session * ++ssam_sessions_first(int begin, struct spdk_ssam_session **smsessions) ++{ ++ int i; ++ ++ for (i = begin; i < SSAM_MAX_SESSION_PER_DEV; i++) { ++ if (smsessions[i] != NULL) { ++ return smsessions[i]; ++ } ++ } ++ return NULL; ++} ++ ++bool ++ssam_sessions_empty(struct spdk_ssam_session **smsessions) ++{ ++ struct spdk_ssam_session *session; ++ ++ session = ssam_sessions_first(0, smsessions); ++ if (session == NULL) { ++ return true; ++ } ++ ++ return false; ++} ++ ++struct spdk_ssam_session * ++ssam_sessions_next(struct spdk_ssam_session **smsessions, struct spdk_ssam_session *smsession) ++{ ++ if (smsession == NULL) { ++ return ssam_sessions_first(0, smsessions); ++ } ++ if (smsession->gfunc_id == SSAM_MAX_SESSION_PER_DEV) { ++ return NULL; ++ } ++ return ssam_sessions_first(smsession->gfunc_id + 1, smsessions); ++} ++ ++void ++ssam_session_insert_io_wait(struct spdk_ssam_session *smsession, ++ struct spdk_ssam_session_io_wait *io_wait) ++{ ++ TAILQ_INSERT_TAIL(&smsession->smdev->io_wait_queue, io_wait, link); ++ smsession->smdev->io_wait_cnt++; ++} ++ ++static void ++ssam_session_remove_io_wait(struct spdk_ssam_dev *smdev, ++ struct spdk_ssam_session_io_wait *session_io_wait) ++{ ++ TAILQ_REMOVE(&smdev->io_wait_queue, session_io_wait, link); ++ smdev->io_wait_cnt--; ++} ++ ++void ++ssam_session_insert_io_wait_r(struct spdk_ssam_dev *smdev, ++ struct spdk_ssam_session_io_wait_r *io_wait_r) ++{ ++ TAILQ_INSERT_TAIL(&smdev->io_wait_queue_r, io_wait_r, link); ++ smdev->io_wait_r_cnt++; ++} ++ ++static void ++ssam_session_remove_io_wait_r(struct spdk_ssam_dev *smdev, ++ struct spdk_ssam_session_io_wait_r *session_io_wait_r) ++{ ++ TAILQ_REMOVE(&smdev->io_wait_queue_r, session_io_wait_r, link); ++ smdev->io_wait_r_cnt--; ++} ++ ++void ++ssam_session_destroy(struct spdk_ssam_session *smsession) ++{ ++ if (smsession == NULL || smsession->smdev == NULL) { ++ return; ++ } ++ /* Remove smsession from the queue in advance to prevent access by the poller thread. */ ++ if (!ssam_sessions_empty(smsession->smdev->smsessions)) { ++ ssam_sessions_remove(smsession->smdev->smsessions, smsession); ++ } ++ // The smdev poller is not deleted here, but at the end of the app. ++} ++ ++ssam_mempool_t * ++spdk_ssam_get_mp(void) ++{ ++ return g_ssam_info.mp; ++} ++ ++uint64_t ++ssam_get_diff_tsc(uint64_t tsc) ++{ ++ return spdk_get_ticks() - tsc; ++} ++ ++int ++spdk_ssam_check_gfunc_id(uint16_t gfunc_id) ++{ ++ enum ssam_device_type type; ++ ++ if (gfunc_id == SPDK_INVALID_GFUNC_ID) { ++ SPDK_ERRLOG("Check gfunc_id(%u) error\n", gfunc_id); ++ return -EINVAL; ++ } ++ ++ type = spdk_ssam_get_virtio_type(gfunc_id); ++ if (type >= SSAM_DEVICE_VIRTIO_MAX) { ++ SPDK_ERRLOG("Check gfunc_id(%u) virtio type(%d) error\n", gfunc_id, type); ++ return -ENODEV; ++ } ++ ++ return 0; ++} ++ ++/* Find a tid which has minimum device */ ++static uint16_t ++ssam_get_min_payload_tid(uint16_t cpu_num) ++{ ++ if (cpu_num == 0) { ++ return SPDK_INVALID_TID; ++ } ++ ++ struct spdk_ssam_dev *smdev = NULL; ++ struct spdk_ssam_dev *tmp = NULL; ++ /* All tid have been used, find a tid which has minimum device */ ++ uint32_t min = UINT32_MAX; ++ uint16_t tid = 0; ++ ++ TAILQ_FOREACH_SAFE(smdev, &g_ssam_devices, tailq, tmp) { ++ if (smdev->active_session_num < min) { ++ min = smdev->active_session_num; ++ tid = smdev->tid; ++ } ++ } ++ ++ return tid; ++} ++ ++/* Get a tid number */ ++uint16_t ++spdk_ssam_get_tid(void) ++{ ++ uint32_t cpu_num; ++ ++ cpu_num = spdk_cpuset_count(&g_ssam_core_mask); ++ if ((cpu_num == 0) || (cpu_num > UINT16_MAX)) { ++ /* If cpu_num > UINT16_MAX, the result of tid will overflow */ ++ SPDK_ERRLOG("CPU num %u not valid.\n", cpu_num); ++ return SPDK_INVALID_TID; ++ } ++ ++ return ssam_get_min_payload_tid((uint16_t)cpu_num); ++} ++ ++void ++spdk_ssam_lock(void) ++{ ++ pthread_mutex_lock(&g_ssam_mutex); ++} ++ ++int ++spdk_ssam_trylock(void) ++{ ++ return pthread_mutex_trylock(&g_ssam_mutex); ++} ++ ++void ++spdk_ssam_unlock(void) ++{ ++ pthread_mutex_unlock(&g_ssam_mutex); ++} ++ ++static struct spdk_ssam_session * ++spdk_ssam_session_find_in_dev(const struct spdk_ssam_dev *smdev, ++ uint16_t gfunc_id) ++{ ++ return smdev->smsessions[gfunc_id]; ++} ++ ++void ++ssam_dump_info_json(struct spdk_ssam_dev *smdev, uint16_t gfunc_id, ++ struct spdk_json_write_ctx *w) ++{ ++ struct spdk_ssam_session *smsession = NULL; ++ ++ spdk_json_write_named_array_begin(w, "session"); ++ if (gfunc_id == UINT16_MAX) { ++ smsession = ssam_sessions_next(smdev->smsessions, NULL); ++ while (smsession != NULL) { ++ smsession->backend->dump_info_json(smsession, w); ++ smsession = ssam_sessions_next(smdev->smsessions, smsession); ++ } ++ } else { ++ smsession = spdk_ssam_session_find_in_dev(smdev, gfunc_id); ++ smsession->backend->dump_info_json(smsession, w); ++ } ++ ++ spdk_json_write_array_end(w); ++} ++ ++const char * ++spdk_ssam_dev_get_name(const struct spdk_ssam_dev *smdev) ++{ ++ if (!smdev) { ++ return ""; ++ } ++ return smdev->name; ++} ++ ++const char * ++spdk_ssam_session_get_name(const struct spdk_ssam_session *smsession) ++{ ++ if (!smsession) { ++ return ""; ++ } ++ return smsession->name; ++} ++ ++struct spdk_ssam_dev * ++spdk_ssam_dev_next(const struct spdk_ssam_dev *smdev) ++{ ++ if (smdev == NULL) { ++ return TAILQ_FIRST(&g_ssam_devices); ++ } ++ ++ return TAILQ_NEXT(smdev, tailq); ++} ++ ++struct spdk_ssam_session * ++spdk_ssam_session_find(uint16_t gfunc_id) ++{ ++ struct spdk_ssam_dev *smdev = NULL; ++ struct spdk_ssam_dev *tmp = NULL; ++ struct spdk_ssam_session *smsession = NULL; ++ ++ TAILQ_FOREACH_SAFE(smdev, &g_ssam_devices, tailq, tmp) { ++ smsession = spdk_ssam_session_find_in_dev(smdev, gfunc_id); ++ if (smsession != NULL) { ++ return smsession; ++ } ++ } ++ ++ return NULL; ++} ++ ++uint16_t ++spdk_ssam_get_gfunc_id_by_name(char *name) ++{ ++ struct spdk_ssam_dev *smdev = NULL; ++ struct spdk_ssam_dev *tmp = NULL; ++ struct spdk_ssam_session *smsession = NULL; ++ uint16_t gfunc_id; ++ TAILQ_FOREACH_SAFE(smdev, &g_ssam_devices, tailq, tmp) { ++ if (smdev != NULL && smdev->active_session_num > 0) { ++ for (gfunc_id = 0; gfunc_id <= SSAM_PF_NUM_MAX_VAL; gfunc_id++) { ++ smsession = spdk_ssam_session_find_in_dev(smdev, gfunc_id); ++ if (smsession != NULL && strcmp(name, smsession->name) == 0) { ++ return gfunc_id; ++ } ++ } ++ } ++ } ++ ++ SPDK_WARNLOG("controller(%s) is not existed\n", name); ++ return SPDK_INVALID_GFUNC_ID; ++} ++ ++static struct spdk_ssam_dev * ++spdk_ssam_dev_find(uint16_t tid) ++{ ++ struct spdk_ssam_dev *smdev = NULL; ++ struct spdk_ssam_dev *tmp = NULL; ++ ++ TAILQ_FOREACH_SAFE(smdev, &g_ssam_devices, tailq, tmp) { ++ if (smdev->tid == tid) { ++ return smdev; ++ } ++ } ++ ++ return NULL; ++} ++ ++int ++ssam_mount_normal(struct spdk_ssam_session *smsession, uint32_t lun_id) ++{ ++ uint16_t gfunc_id = smsession->gfunc_id; ++ uint16_t tid = smsession->smdev->tid; ++ ++ return ssam_function_mount(gfunc_id, lun_id, SSAM_MOUNT_NORMAL, tid); ++} ++ ++int ++ssam_umount_normal(struct spdk_ssam_session *smsession, uint32_t lun_id) ++{ ++ int rc; ++ ++ rc = ssam_function_umount(smsession->gfunc_id, lun_id); ++ if (rc != 0) { ++ SPDK_WARNLOG("%s: function umount failed when add scsi tgt, %d.\n", smsession->name, rc); ++ return rc; ++ } ++ ++ return 0; ++} ++ ++int ++ssam_remount_normal(struct spdk_ssam_session *smsession, uint32_t lun_id) ++{ ++ return ssam_function_mount(smsession->gfunc_id, lun_id, SSAM_MOUNT_NORMAL, smsession->smdev->tid); ++} ++ ++static int ++ssam_remove_session(struct spdk_ssam_session *smsession) ++{ ++ int rc; ++ ++ if (smsession->backend->remove_session != NULL) { ++ rc = smsession->backend->remove_session(smsession); ++ if (rc != 0) { ++ SPDK_ERRLOG("session: %s can not be removed, task cnt %d.\n", ++ smsession->name, smsession->task_cnt); ++ return rc; ++ } ++ } ++ ++ return 0; ++} ++ ++static void ++ssam_dev_thread_exit(void *unused) ++{ ++ (void)unused; ++ spdk_thread_exit(spdk_get_thread()); ++} ++ ++static int ++ssam_tid_to_cpumask(uint16_t tid, struct spdk_cpuset *cpumask) ++{ ++ uint32_t core; ++ uint32_t lcore; ++ uint32_t cnt; ++ ++ for (lcore = 0, cnt = 0; lcore < SPDK_CPUSET_SIZE - 1; lcore++) { ++ if (spdk_cpuset_get_cpu(&g_ssam_core_mask, lcore)) { ++ if (cnt == tid) { ++ core = lcore; ++ spdk_cpuset_set_cpu(cpumask, core, true); ++ return 0; ++ } ++ cnt++; ++ } ++ } ++ ++ return -1; ++} ++ ++void ++ssam_session_start_done(struct spdk_ssam_session *smsession, int response) ++{ ++ if (response == 0) { ++ if (smsession->smdev->active_session_num == UINT32_MAX) { ++ SPDK_ERRLOG("smsession %s: active session num reached upper limit %u\n", ++ smsession->name, smsession->smdev->active_session_num); ++ return; ++ } ++ smsession->smdev->active_session_num++; ++ } ++} ++ ++void ++ssam_set_session_be_freed(void **ctx) ++{ ++ struct ssam_event_user_ctx *_ctx; ++ ++ if (ctx == NULL) { ++ return; ++ } ++ ++ _ctx = SPDK_CONTAINEROF(ctx, struct ssam_event_user_ctx, ctx); ++ _ctx->session_freed = true; ++} ++ ++void ++ssam_send_event_async_done(void **ctx) ++{ ++ struct ssam_event_user_ctx *_ctx; ++ ++ if (ctx == NULL) { ++ return; ++ } ++ ++ _ctx = SPDK_CONTAINEROF(ctx, struct ssam_event_user_ctx, ctx); ++ _ctx->async_done = true; ++} ++ ++void ++ssam_session_stop_done(struct spdk_ssam_session *smsession, int rsp, void **ctx) ++{ ++ if (rsp == 0) { ++ if (smsession->smdev->active_session_num > 0) { ++ smsession->smdev->active_session_num--; ++ } else { ++ SPDK_ERRLOG("smsession %s: active session num reached lower limit %u\n", ++ smsession->name, smsession->smdev->active_session_num); ++ } ++ } ++ // Smdev cannot be free here ++ ++ /* Stop process need async */ ++ ssam_send_event_async_done(ctx); ++} ++ ++void ++ssam_session_unreg_response_cb(struct spdk_ssam_session *smsession) ++{ ++ smsession->rsp_fn = NULL; ++ smsession->rsp_ctx = NULL; ++} ++ ++static int ++ssam_dev_create_register(struct spdk_ssam_dev *smdev, uint16_t tid) ++{ ++ char name[NAME_MAX]; ++ struct spdk_cpuset cpumask; ++ int rc; ++ ++ smdev->tid = tid; ++ ++ rc = snprintf(name, NAME_MAX, "%s%u", "ssam.", smdev->tid); ++ if (rc < 0 || rc >= NAME_MAX) { ++ SPDK_ERRLOG("ssam dev name is too long, tid %u\n", tid); ++ return -EINVAL; ++ } ++ ++ spdk_cpuset_zero(&cpumask); ++ if (ssam_tid_to_cpumask(tid, &cpumask)) { ++ SPDK_ERRLOG("Can not find cpu for tid %u\n", tid); ++ return -EINVAL; ++ } ++ ++ smdev->name = strdup(name); ++ if (smdev->name == NULL) { ++ SPDK_ERRLOG("Failed to create name for ssam controller %s.\n", name); ++ return -EIO; ++ } ++ ++ smdev->thread = spdk_thread_create(smdev->name, &cpumask); ++ if (smdev->thread == NULL) { ++ SPDK_ERRLOG("Failed to create thread for ssam controller %s.\n", name); ++ free(smdev->name); ++ smdev->name = NULL; ++ return -EIO; ++ } ++ ++ rc = ssam_sessions_init(&smdev->smsessions); ++ if (rc != 0) { ++ return rc; ++ } ++ TAILQ_INSERT_TAIL(&g_ssam_devices, smdev, tailq); ++ TAILQ_INIT(&smdev->io_wait_queue); ++ TAILQ_INIT(&smdev->io_wait_queue_r); ++ ++ SPDK_NOTICELOG("Controller %s: new controller added, tid %u\n", smdev->name, tid); ++ ++ return 0; ++} ++ ++void ++ssam_dev_unregister(struct spdk_ssam_dev **dev) ++{ ++ struct spdk_ssam_dev *smdev = *dev; ++ struct spdk_thread *thread = smdev->thread; ++ ++ if (!ssam_sessions_empty(smdev->smsessions)) { ++ SPDK_NOTICELOG("Controller %s still has valid session.\n", ++ smdev->name); ++ return; ++ } ++ memset(smdev->smsessions, 0, SSAM_MAX_SESSION_PER_DEV * sizeof(struct spdk_ssam_session *)); ++ free(smdev->smsessions); ++ smdev->smsessions = NULL; ++ ++ // Used for hot restart. ++ if (smdev->stop_poller != NULL) { ++ spdk_poller_unregister(&smdev->stop_poller); ++ smdev->stop_poller = NULL; ++ } ++ ++ SPDK_NOTICELOG("Controller %s: removed\n", smdev->name); ++ ++ free(smdev->name); ++ smdev->name = NULL; ++ spdk_ssam_lock(); ++ TAILQ_REMOVE(&g_ssam_devices, smdev, tailq); ++ spdk_ssam_unlock(); ++ ++ free(smdev); ++ smdev = NULL; ++ *dev = NULL; ++ ++ spdk_thread_send_msg(thread, ssam_dev_thread_exit, NULL); ++ ++ return; ++} ++ ++static int ++ssam_init_session_fields(struct spdk_ssam_session_reg_info *info, ++ struct spdk_ssam_dev *smdev, struct spdk_ssam_session *smsession) ++{ ++ smsession->mp = g_ssam_info.mp; ++ smsession->initialized = true; ++ smsession->registered = true; ++ smsession->thread = smdev->thread; ++ smsession->backend = info->backend; ++ smsession->smdev = smdev; ++ smsession->gfunc_id = info->gfunc_id; ++ smsession->started = true; ++ smsession->rsp_fn = info->rsp_fn; ++ smsession->rsp_ctx = info->rsp_ctx; ++ smsession->max_queues = info->queues; ++ smsession->queue_size = SPDK_SSAM_DEFAULT_VQ_SIZE; ++ if (info->name == NULL) { ++ smsession->name = spdk_sprintf_alloc("%s_%s_%d", smdev->name, info->type_name, info->gfunc_id); ++ } else { ++ smsession->name = strdup(info->name); ++ } ++ if (smsession->name == NULL) { ++ SPDK_ERRLOG("smsession name alloc failed\n"); ++ return -ENOMEM; ++ } ++ ++ return 0; ++} ++ ++static int ++ssam_add_session(struct spdk_ssam_session_reg_info *info, ++ struct spdk_ssam_dev *smdev, struct spdk_ssam_session **smsession) ++{ ++ struct spdk_ssam_session *l_stsession = NULL; ++ size_t with_ctx_len = sizeof(*l_stsession) + info->session_ctx_size; ++ int rc; ++ ++ if (smdev->active_session_num == SSAM_MAX_SESSION_PER_DEV) { ++ SPDK_ERRLOG("%s reached upper limit %u\n", smdev->name, SSAM_MAX_SESSION_PER_DEV); ++ return -EAGAIN; ++ } ++ ++ if (g_ssam_info.mp == NULL) { ++ SPDK_ERRLOG("No memory pool\n"); ++ return -ENOMEM; ++ } ++ ++ rc = posix_memalign((void **)&l_stsession, SPDK_CACHE_LINE_SIZE, with_ctx_len); ++ if (rc != 0) { ++ SPDK_ERRLOG("smsession alloc failed\n"); ++ return -ENOMEM; ++ } ++ memset(l_stsession, 0, with_ctx_len); ++ ++ rc = ssam_init_session_fields(info, smdev, l_stsession); ++ if (rc != 0) { ++ free(l_stsession); ++ l_stsession = NULL; ++ return rc; ++ } ++ ++ rc = ssam_sessions_insert(smdev->smsessions, l_stsession); ++ if (rc != 0) { ++ return rc; ++ } ++ *smsession = l_stsession; ++ if (smdev->type == VIRTIO_TYPE_UNKNOWN) { ++ smdev->type = info->backend->type; ++ } ++ ++ return 0; ++} ++ ++static int ++ssam_dev_register(struct spdk_ssam_dev **dev, uint16_t tid) ++{ ++ struct spdk_ssam_dev *smdev = NULL; ++ int rc; ++ ++ smdev = calloc(1, sizeof(*smdev)); ++ if (smdev == NULL) { ++ SPDK_ERRLOG("Couldn't alloc device for tid %u.\n", tid); ++ return -1; ++ } ++ ++ rc = ssam_dev_create_register(smdev, tid); ++ if (rc != 0) { ++ free(smdev); ++ smdev = NULL; ++ return -1; ++ } ++ ++ *dev = smdev; ++ ++ return 0; ++} ++ ++int ++spdk_ssam_session_register(struct spdk_ssam_session_reg_info *info, ++ struct spdk_ssam_session **smsession) ++{ ++ struct spdk_ssam_dev *smdev = NULL; ++ int rc; ++ ++ if (spdk_ssam_session_find(info->gfunc_id)) { ++ SPDK_ERRLOG("Session with function id %d already exists.\n", info->gfunc_id); ++ return -EEXIST; ++ } ++ ++ smdev = spdk_ssam_dev_find(info->tid); ++ if (smdev == NULL) { ++ // The smdev has been started during process initialization. Do not need to start the poller here. ++ SPDK_ERRLOG("No device with function id %d tid %u.\n", info->gfunc_id, info->tid); ++ return -ENODEV; ++ } ++ ++ rc = ssam_add_session(info, smdev, smsession); ++ if (rc != 0) { ++ return rc; ++ } ++ ++ return 0; ++} ++ ++int ++spdk_ssam_session_unregister(struct spdk_ssam_session *smsession) ++{ ++ int rc; ++ ++ if (smsession == NULL) { ++ SPDK_ERRLOG("smsession null.\n"); ++ return -EINVAL; ++ } ++ ++ if (smsession->task_cnt > 0) { ++ SPDK_ERRLOG("%s is processing I/O(%d) and cannot be deleted.\n", ++ smsession->name, smsession->task_cnt); ++ return -EBUSY; ++ } ++ ++ if (smsession->pending_async_op_num != 0) { ++ SPDK_ERRLOG("[OFFLOAD_SNIC] %s has internal events(%d) and cannot be deleted.\n", ++ smsession->name, smsession->pending_async_op_num); ++ return -EBUSY; ++ } ++ ++ rc = ssam_remove_session(smsession); ++ if (rc != 0) { ++ return rc; ++ } ++ ++ return 0; ++} ++ ++static void ssam_io_queue_handle(struct spdk_ssam_dev *smdev) ++{ ++ uint64_t count = 0; ++ uint64_t io_wait_cnt = smdev->io_wait_cnt; ++ while (count < io_wait_cnt) { ++ struct spdk_ssam_session_io_wait *io_wait = TAILQ_FIRST(&smdev->io_wait_queue); ++ ssam_session_remove_io_wait(smdev, io_wait); ++ if (io_wait->cb_fn != NULL) { ++ io_wait->cb_fn(io_wait->cb_arg); ++ } ++ count++; ++ } ++} ++ ++struct forward_ctx { ++ struct spdk_ssam_session *smsession; ++ struct ssam_request *io_req; ++}; ++ ++static void ++ssam_handle_forward_req(void *_ctx) ++{ ++ struct forward_ctx *ctx = (struct forward_ctx *)_ctx; ++ ctx->smsession->backend->request_worker(ctx->smsession, ctx->io_req); ++ free(ctx); ++} ++// The resent request that is polled at the beginning of the hot restart is not the smsession of this smdev ++// and needs to be forwarded to the corresponding smdev. ++// If the forwarding is successful, true is returned. Otherwise, false is returned. ++static bool ++ssam_dev_forward_req(struct ssam_request *io_req) ++{ ++ struct spdk_ssam_dev *smdev = NULL; ++ struct forward_ctx *ctx = NULL; ++ int rc; ++ spdk_ssam_lock(); ++ smdev = spdk_ssam_dev_next(NULL); ++ while (smdev != NULL) { ++ if (smdev->smsessions[io_req->gfunc_id] != NULL) { ++ ctx = calloc(1, sizeof(struct forward_ctx)); ++ if (!ctx) { ++ SPDK_ERRLOG("%s: calloc failed.\n", smdev->name); ++ goto out; ++ } ++ ctx->smsession = smdev->smsessions[io_req->gfunc_id]; ++ ctx->io_req = io_req; ++ rc = spdk_thread_send_msg(smdev->smsessions[io_req->gfunc_id]->thread, ssam_handle_forward_req, ctx); ++ if (rc) { ++ SPDK_ERRLOG("%s: send msg error %d.\n", smdev->name, rc); ++ free(ctx); ++ goto out; ++ } ++ spdk_ssam_unlock(); ++ return true; ++ } ++ smdev = spdk_ssam_dev_next(smdev); ++ } ++out: ++ spdk_ssam_unlock(); ++ return false; ++} ++ ++struct ssam_dev_io_complete_arg { ++ struct spdk_ssam_dev *smdev; ++ struct ssam_io_response io_resp; ++}; ++ ++static void ++ssam_dev_io_complete_cb(void *arg) ++{ ++ struct ssam_dev_io_complete_arg *cb_arg = (struct ssam_dev_io_complete_arg *)arg; ++ int rc = ssam_io_complete(cb_arg->smdev->tid, &cb_arg->io_resp); ++ if (rc != 0) { ++ struct spdk_ssam_session_io_wait_r *io_wait_r = ++ calloc(1, sizeof(struct spdk_ssam_session_io_wait_r)); ++ if (io_wait_r == NULL) { ++ SPDK_ERRLOG("calloc for io_wait_r failed\n"); ++ sleep(1); ++ raise(SIGTERM); ++ } ++ io_wait_r->cb_fn = ssam_dev_io_complete_cb; ++ io_wait_r->cb_arg = cb_arg; ++ ssam_session_insert_io_wait_r(cb_arg->smdev, io_wait_r); ++ return; ++ } ++ free(cb_arg); ++ cb_arg = NULL; ++} ++ ++static void ssam_dev_io_complete(struct spdk_ssam_dev *smdev, struct ssam_request *io_req, bool success) ++{ ++ struct ssam_io_response io_resp; ++ struct ssam_virtio_res *virtio_res = (struct ssam_virtio_res*)&io_resp.data; ++ struct ssam_io_message *io_cmd = &io_req->req.cmd; ++ struct iovec io_vec; ++ struct virtio_scsi_cmd_resp resp = {0}; ++ enum ssam_device_type type; ++ uint8_t res_status; ++ int rc; ++ type = spdk_ssam_get_virtio_type(io_req->gfunc_id); ++ ++ if (success) { ++ switch (type) { ++ case SSAM_DEVICE_VIRTIO_BLK: ++ res_status = VIRTIO_BLK_S_OK; ++ break; ++ case SSAM_DEVICE_VIRTIO_SCSI: ++ res_status = VIRTIO_SCSI_S_OK; ++ break; ++ default: ++ res_status = 0; // unknown type, maybe 0 means ok ++ } ++ } else { ++ SPDK_INFOLOG(ssam, "%s: io complete return error gfunc_id %u type %d.\n", ++ smdev->name, io_req->gfunc_id, type); ++ switch (type) { ++ case SSAM_DEVICE_VIRTIO_BLK: ++ res_status = VIRTIO_BLK_S_IOERR; ++ break; ++ case SSAM_DEVICE_VIRTIO_SCSI: ++ res_status = VIRTIO_SCSI_S_FAILURE; ++ break; ++ default: ++ res_status = 1; // unknown type, maybe 1 means error ++ } ++ } ++ ++ memset(&io_resp, 0, sizeof(io_resp)); ++ io_resp.gfunc_id = io_req->gfunc_id; ++ io_resp.iocb_id = io_req->iocb_id; ++ io_resp.status = io_req->status; ++ io_resp.flr_seq = io_req->flr_seq; ++ io_resp.req = io_req; ++ ++ virtio_res->iovs = &io_vec; ++ if (type == SSAM_DEVICE_VIRTIO_SCSI && io_cmd->writable) { ++ virtio_res->iovs->iov_base = io_cmd->iovs[1].iov_base; ++ virtio_res->iovs->iov_len = io_cmd->iovs[1].iov_len; ++ } else { ++ virtio_res->iovs->iov_base = io_cmd->iovs[io_cmd->iovcnt - 1].iov_base; ++ virtio_res->iovs->iov_len = io_cmd->iovs[io_cmd->iovcnt - 1].iov_len; ++ } ++ virtio_res->iovcnt = 1; ++ if (type == SSAM_DEVICE_VIRTIO_SCSI && io_req->type != VMIO_TYPE_VIRTIO_SCSI_CTRL) { ++ resp.response = res_status; ++ virtio_res->rsp = &resp; ++ virtio_res->rsp_len = sizeof(struct virtio_scsi_cmd_resp); ++ } else { ++ virtio_res->rsp = &res_status; ++ virtio_res->rsp_len = sizeof(res_status); ++ } ++ ++ rc = ssam_io_complete(smdev->tid, &io_resp); ++ if (rc != 0) { ++ struct spdk_ssam_session_io_wait_r *io_wait_r = ++ calloc(1, sizeof(struct spdk_ssam_session_io_wait_r)); ++ struct ssam_dev_io_complete_arg *cb_arg = ++ calloc(1, sizeof(struct ssam_dev_io_complete_arg)); ++ if (io_wait_r == NULL || cb_arg == NULL) { ++ SPDK_ERRLOG("calloc for io_wait_r failed\n"); ++ sleep(1); ++ raise(SIGTERM); ++ } ++ cb_arg->smdev = smdev; ++ cb_arg->io_resp = io_resp; ++ io_wait_r->cb_fn = ssam_dev_io_complete_cb; ++ io_wait_r->cb_arg = cb_arg; ++ ssam_session_insert_io_wait_r(smdev, io_wait_r); ++ } ++} ++ ++static void ++ssam_dev_io_request(struct spdk_ssam_dev *smdev, struct ssam_request *io_req) ++{ ++ struct spdk_ssam_session *smsession = NULL; ++ ++ SPDK_INFOLOG(ssam_blk_data, "handling io tid=%u gfunc_id=%u type=%d rw=%u vqid=%u reqid=%u.\n", ++ smdev->tid, io_req->gfunc_id, io_req->type, io_req->req.cmd.writable, ++ io_req->req.cmd.virtio.vq_idx, io_req->req.cmd.virtio.req_idx); ++ ++ smsession = smdev->smsessions[io_req->gfunc_id]; ++ if (smsession == NULL) { ++ if (!ssam_dev_forward_req(io_req)) { ++ SPDK_INFOLOG(ssam, "%s: not have gfunc_id %u yet in io request.\n", ++ smdev->name, io_req->gfunc_id); ++ ssam_dev_io_complete(smdev, io_req, false); ++ } ++ return; ++ } ++ ++ smsession->backend->request_worker(smsession, io_req); ++ return; ++} ++ ++static void ssam_io_wait_r_queue_handle(struct spdk_ssam_dev *smdev) ++{ ++ uint64_t count = 0; ++ uint64_t io_wait_r_cnt = smdev->io_wait_r_cnt > SSAM_MAX_REQ_POLL_SIZE ? SSAM_MAX_REQ_POLL_SIZE : smdev->io_wait_r_cnt; ++ while (count < io_wait_r_cnt) { ++ struct spdk_ssam_session_io_wait_r *io_wait_r = TAILQ_FIRST(&smdev->io_wait_queue_r); ++ ssam_session_remove_io_wait_r(smdev, io_wait_r); ++ if (io_wait_r->cb_fn != NULL) { ++ io_wait_r->cb_fn(io_wait_r->cb_arg); ++ } ++ count++; ++ free(io_wait_r); ++ io_wait_r = NULL; ++ } ++} ++ ++static int ++ssam_dev_request_worker(void *arg) ++{ ++ int io_num; ++ struct ssam_request *io_req[SSAM_MAX_REQ_POLL_SIZE] = {0}; ++ struct spdk_ssam_dev *smdev = arg; ++ ++ // The I/O waiting due to insufficient memory needs to be processed first. ++ if (spdk_unlikely(smdev->io_wait_cnt > 0)) { ++ ssam_io_queue_handle(smdev); ++ return SPDK_POLLER_BUSY; ++ } ++ ++ io_num = ssam_request_poll(smdev->tid, SSAM_MAX_REQ_POLL_SIZE, io_req); ++ if ((io_num <= 0) || (io_num > SSAM_MAX_REQ_POLL_SIZE)) { ++ /* ++ * The rpc delete callback is registered when the bdev deleting. spdk_put_io_channel ++ * executed the RPC delete callback.The stdev_io_no_data_request function continuously ++ * determines whether to perform the spdk_put_io_channel operation to ensure that the ++ * deletion of the bdev does not time out. ++ */ ++ if (spdk_unlikely(smdev->io_wait_r_cnt > 0)) { ++ ssam_io_wait_r_queue_handle(smdev); ++ } ++ return SPDK_POLLER_BUSY; ++ } ++ ++ if (spdk_unlikely(smdev->io_wait_r_cnt > 0)) { ++ ssam_io_wait_r_queue_handle(smdev); ++ } ++ ++ for (int i = 0; i < io_num; i++) { ++ ssam_dev_io_request(smdev, io_req[i]); ++ } ++ ++ return SPDK_POLLER_BUSY; ++} ++ ++static void ++ssam_dev_io_response(struct spdk_ssam_dev *smdev, const struct ssam_dma_rsp *dma_rsp) ++{ ++ struct spdk_ssam_session *smsession = NULL; ++ const struct spdk_ssam_dma_cb *dma_cb = (const struct spdk_ssam_dma_cb *)&dma_rsp->cb; ++ ++ SPDK_INFOLOG(ssam_blk_data, "handle dma resp tid=%u gfunc_id=%u rw=%u vqid=%u task_idx=%u statuc=%u.\n", ++ smdev->tid, dma_cb->gfunc_id, dma_cb->req_dir, ++ dma_cb->vq_idx, dma_cb->task_idx, dma_cb->status); ++ ++ smsession = smdev->smsessions[dma_cb->gfunc_id]; ++ if (smsession == NULL) { ++ smdev->discard_io_num++; ++ SPDK_ERRLOG("smsessions not have gfunc_id %u yet in io response.\n", dma_cb->gfunc_id); ++ return; ++ } ++ ++ smsession->backend->response_worker(smsession, (void *)dma_rsp); ++ ++ return; ++} ++ ++static void ++ssam_dev_print_stuck_io(struct spdk_ssam_dev *smdev) ++{ ++ struct spdk_ssam_session *smsession = NULL; ++ int i; ++ ++ for (i = 0; i < SSAM_MAX_SESSION_PER_DEV; i++) { ++ smsession = smdev->smsessions[i]; ++ if (smsession == NULL) { ++ continue; ++ } ++ if (smsession->task_cnt > 0) { ++ SPDK_ERRLOG("%s: %d IO stuck for %ds\n", smsession->name, ++ smsession->task_cnt, IO_STUCK_TIMEOUT); ++ if (smsession->backend->print_stuck_io_info != NULL) { ++ smsession->backend->print_stuck_io_info(smsession); ++ } ++ } ++ } ++} ++ ++static void ++ssam_dev_io_stuck_check(struct spdk_ssam_dev *smdev) ++{ ++ uint64_t diff_tsc = spdk_get_ticks() - smdev->io_stuck_tsc; ++ ++ if (smdev->io_num == 0) { ++ smdev->io_stuck_tsc = spdk_get_ticks(); ++ return; ++ } ++ ++ if ((diff_tsc / IO_STUCK_TIMEOUT) > spdk_get_ticks_hz()) { ++ ssam_dev_print_stuck_io(smdev); ++ smdev->io_stuck_tsc = spdk_get_ticks(); ++ } ++} ++ ++void ++ssam_dev_io_dec(struct spdk_ssam_dev *smdev) ++{ ++ smdev->io_num--; ++} ++ ++static int ++ssam_dev_response_worker(void *arg) ++{ ++ int io_num; ++ struct spdk_ssam_dev *smdev = arg; ++ struct ssam_dma_rsp dma_rsp[SSAM_MAX_RESP_POLL_SIZE] = {0}; ++ ++ uint64_t ticks = spdk_get_ticks(); ++ if (smdev->stat.poll_cur_tsc == 0) { ++ smdev->stat.poll_cur_tsc = ticks; ++ } else { ++ smdev->stat.poll_tsc += ticks - smdev->stat.poll_cur_tsc; ++ smdev->stat.poll_count++; ++ smdev->stat.poll_cur_tsc = ticks; ++ } ++ ++ io_num = ssam_dma_rsp_poll(smdev->tid, SSAM_MAX_RESP_POLL_SIZE, dma_rsp); ++ if (io_num <= 0 || io_num > SSAM_MAX_RESP_POLL_SIZE) { ++ ssam_dev_io_stuck_check(smdev); ++ return SPDK_POLLER_BUSY; ++ } ++ ++ if (smdev->io_num < ((uint64_t)(uint32_t)io_num)) { ++ SPDK_ERRLOG("%s: DMA response IO num too much, should be %lu but %d\n", ++ smdev->name, smdev->io_num, io_num); ++ smdev->discard_io_num += io_num; ++ return SPDK_POLLER_BUSY; ++ } ++ smdev->io_stuck_tsc = spdk_get_ticks(); ++ ++ for (int i = 0; i < io_num; i++) { ++ ssam_dev_io_response(smdev, dma_rsp + i); ++ } ++ ++ return SPDK_POLLER_BUSY; ++} ++ ++int ++ssam_dev_register_worker_poller(struct spdk_ssam_dev *smdev) ++{ ++ SPDK_NOTICELOG("%s: worker starting.\n", smdev->name); ++ if (smdev->requestq_poller == NULL) { ++ smdev->requestq_poller = SPDK_POLLER_REGISTER(ssam_dev_request_worker, smdev, 0); ++ if (smdev->requestq_poller == NULL) { ++ SPDK_WARNLOG("%s: stdev_request_worker start failed.\n", smdev->name); ++ return -1; ++ } ++ ++ SPDK_INFOLOG(ssam, "%s: started stdev_request_worker poller on lcore %d\n", ++ smdev->name, spdk_env_get_current_core()); ++ } ++ ++ if (smdev->responseq_poller == NULL) { ++ smdev->responseq_poller = SPDK_POLLER_REGISTER(ssam_dev_response_worker, smdev, 0); ++ if (smdev->responseq_poller == NULL) { ++ SPDK_WARNLOG("%s: stdev_response_worker start failed.\n", smdev->name); ++ return -1; ++ } ++ ++ SPDK_INFOLOG(ssam, "%s: started stdev_response_worker poller on lcore %d\n", ++ smdev->name, spdk_env_get_current_core()); ++ } ++ return 0; ++} ++ ++void ++ssam_dev_unregister_worker_poller(struct spdk_ssam_dev *smdev) ++{ ++ if (!ssam_sessions_empty(smdev->smsessions)) { ++ return; ++ } ++ ++ if (smdev->requestq_poller != NULL) { ++ spdk_poller_unregister(&smdev->requestq_poller); ++ smdev->requestq_poller = NULL; ++ } ++ ++ if (smdev->responseq_poller != NULL) { ++ spdk_poller_unregister(&smdev->responseq_poller); ++ smdev->responseq_poller = NULL; ++ } ++} ++// When stopping the worker, need to stop the two pollers first ++// and wait until all sessions are deleted, and then free smdev. ++static int ++ssam_dev_stop_poller(void *arg) { ++ struct spdk_ssam_dev *smdev = arg; ++ struct spdk_ssam_session *smsession = NULL; ++ ++ // special processing is required for virtio-scsi, ++ // because In scsi scenarios, smsessions are not actively or passively removed. ++ if (smdev->type == VIRTIO_TYPE_SCSI && smdev->active_session_num > 0) { ++ for (int i = 0; i < SSAM_MAX_SESSION_PER_DEV; i++) { ++ if (smdev->smsessions[i] != NULL) { ++ smsession = smdev->smsessions[i]; ++ smsession->backend->remove_self(smsession); // remove session ++ } ++ } ++ } ++ ++ // 等待session全部被移除 ++ if (smdev->active_session_num != 0) { ++ return SPDK_POLLER_BUSY; ++ } ++ ++ // 删除smdev的资源 ++ ssam_dev_unregister(&smdev); ++ ++ return SPDK_POLLER_BUSY; ++} ++ ++static void ++ssam_dev_stop_worker_poller(void *args) ++{ ++ struct spdk_ssam_dev *smdev = (struct spdk_ssam_dev *)args; ++ ++ if (smdev->requestq_poller != NULL) { ++ spdk_poller_unregister(&smdev->requestq_poller); ++ smdev->requestq_poller = NULL; ++ } ++ ++ if (smdev->responseq_poller != NULL) { ++ spdk_poller_unregister(&smdev->responseq_poller); ++ smdev->responseq_poller = NULL; ++ } ++ ++ SPDK_NOTICELOG("%s: poller stopped.\n", smdev->name); ++ smdev->stop_poller = SPDK_POLLER_REGISTER(ssam_dev_stop_poller, smdev, 0); ++ if (smdev->stop_poller == NULL) { ++ SPDK_WARNLOG("%s: ssam_dev stop failed.\n", smdev->name); ++ } ++} ++// When starting the worker, need to start the two pollers first ++static void ++ssam_dev_start_worker_poller(void *args) ++{ ++ struct spdk_ssam_dev *smdev = (struct spdk_ssam_dev *)args; ++ ssam_dev_register_worker_poller(smdev); ++} ++ ++static void ++ssam_send_event_response(struct ssam_session_fn_ctx *ev_ctx) ++{ ++ if (ev_ctx->user_ctx.session_freed == true) { ++ goto out; ++ } ++ ++ if (*ev_ctx->rsp_fn != NULL) { ++ (*ev_ctx->rsp_fn)(ev_ctx->rsp_ctx, ev_ctx->rsp); ++ *ev_ctx->rsp_fn = NULL; ++ } ++ ++out: ++ /* ev_ctx be allocated by another thread */ ++ free(ev_ctx); ++ ev_ctx = NULL; ++} ++ ++static void ++ssam_check_send_event_timeout(struct ssam_session_fn_ctx *ev_ctx, spdk_msg_fn fn) ++{ ++ uint64_t diff_tsc = spdk_get_ticks() - ev_ctx->start_tsc; ++ struct spdk_ssam_session *smsession = ev_ctx->smsession; ++ ++ if ((diff_tsc / SEND_EVENT_WAIT_TIME) > spdk_get_ticks_hz()) { ++ /* If timeout, finish send msg, end the process */ ++ SPDK_ERRLOG("Send event to session %s time out.\n", smsession->name); ++ ev_ctx->rsp = -ETIMEDOUT; ++ ssam_send_event_response(ev_ctx); ++ return; ++ } ++ ++ spdk_thread_send_msg(spdk_get_thread(), fn, (void *)ev_ctx); ++ ++ return; ++} ++ ++static void ++ssam_send_event_finish(void *ctx) ++{ ++ struct ssam_session_fn_ctx *ev_ctx = ctx; ++ struct spdk_ssam_session *smsession = ev_ctx->smsession; ++ ++ if ((ev_ctx->rsp == 0) && (ev_ctx->need_async) && (ev_ctx->user_ctx.async_done == false)) { ++ ssam_check_send_event_timeout(ev_ctx, ssam_send_event_finish); ++ return; ++ } ++ ++ if (spdk_ssam_trylock() != 0) { ++ ssam_check_send_event_timeout(ev_ctx, ssam_send_event_finish); ++ return; ++ } ++ ++ if (smsession->pending_async_op_num > 0) { ++ smsession->pending_async_op_num--; ++ } else { ++ SPDK_ERRLOG("[OFFLOAD_SNIC] smsession %s: internal error.\n", smsession->name); ++ } ++ ++ /* If ev_ctx->cb_fn proccess failed, ev_ctx->cpl_fn will not excute */ ++ if ((ev_ctx->rsp == 0) && (ev_ctx->cpl_fn != NULL)) { ++ ev_ctx->cpl_fn(smsession, &ev_ctx->user_ctx.ctx); ++ } ++ ++ spdk_ssam_unlock(); ++ ++ ssam_send_event_response(ev_ctx); ++} ++ ++static void ++ssam_send_event(void *ctx) ++{ ++ struct ssam_session_fn_ctx *ev_ctx = ctx; ++ struct spdk_ssam_session *smsession = ev_ctx->smsession; ++ ++ if (spdk_ssam_trylock() != 0) { ++ ssam_check_send_event_timeout(ev_ctx, ssam_send_event); ++ return; ++ } ++ ++ if (smsession->initialized && (ev_ctx->cb_fn != NULL)) { ++ ev_ctx->user_ctx.async_done = false; ++ ev_ctx->rsp = ev_ctx->cb_fn(smsession, &ev_ctx->user_ctx.ctx); ++ } else { ++ ev_ctx->rsp = 0; ++ ev_ctx->user_ctx.async_done = true; ++ } ++ ++ spdk_ssam_unlock(); ++ // The judgment logic is used to adapt to the hot-restart. ++ // Because the session has been released during the hot restart, ++ // the following ssam_send_event_finish is not required. ++ if (ev_ctx->user_ctx.session_freed) { ++ free(ev_ctx); ++ return; ++ } else { ++ ev_ctx->start_tsc = spdk_get_ticks(); ++ spdk_thread_send_msg(g_ssam_init_thread, ssam_send_event_finish, ctx); ++ } ++} ++ ++static spdk_ssam_session_rsp_fn g_rsp_fn = NULL; ++ ++int ++ssam_send_event_to_session(struct spdk_ssam_session *smsession, spdk_ssam_session_fn fn, ++ spdk_ssam_session_cpl_fn cpl_fn, struct spdk_ssam_send_event_flag send_event_flag, void *ctx) ++{ ++ struct ssam_session_fn_ctx *ev_ctx; ++ int rc; ++ ++ ev_ctx = calloc(1, sizeof(*ev_ctx)); ++ if (ev_ctx == NULL) { ++ SPDK_ERRLOG("Failed to alloc ssam event.\n"); ++ return -ENOMEM; ++ } ++ ++ ev_ctx->smsession = smsession; ++ ev_ctx->cb_fn = fn; ++ ev_ctx->cpl_fn = cpl_fn; ++ ev_ctx->need_async = send_event_flag.need_async; ++ if (send_event_flag.need_rsp == true) { ++ ev_ctx->rsp_fn = &smsession->rsp_fn; ++ ev_ctx->rsp_ctx = smsession->rsp_ctx; ++ } else { ++ ev_ctx->rsp_fn = &g_rsp_fn; ++ ev_ctx->rsp_ctx = NULL; ++ } ++ ++ ev_ctx->user_ctx.ctx = ctx; ++ ev_ctx->user_ctx.session_freed = false; ++ ++ if (smsession->pending_async_op_num < UINT32_MAX) { ++ smsession->pending_async_op_num++; ++ } else { ++ SPDK_ERRLOG("[OFFLOAD_SNIC] smsession %s: internel error, events stuck too much\n", smsession->name); ++ } ++ ++ ev_ctx->start_tsc = spdk_get_ticks(); ++ rc = spdk_thread_send_msg(smsession->thread, ssam_send_event, ev_ctx); ++ if (rc != 0) { ++ SPDK_ERRLOG("send thread msg failed\n"); ++ free(ev_ctx); ++ return rc; ++ } ++ return 0; ++} ++ ++void ++spdk_ssam_config_json(struct spdk_json_write_ctx *w) ++{ ++ struct spdk_ssam_dev *smdev = NULL; ++ struct spdk_ssam_session *smsession = NULL; ++ ++ spdk_json_write_array_begin(w); ++ ++ spdk_ssam_lock(); ++ smdev = spdk_ssam_dev_next(NULL); ++ while (smdev != NULL) { ++ smsession = ssam_sessions_next(smdev->smsessions, NULL); ++ while (smsession != NULL) { ++ smsession->backend->write_config_json(smsession, w); ++ smsession = ssam_sessions_next(smdev->smsessions, smsession); ++ } ++ ++ smdev = spdk_ssam_dev_next(smdev); ++ } ++ ++ spdk_ssam_unlock(); ++ ++ spdk_json_write_array_end(w); ++} ++ ++int ++ssam_get_config(struct spdk_ssam_session *smsession, uint8_t *config, ++ uint32_t len, uint16_t queues) ++{ ++ const struct spdk_ssam_session_backend *backend = smsession->backend; ++ ++ if (backend->ssam_get_config == NULL) { ++ return -1; ++ } ++ ++ return backend->ssam_get_config(smsession, config, len, queues); ++} ++ ++struct dev_destroy_ctx { ++ struct spdk_ssam_session *smsession; ++ void *args; ++}; ++ ++static void spdk_ssam_dev_destroy(void *arg) ++{ ++ struct dev_destroy_ctx *ctx = (struct dev_destroy_ctx *)arg; ++ ctx->smsession->backend->destroy_bdev_device(ctx->smsession, ctx->args); ++ free(ctx); ++} ++ ++void ++spdk_ssam_send_dev_destroy_msg(struct spdk_ssam_session *smsession, void *args) ++{ ++ struct dev_destroy_ctx *ctx = calloc(1, sizeof(struct dev_destroy_ctx)); ++ if (ctx == NULL) { ++ SPDK_ERRLOG("%s: out of memory, destroy dev failed\n", smsession->name); ++ return; ++ } ++ ctx->smsession = smsession; ++ ctx->args = args; ++ spdk_thread_send_msg(g_ssam_init_thread, spdk_ssam_dev_destroy, ctx); ++} ++ ++void ++ssam_poller_start(void) ++{ ++ struct spdk_ssam_dev *smdev = NULL; ++ struct spdk_ssam_dev *tmp = NULL; ++ spdk_ssam_lock(); ++ smdev = spdk_ssam_dev_next(NULL); ++ while (smdev != NULL) { ++ tmp = spdk_ssam_dev_next(smdev); ++ // Send the message to each smdev to start the worker on the smdev. ++ spdk_thread_send_msg(smdev->thread, ssam_dev_start_worker_poller, smdev); ++ smdev = tmp; ++ } ++ spdk_ssam_unlock(); ++} ++ ++static void ++spdk_ssam_fini(void *arg) ++{ ++ struct spdk_ssam_dev *smdev = NULL; ++ struct spdk_ssam_dev *tmp = NULL; ++ SPDK_WARNLOG("ssam is finishing\n"); ++ spdk_ssam_lock(); ++ smdev = spdk_ssam_dev_next(NULL); ++ while (smdev != NULL) { ++ tmp = spdk_ssam_dev_next(smdev); ++ // Send the message to each smdev to stop the worker on the smdev. ++ spdk_thread_send_msg(smdev->thread, ssam_dev_stop_worker_poller, smdev); ++ smdev = tmp; ++ } ++ spdk_ssam_unlock(); ++ ++ spdk_cpuset_zero(&g_ssam_core_mask); ++ ++ g_ssam_fini_cpl_cb(); ++} ++ ++static void * ++spdk_ssam_session_shutdown(void *arg) ++{ ++ SPDK_INFOLOG(ssam, "ssam session Exiting\n"); ++ spdk_thread_send_msg(g_ssam_init_thread, spdk_ssam_fini, NULL); ++ ++ return NULL; ++} ++ ++void ++spdk_ssam_subsystem_fini(spdk_ssam_fini_cb fini_cb) ++{ ++ if (spdk_get_thread() != g_ssam_init_thread) { ++ SPDK_ERRLOG("ssam finish thread not equal init thread, internel error\n"); ++ } ++ ++ g_ssam_fini_cpl_cb = fini_cb; ++ ++ spdk_ssam_session_shutdown(NULL); ++} ++ ++void ++spdk_ssam_subsystem_init(spdk_ssam_init_cb init_cb) ++{ ++ uint32_t i; ++ int ret; ++ int shm_id; ++ ++ g_ssam_init_thread = spdk_get_thread(); ++ if (g_ssam_init_thread == NULL) { ++ ret = -EBUSY; ++ SPDK_ERRLOG("get thread error\n"); ++ goto exit; ++ } ++ ++ /* init ssam core mask */ ++ spdk_cpuset_zero(&g_ssam_core_mask); ++ SPDK_ENV_FOREACH_CORE(i) { ++ spdk_cpuset_set_cpu(&g_ssam_core_mask, i, true); ++ } ++ ++ ret = ssam_set_core_num(spdk_cpuset_count(&g_ssam_core_mask)); ++ if (ret != 0) { ++ goto exit; ++ } ++ ++ ret = spdk_ssam_init(); ++ if (ret != 0) { ++ goto exit; ++ } ++ ++ if (!ssam_get_shm_created()) { ++ shm_id = shm_open(SSAM_SHM, O_CREAT | O_EXCL | O_RDWR, SSAM_SHM_PERMIT); ++ if (shm_id < 0) { ++ SPDK_ERRLOG("failed to create shared memory %s\n", SSAM_SHM); ++ ret = -1; ++ goto exit; ++ } ++ ssam_set_shm_created(true); ++ } ++ ++exit: ++ init_cb(ret); ++ return; ++} ++ ++// Initialize all smdev modules during submodule initialization. ++static int ++ssam_smdev_init(void) ++{ ++ int rc = 0; ++ struct spdk_ssam_dev *smdev; ++ struct spdk_ssam_dev *tmp = NULL; ++ uint16_t core_num = ssam_get_core_num(); ++ for (uint16_t i = 0; i < core_num; ++i) { ++ rc = ssam_dev_register(&smdev, i); ++ if (rc != 0) { ++ goto out; ++ } ++ } ++ ++ rc = ssam_get_hot_upgrade_state(); ++ if (rc != 0) { ++ SPDK_ERRLOG(": virtio upgrade state failed.\n"); ++ return rc; ++ } ++ ++ return 0; ++out: ++ smdev = spdk_ssam_dev_next(NULL); ++ while (smdev != NULL) { ++ tmp = spdk_ssam_dev_next(smdev); ++ ssam_dev_unregister(&smdev); ++ smdev = tmp; ++ } ++ return rc; ++} ++ ++static int ++ssam_server_init(void) ++{ ++ uint32_t mempool_size = spdk_ssam_get_mempool_size(); ++ uint32_t extra_size = spdk_ssam_get_extra_size(); ++ ++ /* Disable dummy I/O for hot restart */ ++ ++ g_ssam_info.mp = ssam_mempool_create(mempool_size * SSAM_MB, extra_size * SSAM_MB); ++ if (g_ssam_info.mp == NULL) { ++ SPDK_ERRLOG("ssam create mempool failed, mempool_size = %uMB.\n", mempool_size); ++ return -ENOMEM; ++ } ++ ++ return 0; ++} ++ ++static void ++ssam_server_exit(void) ++{ ++ if (g_ssam_info.mp != NULL) { ++ ssam_mempool_destroy(g_ssam_info.mp); ++ g_ssam_info.mp = NULL; ++ } ++ ++ memset(&g_ssam_info, 0x0, sizeof(struct spdk_ssam_info)); ++} ++ ++ ++static int ++ssam_check_device_status(void) ++{ ++ uint8_t ready = 0; ++ int times = 0; ++ int rc; ++ ++ do { ++ rc = ssam_check_device_ready(0, 0, &ready); ++ if (rc != 0) { ++ SPDK_ERRLOG("device check failed.\n"); ++ return rc; ++ } ++ ++ if (ready != 0) { ++ break; ++ } ++ ++ usleep(DEVICE_READY_WAIT_TIME); ++ times++; ++ } while (times < DEVICE_READY_TIMEOUT); ++ ++ if (ready == 0) { ++ SPDK_ERRLOG("device has not been ready after 1.5s.\n"); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++ ++static int ++spdk_ssam_init(void) ++{ ++ int rc; ++ ++ rc = ssam_check_device_status(); ++ if (rc != 0) { ++ return rc; ++ } ++ ++ rc = ssam_config_init(); ++ if (rc != 0) { ++ return rc; ++ } ++ ++ rc = ssam_server_init(); ++ if (rc != 0) { ++ ssam_config_exit(); ++ return rc; ++ } ++ ++ rc = ssam_smdev_init(); ++ if (rc != 0) { ++ ssam_server_exit(); ++ ssam_config_exit(); ++ } ++ ++ return rc; ++} ++ ++void ++spdk_ssam_exit(void) ++{ ++ ssam_deinit_device_pcie_list(); ++ ssam_config_exit(); ++ ssam_server_exit(); ++} ++ ++SPDK_LOG_REGISTER_COMPONENT(ssam) +diff --git a/lib/ssam/ssam_blk.c b/lib/ssam/ssam_blk.c +new file mode 100644 +index 0000000..505de7d +--- /dev/null ++++ b/lib/ssam/ssam_blk.c +@@ -0,0 +1,2119 @@ ++/*- ++ * BSD LICENSE ++ * ++ * Copyright (c) Huawei Technologies Co., Ltd. 2021-2021. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * ++ * * Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * * Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in ++ * the documentation and/or other materials provided with the ++ * distribution. ++ * * Neither the name of Intel Corporation nor the names of its ++ * contributors may be used to endorse or promote products derived ++ * from this software without specific prior written permission. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ++ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ++ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ++ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ++ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ++ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ++ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ++ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ++ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ++ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#include ++#include ++ ++#include "spdk/env.h" ++#include "spdk/bdev.h" ++#include "spdk/bdev_module.h" ++#include "spdk/thread.h" ++#include "spdk/likely.h" ++#include "spdk/string.h" ++#include "spdk/util.h" ++ ++#include "ssam_internal.h" ++ ++#define SESSION_STOP_POLLER_PERIOD 1000 ++#define ENQUEUE_TIMES_PER_IO 1000 ++ ++#define IOV_HEADER_TAIL_NUM 2 ++ ++#define SECTOR_SIZE 512 ++#define ALIGNMENT_2M (2048 * 1024) ++#define SERIAL_STRING_LEN 128 ++#define SMSESSION_STOP_TIMEOUT 2 // s ++#define PERF_STAT ++ ++/* Related to (SPDK_SSAM_IOVS_MAX * SPDK_SSAM_MAX_SEG_SIZE) */ ++#define PAYLOAD_SIZE_MAX (2048U * 1024) ++ ++#define RETRY_TIMEOUT 120 ++ ++/* Minimal set of features supported by every virtio-blk device */ ++#define SPDK_SSAM_BLK_FEATURES_BASE (SPDK_SSAM_FEATURES | \ ++ (1ULL << VIRTIO_BLK_F_SIZE_MAX) | (1ULL << VIRTIO_BLK_F_SEG_MAX) | \ ++ (1ULL << VIRTIO_BLK_F_GEOMETRY) | (1ULL << VIRTIO_BLK_F_BLK_SIZE) | \ ++ (1ULL << VIRTIO_BLK_F_TOPOLOGY) | (1ULL << VIRTIO_BLK_F_BARRIER) | \ ++ (1ULL << VIRTIO_BLK_F_SCSI) | (1ULL << VIRTIO_BLK_F_CONFIG_WCE) | \ ++ (1ULL << VIRTIO_BLK_F_MQ)) ++ ++extern bool g_ssam_subsystem_exit; ++ ++struct ssam_task_stat { ++ uint64_t start_tsc; ++ uint64_t dma_start_tsc; ++ uint64_t dma_end_tsc; ++ uint64_t bdev_start_tsc; ++ uint64_t bdev_func_tsc; ++ uint64_t bdev_end_tsc; ++ uint64_t complete_start_tsc; ++ uint64_t complete_end_tsc; ++}; ++ ++struct spdk_ssam_blk_task { ++ /* Returned status of I/O processing, it can be VIRTIO_BLK_S_OK, ++ * VIRTIO_BLK_S_IOERR or VIRTIO_BLK_S_UNSUPP ++ */ ++ volatile uint8_t *status; ++ ++ /* Number of bytes processed successfully */ ++ uint32_t used_len; ++ ++ /* Records the amount of valid data in the struct iovec iovs array. */ ++ uint32_t iovcnt; ++ struct ssam_iovec iovs; ++ ++ /* If set, the task is currently used for I/O processing. */ ++ bool used; ++ ++ /* For bdev io wait */ ++ struct spdk_bdev_io_wait_entry bdev_io_wait; ++ struct spdk_ssam_session_io_wait session_io_wait; ++ struct spdk_ssam_blk_session *bsmsession; ++ ++ /* Size of whole payload in bytes */ ++ uint32_t payload_size; ++ ++ /* ssam request data */ ++ struct ssam_request *io_req; ++ ++ uint16_t vq_idx; ++ uint16_t req_idx; ++ uint16_t task_idx; ++ struct ssam_task_stat task_stat; ++}; ++ ++struct ssam_blk_stat { ++ uint64_t count; ++ uint64_t start_count; ++ uint64_t total_tsc; // pre_dma <- -> post_return ++ uint64_t dma_tsc; // pre_dma <- -> post_dma ++ uint64_t dma_count; ++ uint64_t dma_complete_count; ++ uint64_t bdev_tsc; // pre_bdev <- -> post_bdev ++ uint64_t bdev_submit_tsc; // <- spdk_bdev_xxx -> ++ uint64_t bdev_count; ++ uint64_t bdev_complete_count; ++ uint64_t complete_tsc; // pre_return <- -> post_return ++ uint64_t internel_tsc; // total_tsc - dma_tsc - bdev_tsc - complete_tsc ++ ++ uint64_t complete_read_ios; // Number of successfully completed read requests ++ uint64_t err_read_ios; // Number of failed completed read requests ++ uint64_t complete_write_ios; // Number of successfully completed write requests ++ uint64_t err_write_ios; // Number of failed completed write requests ++ uint64_t flush_ios; // Total number of flush requests ++ uint64_t complete_flush_ios; // Number of successfully completed flush requests ++ uint64_t err_flush_ios; // Number of failed completed flush requests ++ uint64_t other_ios; ++ uint64_t complete_other_ios; ++ uint64_t err_other_ios; ++ uint64_t fatal_ios; // Number of discarded requests ++ uint64_t io_retry; ++}; ++ ++struct spdk_ssam_blk_session { ++ /* The parent session must be the very first field in this struct */ ++ struct spdk_ssam_session smsession; ++ struct spdk_poller *stop_poller; ++ struct spdk_bdev *bdev; ++ struct spdk_bdev_desc *bdev_desc; ++ struct spdk_io_channel *io_channel; ++ ++ /* volume id*/ ++ char *serial; ++ ++ /* accumulated I/O statistics */ ++ struct spdk_bdev_io_stat stat; ++ ++ // Current count of bdev operations for hot-restart. ++ int32_t bdev_count; ++ ++ // poller for waiting bdev finish when hot-restart ++ struct spdk_poller *stop_bdev_poller; ++ ++ /* controller statistics. */ ++ struct ssam_blk_stat blk_stat; ++ ++ /* if set, all writes to the device will fail with ++ * VIRTIO_BLK_S_IOERR error code ++ */ ++ bool readonly; ++ ++ /* if set, indicate the session not have a bdev, all writes to the device ++ * will fail with VIRTIO_BLK_S_IOERR error code ++ */ ++ bool no_bdev; ++}; ++ ++struct ssam_blk_session_ctx { ++ struct spdk_ssam_blk_session *bsmsession; ++ void **user_ctx; ++}; ++ ++static const struct spdk_ssam_session_backend g_ssam_blk_session_backend; ++static int ssam_blk_remove_session(struct spdk_ssam_session *smsession); ++static void ssam_blk_request_worker(struct spdk_ssam_session *smsession, void *arg); ++static void ssam_blk_destroy_bdev_device(struct spdk_ssam_session *smsession, void *args); ++static void ssam_blk_response_worker(struct spdk_ssam_session *smsession, void *arg); ++static void ssam_blk_no_data_request_worker(struct spdk_ssam_session *smsession); ++static inline void ssam_request_queue_io(struct spdk_ssam_blk_task *task); ++static void ssam_task_complete(struct spdk_ssam_blk_task *task, uint8_t status); ++static void ssam_data_request_para(struct ssam_dma_request *dma_req, ++ struct spdk_ssam_blk_task *task, uint32_t type, uint8_t status); ++static void ssam_blk_print_stuck_io_info(struct spdk_ssam_session *smsession); ++static int ssam_process_blk_request(struct spdk_ssam_blk_task *task); ++static void ssam_free_task_pool(struct spdk_ssam_blk_session *bsmsession); ++static int ssam_blk_io_complete(struct spdk_ssam_dev *smdev, struct ssam_request *io_req, uint8_t status); ++static void ssam_session_io_resubmit(void *arg); ++ ++static inline struct spdk_ssam_blk_session * ++ssam_to_blk_session(struct spdk_ssam_session *smsession) ++{ ++ return (struct spdk_ssam_blk_session *)smsession; ++} ++ ++static void ++ssam_blk_dump_info_json(struct spdk_ssam_session *smsession, ++ struct spdk_json_write_ctx *w) ++{ ++ struct spdk_ssam_blk_session *bsmsession = ssam_to_blk_session(smsession); ++ ++ spdk_json_write_object_begin(w); ++ spdk_json_write_named_string(w, "name", spdk_ssam_session_get_name(smsession)); ++ spdk_json_write_named_uint32(w, "function_id", (uint32_t)smsession->gfunc_id); ++ spdk_json_write_named_uint32(w, "queues", (uint32_t)smsession->max_queues); ++ ++ spdk_json_write_named_object_begin(w, "block"); ++ spdk_json_write_named_bool(w, "readonly", bsmsession->readonly); ++ spdk_json_write_name(w, "bdev"); ++ if (bsmsession->bdev != NULL) { ++ spdk_json_write_string(w, spdk_bdev_get_name(bsmsession->bdev)); ++ } else { ++ spdk_json_write_null(w); ++ } ++ spdk_json_write_object_end(w); ++ ++ spdk_json_write_object_end(w); ++} ++ ++static void ++ssam_dev_bdev_remove_cpl_cb(struct spdk_ssam_session *smsession, void **unnused) ++{ ++ /* All sessions have been notified, time to close the bdev */ ++ struct spdk_ssam_blk_session *bsmsession = ssam_to_blk_session(smsession); ++ ++ if (bsmsession == NULL) { ++ return; ++ } ++ ++ if (bsmsession->bdev_desc != NULL) { ++ spdk_bdev_close(bsmsession->bdev_desc); ++ bsmsession->bdev_desc = NULL; ++ } ++ ++ /* bdev not create by ssam blk, no need be freed here */ ++ bsmsession->bdev = NULL; ++} ++ ++static void ++ssam_blk_stop_cpl_cb(struct spdk_ssam_session *smsession, void **ctx) ++{ ++ struct spdk_ssam_blk_session *bsmsession = ssam_to_blk_session(smsession); ++ spdk_ssam_session_rsp_fn rsp_fn = smsession->rsp_fn; ++ void *rsp_ctx = smsession->rsp_ctx; ++ int rc; ++ ++ ssam_dev_bdev_remove_cpl_cb(smsession, NULL); ++ rc = ssam_virtio_blk_resize(smsession->gfunc_id, 0); ++ if (rc != 0) { ++ SPDK_WARNLOG("%s: virtio blk resize failed when remove session.\n", smsession->name); ++ } ++ ++ /* Can not umount function here, whenever the gfunc_id must be mounted to ++ * the dummy tid or to the specific tid ++ */ ++ ++ SPDK_NOTICELOG("BLK controller %s deleted\n", smsession->name); ++ ++ if (smsession->name != NULL) { ++ free(smsession->name); ++ smsession->name = NULL; ++ } ++ ++ ssam_set_session_be_freed(ctx); ++ memset(bsmsession, 0, sizeof(*bsmsession)); ++ free(bsmsession); ++ ++ if (rsp_fn != NULL) { ++ rsp_fn(rsp_ctx, 0); ++ rsp_fn = NULL; ++ } ++} ++ ++static void ++ssam_task_stat_tick(uint64_t *tsc) ++{ ++#ifdef PERF_STAT ++ *tsc = spdk_get_ticks(); ++#endif ++ return; ++} ++ ++static void ++ssam_blk_stat_statistics(struct spdk_ssam_blk_task *task, uint8_t status) ++{ ++#ifdef PERF_STAT ++ struct spdk_ssam_blk_session *bsmsession = task->bsmsession; ++ uint64_t dma_tsc = task->task_stat.dma_end_tsc - task->task_stat.dma_start_tsc; ++ uint64_t bdev_tsc = task->task_stat.bdev_end_tsc - task->task_stat.bdev_start_tsc; ++ uint64_t bdev_submit_tsc = task->task_stat.bdev_func_tsc - task->task_stat.bdev_start_tsc; ++ uint64_t complete_tsc = task->task_stat.complete_end_tsc - task->task_stat.complete_start_tsc; ++ uint64_t total_tsc = task->task_stat.complete_end_tsc - task->task_stat.start_tsc; ++ struct virtio_blk_outhdr *req = (struct virtio_blk_outhdr *)task->io_req->req.cmd.header; ++ ++ if (req->type == VIRTIO_BLK_T_IN) { // read ++ bsmsession->stat.read_latency_ticks += total_tsc; ++ bsmsession->stat.bytes_read += task->payload_size; ++ bsmsession->stat.num_read_ops++; ++ if (status == VIRTIO_BLK_S_OK) { ++ bsmsession->blk_stat.complete_read_ios++; ++ } else { ++ bsmsession->blk_stat.err_read_ios++; ++ } ++ } else if (req->type == VIRTIO_BLK_T_OUT) { // write ++ bsmsession->stat.write_latency_ticks += total_tsc; ++ bsmsession->stat.bytes_written += task->payload_size; ++ bsmsession->stat.num_write_ops++; ++ if (status == VIRTIO_BLK_S_OK) { ++ bsmsession->blk_stat.complete_write_ios++; ++ } else { ++ bsmsession->blk_stat.err_write_ios++; ++ } ++ } else if (req->type == VIRTIO_BLK_T_FLUSH) { // flush ++ bsmsession->blk_stat.flush_ios++; ++ if (status == VIRTIO_BLK_S_OK) { ++ bsmsession->blk_stat.complete_flush_ios++; ++ } else { ++ bsmsession->blk_stat.err_flush_ios++; ++ } ++ } else { ++ bsmsession->blk_stat.other_ios++; ++ if (status == VIRTIO_BLK_S_OK) { ++ bsmsession->blk_stat.complete_other_ios++; ++ } else { ++ bsmsession->blk_stat.err_other_ios++; ++ } ++ } ++ ++ bsmsession->blk_stat.dma_tsc += dma_tsc; ++ bsmsession->blk_stat.bdev_tsc += bdev_tsc; ++ bsmsession->blk_stat.bdev_submit_tsc += bdev_submit_tsc; ++ bsmsession->blk_stat.complete_tsc += complete_tsc; ++ bsmsession->blk_stat.total_tsc += total_tsc; ++ bsmsession->blk_stat.internel_tsc += total_tsc - complete_tsc - bdev_tsc - dma_tsc; ++ bsmsession->blk_stat.count += 1; ++#endif ++} ++ ++static void ++ssam_blk_configs(uint8_t *config, struct virtio_blk_config *blkcfg, ++ uint32_t len, struct spdk_bdev *bdev) ++{ ++ uint32_t cfg_len; ++ ++ /* minimum I/O size in blocks */ ++ blkcfg->min_io_size = 1; ++ ++ if (bdev && spdk_bdev_io_type_supported(bdev, SPDK_BDEV_IO_TYPE_UNMAP)) { ++ /* 32768 sectors is 16MiB, expressed in 512 Bytes */ ++ blkcfg->max_discard_sectors = 32768; ++ blkcfg->max_discard_seg = 1; ++ /* expressed in 512 Bytes sectors */ ++ blkcfg->discard_sector_alignment = blkcfg->blk_size / SECTOR_SIZE; ++ } ++ if (bdev && spdk_bdev_io_type_supported(bdev, SPDK_BDEV_IO_TYPE_WRITE_ZEROES)) { ++ /* 32768 sectors is 16MiB, expressed in 512 Bytes */ ++ blkcfg->max_write_zeroes_sectors = 32768; ++ blkcfg->max_write_zeroes_seg = 1; ++ } ++ ++ cfg_len = sizeof(struct virtio_blk_config); ++ memcpy(config, blkcfg, (unsigned long)spdk_min(len, cfg_len)); ++ if (len < cfg_len) { ++ SPDK_NOTICELOG("Out config len %u < total config len %u\n", len, cfg_len); ++ } ++ ++ return; ++} ++ ++static int ++ssam_blk_get_config(struct spdk_ssam_session *smsession, uint8_t *config, ++ uint32_t len, uint16_t queues) ++{ ++ struct virtio_blk_config blkcfg; ++ struct spdk_ssam_blk_session *bsmsession = NULL; ++ struct spdk_bdev *bdev = NULL; ++ uint32_t blk_size; ++ uint64_t blkcnt; ++ ++ memset(&blkcfg, 0, sizeof(blkcfg)); ++ bsmsession = ssam_to_blk_session(smsession); ++ if (bsmsession == NULL) { ++ SPDK_ERRLOG("session is null.\n"); ++ return -1; ++ } ++ bdev = bsmsession->bdev; ++ if (bdev == NULL) { ++ return -1; ++ } ++ blk_size = spdk_bdev_get_block_size(bdev); ++ blkcnt = spdk_bdev_get_num_blocks(bdev); ++ /* ssam will use this configuration, this is the max capability of ++ * the ssam, configurations will be obtained through negotiation ++ * in the future. ++ */ ++ blkcfg.size_max = SPDK_SSAM_MAX_SEG_SIZE; ++ blkcfg.seg_max = SPDK_SSAM_IOVS_MAX; ++ ++ if (blk_size == 0) { ++ SPDK_ERRLOG("bdev's blk_size %u error.\n", blk_size); ++ return -1; ++ } ++ if (blkcnt > (UINT64_MAX / blk_size)) { ++ SPDK_ERRLOG("bdev's blkcnt %lu or blk_size %u out of range.\n", ++ blkcnt, blk_size); ++ return -1; ++ } ++ blkcfg.blk_size = blk_size; ++ /* expressed in 512 Bytes sectors */ ++ blkcfg.capacity = (blkcnt * blk_size) / 512; ++ blkcfg.num_queues = 1; // TODO: 1 change to queues after the VBS problem is fixed ++ ssam_blk_configs(config, &blkcfg, len, bdev); ++ ++ return 0; ++} ++ ++static void ++ssam_blk_write_config_json(struct spdk_ssam_session *smsession, ++ struct spdk_json_write_ctx *w) ++{ ++ struct spdk_ssam_blk_session *bsmsession = ssam_to_blk_session(smsession); ++ ++ if (bsmsession == NULL || bsmsession->bdev == NULL) { ++ return; ++ } ++ ++ spdk_json_write_object_begin(w); ++ spdk_json_write_named_string(w, "method", "create_blk_controller"); ++ ++ spdk_json_write_named_object_begin(w, "params"); ++ spdk_json_write_named_string(w, "dev_name", spdk_bdev_get_name(bsmsession->bdev)); ++ char *gfunc_id = spdk_sprintf_alloc("%u", bsmsession->smsession.gfunc_id); ++ if (gfunc_id == NULL) { ++ SPDK_ERRLOG("alloc for gfunc_id failed\n"); ++ } else { ++ spdk_json_write_named_string(w, "index", gfunc_id); ++ free(gfunc_id); ++ } ++ spdk_json_write_named_bool(w, "readonly", bsmsession->readonly); ++ if (bsmsession->serial != NULL) { ++ spdk_json_write_named_string(w, "serial", bsmsession->serial); ++ } ++ spdk_json_write_object_end(w); ++ ++ spdk_json_write_object_end(w); ++} ++ ++static void ++ssam_blk_show_iostat_json(struct spdk_ssam_session *smsession, uint32_t id, struct spdk_json_write_ctx *w) ++{ ++ struct spdk_ssam_blk_session *bsmsession = ssam_to_blk_session(smsession); ++ struct spdk_bdev *bdev = spdk_ssam_get_session_bdev(smsession); ++ struct spdk_bdev_io_stat stat = {0}; ++ struct ssam_blk_stat blk_stat; ++ uint64_t ticks_hz = spdk_get_ticks_hz(); ++ uint64_t poll_count = smsession->smdev->stat.poll_count; ++ ++ memcpy(&stat, &bsmsession->stat, sizeof(struct spdk_bdev_io_stat)); /* a little question, mutex */ ++ memcpy(&blk_stat, &bsmsession->blk_stat, sizeof(struct ssam_blk_stat)); ++ spdk_json_write_object_begin(w); ++ ++ spdk_json_write_named_uint32(w, "function_id", smsession->gfunc_id); ++ if (smsession->smdev->stat.poll_count == 0) { ++ poll_count = 1; ++ } ++ spdk_json_write_named_string_fmt(w, "poll_lat", "%.9f", ++ (float)smsession->smdev->stat.poll_tsc / poll_count / ticks_hz); ++ spdk_json_write_named_string(w, "bdev_name", (bdev == NULL) ? "" : spdk_bdev_get_name(bdev)); ++ spdk_json_write_named_uint64(w, "bytes_read", stat.bytes_read); ++ spdk_json_write_named_uint64(w, "num_read_ops", stat.num_read_ops); ++ spdk_json_write_named_uint64(w, "bytes_written", stat.bytes_written); ++ spdk_json_write_named_uint64(w, "num_write_ops", stat.num_write_ops); ++ spdk_json_write_named_uint64(w, "read_latency_ticks", stat.read_latency_ticks); ++ spdk_json_write_named_uint64(w, "write_latency_ticks", stat.write_latency_ticks); ++ spdk_json_write_named_uint64(w, "complete_read_ios", blk_stat.complete_read_ios); ++ spdk_json_write_named_uint64(w, "err_read_ios", blk_stat.err_read_ios); ++ spdk_json_write_named_uint64(w, "complete_write_ios", blk_stat.complete_write_ios); ++ spdk_json_write_named_uint64(w, "err_write_ios", blk_stat.err_write_ios); ++ spdk_json_write_named_uint64(w, "flush_ios", blk_stat.flush_ios); ++ spdk_json_write_named_uint64(w, "complete_flush_ios", blk_stat.complete_flush_ios); ++ spdk_json_write_named_uint64(w, "err_flush_ios", blk_stat.err_flush_ios); ++ spdk_json_write_named_uint64(w, "other_ios", blk_stat.other_ios); ++ spdk_json_write_named_uint64(w, "complete_other_ios", blk_stat.complete_other_ios); ++ spdk_json_write_named_uint64(w, "err_other_ios", blk_stat.err_other_ios); ++ ++ spdk_json_write_named_uint64(w, "fatal_ios", blk_stat.fatal_ios); ++ spdk_json_write_named_uint64(w, "io_retry", blk_stat.io_retry); ++ spdk_json_write_named_object_begin(w, "counters"); ++ spdk_json_write_named_uint64(w, "start_count", blk_stat.start_count); ++ spdk_json_write_named_uint64(w, "dma_count", blk_stat.dma_count); ++ spdk_json_write_named_uint64(w, "dma_complete_count", blk_stat.dma_complete_count); ++ spdk_json_write_named_uint64(w, "bdev_count", blk_stat.bdev_count); ++ spdk_json_write_named_uint64(w, "bdev_complete_count", blk_stat.bdev_complete_count); ++ spdk_json_write_object_end(w); ++ spdk_json_write_named_object_begin(w, "details"); ++ spdk_json_write_named_uint64(w, "count", blk_stat.count); ++ if (blk_stat.count == 0) { ++ blk_stat.count = 1; ++ } ++ spdk_json_write_named_string_fmt(w, "total_lat", "%.9f", (float)blk_stat.total_tsc / blk_stat.count / ticks_hz); ++ spdk_json_write_named_string_fmt(w, "dma_lat", "%.9f", (float)blk_stat.dma_tsc / blk_stat.count / ticks_hz); ++ spdk_json_write_named_string_fmt(w, "bdev_lat", "%.9f", (float)blk_stat.bdev_tsc / blk_stat.count / ticks_hz); ++ spdk_json_write_named_string_fmt(w, "bdev_submit_lat", "%.9f", (float)blk_stat.bdev_submit_tsc / blk_stat.count / ticks_hz); ++ spdk_json_write_named_string_fmt(w, "complete_lat", "%.9f", (float)blk_stat.complete_tsc / blk_stat.count / ticks_hz); ++ spdk_json_write_named_string_fmt(w, "internel_lat", "%.9f", (float)blk_stat.internel_tsc / blk_stat.count / ticks_hz); ++ spdk_json_write_object_end(w); ++ spdk_json_write_object_end(w); ++} ++ ++static void ++ssam_blk_clear_iostat_json(struct spdk_ssam_session *smsession) ++{ ++ struct spdk_ssam_blk_session *bsmsession = ssam_to_blk_session(smsession); ++ memset(&bsmsession->stat, 0, sizeof(struct spdk_bdev_io_stat) - sizeof(uint64_t)); // exclude ticks_rate ++ memset(&bsmsession->blk_stat, 0, sizeof(struct ssam_blk_stat)); ++} ++ ++static struct spdk_bdev *ssam_blk_get_bdev(struct spdk_ssam_session *smsession, uint32_t id) ++{ ++ struct spdk_bdev *bdev = spdk_ssam_get_session_bdev(smsession); ++ ++ return bdev; ++} ++ ++static const struct spdk_ssam_session_backend g_ssam_blk_session_backend = { ++ .type = VIRTIO_TYPE_BLK, ++ .remove_session = ssam_blk_remove_session, ++ .request_worker = ssam_blk_request_worker, ++ .destroy_bdev_device = ssam_blk_destroy_bdev_device, ++ .response_worker = ssam_blk_response_worker, ++ .no_data_req_worker = ssam_blk_no_data_request_worker, ++ .ssam_get_config = ssam_blk_get_config, ++ .print_stuck_io_info = ssam_blk_print_stuck_io_info, ++ .dump_info_json = ssam_blk_dump_info_json, ++ .write_config_json = ssam_blk_write_config_json, ++ .show_iostat_json = ssam_blk_show_iostat_json, ++ .clear_iostat_json = ssam_blk_clear_iostat_json, ++ .get_bdev = ssam_blk_get_bdev, ++ .remove_self = NULL, ++}; ++ ++// Clean Smsession ++static int ++ssam_destroy_poller_cb(void *arg) ++{ ++ struct spdk_ssam_blk_session *bsmsession = (struct spdk_ssam_blk_session *)arg; ++ struct spdk_ssam_session *smsession = &bsmsession->smsession; ++ struct spdk_ssam_dev *smdev = smsession->smdev; ++ ++ SPDK_NOTICELOG("%s: remaining %u tasks\n", smsession->name, smsession->task_cnt); ++ ++ // stop poller ++ spdk_poller_unregister(&bsmsession->stop_bdev_poller); ++ ++ // remove session ++ ssam_sessions_remove(smdev->smsessions, smsession); ++ smdev->active_session_num--; ++ smsession->smdev = NULL; ++ ++ // put ioChannle ++ if (bsmsession->io_channel != NULL) { ++ spdk_put_io_channel(bsmsession->io_channel); ++ bsmsession->io_channel = NULL; ++ } ++ ++ // close bdev device, last step, async ++ spdk_ssam_send_dev_destroy_msg(smsession, NULL); ++ ++ // free smsession not here, but after close bdev device; ++ // see ssam_blk_destroy_bdev_device() ++ ++ return SPDK_POLLER_BUSY; ++} ++ ++static int ++ssam_session_bdev_remove_cb(struct spdk_ssam_session *smsession, void **ctx) ++{ ++ struct spdk_ssam_blk_session *bsmsession = ssam_to_blk_session(smsession); ++ int rc = 0; ++ ++ // smsession already removed ++ if (!smsession->started) { ++ return 0; ++ } else { ++ smsession->started = false; ++ } ++ ++ bsmsession->stop_bdev_poller = SPDK_POLLER_REGISTER(ssam_destroy_poller_cb, ++ bsmsession, 0); ++ ++ rc = ssam_virtio_blk_resize(smsession->gfunc_id, 0); ++ if (rc != 0) { ++ SPDK_WARNLOG("%s: virtio blk resize failed when remove session.\n", smsession->name); ++ } ++ ++ ssam_set_session_be_freed(ctx); ++ ssam_send_event_async_done(ctx); ++ ++ return 0; ++} ++ ++static void ++ssam_bdev_remove_cb(void *remove_ctx) ++{ ++ struct spdk_ssam_session *smsession = remove_ctx; ++ struct spdk_ssam_send_event_flag send_event_flag = { ++ .need_async = false, ++ .need_rsp = true, ++ }; ++ ++ SPDK_WARNLOG("%s: hot-removing bdev - all further requests will be stucked.\n", ++ smsession->name); ++ ++ ssam_send_event_to_session(smsession, ssam_session_bdev_remove_cb, ++ NULL, send_event_flag, NULL); ++} ++ ++static void ++ssam_session_bdev_resize_cb(struct spdk_ssam_session *smsession, void **ctx) ++{ ++ struct spdk_ssam_blk_session *bsmsession = ssam_to_blk_session(smsession); ++ int rc; ++ ++ rc = ssam_virtio_blk_resize(smsession->gfunc_id, bsmsession->bdev->blockcnt); ++ if (rc != 0) { ++ SPDK_WARNLOG("%s: virtio blk resize failed.\n", smsession->name); ++ } ++} ++ ++static void ++ssam_blk_resize_cb(void *resize_ctx) ++{ ++ struct spdk_ssam_session *smsession = resize_ctx; ++ struct spdk_ssam_send_event_flag send_event_flag = { ++ .need_async = false, ++ .need_rsp = true, ++ }; ++ ++ ssam_send_event_to_session(smsession, NULL, ssam_session_bdev_resize_cb, send_event_flag, NULL); ++} ++ ++static void ++ssam_bdev_event_cb(enum spdk_bdev_event_type type, struct spdk_bdev *bdev, ++ void *event_ctx) ++{ ++ SPDK_DEBUGLOG(ssam_blk, "Bdev event: type %d, name %s\n", ++ type, bdev->name); ++ ++ switch (type) { ++ case SPDK_BDEV_EVENT_REMOVE: ++ SPDK_NOTICELOG("bdev name (%s) received event(SPDK_BDEV_EVENT_REMOVE)\n", ++ bdev->name); ++ ssam_bdev_remove_cb(event_ctx); ++ break; ++ case SPDK_BDEV_EVENT_RESIZE: ++ SPDK_NOTICELOG("bdev name (%s) received event(SPDK_BDEV_EVENT_RESIZE)\n", ++ bdev->name); ++ ssam_blk_resize_cb(event_ctx); ++ break; ++ default: ++ SPDK_NOTICELOG("Unsupported bdev event: type %d\n", type); ++ break; ++ } ++} ++ ++static void ++ssam_free_task_pool(struct spdk_ssam_blk_session *bsmsession) ++{ ++ struct spdk_ssam_session *smsession = &bsmsession->smsession; ++ struct spdk_ssam_virtqueue *vq = NULL; ++ uint16_t max_queues = smsession->max_queues; ++ uint16_t i; ++ ++ if (max_queues > SPDK_SSAM_MAX_VQUEUES) { ++ return; ++ } ++ ++ for (i = 0; i < max_queues; i++) { ++ vq = &smsession->virtqueue[i]; ++ if (vq->tasks != NULL) { ++ spdk_free(vq->tasks); ++ vq->tasks = NULL; ++ } ++ ++ if (vq->index != NULL) { ++ spdk_free(vq->index); ++ vq->index = NULL; ++ } ++ } ++} ++ ++static int ++ssam_alloc_task_pool(struct spdk_ssam_blk_session *bsmsession) ++{ ++ struct spdk_ssam_session *smsession = &bsmsession->smsession; ++ struct spdk_ssam_virtqueue *vq = NULL; ++ struct spdk_ssam_blk_task *task = NULL; ++ uint16_t max_queues = smsession->max_queues; ++ uint32_t task_cnt = smsession->queue_size; ++ uint16_t i; ++ uint32_t j; ++ ++ if ((max_queues > SPDK_SSAM_MAX_VQUEUES) || (max_queues == 0)) { ++ SPDK_ERRLOG("%s: max_queues %u invalid\n", smsession->name, max_queues); ++ return -EINVAL; ++ } ++ ++ if ((task_cnt == 0) || (task_cnt > SPDK_SSAM_MAX_VQ_SIZE)) { ++ SPDK_ERRLOG("%s: virtuque size %u invalid\n", smsession->name, task_cnt); ++ return -EINVAL; ++ } ++ ++ for (i = 0; i < max_queues; i++) { ++ vq = &smsession->virtqueue[i]; ++ vq->smsession = smsession; ++ vq->num = task_cnt; ++ vq->use_num = 0; ++ vq->index_l = 0; ++ vq->index_r = 0; ++ vq->tasks = spdk_zmalloc(sizeof(struct spdk_ssam_blk_task) * task_cnt, ++ SPDK_CACHE_LINE_SIZE, NULL, ++ SPDK_ENV_LCORE_ID_ANY, SPDK_MALLOC_DMA); ++ vq->index = spdk_zmalloc(sizeof(uint32_t) * task_cnt, ++ SPDK_CACHE_LINE_SIZE, NULL, ++ SPDK_ENV_LCORE_ID_ANY, SPDK_MALLOC_DMA); ++ if (vq->tasks == NULL || vq->index == NULL) { ++ SPDK_ERRLOG("%s: failed to allocate %"PRIu32" tasks for virtqueue %"PRIu16"\n", ++ smsession->name, task_cnt, i); ++ ssam_free_task_pool(bsmsession); ++ return -ENOMEM; ++ } ++ for (j = 0; j < task_cnt; j++) { ++ task = &((struct spdk_ssam_blk_task *)vq->tasks)[j]; ++ task->bsmsession = bsmsession; ++ task->task_idx = j; ++ vq->index[j] = j; ++ } ++ } ++ ++ return 0; ++} ++ ++static void ++ssam_blk_print_stuck_io_info(struct spdk_ssam_session *smsession) ++{ ++ struct spdk_ssam_blk_task *tasks; ++ struct spdk_ssam_blk_task *task; ++ int i, j; ++ ++ for (i = 0; i < smsession->max_queues; i++) { ++ for (j = 0; j < smsession->queue_size; j++) { ++ tasks = (struct spdk_ssam_blk_task *)smsession->virtqueue[i].tasks; ++ task = &tasks[j]; ++ if (task == NULL) { ++ continue; ++ } ++ if (task->used) { ++ SPDK_INFOLOG(ssam_blk, "%s: stuck io payload_size %u, vq_idx %u, req_idx %u\n", ++ smsession->name, task->payload_size, task->vq_idx, task->req_idx); ++ } ++ } ++ } ++} ++ ++static uint16_t ++get_req_idx(struct spdk_ssam_blk_task *task) ++{ ++ return task->io_req->req.cmd.virtio.req_idx; ++} ++ ++static void ++ssam_blk_task_init(struct spdk_ssam_blk_task *task) ++{ ++ task->used = true; ++ task->iovcnt = 0; ++ task->io_req = NULL; ++ task->payload_size = 0; ++ memset(&task->task_stat, 0, sizeof(task->task_stat)); ++ ssam_task_stat_tick(&task->task_stat.start_tsc); ++} ++ ++static void ++ssam_blk_task_finish(struct spdk_ssam_blk_task *task) ++{ ++ struct spdk_ssam_session *smsession = &task->bsmsession->smsession; ++ struct spdk_ssam_virtqueue *vq = &smsession->virtqueue[task->vq_idx]; ++ ++ if (smsession->task_cnt == 0) { ++ SPDK_ERRLOG("smsession %s: task internel error\n", smsession->name); ++ return; ++ } ++ ++ task->io_req = NULL; ++ task->payload_size = 0; ++ ++ if (task->iovs.virt.sges[0].iov_base != NULL) { ++ ssam_mempool_free(smsession->mp, task->iovs.virt.sges[0].iov_base); ++ task->iovs.virt.sges[0].iov_base = NULL; ++ } ++ ++ memset(&task->iovs, 0, sizeof(task->iovs)); ++ ++ task->iovcnt = 0; ++ smsession->task_cnt--; ++ task->used = false; ++ vq->index[vq->index_l] = task->task_idx; ++ vq->index_l = (vq->index_l + 1) & 0xFF; ++ vq->use_num--; ++} ++ ++static int ++ssam_blk_io_complete(struct spdk_ssam_dev *smdev, struct ssam_request *io_req, uint8_t status) ++{ ++ struct ssam_io_response io_resp; ++ struct ssam_virtio_res *virtio_res = (struct ssam_virtio_res*)&io_resp.data; ++ struct ssam_io_message *io_cmd = &io_req->req.cmd; ++ struct iovec io_vec; ++ uint8_t res_status = status; ++ int rc; ++ ++ if (status != VIRTIO_BLK_S_OK) { ++ SPDK_ERRLOG("ssam io complete return error tid=%u gfunc_id:%u.\n", smdev->tid, io_req->gfunc_id); ++ } ++ ++ memset(&io_resp, 0, sizeof(io_resp)); ++ io_resp.gfunc_id = io_req->gfunc_id; ++ io_resp.iocb_id = io_req->iocb_id; ++ io_resp.status = io_req->status; ++ io_resp.req = io_req; ++ io_resp.flr_seq = io_req->flr_seq; ++ ++ virtio_res->iovs = &io_vec; ++ virtio_res->iovs->iov_base = io_cmd->iovs[io_cmd->iovcnt - 1].iov_base; ++ virtio_res->iovs->iov_len = io_cmd->iovs[io_cmd->iovcnt - 1].iov_len; ++ virtio_res->iovcnt = 1; ++ virtio_res->rsp = &res_status; ++ virtio_res->rsp_len = sizeof(res_status); ++ ++ rc = ssam_io_complete(smdev->tid, &io_resp); ++ if (rc != 0) { ++ return rc; ++ } ++ ++ ssam_dev_io_dec(smdev); ++ return 0; ++} ++ ++struct ssam_task_complete_arg { ++ struct spdk_ssam_blk_task *task; ++ uint8_t status; ++}; ++ ++static void ++ssam_task_complete_cb(void *arg) ++{ ++ struct ssam_task_complete_arg *cb_arg = (struct ssam_task_complete_arg *)arg; ++ struct spdk_ssam_session *smsession = &cb_arg->task->bsmsession->smsession; ++ struct spdk_ssam_blk_task *task = cb_arg->task; ++ int rc = ssam_blk_io_complete(smsession->smdev, task->io_req, cb_arg->status); ++ if (rc != 0) { ++ struct spdk_ssam_session_io_wait_r *io_wait_r = ++ calloc(1, sizeof(struct spdk_ssam_session_io_wait_r)); ++ if (io_wait_r == NULL) { ++ SPDK_ERRLOG("calloc for io_wait_r failed\n"); ++ sleep(1); ++ raise(SIGTERM); ++ } ++ io_wait_r->cb_fn = ssam_task_complete_cb; ++ io_wait_r->cb_arg = cb_arg; ++ ssam_session_insert_io_wait_r(smsession->smdev, io_wait_r); ++ return; ++ } ++ ssam_task_stat_tick(&task->task_stat.complete_end_tsc); ++ ssam_blk_stat_statistics(task, cb_arg->status); ++ ssam_blk_task_finish(task); ++ free(cb_arg); ++ cb_arg = NULL; ++} ++ ++static void ++ssam_task_complete(struct spdk_ssam_blk_task *task, uint8_t status) ++{ ++ struct spdk_ssam_session *smsession = &task->bsmsession->smsession; ++ if (status != VIRTIO_BLK_S_OK) { ++ SPDK_ERRLOG("ssam task return error tid=%u gfunc_id:%u.\n", ++ smsession->smdev->tid, task->io_req->gfunc_id); ++ } ++ SPDK_INFOLOG(ssam_blk_data, "handled io tid=%u gfunc_id=%u rw=%u vqid=%u reqid=%u status=%u.\n", ++ smsession->smdev->tid, smsession->gfunc_id, task->io_req->req.cmd.writable, ++ task->io_req->req.cmd.virtio.vq_idx, task->io_req->req.cmd.virtio.req_idx, status); ++ ssam_task_stat_tick(&task->task_stat.complete_start_tsc); ++ int rc = ssam_blk_io_complete(smsession->smdev, task->io_req, status); ++ if (rc != 0) { ++ struct spdk_ssam_session_io_wait_r *io_wait_r = ++ calloc(1, sizeof(struct spdk_ssam_session_io_wait_r)); ++ struct ssam_task_complete_arg *cb_arg = ++ calloc(1, sizeof(struct ssam_task_complete_arg)); ++ if (io_wait_r == NULL || cb_arg == NULL) { ++ SPDK_ERRLOG("calloc for io_wait_r failed\n"); ++ sleep(1); ++ raise(SIGTERM); ++ } ++ cb_arg->status = status; ++ cb_arg->task = task; ++ io_wait_r->cb_fn = ssam_task_complete_cb; ++ io_wait_r->cb_arg = cb_arg; ++ ssam_session_insert_io_wait_r(smsession->smdev, io_wait_r); ++ return; ++ } ++ ssam_task_stat_tick(&task->task_stat.complete_end_tsc); ++ ssam_blk_stat_statistics(task, status); ++ ssam_blk_task_finish(task); ++} ++ ++struct ssam_blk_dma_data_request_arg { ++ struct spdk_ssam_dev *smdev; ++ struct spdk_ssam_blk_task *task; ++ struct ssam_dma_request dma_req; ++}; ++ ++static void ++ssam_blk_dma_data_request_cb(void *arg) ++{ ++ struct ssam_blk_dma_data_request_arg *cb_arg = (struct ssam_blk_dma_data_request_arg *)arg; ++ int ret = ssam_dma_data_request(cb_arg->smdev->tid, &cb_arg->dma_req); ++ if (ret == -ENOMEM || ret == -EIO) { ++ struct spdk_ssam_session_io_wait_r *io_wait_r = ++ calloc(1, sizeof(struct spdk_ssam_session_io_wait_r)); ++ if (io_wait_r == NULL) { ++ SPDK_ERRLOG("calloc for io_wait_r failed\n"); ++ sleep(1); ++ raise(SIGTERM); ++ } ++ io_wait_r->cb_fn = ssam_blk_dma_data_request_cb; ++ io_wait_r->cb_arg = cb_arg; ++ ssam_session_insert_io_wait_r(cb_arg->smdev, io_wait_r); ++ return; ++ } ++ if (ret < 0) { ++ SPDK_ERRLOG("%s: ssam dma data request failed:%s\n", ++ cb_arg->task->bsmsession->smsession.name, spdk_strerror(-ret)); ++ ssam_task_complete(cb_arg->task, VIRTIO_BLK_S_IOERR); ++ } ++ free(cb_arg); ++ cb_arg = NULL; ++} ++ ++static void ++ssam_res_dma_process(struct spdk_ssam_session *smsession, ++ struct spdk_ssam_blk_task *task, uint32_t type, uint8_t status) ++{ ++ struct ssam_dma_request dma_req = {0}; ++ uint16_t tid = smsession->smdev->tid; ++ int ret; ++ ++ ssam_data_request_para(&dma_req, task, type, status); ++ ssam_task_stat_tick(&task->task_stat.dma_start_tsc); ++ task->bsmsession->blk_stat.dma_count++; ++ ret = ssam_dma_data_request(tid, &dma_req); ++ if (ret == -ENOMEM || ret == -EIO) { ++ struct spdk_ssam_session_io_wait_r *io_wait_r = ++ calloc(1, sizeof(struct spdk_ssam_session_io_wait_r)); ++ struct ssam_blk_dma_data_request_arg *cb_arg = ++ calloc(1, sizeof(struct ssam_blk_dma_data_request_arg)); ++ if (io_wait_r == NULL || cb_arg == NULL) { ++ SPDK_ERRLOG("calloc for io_wait_r failed\n"); ++ sleep(1); ++ raise(SIGTERM); ++ } ++ cb_arg->smdev = smsession->smdev; ++ cb_arg->dma_req = dma_req; ++ cb_arg->task = task; ++ io_wait_r->cb_fn = ssam_blk_dma_data_request_cb; ++ io_wait_r->cb_arg = cb_arg; ++ ssam_session_insert_io_wait_r(smsession->smdev, io_wait_r); ++ return; ++ } ++ ++ if (ret < 0) { ++ SPDK_ERRLOG("%s: ssam dma data request failed:%s\n", smsession->name, spdk_strerror(-ret)); ++ ssam_task_complete(task, VIRTIO_BLK_S_IOERR); ++ } ++} ++ ++static void ++ssam_blk_request_finish(bool success, struct spdk_ssam_blk_task *task) ++{ ++ uint8_t res_status = success ? VIRTIO_BLK_S_OK : VIRTIO_BLK_S_IOERR; ++ const struct virtio_blk_outhdr *req = NULL; ++ struct spdk_ssam_session *smsession = &task->bsmsession->smsession; ++ if (res_status != VIRTIO_BLK_S_OK) { ++ SPDK_ERRLOG("request finish return error gfunc_id=%u.\n", smsession->gfunc_id); ++ } ++ ++ req = (struct virtio_blk_outhdr *)task->io_req->req.cmd.header; ++ switch (req->type) { ++ case VIRTIO_BLK_T_IN: ++ case VIRTIO_BLK_T_GET_ID: ++ ssam_res_dma_process(smsession, task, SSAM_REQUEST_DATA_STORE, res_status); ++ break; ++ ++ case VIRTIO_BLK_T_OUT: ++ case VIRTIO_BLK_T_DISCARD: ++ case VIRTIO_BLK_T_WRITE_ZEROES: ++ case VIRTIO_BLK_T_FLUSH: ++ ssam_task_complete(task, res_status); ++ break; ++ ++ default: ++ ssam_task_complete(task, VIRTIO_BLK_S_IOERR); ++ SPDK_ERRLOG("Not supported request type '%"PRIu32"'.\n", req->type); ++ break; ++ } ++} ++ ++static void ++ssam_blk_req_complete_cb(struct spdk_bdev_io *bdev_io, bool success, void *cb_arg) ++{ ++ struct spdk_ssam_blk_task *task = cb_arg; ++ ++ if (spdk_unlikely(spdk_get_shutdown_sig_received())) { ++ /* ++ * In the hot restart process, when this callback is triggered, ++ * the task and bdev_io memory may have been released. ++ * Therefore, task and bdev_io are not released in this scenario. ++ */ ++ return; ++ } ++ ++ /* Second part start of read and write */ ++ SPDK_INFOLOG(ssam_blk_data, "backend io finish tid=%u gfunc_id=%u rw=%u vqid=%u reqid=%u success=%d.\n", ++ task->bsmsession->smsession.smdev->tid, task->bsmsession->smsession.gfunc_id, ++ task->io_req->req.cmd.writable, task->io_req->req.cmd.virtio.vq_idx, task->io_req->req.cmd.virtio.req_idx, ++ success); ++ task->bsmsession->bdev_count--; ++ task->bsmsession->blk_stat.bdev_complete_count++; ++ ssam_task_stat_tick(&task->task_stat.bdev_end_tsc); ++ ++ spdk_bdev_free_io(bdev_io); ++ ssam_blk_request_finish(success, task); ++} ++ ++static int ++ssam_request_rc_process(int rc, struct spdk_ssam_blk_task *task) ++{ ++ if (rc == 0) { ++ return rc; ++ } ++ ++ if (rc == -ENOMEM) { ++ SPDK_WARNLOG("No memory, start to queue io.\n"); ++ ssam_request_queue_io(task); ++ } else { ++ SPDK_ERRLOG("IO error, gfunc_id=%u.\n", task->bsmsession->smsession.gfunc_id); ++ ssam_task_complete(task, VIRTIO_BLK_S_IOERR); ++ return -1; ++ } ++ ++ return rc; ++} ++ ++static bool ++ssam_is_req_sector_err(uint64_t sector) ++{ ++ if (sector > (UINT64_MAX / SECTOR_SIZE)) { ++ SPDK_ERRLOG("req sector out of range, need less or equal than %lu, actually %lu\n", ++ (UINT64_MAX / SECTOR_SIZE), sector); ++ return true; ++ } ++ ++ return false; ++} ++ ++static int ++ssam_virtio_read_write_process(struct spdk_ssam_blk_task *task, ++ const struct virtio_blk_outhdr *req) ++{ ++ struct spdk_ssam_blk_session *bsmsession = task->bsmsession; ++ struct ssam_io_message *io_cmd = NULL; ++ uint32_t payload_size = task->payload_size; ++ int rc; ++ ++ io_cmd = &task->io_req->req.cmd; ++ ++ if (ssam_is_req_sector_err(req->sector)) { ++ SPDK_ERRLOG("rw check sector error, gfunc_id=%u.\n", bsmsession->smsession.gfunc_id); ++ ssam_task_complete(task, VIRTIO_BLK_S_IOERR); ++ return -1; ++ } ++ ++ if (spdk_unlikely(payload_size == 0 || (payload_size & (SECTOR_SIZE - 1)) != 0)) { ++ SPDK_ERRLOG("%s - passed IO buffer is not multiple of 512 Bytes (req_idx = %"PRIu16"), " ++ "payload_size = %u, iovcnt = %u.\n", req->type ? "WRITE" : "READ", ++ get_req_idx(task), payload_size, io_cmd->iovcnt); ++ ssam_task_complete(task, VIRTIO_BLK_S_UNSUPP); ++ return -1; ++ } ++ ++ if (req->type == VIRTIO_BLK_T_IN) { ++ bsmsession->bdev_count++; ++ ssam_task_stat_tick(&task->task_stat.bdev_start_tsc); ++ rc = spdk_bdev_readv(bsmsession->bdev_desc, bsmsession->io_channel, ++ task->iovs.virt.sges, task->iovcnt, req->sector * SECTOR_SIZE, ++ payload_size, ssam_blk_req_complete_cb, task); ++ ssam_task_stat_tick(&task->task_stat.bdev_func_tsc); ++ } else if (!bsmsession->readonly) { ++ bsmsession->bdev_count++; ++ ssam_task_stat_tick(&task->task_stat.bdev_start_tsc); ++ rc = spdk_bdev_writev(bsmsession->bdev_desc, bsmsession->io_channel, ++ task->iovs.virt.sges, task->iovcnt, req->sector * SECTOR_SIZE, ++ payload_size, ssam_blk_req_complete_cb, task); ++ ssam_task_stat_tick(&task->task_stat.bdev_func_tsc); ++ } else { ++ SPDK_DEBUGLOG(ssam_blk, "Device is in read-only mode!\n"); ++ rc = -1; ++ } ++ ++ return ssam_request_rc_process(rc, task); ++} ++ ++static int ++ssam_virtio_discard_process(struct spdk_ssam_blk_task *task) ++{ ++ int rc; ++ struct spdk_ssam_blk_session *bsmsession = task->bsmsession; ++ struct virtio_blk_discard_write_zeroes *desc = task->iovs.virt.sges[0].iov_base; ++ ++ if (ssam_is_req_sector_err(desc->sector)) { ++ SPDK_ERRLOG("discard check sector error, gfunc_id=%u.\n", bsmsession->smsession.gfunc_id); ++ ssam_task_complete(task, VIRTIO_BLK_S_IOERR); ++ return -1; ++ } ++ ++ if (task->payload_size != sizeof(*desc)) { ++ SPDK_ERRLOG("Invalid discard payload size: %u\n", task->payload_size); ++ ssam_task_complete(task, VIRTIO_BLK_S_IOERR); ++ return -1; ++ } ++ ++ if (desc->flags & VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP) { ++ SPDK_ERRLOG("UNMAP flag is only used for WRITE ZEROES command\n"); ++ ssam_task_complete(task, VIRTIO_BLK_S_UNSUPP); ++ return -1; ++ } ++ bsmsession->bdev_count++; ++ rc = spdk_bdev_unmap(bsmsession->bdev_desc, bsmsession->io_channel, ++ desc->sector * SECTOR_SIZE, desc->num_sectors * SECTOR_SIZE, ++ ssam_blk_req_complete_cb, task); ++ ++ return ssam_request_rc_process(rc, task); ++} ++ ++static int ++ssam_virtio_write_zeroes_process(struct spdk_ssam_blk_task *task) ++{ ++ int rc; ++ struct spdk_ssam_blk_session *bsmsession = task->bsmsession; ++ struct virtio_blk_discard_write_zeroes *desc = task->iovs.virt.sges[0].iov_base; ++ ++ if (ssam_is_req_sector_err(desc->sector)) { ++ SPDK_ERRLOG("write zeros check sector error, gfunc_id=%u.\n", bsmsession->smsession.gfunc_id); ++ ssam_task_complete(task, VIRTIO_BLK_S_IOERR); ++ return -1; ++ } ++ ++ if (task->payload_size != sizeof(*desc)) { ++ SPDK_NOTICELOG("Invalid write zeroes payload size: %u\n", task->payload_size); ++ ssam_task_complete(task, VIRTIO_BLK_S_IOERR); ++ return -1; ++ } ++ ++ if (desc->flags & VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP) { ++ SPDK_WARNLOG("Ignore the unmap flag for WRITE ZEROES from %"PRIx64", len %"PRIx64"\n", ++ (uint64_t)desc->sector * SECTOR_SIZE, (uint64_t)desc->num_sectors * SECTOR_SIZE); ++ } ++ bsmsession->bdev_count++; ++ rc = spdk_bdev_write_zeroes(bsmsession->bdev_desc, bsmsession->io_channel, ++ desc->sector * SECTOR_SIZE, desc->num_sectors * SECTOR_SIZE, ssam_blk_req_complete_cb, task); ++ ++ return ssam_request_rc_process(rc, task); ++} ++ ++static int ++ssam_virtio_flush_process(struct spdk_ssam_blk_task *task, ++ const struct virtio_blk_outhdr *req) ++{ ++ int rc; ++ struct spdk_ssam_blk_session *bsmsession = task->bsmsession; ++ uint64_t blockcnt = spdk_bdev_get_num_blocks(bsmsession->bdev); ++ uint32_t blocklen = spdk_bdev_get_block_size(bsmsession->bdev); ++ uint64_t flush_bytes; ++ ++ if (blocklen == 0) { ++ SPDK_ERRLOG("bdev's blocklen %u error.\n", blocklen); ++ ssam_task_complete(task, VIRTIO_BLK_S_IOERR); ++ return -1; ++ } ++ if (req->sector != 0) { ++ SPDK_ERRLOG("sector must be zero for flush command\n"); ++ ssam_task_complete(task, VIRTIO_BLK_S_IOERR); ++ return -1; ++ } ++ ++ if (blockcnt > (UINT64_MAX / blocklen)) { ++ SPDK_ERRLOG("bdev's blockcnt %lu or blocklen %u out of range.\n", ++ blockcnt, blocklen); ++ ssam_task_complete(task, VIRTIO_BLK_S_IOERR); ++ return -1; ++ } ++ flush_bytes = blockcnt * blocklen; ++ bsmsession->bdev_count++; ++ rc = spdk_bdev_flush(bsmsession->bdev_desc, bsmsession->io_channel, ++ 0, flush_bytes, ssam_blk_req_complete_cb, task); ++ ++ return ssam_request_rc_process(rc, task); ++} ++ ++static int ++ssam_virtio_get_id_process(struct spdk_ssam_blk_task *task) ++{ ++ uint32_t used_length; ++ struct spdk_ssam_blk_session *bsmsession = task->bsmsession; ++ ++ if (task->iovcnt == 0 || task->payload_size == 0) { ++ SPDK_ERRLOG("check task param error, gfunc_id=%u.\n", bsmsession->smsession.gfunc_id); ++ ssam_task_complete(task, VIRTIO_BLK_S_UNSUPP); ++ return -1; ++ } ++ ++ used_length = spdk_min((size_t)VIRTIO_BLK_ID_BYTES, task->iovs.virt.sges[0].iov_len); ++ if (bsmsession->serial == NULL) { ++ spdk_strcpy_pad(task->iovs.virt.sges[0].iov_base, spdk_bdev_get_product_name(bsmsession->bdev), ++ used_length, ' '); ++ } else { ++ spdk_strcpy_pad(task->iovs.virt.sges[0].iov_base, bsmsession->serial, ++ used_length, ' '); ++ } ++ bsmsession->blk_stat.bdev_complete_count++; ++ ssam_blk_request_finish(true, task); ++ ++ return 0; ++} ++ ++static int ++ssam_io_process(struct spdk_ssam_blk_task *task, const struct virtio_blk_outhdr *req) ++{ ++ int rc; ++ SPDK_INFOLOG(ssam_blk_data, "backend io start tid=%u gfunc_id=%u reqtype=%d rw=%u vqid=%u reqid=%u offset=%llu length=%u.\n", ++ task->bsmsession->smsession.smdev->tid, task->bsmsession->smsession.gfunc_id, req->type, ++ task->io_req->req.cmd.writable, task->io_req->req.cmd.virtio.vq_idx, task->io_req->req.cmd.virtio.req_idx, ++ req->sector * SECTOR_SIZE, task->payload_size); ++ task->bsmsession->blk_stat.bdev_count++; ++ switch (req->type) { ++ case VIRTIO_BLK_T_IN: ++ case VIRTIO_BLK_T_OUT: ++ rc = ssam_virtio_read_write_process(task, req); ++ break; ++ case VIRTIO_BLK_T_DISCARD: ++ rc = ssam_virtio_discard_process(task); ++ break; ++ case VIRTIO_BLK_T_WRITE_ZEROES: ++ rc = ssam_virtio_write_zeroes_process(task); ++ break; ++ case VIRTIO_BLK_T_FLUSH: ++ rc = ssam_virtio_flush_process(task, req); ++ break; ++ case VIRTIO_BLK_T_GET_ID: ++ rc = ssam_virtio_get_id_process(task); ++ break; ++ default: ++ SPDK_ERRLOG("Not supported request type '%"PRIu32"'.\n", req->type); ++ ssam_task_complete(task, VIRTIO_BLK_S_UNSUPP); ++ return -1; ++ } ++ ++ return rc; ++} ++ ++static int ++ssam_process_blk_request(struct spdk_ssam_blk_task *task) ++{ ++ int ret; ++ struct iovec *iov = NULL; ++ const struct virtio_blk_outhdr *req = NULL; ++ struct ssam_io_message *io_cmd = NULL; ++ ++ io_cmd = &task->io_req->req.cmd; ++ /* get req header */ ++ if (spdk_unlikely(io_cmd->iovs[0].iov_len != sizeof(*req))) { ++ SPDK_ERRLOG("First descriptor size is %zu but expected %zu (req_idx = %"PRIu16").\n", ++ io_cmd->iovs[0].iov_len, sizeof(*req), get_req_idx(task)); ++ ssam_task_complete(task, VIRTIO_BLK_S_UNSUPP); ++ return -1; ++ } ++ ++ req = (struct virtio_blk_outhdr *)io_cmd->header; ++ /* get req tail */ ++ iov = &io_cmd->iovs[io_cmd->iovcnt - 1]; ++ if (spdk_unlikely(iov->iov_len != 1)) { ++ SPDK_ERRLOG("Last descriptor size is %zu but expected %d (req_idx = %"PRIu16").\n", ++ iov->iov_len, 1, get_req_idx(task)); ++ ssam_task_complete(task, VIRTIO_BLK_S_UNSUPP); ++ return -1; ++ } ++ ++ ret = ssam_io_process(task, req); ++ if (ret < 0) { ++ SPDK_ERRLOG("ssam io process failed(%d)\n", ret); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int ++ssam_get_payload_size(struct ssam_request *io_req, uint32_t *payload_size) ++{ ++ struct ssam_io_message *io_cmd = &io_req->req.cmd; ++ uint32_t payload = 0; ++ uint32_t i; ++ ++ for (i = 1; i < io_cmd->iovcnt - 1; i++) { ++ if (spdk_unlikely((UINT32_MAX - io_cmd->iovs[i].iov_len) < payload)) { ++ SPDK_ERRLOG("payload size overflow\n"); ++ return -1; ++ } ++ payload += io_cmd->iovs[i].iov_len; ++ } ++ ++ if (spdk_unlikely(payload > PAYLOAD_SIZE_MAX)) { ++ SPDK_ERRLOG("payload size larger than %u, payload_size = %u\n", ++ PAYLOAD_SIZE_MAX, payload); ++ return -1; ++ } ++ ++ *payload_size = payload; ++ ++ return 0; ++} ++ ++static int ++ssam_task_iovs_memory_get(struct spdk_ssam_blk_task *task) ++{ ++ struct ssam_mempool *mp = task->bsmsession->smsession.mp; ++ void *buffer = NULL; ++ uint64_t phys_addr = 0; ++ ++ if (task->payload_size == 0) { ++ /* request type of VIRTIO_BLK_T_FLUSH does not have payload */ ++ task->iovs.virt.sges[0].iov_base = NULL; ++ return 0; ++ } ++ ++ task->iovs.virt.sges[0].iov_base = NULL; ++ task->iovs.phys.sges[0].iov_base = NULL; ++ task->iovs.virt.sges[0].iov_len = task->payload_size; ++ task->iovs.phys.sges[0].iov_len = task->payload_size; ++ task->iovcnt = 1; ++ ++ buffer = ssam_mempool_alloc(mp, task->payload_size, &phys_addr); ++ if (spdk_unlikely(buffer == NULL)) { ++ return -ENOMEM; ++ } ++ ++ /* ssam request max IO size is PAYLOAD_SIZE_MAX, only use one iov to save data */ ++ task->iovs.virt.sges[0].iov_base = buffer; ++ task->iovs.phys.sges[0].iov_base = (void *)phys_addr; ++ ++ return 0; ++} ++ ++static void ++ssam_data_request_para(struct ssam_dma_request *dma_req, struct spdk_ssam_blk_task *task, ++ uint32_t type, uint8_t status) ++{ ++ struct ssam_io_message *io_cmd = NULL; ++ struct spdk_ssam_dma_cb dma_cb = { ++ .status = status, ++ .req_dir = type, ++ .gfunc_id = task->io_req->gfunc_id, ++ .vq_idx = task->vq_idx, ++ .task_idx = task->task_idx ++ }; ++ ++ io_cmd = &task->io_req->req.cmd; ++ dma_req->cb = (void *)*(uint64_t *)&dma_cb; ++ dma_req->gfunc_id = task->io_req->gfunc_id; ++ dma_req->flr_seq = task->io_req->flr_seq; ++ dma_req->direction = type; ++ dma_req->data_len = task->payload_size; ++ if (type == SSAM_REQUEST_DATA_STORE) { ++ dma_req->src = task->iovs.phys.sges; ++ dma_req->src_num = task->iovcnt; ++ dma_req->dst = &io_cmd->iovs[1]; ++ /* dma data iovs does not contain header and tail */ ++ dma_req->dst_num = io_cmd->iovcnt - IOV_HEADER_TAIL_NUM; ++ } else if (type == SSAM_REQUEST_DATA_LOAD) { ++ dma_req->src = &io_cmd->iovs[1]; ++ /* dma data iovs does not contain header and tail */ ++ dma_req->src_num = io_cmd->iovcnt - IOV_HEADER_TAIL_NUM; ++ dma_req->dst = task->iovs.phys.sges; ++ dma_req->dst_num = task->iovcnt; ++ } ++} ++ ++static void ++ssam_request_dma_process(struct spdk_ssam_session *smsession, struct spdk_ssam_blk_task *task) ++{ ++ struct virtio_blk_outhdr *req = NULL; ++ int ret; ++ ++ req = (struct virtio_blk_outhdr *)task->io_req->req.cmd.header; ++ SPDK_INFOLOG(ssam_blk_data, "request dma request io tid=%u gfunc_id=%u reqtype=%d rw=%u vqid=%u reqid=%u.\n", ++ smsession->smdev->tid, smsession->gfunc_id, req->type, task->io_req->req.cmd.writable, ++ task->io_req->req.cmd.virtio.vq_idx, task->io_req->req.cmd.virtio.req_idx); ++ ++ switch (req->type) { ++ case VIRTIO_BLK_T_IN: ++ case VIRTIO_BLK_T_GET_ID: ++ case VIRTIO_BLK_T_FLUSH: ++ ret = ssam_process_blk_request(task); ++ if (ret < 0) { ++ SPDK_ERRLOG("====== Task: req_idx %u failed ======\n", task->req_idx); ++ } ++ break; ++ ++ case VIRTIO_BLK_T_OUT: ++ case VIRTIO_BLK_T_DISCARD: ++ case VIRTIO_BLK_T_WRITE_ZEROES: ++ /* dma request: Host -> ipu */ ++ ssam_res_dma_process(smsession, task, SSAM_REQUEST_DATA_LOAD, 0); ++ break; ++ ++ default: ++ ssam_task_complete(task, VIRTIO_BLK_S_IOERR); ++ SPDK_ERRLOG("Not supported request type '%"PRIu32"'.\n", req->type); ++ } ++} ++ ++struct ssam_blk_io_complete_arg { ++ struct spdk_ssam_dev *smdev; ++ struct ssam_request *io_req; ++}; ++ ++static void ++ssam_blk_io_complete_cb(void *arg) ++{ ++ struct ssam_blk_io_complete_arg *cb_arg = (struct ssam_blk_io_complete_arg *)arg; ++ int rc = ssam_blk_io_complete(cb_arg->smdev, cb_arg->io_req, VIRTIO_BLK_S_IOERR); ++ if (rc != 0) { ++ struct spdk_ssam_session_io_wait_r *io_wait_r = ++ calloc(1, sizeof(struct spdk_ssam_session_io_wait_r)); ++ if (io_wait_r == NULL) { ++ SPDK_ERRLOG("calloc for io_wait_r failed\n"); ++ sleep(1); ++ raise(SIGTERM); ++ } ++ io_wait_r->cb_fn = ssam_blk_io_complete_cb; ++ io_wait_r->cb_arg = cb_arg; ++ ssam_session_insert_io_wait_r(cb_arg->smdev, io_wait_r); ++ return; ++ } ++ free(cb_arg); ++ cb_arg = NULL; ++} ++ ++static void ++ssam_process_blk_task(struct spdk_ssam_session *smsession, struct ssam_request *io_req, ++ uint16_t vq_idx, uint16_t req_idx, uint32_t payload_size) ++{ ++ int rc; ++ struct spdk_ssam_blk_task *task = NULL; ++ struct spdk_ssam_virtqueue *vq = &smsession->virtqueue[vq_idx]; ++ ++ if (spdk_unlikely(vq->use_num >= vq->num)) { ++ SPDK_ERRLOG("Session:%s vq(%hu) task_cnt(%u) limit(%u).\n", smsession->name, vq_idx, vq->use_num, vq->num); ++ rc = ssam_blk_io_complete(smsession->smdev, io_req, VIRTIO_BLK_S_IOERR); ++ if (rc != 0) { ++ struct spdk_ssam_session_io_wait_r *io_wait_r = ++ calloc(1, sizeof(struct spdk_ssam_session_io_wait_r)); ++ struct ssam_blk_io_complete_arg *cb_arg = ++ calloc(1, sizeof(struct ssam_blk_io_complete_arg)); ++ if (io_wait_r == NULL || cb_arg == NULL) { ++ SPDK_ERRLOG("calloc for io_wait_r failed\n"); ++ sleep(1); ++ raise(SIGTERM); ++ } ++ cb_arg->smdev = smsession->smdev; ++ cb_arg->io_req = io_req; ++ io_wait_r->cb_fn = ssam_blk_io_complete_cb; ++ io_wait_r->cb_arg = cb_arg; ++ ssam_session_insert_io_wait_r(smsession->smdev, io_wait_r); ++ } ++ return; ++ } ++ ++ uint32_t index = vq->index[vq->index_r]; ++ task = &((struct spdk_ssam_blk_task *)vq->tasks)[index]; ++ if (spdk_unlikely(task->used)) { ++ SPDK_ERRLOG("%s: vq(%u) task with idx %u is already pending.\n", smsession->name, vq_idx, index); ++ rc = ssam_blk_io_complete(smsession->smdev, io_req, VIRTIO_BLK_S_IOERR); ++ if (rc != 0) { ++ struct spdk_ssam_session_io_wait_r *io_wait_r = ++ calloc(1, sizeof(struct spdk_ssam_session_io_wait_r)); ++ struct ssam_blk_io_complete_arg *cb_arg = ++ calloc(1, sizeof(struct ssam_blk_io_complete_arg)); ++ if (io_wait_r == NULL || cb_arg == NULL) { ++ SPDK_ERRLOG("calloc for io_wait_r failed\n"); ++ sleep(1); ++ raise(SIGTERM); ++ } ++ cb_arg->smdev = smsession->smdev; ++ cb_arg->io_req = io_req; ++ io_wait_r->cb_fn = ssam_blk_io_complete_cb; ++ io_wait_r->cb_arg = cb_arg; ++ ssam_session_insert_io_wait_r(smsession->smdev, io_wait_r); ++ } ++ return; ++ } ++ ++ smsession->task_cnt++; ++ vq->index_r = (vq->index_r + 1) & 0xFF; ++ vq->use_num++; ++ ++ ssam_blk_task_init(task); ++ task->io_req = io_req; ++ task->vq_idx = vq_idx; ++ task->req_idx = req_idx; ++ task->payload_size = payload_size; ++ task->session_io_wait.cb_fn = ssam_session_io_resubmit; ++ task->session_io_wait.cb_arg = task; ++ ++ rc = ssam_task_iovs_memory_get(task); ++ if (rc != 0) { ++ ssam_session_insert_io_wait(smsession, &task->session_io_wait); ++ return; ++ } ++ ++ ssam_request_dma_process(smsession, task); ++ return; ++} ++ ++static void ++ssam_process_vq(struct spdk_ssam_session *smsession, struct ssam_request *io_req) ++{ ++ struct ssam_io_message *io_cmd = &io_req->req.cmd; ++ uint16_t vq_idx = io_cmd->virtio.vq_idx; ++ uint16_t req_idx = io_cmd->virtio.req_idx; ++ uint32_t payload_size = 0; ++ int rc; ++ ++ if (vq_idx >= smsession->max_queues) { ++ SPDK_ERRLOG("vq_idx out of range, need less than %u, actually %u\n", ++ smsession->max_queues, vq_idx); ++ goto err; ++ } ++ ++ if (io_req->status != SSAM_IO_STATUS_OK) { ++ SPDK_WARNLOG("%s: ssam request status invalid, but still process, status=%d\n", ++ smsession->name, io_req->status); ++ goto err; ++ } ++ ++ rc = ssam_get_payload_size(io_req, &payload_size); ++ if (rc != 0) { ++ goto err; ++ } ++ ++ ssam_process_blk_task(smsession, io_req, vq_idx, req_idx, payload_size); ++ return; ++ ++err: ++ rc = ssam_blk_io_complete(smsession->smdev, io_req, VIRTIO_BLK_S_IOERR); ++ if (rc != 0) { ++ struct spdk_ssam_session_io_wait_r *io_wait_r = ++ calloc(1, sizeof(struct spdk_ssam_session_io_wait_r)); ++ struct ssam_blk_io_complete_arg *cb_arg = ++ calloc(1, sizeof(struct ssam_blk_io_complete_arg)); ++ if (io_wait_r == NULL || cb_arg == NULL) { ++ SPDK_ERRLOG("calloc for io_wait_r failed\n"); ++ sleep(1); ++ raise(SIGTERM); ++ } ++ cb_arg->smdev = smsession->smdev; ++ cb_arg->io_req = io_req; ++ io_wait_r->cb_fn = ssam_blk_io_complete_cb; ++ io_wait_r->cb_arg = cb_arg; ++ ssam_session_insert_io_wait_r(smsession->smdev, io_wait_r); ++ } ++ return; ++} ++ ++static void ++ssam_no_bdev_put_io_channel(struct spdk_ssam_session *smsession) ++{ ++ struct spdk_ssam_blk_session *bsmsession = ssam_to_blk_session(smsession); ++ ++ if (smsession->task_cnt == 0 && (bsmsession->io_channel != NULL)) { ++ spdk_put_io_channel(bsmsession->io_channel); ++ bsmsession->io_channel = NULL; ++ } ++} ++ ++struct ssam_no_bdev_process_vq_arg { ++ struct spdk_ssam_session *smsession; ++ struct ssam_request *io_req; ++}; ++ ++static void ++ssam_no_bdev_process_vq_cb(void *arg) ++{ ++ struct ssam_no_bdev_process_vq_arg *cb_arg = (struct ssam_no_bdev_process_vq_arg *)arg; ++ int rc = ssam_blk_io_complete(cb_arg->smsession->smdev, cb_arg->io_req, VIRTIO_BLK_S_IOERR); ++ if (rc != 0) { ++ struct spdk_ssam_session_io_wait_r *io_wait_r = ++ calloc(1, sizeof(struct spdk_ssam_session_io_wait_r)); ++ if (io_wait_r == NULL) { ++ SPDK_ERRLOG("calloc for io_wait_r failed\n"); ++ sleep(1); ++ raise(SIGTERM); ++ } ++ io_wait_r->cb_fn = ssam_no_bdev_process_vq_cb; ++ io_wait_r->cb_arg = cb_arg; ++ ssam_session_insert_io_wait_r(cb_arg->smsession->smdev, io_wait_r); ++ return; ++ } ++ ssam_no_bdev_put_io_channel(cb_arg->smsession); ++ free(cb_arg); ++ cb_arg = NULL; ++} ++ ++static void ++ssam_no_bdev_process_vq(struct spdk_ssam_session *smsession, struct ssam_request *io_req) ++{ ++ SPDK_ERRLOG("gfunc_id %u No bdev, aborting request, return EIO\n", io_req->gfunc_id); ++ int rc = ssam_blk_io_complete(smsession->smdev, io_req, VIRTIO_BLK_S_IOERR); ++ if (rc != 0) { ++ struct spdk_ssam_session_io_wait_r *io_wait_r = ++ calloc(1, sizeof(struct spdk_ssam_session_io_wait_r)); ++ struct ssam_no_bdev_process_vq_arg *cb_arg = ++ calloc(1, sizeof(struct ssam_no_bdev_process_vq_arg)); ++ if (io_wait_r == NULL || cb_arg == NULL) { ++ SPDK_ERRLOG("calloc for io_wait_r failed\n"); ++ sleep(1); ++ raise(SIGTERM); ++ } ++ cb_arg->smsession = smsession; ++ cb_arg->io_req = io_req; ++ io_wait_r->cb_fn = ssam_no_bdev_process_vq_cb; ++ io_wait_r->cb_arg = cb_arg; ++ ssam_session_insert_io_wait_r(smsession->smdev, io_wait_r); ++ return; ++ } ++ SPDK_WARNLOG("Aborting request because no this controller\n"); ++ ++ ssam_no_bdev_put_io_channel(smsession); ++} ++ ++static void ++ssam_blk_response_worker(struct spdk_ssam_session *smsession, void *arg) ++{ ++ struct ssam_dma_rsp *dma_rsp = (struct ssam_dma_rsp*)arg; ++ struct spdk_ssam_dma_cb *dma_cb = (struct spdk_ssam_dma_cb *)&dma_rsp->cb; ++ struct spdk_ssam_blk_task *task = NULL; ++ uint16_t vq_idx = dma_cb->vq_idx; ++ uint16_t task_idx = dma_cb->task_idx; ++ uint8_t req_dir = dma_cb->req_dir; ++ ++ if (vq_idx >= smsession->max_queues) { ++ smsession->smdev->discard_io_num++; ++ SPDK_ERRLOG("vq_idx out of range, need less than %u, actually %u\n", ++ smsession->max_queues, vq_idx); ++ return; ++ } ++ ++ task = &((struct spdk_ssam_blk_task *)smsession->virtqueue[vq_idx].tasks)[task_idx]; ++ if (dma_rsp->status != 0) { ++ ssam_task_complete(task, VIRTIO_BLK_S_IOERR); ++ SPDK_ERRLOG("dma data process failed!\n"); ++ return; ++ } ++ if (dma_rsp->last_flag == 0) { ++ ssam_task_complete(task, VIRTIO_BLK_S_IOERR); ++ SPDK_ERRLOG("last_flag should not equal 0!\n"); ++ return; ++ } ++ ssam_task_stat_tick(&task->task_stat.dma_end_tsc); ++ task->bsmsession->blk_stat.dma_complete_count++; ++ if (req_dir == SSAM_REQUEST_DATA_LOAD) { ++ /* Write data ready, start a request to backend */ ++ ssam_process_blk_request(task); ++ } else if (req_dir == SSAM_REQUEST_DATA_STORE) { ++ /* Data have been read by user, complete the task */ ++ ssam_task_complete(task, dma_cb->status); ++ } ++} ++ ++static int ++ssam_blk_check_io_req(struct spdk_ssam_dev *smdev, struct ssam_request *io_req) ++{ ++ struct ssam_io_message *io_cmd = NULL; ++ uint16_t vq_idx; ++ uint16_t req_idx; ++ const struct virtio_blk_outhdr *req = NULL; ++ ++ if (io_req == NULL) { ++ SPDK_ERRLOG("%s: received a NULL IO message\n", smdev->name); ++ return -1; ++ } ++ ++ io_cmd = &io_req->req.cmd; ++ vq_idx = io_cmd->virtio.vq_idx; ++ req_idx = io_cmd->virtio.req_idx; ++ req = (struct virtio_blk_outhdr *)io_cmd->header; ++ ++ if (io_cmd->iovs == NULL) { ++ SPDK_ERRLOG("%s: received an empty IO, vq_idx:%u, req_idx:%u\n", ++ smdev->name, vq_idx, req_idx); ++ return -1; ++ } ++ ++ if (io_cmd->iovcnt < IOV_HEADER_TAIL_NUM) { ++ SPDK_ERRLOG("%s: iovcnt %u less than %d but expected not less than %d\n", ++ smdev->name, io_cmd->iovcnt, IOV_HEADER_TAIL_NUM, IOV_HEADER_TAIL_NUM); ++ return -1; ++ } ++ ++ if ((io_cmd->iovcnt == IOV_HEADER_TAIL_NUM) && (req->type != VIRTIO_BLK_T_FLUSH)) { ++ SPDK_ERRLOG("%s: received an IO not contain valid data, iovcnt:%u, vq_idx:%u, " ++ "req_idx:%u, req_type:%u, req_ioprio:%u, req_sector:%llu\n", ++ smdev->name, io_cmd->iovcnt, vq_idx, req_idx, req->type, req->ioprio, req->sector); ++ return -1; ++ } ++ ++ if (io_cmd->iovcnt > (SPDK_SSAM_IOVS_MAX + IOV_HEADER_TAIL_NUM)) { ++ SPDK_ERRLOG("%s: received too much IO, iovcnt:%u, vq_idx:%u, req_idx:%u\n", ++ smdev->name, io_cmd->iovcnt, vq_idx, req_idx); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++static void ++ssam_blk_request_worker(struct spdk_ssam_session *smsession, void *arg) ++{ ++ struct spdk_ssam_dev *smdev = smsession->smdev; ++ struct spdk_ssam_blk_session *bsmsession = ssam_to_blk_session(smsession); ++ struct ssam_request *io_req = (struct ssam_request*)arg; ++ int ret; ++ ++ smdev->io_num++; ++ bsmsession->blk_stat.start_count++; ++ ++ ret = ssam_blk_check_io_req(smdev, io_req); ++ if (ret < 0) { ++ smdev->discard_io_num++; ++ return; ++ } ++ ++ if (bsmsession->no_bdev) { ++ ssam_no_bdev_process_vq(smsession, io_req); ++ } else { ++ ssam_process_vq(smsession, io_req); ++ } ++} ++ ++static void ++ssam_blk_no_data_request_worker(struct spdk_ssam_session *smsession) ++{ ++ struct spdk_ssam_blk_session *bsmsession = NULL; ++ ++ bsmsession = ssam_to_blk_session(smsession); ++ if (bsmsession->no_bdev) { ++ ssam_no_bdev_put_io_channel(smsession); ++ } ++} ++ ++static void ++ssam_blk_destroy_bdev_device(struct spdk_ssam_session *smsession, void *args) ++{ ++ struct spdk_ssam_blk_session *bsmsession = ssam_to_blk_session(smsession); ++ spdk_bdev_close(bsmsession->bdev_desc); ++ ++ // free taskpool ++ ssam_free_task_pool(bsmsession); ++ ++ // free ++ free(bsmsession); ++} ++ ++static void ++ssam_request_resubmit(void *arg) ++{ ++ struct spdk_ssam_blk_task *task = (struct spdk_ssam_blk_task *)arg; ++ int rc; ++ ++ rc = ssam_process_blk_request(task); ++ if (rc == 0) { ++ SPDK_DEBUGLOG(ssam_blk_data, "====== Task: req_idx = %"PRIu16" resubmitted ======\n", ++ get_req_idx(task)); ++ } else { ++ SPDK_WARNLOG("====== Task: req_idx = %"PRIu16" failed ======\n", get_req_idx(task)); ++ } ++} ++ ++static inline void ++ssam_request_queue_io(struct spdk_ssam_blk_task *task) ++{ ++ int rc; ++ struct spdk_ssam_blk_session *bsmsession = task->bsmsession; ++ ++ task->bdev_io_wait.bdev = bsmsession->bdev; ++ task->bdev_io_wait.cb_fn = ssam_request_resubmit; ++ task->bdev_io_wait.cb_arg = task; ++ ++ rc = spdk_bdev_queue_io_wait(bsmsession->bdev, bsmsession->io_channel, &task->bdev_io_wait); ++ if (rc != 0) { ++ SPDK_ERRLOG("%s: failed to queue I/O, rc=%d\n", bsmsession->smsession.name, rc); ++ ssam_task_complete(task, VIRTIO_BLK_S_IOERR); ++ } ++} ++ ++static void ++ssam_session_io_resubmit(void *arg) ++{ ++ struct spdk_ssam_blk_task *task = (struct spdk_ssam_blk_task *)arg; ++ struct spdk_ssam_session *smsession = &task->bsmsession->smsession; ++ int rc; ++ ++ rc = ssam_task_iovs_memory_get(task); ++ if (rc != 0) { ++ ssam_session_insert_io_wait(smsession, &task->session_io_wait); ++ return; ++ } ++ ssam_request_dma_process(smsession, task); ++} ++ ++static void ++ssam_blk_start_post_cb(struct spdk_ssam_session *smsession, void **arg) ++{ ++ struct spdk_ssam_blk_session *bsmsession = ssam_to_blk_session(smsession); ++ int rc; ++ ++ rc = ssam_virtio_blk_resize(smsession->gfunc_id, bsmsession->bdev->blockcnt); ++ if (rc != 0) { ++ SPDK_WARNLOG("%s: virtio blk resize failed.\n", smsession->name); ++ } ++ ++ rc = ssam_mount_normal(smsession, 0); ++ if (rc != SSAM_MOUNT_OK) { ++ SPDK_WARNLOG("%s: mount ssam volume failed\n", smsession->name); ++ } ++ ++ // Smdev poller is not created here, but is created in the initialization process. ++ SPDK_NOTICELOG("BLK controller %s created with bdev %s, queues %u\n", ++ smsession->name, spdk_bdev_get_name(bsmsession->bdev), smsession->max_queues); ++} ++ ++static int ++ssam_blk_start_cb(struct spdk_ssam_session *smsession, void **ctx) ++{ ++ struct spdk_ssam_blk_session *bsmsession = ssam_to_blk_session(smsession); ++ ++ if (bsmsession->bdev == NULL) { ++ SPDK_ERRLOG("%s: session not have a bdev.\n", smsession->name); ++ return -ENODEV; ++ } ++ ++ bsmsession->io_channel = spdk_bdev_get_io_channel(bsmsession->bdev_desc); ++ if (bsmsession->io_channel == NULL) { ++ ssam_free_task_pool(bsmsession); ++ SPDK_ERRLOG("%s: I/O channel allocation failed\n", smsession->name); ++ return -ENODEV; ++ } ++ ++ ssam_session_start_done(smsession, 0); ++ ++ ssam_send_event_async_done(ctx); ++ ++ return 0; ++} ++ ++static int ++ssam_blk_start(struct spdk_ssam_session *smsession) ++{ ++ struct spdk_ssam_blk_session *bsmsession = ssam_to_blk_session(smsession); ++ struct spdk_ssam_send_event_flag send_event_flag = { ++ .need_async = true, ++ .need_rsp = true, ++ }; ++ int rc = ssam_alloc_task_pool(bsmsession); ++ if (rc != 0) { ++ SPDK_ERRLOG("%s: failed to alloc task pool.\n", smsession->name); ++ return rc; ++ } ++ return ssam_send_event_to_session(smsession, ssam_blk_start_cb, ssam_blk_start_post_cb, send_event_flag, NULL); ++} ++ ++static void ++ssam_blk_destroy_session(struct ssam_blk_session_ctx *ctx) ++{ ++ struct spdk_ssam_blk_session *bsmsession = ctx->bsmsession; ++ struct spdk_ssam_session *smsession = &bsmsession->smsession; ++ ++ if (smsession->task_cnt > 0) { ++ return; ++ } ++ ++ /* If in ssam subsystem finish process, session registered flag will ++ * be set to false first, bdev will be removed in ssam_bdev_remove_cb() ++ * call back process, waiting for the call back process finish first. ++ */ ++ if ((smsession->registered == false) && (bsmsession->bdev != NULL)) { ++ return; ++ } ++ ++ SPDK_NOTICELOG("%s: removing on lcore %d\n", ++ smsession->name, spdk_env_get_current_core()); ++ ++ ssam_session_destroy(smsession); ++ ++ if (bsmsession->io_channel != NULL) { ++ spdk_put_io_channel(bsmsession->io_channel); ++ bsmsession->io_channel = NULL; ++ } ++ ssam_free_task_pool(bsmsession); ++ ++ if (bsmsession->serial != NULL) { ++ free(bsmsession->serial); ++ } ++ spdk_poller_unregister(&bsmsession->stop_poller); ++ ++ ssam_session_stop_done(smsession, 0, ctx->user_ctx); ++ free(ctx); ++ ++ return; ++} ++ ++static int ++ssam_destroy_session_poller_cb(void *arg) ++{ ++ struct ssam_blk_session_ctx *ctx = arg; ++ ++ if (spdk_ssam_trylock() != 0) { ++ return SPDK_POLLER_BUSY; ++ } ++ ++ ssam_blk_destroy_session(ctx); ++ ++ spdk_ssam_unlock(); ++ ++ return SPDK_POLLER_BUSY; ++} ++ ++static int ++ssam_blk_stop_cb(struct spdk_ssam_session *smsession, void **ctx) ++{ ++ struct spdk_ssam_blk_session *bsmsession = ssam_to_blk_session(smsession); ++ // smsession already removed ++ if (!smsession->started) { ++ return 0; ++ } else { ++ smsession->started = false; ++ } ++ ++ struct ssam_blk_session_ctx *_ctx = ++ (struct ssam_blk_session_ctx *)calloc(1, sizeof(struct ssam_blk_session_ctx)); ++ ++ if (_ctx == NULL) { ++ SPDK_ERRLOG("%s: calloc blk session ctx error.\n", smsession->name); ++ return -ENOMEM; ++ } ++ ++ _ctx->bsmsession = bsmsession; ++ _ctx->user_ctx = ctx; ++ ++ bsmsession->stop_poller = SPDK_POLLER_REGISTER(ssam_destroy_session_poller_cb, ++ _ctx, SESSION_STOP_POLLER_PERIOD); ++ if (bsmsession->stop_poller == NULL) { ++ SPDK_WARNLOG("%s: ssam_destroy_session_poller_cb start failed.\n", smsession->name); ++ ssam_session_stop_done(smsession, -EBUSY, ctx); ++ free(_ctx); ++ return -EBUSY; ++ } ++ ++ return 0; ++} ++ ++static int ++ssam_blk_stop(struct spdk_ssam_session *smsession) ++{ ++ struct spdk_ssam_send_event_flag send_event_flag = { ++ .need_async = true, ++ .need_rsp = true, ++ }; ++ return ssam_send_event_to_session(smsession, ssam_blk_stop_cb, ssam_blk_stop_cpl_cb, send_event_flag, NULL); ++} ++ ++static int ++ssam_blk_remove_session(struct spdk_ssam_session *smsession) ++{ ++ SPDK_NOTICELOG("session gfunc_id=%u removing\n", smsession->gfunc_id); ++ int ret = ssam_blk_stop(smsession); ++ if ((ret != 0) && (smsession->registered == true)) { ++ (void)ssam_remount_normal(smsession, 0); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++const char * ++spdk_ssam_get_bdev_name_by_gfunc_id(uint16_t gfunc_id) ++{ ++ struct spdk_ssam_session *smsession; ++ struct spdk_ssam_blk_session *bsmsession = NULL; ++ ++ smsession = spdk_ssam_session_find(gfunc_id); ++ if (smsession == NULL) { ++ return NULL; ++ } ++ bsmsession = ssam_to_blk_session(smsession); ++ ++ return spdk_bdev_get_name(bsmsession->bdev); ++} ++ ++struct spdk_bdev * ++spdk_ssam_get_session_bdev(struct spdk_ssam_session *smsession) ++{ ++ struct spdk_ssam_blk_session *bsmsession = ssam_to_blk_session(smsession); ++ ++ return bsmsession->bdev; ++} ++ ++int ++spdk_ssam_blk_construct(struct spdk_ssam_session_reg_info *info, const char *dev_name, ++ bool readonly, char *serial) ++{ ++ struct spdk_ssam_session *smsession = NULL; ++ struct spdk_ssam_blk_session *bsmsession = NULL; ++ struct spdk_bdev *bdev = NULL; ++ uint32_t session_ctx_size = sizeof(struct spdk_ssam_blk_session) - ++ sizeof(struct spdk_ssam_session); ++ uint16_t tid; ++ int ret = 0; ++ int rc; ++ ++ spdk_ssam_lock(); ++ ++ tid = spdk_ssam_get_tid(); ++ if (tid == SPDK_INVALID_TID) { ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ info->tid = tid; ++ info->backend = &g_ssam_blk_session_backend; ++ info->session_ctx_size = session_ctx_size; ++ strncpy(info->type_name, SPDK_SESSION_TYPE_BLK, SPDK_SESSION_TYPE_MAX_LEN); ++ ret = spdk_ssam_session_register(info, &smsession); ++ if (ret != 0) { ++ goto out; ++ } ++ ++ bsmsession = ssam_to_blk_session(smsession); ++ ++ ret = spdk_bdev_open_ext(dev_name, true, ssam_bdev_event_cb, smsession, ++ &bsmsession->bdev_desc); ++ if (ret != 0) { ++ SPDK_ERRLOG("function id %d: could not open bdev, error:%s\n", info->gfunc_id, spdk_strerror(-ret)); ++ goto out; ++ } ++ bdev = spdk_bdev_desc_get_bdev(bsmsession->bdev_desc); ++ bsmsession->bdev = bdev; ++ bsmsession->readonly = readonly; ++ ++ if (serial == NULL) { ++ SPDK_INFOLOG(ssam_blk, "function id %d: not set volume id.\n", info->gfunc_id); ++ } else { ++ bsmsession->serial = calloc(SERIAL_STRING_LEN, sizeof(char)); ++ if (!bsmsession->serial) { ++ SPDK_ERRLOG("no memory for alloc.\n"); ++ goto out; ++ } ++ (void)snprintf(bsmsession->serial, SERIAL_STRING_LEN, "%s", serial); ++ } ++ ++ ret = ssam_blk_start(smsession); ++ if (ret != 0) { ++ SPDK_ERRLOG("%s: start failed\n", smsession->name); ++ goto out; ++ } ++ ++ SPDK_INFOLOG(ssam_blk, "function id %d: using bdev '%s'\n", info->gfunc_id, dev_name); ++out: ++ if ((ret != 0) && (smsession != NULL) && (smsession->smdev != NULL)) { ++ ssam_session_unreg_response_cb(smsession); ++ rc = spdk_ssam_session_unregister(smsession); ++ if (rc != 0) { ++ SPDK_ERRLOG("function id %d: blk construct failed and session remove failed, ret=%d\n", ++ info->gfunc_id, ret); ++ } ++ } ++ spdk_ssam_unlock(); ++ return ret; ++} ++ ++SPDK_LOG_REGISTER_COMPONENT(ssam_blk) ++SPDK_LOG_REGISTER_COMPONENT(ssam_blk_data) +diff --git a/lib/ssam/ssam_config.c b/lib/ssam/ssam_config.c +new file mode 100644 +index 0000000..2ae01a6 +--- /dev/null ++++ b/lib/ssam/ssam_config.c +@@ -0,0 +1,421 @@ ++/*- ++ * BSD LICENSE ++ * ++ * Copyright (c) Huawei Technologies Co., Ltd. 2022-2022. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * ++ * * Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * * Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in ++ * the documentation and/or other materials provided with the ++ * distribution. ++ * * Neither the name of Intel Corporation nor the names of its ++ * contributors may be used to endorse or promote products derived ++ * from this software without specific prior written permission. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ++ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ++ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ++ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ++ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ++ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ++ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ++ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ++ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ++ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#include ++#include ++#include ++ ++#include "spdk/string.h" ++#include "spdk/file.h" ++#include "ssam_internal.h" ++ ++#define SSAM_DEFAULT_DMA_QUEUE_NUM 2 ++#define SSAM_DEFAULT_MEMPOOL_SIZE 1022 ++#define SSAM_DEFAULT_MEMPOOL_EXTRA_SIZE 0 ++#define SSAM_MAX_CORE_NUM 15 ++#define SSAM_MAX_CORE_NUM_WITH_LARGE_IO 10 ++#define SSAM_MAX_DMA_QUEUE_NUM 4 ++#define SPDK_SSAM_VIRTIO_BLK_DEFAULT_FEATURE 0x7f11001046 ++#define SPDK_SSAM_VIRTIO_SCSI_DEFAULT_FEATURE 0x7f11000007 ++ ++struct ssam_user_config { ++ uint32_t mempool_size; ++ uint32_t queues; ++ uint32_t extra_size; ++ uint32_t dma_queue_num; ++}; ++ ++struct ssam_config { ++ struct ssam_user_config user_config; ++ struct ssam_hostep_info ep_info; ++ uint32_t core_num; ++ bool shm_created; ++}; ++ ++static struct ssam_config g_ssam_config; ++ ++static int ++ssam_heap_malloc(const char *type, size_t size, int socket_arg, ++ unsigned int flags, size_t align, size_t bound, bool contig, struct ssam_melem *mem) ++{ ++ void *addr = NULL; ++ unsigned long long pg_size; ++ int socket_id; ++ int rc; ++ ++ addr = rte_malloc_socket(type, size, align, socket_arg); ++ if (addr == NULL) { ++ return -ENOMEM; ++ } ++ ++ rc = ssam_malloc_elem_from_addr(addr, &pg_size, &socket_id); ++ if (rc != 0) { ++ ssam_free_ex(addr); ++ return -ENOMEM; ++ } ++ ++ mem->addr = addr; ++ mem->iova = rte_malloc_virt2iova(addr); ++ mem->page_sz = pg_size; ++ mem->socket_id = socket_id; ++ return 0; ++} ++ ++static int ++ssam_heap_free(void *addr) ++{ ++ return ssam_free_ex(addr); ++} ++ ++static void ++ssam_get_ssam_lib_init_config(struct ssam_lib_args *cfg) ++{ ++ uint32_t core_num = g_ssam_config.core_num; ++ ++ cfg->role = 1; ++ cfg->dma_queue_num = g_ssam_config.user_config.dma_queue_num; ++ cfg->ssam_heap_malloc = ssam_heap_malloc; ++ cfg->ssam_heap_free = ssam_heap_free; ++ ++ /* The number of tid is 1 greater than the number of cores. */ ++ cfg->core_num = core_num; ++} ++ ++void ssam_set_shm_created(bool shm_created) ++{ ++ g_ssam_config.shm_created = shm_created; ++} ++ ++bool ssam_get_shm_created(void) ++{ ++ return g_ssam_config.shm_created; ++} ++ ++int ++ssam_set_core_num(uint32_t core_num) ++{ ++ if (core_num > SSAM_MAX_CORE_NUM) { ++ SPDK_ERRLOG("Invalid coremask, total cores need less or equal than %d, " ++ "actually %u, please check startup item.\n", ++ SSAM_MAX_CORE_NUM, core_num); ++ return -EINVAL; ++ } ++ if (g_ssam_config.user_config.dma_queue_num == SSAM_MAX_DMA_QUEUE_NUM ++ && core_num > SSAM_MAX_CORE_NUM_WITH_LARGE_IO) { ++ SPDK_ERRLOG("Invalid coremask, total cores need less or equal than %d, " ++ "actually %u, please check startup item.\n", ++ SSAM_MAX_CORE_NUM_WITH_LARGE_IO, core_num); ++ return -EINVAL; ++ } ++ g_ssam_config.core_num = core_num; ++ return 0; ++} ++ ++uint16_t ++ssam_get_core_num(void) ++{ ++ return (uint16_t)g_ssam_config.core_num; ++} ++ ++uint32_t ++spdk_ssam_get_mempool_size(void) ++{ ++ return g_ssam_config.user_config.mempool_size; ++} ++ ++uint32_t ++spdk_ssam_get_extra_size(void) ++{ ++ return g_ssam_config.user_config.extra_size; ++} ++ ++uint16_t ++spdk_ssam_get_queues(void) ++{ ++ uint16_t cfg_queues = (uint16_t)g_ssam_config.user_config.queues; ++ ++ if (cfg_queues == 0) { ++ SPDK_INFOLOG(ssam_config, "Use default queues number: %u.\n", SPDK_SSAM_DEFAULT_VQUEUES); ++ return SPDK_SSAM_DEFAULT_VQUEUES; ++ } ++ return cfg_queues; ++} ++ ++enum ssam_device_type ++spdk_ssam_get_virtio_type(uint16_t gfunc_id) ++{ ++ uint16_t vf_start, vf_end; ++ struct ssam_pf_list *pf = g_ssam_config.ep_info.host_pf_list; ++ ++ for (uint32_t i = 0; i < SSAM_HOSTEP_NUM_MAX; i++) { ++ if (pf[i].pf_funcid == UINT16_MAX) { ++ continue; ++ } ++ if (gfunc_id == pf[i].pf_funcid) { ++ return pf[i].pf_type; ++ } ++ ++ vf_start = pf[i].vf_funcid_start; ++ if (((uint32_t)vf_start + (uint32_t)pf[i].vf_num) > UINT16_MAX) { ++ SPDK_ERRLOG("vf_start %u + vf_num %u out of range, need less or equal than %u.\n", ++ vf_start, pf[i].vf_num, UINT16_MAX); ++ continue; ++ } ++ vf_end = vf_start + pf[i].vf_num; ++ if ((gfunc_id >= vf_start) && (gfunc_id < vf_end)) { ++ return pf[i].pf_type; ++ } ++ } ++ ++ return SSAM_DEVICE_VIRTIO_MAX; ++} ++ ++static void ++ssam_get_virtio_blk_config(struct ssam_virtio_config *cfg) ++{ ++ struct virtio_blk_config *dev_cfg = (struct virtio_blk_config *)cfg->device_config; ++ ++ cfg->device_feature = SPDK_SSAM_VIRTIO_BLK_DEFAULT_FEATURE; ++ cfg->queue_num = g_ssam_config.user_config.queues; ++ cfg->config_len = sizeof(struct virtio_blk_config); ++ ++ memset(dev_cfg, 0, cfg->config_len); ++ dev_cfg->blk_size = 0x200; ++ dev_cfg->min_io_size = 0; ++ dev_cfg->capacity = 0; ++ dev_cfg->num_queues = cfg->queue_num; ++ dev_cfg->seg_max = 0x7d; ++ dev_cfg->size_max = 0x200000; ++ cfg->queue_size = VIRITO_DEFAULT_QUEUE_SIZE; ++ ++ return; ++} ++ ++static void ++ssam_get_virtio_scsi_config(struct ssam_virtio_config *cfg) ++{ ++ struct virtio_scsi_config *dev_cfg = (struct virtio_scsi_config *)cfg->device_config; ++ ++ cfg->device_feature = SPDK_SSAM_VIRTIO_SCSI_DEFAULT_FEATURE; ++ cfg->queue_num = g_ssam_config.user_config.queues; ++ cfg->config_len = sizeof(struct virtio_scsi_config); ++ ++ memset(dev_cfg, 0, sizeof(struct virtio_scsi_config)); ++ dev_cfg->num_queues = 0x04; ++ dev_cfg->seg_max = 0x6f; ++ dev_cfg->max_sectors = 0x1ff; ++ dev_cfg->cmd_per_lun = 0x80; ++ dev_cfg->event_info_size = 0; ++ dev_cfg->sense_size = 0x60; ++ dev_cfg->cdb_size = 0x20; ++ dev_cfg->max_channel = 0; ++ dev_cfg->max_target = SPDK_SSAM_SCSI_CTRLR_MAX_DEVS; ++ dev_cfg->max_lun = 0xff; ++ cfg->queue_size = VIRITO_DEFAULT_QUEUE_SIZE; ++ ++ return; ++} ++ ++static int ++ssam_virtio_config_get(struct ssam_pf_list *pf, struct ssam_function_config *cfg) ++{ ++ int ret = 0; ++ ++ cfg->gfunc_id = pf->pf_funcid; ++ cfg->type = pf->pf_type; ++ switch (cfg->type) { ++ case SSAM_DEVICE_VIRTIO_BLK: ++ ssam_get_virtio_blk_config(&cfg->virtio_config); ++ break; ++ case SSAM_DEVICE_VIRTIO_SCSI: ++ ssam_get_virtio_scsi_config(&cfg->virtio_config); ++ break; ++ default: { ++ SPDK_ERRLOG("function config init fail (%d|%d) \n", cfg->gfunc_id, cfg->type); ++ ret = -EINVAL; ++ break; ++ } ++ } ++ ++ return ret; ++} ++ ++static int ++spdk_ssam_setup_pf(struct ssam_pf_list *pf, struct ssam_function_config *cfg) ++{ ++ int rc; ++ ++ rc = ssam_setup_function(pf->pf_funcid, pf->vf_num, pf->pf_type); ++ if (rc != 0) { ++ SPDK_ERRLOG("ssam init function(%u) failed:%s\n", pf->pf_funcid, spdk_strerror(-rc)); ++ return rc; ++ } ++ rc = ssam_write_function_config(cfg); ++ if (rc != 0) { ++ SPDK_ERRLOG("ssam write function(%d) config failed:%s\n", cfg->gfunc_id, spdk_strerror(-rc)); ++ return rc; ++ } ++ ++ return 0; ++} ++ ++static int ++spdk_ssam_setup_vf(struct ssam_pf_list *pf, struct ssam_function_config *cfg) ++{ ++ struct ssam_function_config l_cfg; ++ uint16_t vf_funcid_start = pf->vf_funcid_start; ++ uint16_t vf_num = pf->vf_num; ++ int rc; ++ uint16_t i; ++ ++ if (((uint32_t)vf_funcid_start + (uint32_t)vf_num) > UINT16_MAX) { ++ SPDK_ERRLOG("vf_funcid_start %u or vf_num %u out of range.\n", ++ vf_funcid_start, vf_num); ++ return -1; ++ } ++ ++ memcpy(&l_cfg, cfg, sizeof(struct ssam_function_config)); ++ for (i = vf_funcid_start; i < vf_funcid_start + vf_num; i++) { ++ l_cfg.gfunc_id = i; ++ rc = ssam_write_function_config(&l_cfg); ++ if (rc != 0) { ++ SPDK_ERRLOG("ssam write function(%u) config failed:%s\n", i, spdk_strerror(-rc)); ++ return rc; ++ } ++ } ++ ++ return 0; ++} ++ ++static int ++ssam_virtio_config_init(struct ssam_hostep_info *ep_info) ++{ ++ int rc = 0; ++ uint32_t i; ++ struct ssam_function_config cfg = {0}; ++ struct ssam_pf_list *pf = ep_info->host_pf_list; ++ ++ if (ssam_get_shm_created()) { ++ /* If server is crashed from last time, no need setup config this time */ ++ return 0; ++ } ++ ++ for (i = 0; i < SSAM_HOSTEP_NUM_MAX; i++) { ++ if (pf[i].pf_funcid == UINT16_MAX) { ++ continue; ++ } ++ rc = ssam_virtio_config_get(&pf[i], &cfg); ++ if (rc != 0) { ++ return rc; ++ } ++ rc = spdk_ssam_setup_pf(&pf[i], &cfg); ++ if (rc != 0) { ++ return rc; ++ } ++ rc = spdk_ssam_setup_vf(&pf[i], &cfg); ++ if (rc != 0) { ++ return rc; ++ } ++ } ++ ++ return rc; ++} ++ ++static int ++ssam_virtio_init(void) ++{ ++ struct ssam_lib_args ssam_args; ++ struct ssam_hostep_info *ep_info = &g_ssam_config.ep_info; ++ int rc; ++ ++ ssam_get_ssam_lib_init_config(&ssam_args); ++ ++ rc = ssam_lib_init(&ssam_args, ep_info); ++ if (rc != 0) { ++ SPDK_ERRLOG("Failed to init ssam:%s\n", spdk_strerror(-rc)); ++ return rc; ++ } ++ ++ rc = ssam_virtio_config_init(ep_info); ++ if (rc != 0) { ++ SPDK_ERRLOG("ssam virtio device init failed:%s\n", spdk_strerror(-rc)); ++ if (ssam_lib_exit() != 0) { ++ SPDK_WARNLOG("ssam lib exit failed\n"); ++ } ++ return rc; ++ } ++ ++ return 0; ++} ++ ++void ++spdk_ssam_user_config_init(void) ++{ ++ struct ssam_user_config *user_config = &g_ssam_config.user_config; ++ ++ user_config->queues = SPDK_SSAM_DEFAULT_VQUEUES; ++ user_config->dma_queue_num = SSAM_DEFAULT_DMA_QUEUE_NUM; ++ user_config->mempool_size = SSAM_DEFAULT_MEMPOOL_SIZE; ++ user_config->extra_size = SSAM_DEFAULT_MEMPOOL_EXTRA_SIZE; ++} ++ ++static void ++ssam_virtio_exit(void) ++{ ++ int rc; ++ ++ rc = ssam_lib_exit(); ++ if (rc != 0) { ++ SPDK_WARNLOG("ssam lib exit failed\n"); ++ } ++} ++ ++int ++ssam_config_init(void) ++{ ++ int rc; ++ ++ rc = ssam_virtio_init(); ++ if (rc != 0) { ++ return rc; ++ } ++ ++ return 0; ++} ++ ++void ++ssam_config_exit(void) ++{ ++ ssam_virtio_exit(); ++} ++ ++SPDK_LOG_REGISTER_COMPONENT(ssam_config) +diff --git a/lib/ssam/ssam_config.h b/lib/ssam/ssam_config.h +new file mode 100644 +index 0000000..c32a3bb +--- /dev/null ++++ b/lib/ssam/ssam_config.h +@@ -0,0 +1,52 @@ ++/*- ++ * BSD LICENSE ++ * ++ * Copyright (c) Huawei Technologies Co., Ltd. 2022-2022. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * ++ * * Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * * Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in ++ * the documentation and/or other materials provided with the ++ * distribution. ++ * * Neither the name of Intel Corporation nor the names of its ++ * contributors may be used to endorse or promote products derived ++ * from this software without specific prior written permission. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ++ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ++ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ++ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ++ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ++ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ++ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ++ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ++ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ++ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#ifndef SSAM_CONFIG_H ++#define SSAM_CONFIG_H ++ ++int ssam_set_core_num(uint32_t core_num); ++ ++uint16_t ssam_get_core_num(void); ++ ++uint32_t spdk_ssam_get_mempool_size(void); ++ ++uint32_t spdk_ssam_get_extra_size(void); ++ ++uint16_t spdk_ssam_get_queues(void); ++ ++enum ssam_device_type spdk_ssam_get_virtio_type(uint16_t gfunc_id); ++ ++int ssam_config_init(void); ++ ++void ssam_config_exit(void); ++ ++#endif /* SSAM_CONFIG_H */ +diff --git a/lib/ssam/ssam_device_pcie.c b/lib/ssam/ssam_device_pcie.c +new file mode 100644 +index 0000000..dd97f91 +--- /dev/null ++++ b/lib/ssam/ssam_device_pcie.c +@@ -0,0 +1,250 @@ ++/*- ++ * BSD LICENSE ++ * ++ * Copyright (c) Huawei Technologies Co., Ltd. 2024-2024. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * ++ * * Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * * Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in ++ * the documentation and/or other materials provided with the ++ * distribution. ++ * * Neither the name of Intel Corporation nor the names of its ++ * contributors may be used to endorse or promote products derived ++ * from this software without specific prior written permission. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ++ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ++ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ++ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ++ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ++ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ++ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ++ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ++ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ++ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#include "spdk/string.h" ++#include "spdk/file.h" ++#include "ssam_internal.h" ++ ++#define SSAM_KEY_MAX_LEN 16 ++#define SSAM_TYPE_MAX_LEN 12 ++#define SSAM_DBDF_MAX_LEN 16 ++ ++struct ssam_device_pcie_info { ++ uint32_t func_id; ++ char type[SSAM_TYPE_MAX_LEN]; ++ char dbdf[SSAM_DBDF_MAX_LEN]; ++}; ++ ++struct ssam_device_pcie_list { ++ uint32_t size; ++ struct ssam_device_pcie_info *device_pcie_list; ++}; ++ ++static struct ssam_device_pcie_list g_ssam_device_pcie_list = { ++ .size = 0, ++ .device_pcie_list = NULL, ++}; ++ ++void ++ssam_deinit_device_pcie_list(void) ++{ ++ if (g_ssam_device_pcie_list.device_pcie_list != NULL) { ++ free(g_ssam_device_pcie_list.device_pcie_list); ++ g_ssam_device_pcie_list.device_pcie_list = NULL; ++ } ++} ++ ++static int ++ssam_alloc_device_pcie_list(struct spdk_json_val *values, size_t num_values) ++{ ++ size_t i; ++ uint32_t size = 0; ++ ++ for (i = 0; i < num_values; i++) { ++ if (values[i].type == SPDK_JSON_VAL_OBJECT_END) { ++ size++; ++ } ++ } ++ ++ if (g_ssam_device_pcie_list.device_pcie_list == NULL) { ++ g_ssam_device_pcie_list.size = size; ++ g_ssam_device_pcie_list.device_pcie_list = calloc(size, sizeof(struct ssam_device_pcie_info)); ++ if (g_ssam_device_pcie_list.device_pcie_list == NULL) { ++ SPDK_ERRLOG("Unable to allocate enough memory for device_pcie_list\n"); ++ return -ENOMEM; ++ } ++ } ++ return 0; ++} ++ ++static void ++ssam_set_device_pcie_index(struct spdk_json_val *value, uint32_t cur_index) ++{ ++ char val[16]; ++ uint32_t gfunc_id; ++ if (value->type != SPDK_JSON_VAL_NUMBER || value->len > 5) { ++ SPDK_ERRLOG("device pcie gfunc id is invalid, type: %u, len: %u\n", value->type, value->len); ++ return; ++ } ++ ++ memset(val, 0, 16); ++ memcpy(val, value->start, value->len); ++ gfunc_id = spdk_strtol(val, 10); ++ if (gfunc_id >= SPDK_INVALID_GFUNC_ID) { ++ SPDK_ERRLOG("device pcie gfunc id(%u) is more than %u\n", gfunc_id, SPDK_INVALID_GFUNC_ID); ++ return; ++ } ++ g_ssam_device_pcie_list.device_pcie_list[cur_index].func_id = gfunc_id; ++} ++ ++static void ++ssam_set_device_pcie_dbdf(struct spdk_json_val *value, uint32_t cur_index) ++{ ++ if (value->type != SPDK_JSON_VAL_STRING || value->len >= SSAM_DBDF_MAX_LEN) { ++ SPDK_ERRLOG("device pcie dbdf is invalid, type: %u, len: %u\n", value->type, value->len); ++ return; ++ } ++ ++ memset(g_ssam_device_pcie_list.device_pcie_list[cur_index].dbdf, 0, SSAM_DBDF_MAX_LEN); ++ memcpy(g_ssam_device_pcie_list.device_pcie_list[cur_index].dbdf, value->start, value->len); ++} ++ ++static void ++ssam_set_device_pcie_type(struct spdk_json_val *value, uint32_t cur_index) ++{ ++ if (value->type != SPDK_JSON_VAL_STRING || value->len >= SSAM_TYPE_MAX_LEN) { ++ SPDK_ERRLOG("device pcie type is invalid, type: %u, len: %u\n", value->type, value->len); ++ return; ++ } ++ ++ memset(g_ssam_device_pcie_list.device_pcie_list[cur_index].type, 0, SSAM_TYPE_MAX_LEN); ++ memcpy(g_ssam_device_pcie_list.device_pcie_list[cur_index].type, value->start, value->len); ++} ++ ++static void ++ssam_init_device_pcie_list_by_values(struct spdk_json_val *values, size_t num_values) ++{ ++ char key[SSAM_KEY_MAX_LEN]; ++ uint32_t cur_index = 0; ++ size_t i; ++ ++ for (i = 0; i < num_values; i++) { ++ if (values[i].type == SPDK_JSON_VAL_OBJECT_END) { ++ cur_index++; ++ } ++ if (values[i].type != SPDK_JSON_VAL_NAME || values[i].len >= SSAM_KEY_MAX_LEN) { ++ continue; ++ } ++ ++ memset(key, 0, SSAM_KEY_MAX_LEN); ++ memcpy(key, values[i].start, values[i].len); ++ ++ /* point to val */ ++ i++; ++ ++ if (strcmp(key, "index") == 0) { ++ ssam_set_device_pcie_index(&values[i], cur_index); ++ } else if (strcmp(key, "dbdf") == 0) { ++ ssam_set_device_pcie_dbdf(&values[i], cur_index); ++ } else if (strcmp(key, "type") == 0) { ++ ssam_set_device_pcie_type(&values[i], cur_index); ++ } ++ } ++} ++ ++int ++ssam_init_device_pcie_list(void) ++{ ++ FILE *fp = NULL; ++ void *buf = NULL; ++ ssize_t rc = 0; ++ size_t size; ++ size_t num_values; ++ struct spdk_json_val *values = NULL; ++ ++ fp = popen("dpak-smi info -t device_pcie_list -f storage", "r"); ++ if (fp == NULL) { ++ SPDK_ERRLOG("execute dpak-smi failed\n"); ++ return -EINVAL; ++ } ++ ++ buf = spdk_posix_file_load(fp, &size); ++ if (buf == NULL) { ++ SPDK_ERRLOG("get size of json failed\n"); ++ rc = -EINVAL; ++ goto invalid; ++ } ++ ++ rc = spdk_json_parse(buf, size, NULL, 0, NULL, SPDK_JSON_PARSE_FLAG_ALLOW_COMMENTS); ++ if (rc < 0) { ++ SPDK_ERRLOG("dpak-smi error: %s\n", (char *)buf); ++ goto invalid; ++ } ++ num_values = (size_t)rc; ++ values = calloc(num_values, sizeof(*values)); ++ if (values == NULL) { ++ SPDK_ERRLOG("Unable to allocate enough memory for values\n"); ++ rc = -ENOMEM; ++ goto invalid; ++ } ++ ++ rc = spdk_json_parse(buf, size, values, num_values, NULL, ++ SPDK_JSON_PARSE_FLAG_ALLOW_COMMENTS | SPDK_JSON_PARSE_FLAG_DECODE_IN_PLACE); ++ if (rc <= 0) { ++ SPDK_ERRLOG("parse json to values failed\n"); ++ goto invalid; ++ } ++ ++ rc = ssam_alloc_device_pcie_list(values, num_values); ++ if (rc != 0) { ++ goto invalid; ++ } ++ ++ ssam_init_device_pcie_list_by_values(values, num_values); ++ rc = 0; ++ ++invalid: ++ if (values != NULL) { ++ free(values); ++ values = NULL; ++ } ++ if (buf != NULL) { ++ free(buf); ++ buf = NULL; ++ } ++ if (fp != NULL) { ++ pclose(fp); ++ fp = NULL; ++ } ++ return rc; ++} ++ ++void ++ssam_dump_device_pcie_list(struct spdk_json_write_ctx *w) ++{ ++ uint32_t i; ++ spdk_json_write_named_array_begin(w, "device_pcie_list"); ++ for (i = 0; i < g_ssam_device_pcie_list.size; i++) { ++ spdk_json_write_object_begin(w); ++ spdk_json_write_named_uint32(w, "index", g_ssam_device_pcie_list.device_pcie_list[i].func_id); ++ spdk_json_write_named_string(w, "dbdf", g_ssam_device_pcie_list.device_pcie_list[i].dbdf); ++ spdk_json_write_named_string(w, "type", g_ssam_device_pcie_list.device_pcie_list[i].type); ++ spdk_json_write_object_end(w); ++ } ++ spdk_json_write_array_end(w); ++} ++ ++uint32_t ++ssam_get_device_pcie_list_size(void) ++{ ++ return g_ssam_device_pcie_list.size; ++} +\ No newline at end of file +diff --git a/lib/ssam/ssam_internal.h b/lib/ssam/ssam_internal.h +new file mode 100644 +index 0000000..6de827f +--- /dev/null ++++ b/lib/ssam/ssam_internal.h +@@ -0,0 +1,523 @@ ++/*- ++ * BSD LICENSE ++ * ++ * Copyright (c) Huawei Technologies Co., Ltd. 2021-2021. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * ++ * * Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * * Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in ++ * the documentation and/or other materials provided with the ++ * distribution. ++ * * Neither the name of Intel Corporation nor the names of its ++ * contributors may be used to endorse or promote products derived ++ * from this software without specific prior written permission. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ++ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ++ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ++ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ++ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ++ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ++ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ++ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ++ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ++ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#ifndef SSAM_INTERNAL_H ++#define SSAM_INTERNAL_H ++ ++#include "stdint.h" ++ ++#include ++#include ++ ++#include "spdk_internal/thread.h" ++#include "spdk/log.h" ++#include "spdk/util.h" ++#include "spdk/rpc.h" ++#include "spdk/bdev.h" ++#include "spdk/ssam.h" ++#include "ssam_config.h" ++ ++#define SPDK_SSAM_FEATURES ((1ULL << VHOST_F_LOG_ALL) | \ ++ (1ULL << VHOST_USER_F_PROTOCOL_FEATURES) | \ ++ (1ULL << VIRTIO_F_VERSION_1) | \ ++ (1ULL << VIRTIO_F_NOTIFY_ON_EMPTY) | \ ++ (1ULL << VIRTIO_RING_F_EVENT_IDX) | \ ++ (1ULL << VIRTIO_RING_F_INDIRECT_DESC) | \ ++ (1ULL << VIRTIO_F_RING_PACKED)) ++ ++#define VIRITO_DEFAULT_QUEUE_SIZE 256 ++ ++#define SPDK_SSAM_VQ_MAX_SUBMISSIONS 16 ++#define SPDK_SSAM_MAX_VQUEUES 256 ++#define SPDK_SSAM_MAX_VQ_SIZE 256 ++#define SSAM_JSON_DEFAULT_QUEUES_NUM 16 ++ ++/* ssam not support config vq size so far */ ++#define SPDK_SSAM_DEFAULT_VQ_SIZE SPDK_SSAM_MAX_VQ_SIZE ++#define SPDK_SSAM_DEFAULT_VQUEUES 16 ++#define SPDK_SSAM_IOVS_MAX 32 ++#define SPDK_SSAM_MAX_SEG_SIZE (32 * 1024) ++ ++#define SPDK_INVALID_GFUNC_ID UINT16_MAX ++#define SPDK_INVALID_CORE_ID UINT16_MAX ++ ++#define SSAM_PF_MAX_NUM 32 ++#define SPDK_SSAM_SCSI_CTRLR_MAX_DEVS 255 ++#define SSAM_VIRTIO_SCSI_LUN_ID 0x400001 ++#define SPDK_SSAM_SCSI_DEFAULT_VQUEUES 128 ++#define SSAM_MAX_SESSION_PER_DEV UINT16_MAX ++ ++#define SPDK_LIMIT_LOG_MAX_INTERNEL_IN_MS 3000 ++#define SPDK_CONVERT_MS_TO_US 1000 ++ ++typedef void (*spdk_ssam_session_io_wait_cb)(void *cb_arg); ++ ++struct spdk_ssam_session_io_wait { ++ spdk_ssam_session_io_wait_cb cb_fn; ++ void *cb_arg; ++ TAILQ_ENTRY(spdk_ssam_session_io_wait) link; ++}; ++ ++typedef void (*spdk_ssam_session_io_wait_r_cb)(void *cb_arg); ++ ++struct spdk_ssam_session_io_wait_r { ++ spdk_ssam_session_io_wait_r_cb cb_fn; ++ void *cb_arg; ++ TAILQ_ENTRY(spdk_ssam_session_io_wait_r) link; ++}; ++ ++struct spdk_ssam_virtqueue { ++ void *tasks; ++ struct spdk_ssam_session *smsession; ++ uint32_t *index; ++ int num; ++ int use_num; ++ int index_l; ++ int index_r; ++}; ++ ++struct spdk_ssam_session_backend { ++ enum virtio_type type; ++ int (*remove_session)(struct spdk_ssam_session *smsession); ++ void (*remove_self)(struct spdk_ssam_session *smsession); ++ void (*request_worker)(struct spdk_ssam_session *smsession, void *arg); ++ void (*destroy_bdev_device)(struct spdk_ssam_session *smsession, void *args); ++ void (*response_worker)(struct spdk_ssam_session *smsession, void *arg); ++ void (*no_data_req_worker)(struct spdk_ssam_session *smsession); ++ ++ int (*ssam_get_config)(struct spdk_ssam_session *smsession, ++ uint8_t *config, uint32_t len, uint16_t queues); ++ int (*ssam_set_config)(struct spdk_ssam_session *smsession, ++ uint8_t *config, uint32_t offset, uint32_t size, uint32_t flags); ++ ++ void (*print_stuck_io_info)(struct spdk_ssam_session *smsession); ++ ++ void (*dump_info_json)(struct spdk_ssam_session *smsession, ++ struct spdk_json_write_ctx *w); ++ void (*write_config_json)(struct spdk_ssam_session *smsession, ++ struct spdk_json_write_ctx *w); ++ void (*show_iostat_json)(struct spdk_ssam_session *smsession, uint32_t id, ++ struct spdk_json_write_ctx *w); ++ void (*clear_iostat_json)(struct spdk_ssam_session *smsession); ++ struct spdk_bdev *(*get_bdev)(struct spdk_ssam_session *smsession, uint32_t id); ++}; ++ ++struct spdk_ssam_session { ++ /* Unique session name, format as ssam.tid.gfunc_id. */ ++ char *name; ++ ++ struct spdk_ssam_dev *smdev; ++ ++ /* Session poller thread, same as ssam dev poller thread */ ++ struct spdk_thread *thread; ++ struct ssam_mempool *mp; ++ const struct spdk_ssam_session_backend *backend; ++ spdk_ssam_session_rsp_fn rsp_fn; ++ void *rsp_ctx; ++ struct spdk_ssam_virtqueue virtqueue[SPDK_SSAM_MAX_VQUEUES]; ++ ++ /* Number of processing tasks, can not remove session when task_cnt > 0 */ ++ int task_cnt; ++ ++ /* Number of pending asynchronous operations */ ++ uint32_t pending_async_op_num; ++ ++ /* ssam global virtual function id */ ++ uint16_t gfunc_id; ++ ++ /* Depth of virtio-blk virtqueue */ ++ uint16_t queue_size; ++ ++ /* Number of virtio-blk virtqueue */ ++ uint16_t max_queues; ++ bool started; ++ bool initialized; ++ ++ /* spdk_ssam_session_fn process finish flag */ ++ bool async_done; ++ ++ bool registered; ++ ++ TAILQ_ENTRY(spdk_ssam_session) tailq; ++}; ++ ++struct ssam_iovs { ++ struct iovec sges[SPDK_SSAM_IOVS_MAX]; ++}; ++ ++struct ssam_iovec { ++ struct ssam_iovs virt; /* virt's iov_base is virtual address */ ++ struct ssam_iovs phys; /* phys's iov_base is physical address */ ++}; ++ ++struct ssam_stat { ++ uint64_t poll_cur_tsc; ++ uint64_t poll_tsc; ++ uint64_t poll_count; ++}; ++ ++struct spdk_ssam_dev { ++ /* ssam device name, format as ssam.tid */ ++ char *name; ++ /* virtio type */ ++ enum virtio_type type; ++ ++ /* ssam device poller thread, same as session poller thread */ ++ struct spdk_thread *thread; ++ struct spdk_poller *requestq_poller; ++ struct spdk_poller *responseq_poller; ++ struct spdk_poller *stop_poller; ++ ++ /* Store sessions of this dev, max number is SSAM_MAX_SESSION_PER_DEV */ ++ struct spdk_ssam_session **smsessions; ++ ++ TAILQ_ENTRY(spdk_ssam_dev) tailq; ++ ++ /* IO num that is on flight */ ++ uint64_t io_num; ++ ++ uint64_t discard_io_num; ++ ++ /* IO stuck ticks in dma process */ ++ uint64_t io_stuck_tsc; ++ struct ssam_stat stat; ++ ++ uint64_t io_wait_cnt; ++ uint64_t io_wait_r_cnt; ++ ++ /* Number of started and actively polled sessions */ ++ uint32_t active_session_num; ++ ++ /* Information of tid, indicate from which ssam queue to receive or send data */ ++ uint16_t tid; ++ TAILQ_HEAD(, spdk_ssam_session_io_wait) io_wait_queue; ++ TAILQ_HEAD(, spdk_ssam_session_io_wait_r) io_wait_queue_r; ++}; ++ ++struct spdk_ssam_dma_cb { ++ uint8_t status; ++ uint8_t req_dir; ++ uint16_t vq_idx; ++ uint16_t task_idx; ++ uint16_t gfunc_id; ++}; ++ ++struct spdk_ssam_send_event_flag { ++ bool need_async; ++ bool need_rsp; ++}; ++ ++/** ++ * Remove a session from sessions array. ++ * ++ * \param smsessions sessions array. ++ * \param smsession the session to be removed. ++ */ ++void ssam_sessions_remove(struct spdk_ssam_session **smsessions, ++ struct spdk_ssam_session *smsession); ++ ++/** ++ * Check out whether sessions is empty or not. ++ * ++ * \param smsessions sessions array. ++ * \return true indicate sessions is empty or false not empty. ++*/ ++bool ssam_sessions_empty(struct spdk_ssam_session **smsessions); ++ ++/** ++ * Get next session in sessions array, begin with current session. ++ * ++ * \param smsessions sessions array. ++ * \param smsession the begin session. ++ * \return the next session found or null not found. ++ */ ++struct spdk_ssam_session * ssam_sessions_next(struct spdk_ssam_session **smsessions, ++ struct spdk_ssam_session *smsession); ++ ++/** ++ * Insert io wait task to session. ++ * ++ * \param smsession the session that io wait insert to. ++ * \param io_wait the io wait to be insert. ++ */ ++void ssam_session_insert_io_wait(struct spdk_ssam_session *smsession, ++ struct spdk_ssam_session_io_wait *io_wait); ++ ++/** ++ * Insert io wait compilete or dma task to smdev. ++ * ++ * \param smdev the smdev that io wait insert to. ++ * \param io_wait_r the io wait to be insert. ++ */ ++void ssam_session_insert_io_wait_r(struct spdk_ssam_dev *smdev, ++ struct spdk_ssam_session_io_wait_r *io_wait_r); ++ ++/** ++ * Remove session from sessions and then stop session dev poller. ++ * ++ * \param smsession the session that to be removed. ++ */ ++void ssam_session_destroy(struct spdk_ssam_session *smsession); ++ ++/** ++ * Show a ssam device info in json format. ++ * ++ * \param smdev ssam device. ++ * \param gfunc_id ssam global vf id. ++ * \param arg user-provided parameter. ++ */ ++void ssam_dump_info_json(struct spdk_ssam_dev *smdev, uint16_t gfunc_id, ++ struct spdk_json_write_ctx *w); ++ ++/** ++ * Get a ssam device name. ++ * ++ * \param smdev ssam device. ++ * \return ssam device name or NULL ++ */ ++const char *spdk_ssam_dev_get_name(const struct spdk_ssam_dev *smdev); ++ ++/** ++ * Get a ssam session name. ++ * ++ * \param smdev smsession session. ++ * \return ssam session name or NULL ++ */ ++const char *spdk_ssam_session_get_name(const struct spdk_ssam_session *smsession); ++ ++/** ++ * Call a function of the provided ssam session. ++ * The function will be called on this session's thread. ++ * ++ * \param smsession ssam session. ++ * \param fn function to call on each session's thread ++ * \param cpl_fn function to be called at the end of the ssam management thread. ++ * Optional, can be NULL. ++ * \param send_event_flag whether an asynchronous operation or response is required ++ * \param ctx additional argument to the both callbacks ++ * \return error code ++ */ ++int ssam_send_event_to_session(struct spdk_ssam_session *smsession, spdk_ssam_session_fn fn, ++ spdk_ssam_session_cpl_fn cpl_fn, struct spdk_ssam_send_event_flag send_event_flag, void *ctx); ++ ++/** ++ * Finish a blocking ssam_send_event_to_session() call and finally ++ * start the session. This must be called on the target lcore, which ++ * will now receive all session-related messages (e.g. from ++ * ssam_send_event_to_session()). ++ * ++ * Must be called under the global ssam lock. ++ * ++ * \param smsession ssam session ++ * \param response return code ++ */ ++void ssam_session_start_done(struct spdk_ssam_session *smsession, int response); ++ ++/** ++ * Finish a blocking ssam_send_event_to_session() call and finally ++ * stop the session. This must be called on the session's lcore which ++ * used to receive all session-related messages (e.g. from ++ * ssam_send_event_to_session()). After this call, the session- ++ * related messages will be once again processed by any arbitrary thread. ++ * ++ * Must be called under the global ssam lock. ++ * ++ * \param smsession ssam session ++ * \param rsp return code ++ * \param ctx user context ++ */ ++void ssam_session_stop_done(struct spdk_ssam_session *smsession, int rsp, void **ctx); ++ ++/** ++ * Set session be freed, so that not access session any more. ++ * ++ * \param ctx user context ++ */ ++void ssam_set_session_be_freed(void **ctx); ++ ++/** ++ * Find a ssam device in the global g_ssam_devices list by gfunc_id, ++ * if find the ssam device, register a session to the existent ssam device ++ * sessions list, if not find, first create a ssam device to the global ++ * g_ssam_devices list, and then register a session to the new ssam device ++ * sessions list. ++ * ++ * Must be called under the global ssam lock. ++ * ++ * \param info ssam session register info. ++ * \param smsession ssam session created. ++ * \return 0 for success or negative for failed. ++ */ ++int spdk_ssam_session_register(struct spdk_ssam_session_reg_info *info, ++ struct spdk_ssam_session **smsession); ++ ++/** ++ * unregister smsession response call back function. ++ * ++ * \param smsession ssam session ++ */ ++void ssam_session_unreg_response_cb(struct spdk_ssam_session *smsession); ++ ++void ssam_dev_unregister(struct spdk_ssam_dev **dev); ++ ++void ssam_send_event_async_done(void **ctx); ++ ++void spdk_ssam_send_dev_destroy_msg(struct spdk_ssam_session *smsession, void *args); ++ ++/** ++ * Get ssam config. ++ * ++ * \param smsession ssam session ++ * \param config a memory region to store config. ++ * \param len the input config param memory region length. ++ * \return 0 success or -1 failed. ++ */ ++int ssam_get_config(struct spdk_ssam_session *smsession, uint8_t *config, ++ uint32_t len, uint16_t queues); ++ ++/** ++ * Mount gfunc_id volume to the ssam normal queue. ++ * ++ * \param smsession ssam session ++ * \param lun_id lun id of gfunc_id. ++ * ++ * \return 0 success or not 0 failed. ++ */ ++int ssam_mount_normal(struct spdk_ssam_session *smsession, uint32_t lun_id); ++ ++/** ++ * Unmount function. ++ * ++ * \param smsession ssam session ++ * \param lun_id lun id of gfunc_id. ++ * ++ * \return 0 success or not 0 failed. ++ */ ++int ssam_umount_normal(struct spdk_ssam_session *smsession, uint32_t lun_id); ++ ++/** ++ * Mount gfunc_id volume to the ssam normal queue again. ++ * ++ * \param smsession ssam session ++ * \param lun_id lun id of gfunc_id. ++ * ++ * \return 0 success or not 0 failed. ++ */ ++int ssam_remount_normal(struct spdk_ssam_session *smsession, uint32_t lun_id); ++ ++/** ++ * Register worker poller to dev. ++ * ++ * \param smdev the dev that to be registered worker poller. ++ * \return 0 success or not 0 failed. ++ */ ++int ssam_dev_register_worker_poller(struct spdk_ssam_dev *smdev); ++ ++/** ++ * Unregister worker poller for dev. ++ * ++ * \param smdev the dev that to be unregistered worker poller. ++ */ ++void ssam_dev_unregister_worker_poller(struct spdk_ssam_dev *smdev); ++ ++/** ++ * Get the differential value of the current tsc. ++ * ++ * \param tsc the current tsc. ++ * \return the differential value. ++ */ ++uint64_t ssam_get_diff_tsc(uint64_t tsc); ++ ++/** ++ * Get the bdev name of the specific gfunc_id. ++ * ++ * \param gfunc_id ssam global vf id. ++ * ++ * \return the bdev name of gfunc_id ++ */ ++const char *spdk_ssam_get_bdev_name_by_gfunc_id(uint16_t gfunc_id); ++ ++/** ++ * Remove a ssam session. Remove a session associate to the unique gfunc_id, ++ * then remove the ssam device if the device not have a session any more. ++ * ++ * Notice that this interface cannot be reentrant, so must call spdk_ssam_lock first. ++ * ++ * \param smsession ssam session ++ * ++ * \return 0 on success, negative errno on error. ++ */ ++int spdk_ssam_session_unregister(struct spdk_ssam_session *smsession); ++ ++/** ++ * Get ssam iostat. ++ * ++ * \param smsession ssam session ++ * \param stat a memory region to store iostat. ++ */ ++void spdk_ssam_get_iostat(struct spdk_ssam_session *smsession, ++ struct spdk_bdev_io_stat *stat); ++ ++/** ++ * Decrease dev io num. ++ * ++ * \param smdev ssam device. ++ */ ++void ssam_dev_io_dec(struct spdk_ssam_dev *smdev); ++ ++/** ++ * Get ssam session bdev. ++ * ++ * \param smsession ssam session ++ * ++ * \return the session bdev. ++ */ ++struct spdk_bdev *spdk_ssam_get_session_bdev(struct spdk_ssam_session *smsession); ++ ++/** ++ * free memory with rte. ++ * ++ * \param smsession ssam session ++ * ++ * \return 0 on success. ++ */ ++int ssam_free_ex(void *addr); ++ ++/** ++ * Get elem info from memory addr. ++ * ++ * \param memory addr ++ * ++ */ ++int ssam_malloc_elem_from_addr(const void *data, unsigned long long *pg_size, int *socket_id); ++ ++#endif /* SSAM_INTERNAL_H */ +diff --git a/lib/ssam/ssam_malloc.c b/lib/ssam/ssam_malloc.c +new file mode 100644 +index 0000000..4c7b720 +--- /dev/null ++++ b/lib/ssam/ssam_malloc.c +@@ -0,0 +1,56 @@ ++/*- ++ * BSD LICENSE ++ * ++ * Copyright (c) Huawei Technologies Co., Ltd. 2022-2022. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * ++ * * Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * * Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in ++ * the documentation and/or other materials provided with the ++ * distribution. ++ * * Neither the name of Intel Corporation nor the names of its ++ * contributors may be used to endorse or promote products derived ++ * from this software without specific prior written permission. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ++ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ++ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ++ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ++ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ++ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ++ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ++ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ++ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ++ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#include ++#include "spdk/env.h" ++ ++#include "ssam_internal.h" ++ ++int ssam_free_ex(void *addr) ++{ ++ spdk_free(addr); ++ return 0; ++} ++ ++int ssam_malloc_elem_from_addr(const void *data, unsigned long long *pg_size, int *socket_id) ++{ ++ struct rte_memseg_list *msl = NULL; ++ ++ msl = rte_mem_virt2memseg_list(data); ++ if (msl == NULL) { ++ return -1; ++ } ++ ++ *socket_id = msl->socket_id; ++ *pg_size = msl->page_sz; ++ return 0; ++} +\ No newline at end of file +diff --git a/lib/ssam/ssam_rpc.c b/lib/ssam/ssam_rpc.c +new file mode 100644 +index 0000000..effa8e5 +--- /dev/null ++++ b/lib/ssam/ssam_rpc.c +@@ -0,0 +1,1688 @@ ++/*- ++ * BSD LICENSE ++ * ++ * Copyright (c) Huawei Technologies Co., Ltd. 2021-2021. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * ++ * * Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * * Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in ++ * the documentation and/or other materials provided with the ++ * distribution. ++ * * Neither the name of Intel Corporation nor the names of its ++ * contributors may be used to endorse or promote products derived ++ * from this software without specific prior written permission. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ++ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ++ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ++ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ++ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ++ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ++ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ++ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ++ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ++ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#include "spdk/string.h" ++#include "spdk/env.h" ++#include "spdk/bdev_module.h" ++#include "spdk/ssam.h" ++#include "spdk/bdev.h" ++ ++#include "ssam_internal.h" ++#include "ssam_config.h" ++#include "rte_malloc.h" ++ ++static int spdk_ssam_rpc_get_gfunc_id_by_dbdf(char *dbdf, uint16_t *gfunc_id); ++ ++struct rpc_ssam_blk_ctrlr { ++ char *dev_name; ++ char *index; ++ bool readonly; ++ char *serial; ++}; ++ ++static const struct spdk_json_object_decoder g_rpc_construct_ssam_blk_ctrlr[] = { ++ {"dev_name", offsetof(struct rpc_ssam_blk_ctrlr, dev_name), spdk_json_decode_string}, ++ {"index", offsetof(struct rpc_ssam_blk_ctrlr, index), spdk_json_decode_string}, ++ {"readonly", offsetof(struct rpc_ssam_blk_ctrlr, readonly), spdk_json_decode_bool, true}, ++ {"serial", offsetof(struct rpc_ssam_blk_ctrlr, serial), spdk_json_decode_string, true}, ++}; ++ ++static void ++free_rpc_ssam_blk_ctrlr(struct rpc_ssam_blk_ctrlr *req) ++{ ++ if (req->dev_name != NULL) { ++ free(req->dev_name); ++ req->dev_name = NULL; ++ } ++ ++ if (req->index != NULL) { ++ free(req->index); ++ req->index = NULL; ++ } ++ ++ if (req->serial != NULL) { ++ free(req->serial); ++ req->serial = NULL; ++ } ++} ++ ++static int ++spdk_ssam_rpc_para_check(uint16_t gfunc_id) ++{ ++ int rc; ++ ++ rc = spdk_ssam_check_gfunc_id(gfunc_id); ++ if (rc != 0) { ++ return rc; ++ } ++ ++ return 0; ++} ++ ++static int ++spdk_ssam_rpc_para_check_type(uint16_t gfunc_id, enum ssam_device_type target_type) ++{ ++ int rc; ++ enum ssam_device_type type; ++ ++ rc = spdk_ssam_rpc_para_check(gfunc_id); ++ if (rc != 0) { ++ return rc; ++ } ++ ++ type = spdk_ssam_get_virtio_type(gfunc_id); ++ if (type == target_type) { ++ return 0; ++ } ++ SPDK_ERRLOG("Invalid virtio type, need type %d, actually %d\n", target_type, type); ++ ++ return -EINVAL; ++} ++ ++static void ++rpc_ssam_send_response_cb(void *arg, int rsp) ++{ ++ struct spdk_jsonrpc_request *request = arg; ++ ++ if (rsp != 0) { ++ spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, ++ spdk_strerror(-rsp)); ++ } else { ++ spdk_jsonrpc_send_bool_response(request, true); ++ } ++ return; ++} ++ ++struct ssam_log_command_info { ++ char *user_name; ++ char *event; ++ char *src_addr; ++}; ++ ++static const struct spdk_json_object_decoder g_rpc_construct_log_command_info[] = { ++ {"user_name", offsetof(struct ssam_log_command_info, user_name), spdk_json_decode_string}, ++ {"event", offsetof(struct ssam_log_command_info, event), spdk_json_decode_string}, ++ {"src_addr", offsetof(struct ssam_log_command_info, src_addr), spdk_json_decode_string}, ++}; ++ ++static void ++free_rpc_ssam_log_command_info(struct ssam_log_command_info *req) ++{ ++ if (req->user_name != NULL) { ++ free(req->user_name); ++ req->user_name = NULL; ++ } ++ if (req->event != NULL) { ++ free(req->event); ++ req->event = NULL; ++ } ++ if (req->src_addr != NULL) { ++ free(req->src_addr); ++ req->src_addr = NULL; ++ } ++} ++ ++static void ++rpc_ssam_log_command_info(struct spdk_jsonrpc_request *request, ++ const struct spdk_json_val *params) ++{ ++ struct ssam_log_command_info req = {0}; ++ int rc; ++ ++ if (params == NULL) { ++ SPDK_ERRLOG("log info params error, skip\n"); ++ goto invalid; ++ } ++ ++ rc = spdk_json_decode_object(params, g_rpc_construct_log_command_info, ++ SPDK_COUNTOF(g_rpc_construct_log_command_info), &req); ++ if (rc != 0) { ++ SPDK_ERRLOG("decode cmd info failed\n"); ++ rc = -EINVAL; ++ goto invalid; ++ } ++ ++ SPDK_NOTICELOG("log event: from %s user %s event %s\n", req.src_addr, req.user_name, req.event); ++ ++invalid: ++ free_rpc_ssam_log_command_info(&req); ++ spdk_jsonrpc_send_bool_response(request, true); ++ return; ++} ++SPDK_RPC_REGISTER("log_command_info", rpc_ssam_log_command_info, ++ SPDK_RPC_RUNTIME) ++ ++static int ++rpc_ssam_session_reg_response_cb(struct spdk_ssam_session *smsession, ++ struct spdk_jsonrpc_request *request) ++{ ++ if (smsession->rsp_fn != NULL) { ++ return -1; ++ } ++ smsession->rsp_fn = rpc_ssam_send_response_cb; ++ smsession->rsp_ctx = request; ++ return 0; ++} ++ ++static void ++rpc_init_session_reg_info(struct spdk_ssam_session_reg_info *info, ++ uint16_t queues, uint16_t gfunc_id, struct spdk_jsonrpc_request *request) ++{ ++ info->queues = queues; ++ info->gfunc_id = gfunc_id; ++ info->rsp_ctx = (void *)request; ++ info->rsp_fn = rpc_ssam_send_response_cb; ++} ++ ++static void ++free_rpc_ssam_session_reg_info(struct spdk_ssam_session_reg_info *info) ++{ ++ if (info->name != NULL) { ++ free(info->name); ++ info->name = NULL; ++ } ++ if (info->dbdf != NULL) { ++ free(info->dbdf); ++ info->dbdf = NULL; ++ } ++} ++ ++static uint16_t ++rpc_ssam_get_gfunc_id_by_index(char *index) ++{ ++ uint16_t gfunc_id, i; ++ int rc; ++ if (strlen(index) <= 0x5) { ++ for (i = 0; i < strlen(index); i++) { ++ if (!isdigit(index[i])) { ++ return SPDK_INVALID_GFUNC_ID; ++ } ++ } ++ gfunc_id = spdk_strtol(index, 10) > SPDK_INVALID_GFUNC_ID ? SPDK_INVALID_GFUNC_ID : spdk_strtol(index, 10); ++ } else { ++ rc = spdk_ssam_rpc_get_gfunc_id_by_dbdf(index, &gfunc_id); ++ if (rc != 0) { ++ return SPDK_INVALID_GFUNC_ID; ++ } ++ } ++ return gfunc_id; ++} ++ ++static void ++rpc_ssam_create_blk_controller(struct spdk_jsonrpc_request *request, ++ const struct spdk_json_val *params) ++{ ++ struct spdk_ssam_session_reg_info info = {0}; ++ struct rpc_ssam_blk_ctrlr req = {0}; ++ uint16_t gfunc_id = SPDK_INVALID_GFUNC_ID; ++ uint16_t queues; ++ int rc; ++ ++ if (params == NULL) { ++ SPDK_ERRLOG("rpc_ssam_create_blk_controller params null\n"); ++ rc = -EINVAL; ++ goto invalid; ++ } ++ ++ rc = spdk_json_decode_object(params, g_rpc_construct_ssam_blk_ctrlr, ++ SPDK_COUNTOF(g_rpc_construct_ssam_blk_ctrlr), &req); ++ if (rc != 0) { ++ SPDK_DEBUGLOG(ssam_rpc, "spdk_json_decode_object failed\n"); ++ rc = -EINVAL; ++ goto invalid; ++ } ++ ++ gfunc_id = rpc_ssam_get_gfunc_id_by_index(req.index); ++ rc = spdk_ssam_rpc_para_check_type(gfunc_id, SSAM_DEVICE_VIRTIO_BLK); ++ if (rc != 0) { ++ goto invalid; ++ } ++ ++ if (req.dev_name == NULL) { ++ rc = -ENODEV; ++ goto invalid; ++ } ++ ++ queues = spdk_ssam_get_queues(); ++ if (queues > SPDK_SSAM_MAX_VQUEUES) { ++ SPDK_ERRLOG("Queue number out of range, need less or equal than %u, actually %u.\n", ++ SPDK_SSAM_MAX_VQUEUES, queues); ++ rc = -EINVAL; ++ goto invalid; ++ } ++ ++ rpc_init_session_reg_info(&info, queues, gfunc_id, request); ++ ++ rc = spdk_ssam_blk_construct(&info, req.dev_name, req.readonly, req.serial); ++ if (rc < 0) { ++ goto invalid; ++ } ++ ++ free_rpc_ssam_blk_ctrlr(&req); ++ free_rpc_ssam_session_reg_info(&info); ++ return; ++ ++invalid: ++ free_rpc_ssam_blk_ctrlr(&req); ++ free_rpc_ssam_session_reg_info(&info); ++ spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, ++ spdk_strerror(-rc)); ++ return; ++} ++SPDK_RPC_REGISTER("create_blk_controller", rpc_ssam_create_blk_controller, ++ SPDK_RPC_RUNTIME) ++ ++struct rpc_delete_ssam_ctrlr { ++ char *index; ++}; ++ ++static const struct spdk_json_object_decoder g_rpc_delete_ssam_ctrlr_decoder[] = { ++ {"index", offsetof(struct rpc_delete_ssam_ctrlr, index), spdk_json_decode_string}, ++}; ++ ++static void ++free_rpc_delete_ssam_ctrlr(struct rpc_delete_ssam_ctrlr *req) ++{ ++ if (req->index != NULL) { ++ free(req->index); ++ req->index = NULL; ++ } ++} ++ ++static void ++rpc_ssam_delete_controller(struct spdk_jsonrpc_request *request, ++ const struct spdk_json_val *params) ++{ ++ struct rpc_delete_ssam_ctrlr req = {0}; ++ uint16_t gfunc_id = SPDK_INVALID_GFUNC_ID; ++ struct spdk_ssam_session *smsession; ++ int rc; ++ ++ if (params == NULL) { ++ SPDK_ERRLOG("rpc_ssam_delete_controller params null\n"); ++ rc = -EINVAL; ++ goto invalid; ++ } ++ ++ rc = spdk_json_decode_object(params, g_rpc_delete_ssam_ctrlr_decoder, ++ SPDK_COUNTOF(g_rpc_delete_ssam_ctrlr_decoder), &req); ++ if (rc != 0) { ++ SPDK_DEBUGLOG(ssam_rpc, "spdk_json_decode_object failed\n"); ++ rc = -EINVAL; ++ goto invalid; ++ } ++ ++ gfunc_id = rpc_ssam_get_gfunc_id_by_index(req.index); ++ rc = spdk_ssam_rpc_para_check(gfunc_id); ++ if (rc != 0) { ++ goto invalid; ++ } ++ ++ spdk_ssam_lock(); ++ smsession = spdk_ssam_session_find(gfunc_id); ++ if (smsession == NULL) { ++ SPDK_ERRLOG("Couldn't find session with function id %d.\n", gfunc_id); ++ rc = -ENODEV; ++ spdk_ssam_unlock(); ++ goto invalid; ++ } ++ ++ rc = rpc_ssam_session_reg_response_cb(smsession, request); ++ if (rc != 0) { ++ SPDK_ERRLOG("The controller is being operated.\n"); ++ rc = -EALREADY; ++ spdk_ssam_unlock(); ++ goto invalid; ++ } ++ ++ rc = spdk_ssam_session_unregister(smsession); ++ if (rc != 0) { ++ /* ++ * Unregitster response cb to avoid use request in the cb function, ++ * because if error happend, request will be responsed immediately ++ */ ++ ssam_session_unreg_response_cb(smsession); ++ spdk_ssam_unlock(); ++ goto invalid; ++ } ++ spdk_ssam_unlock(); ++ ++ free_rpc_delete_ssam_ctrlr(&req); ++ return; ++ ++invalid: ++ free_rpc_delete_ssam_ctrlr(&req); ++ spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, ++ spdk_strerror(-rc)); ++ return; ++} ++SPDK_RPC_REGISTER("delete_controller", rpc_ssam_delete_controller, SPDK_RPC_RUNTIME) ++ ++struct rpc_delete_ssam_scsi_ctrlr { ++ char *name; ++}; ++ ++static const struct spdk_json_object_decoder g_rpc_delete_ssam_scsi_ctrlr_decoder[] = { ++ {"name", offsetof(struct rpc_delete_ssam_scsi_ctrlr, name), spdk_json_decode_string}, ++}; ++ ++static void ++free_rpc_delete_ssam_scsi_ctrlrs(struct rpc_delete_ssam_scsi_ctrlr *req) ++{ ++ if (req->name != NULL) { ++ free(req->name); ++ req->name = NULL; ++ } ++} ++ ++static void ++rpc_ssam_delete_scsi_controller(struct spdk_jsonrpc_request *request, ++ const struct spdk_json_val *params) ++{ ++ struct rpc_delete_ssam_scsi_ctrlr req = {0}; ++ uint16_t gfunc_id = SPDK_INVALID_GFUNC_ID; ++ struct spdk_ssam_session *smsession; ++ int rc; ++ ++ if (params == NULL) { ++ SPDK_ERRLOG("rpc_ssam_delete_controller params null\n"); ++ rc = -EINVAL; ++ goto invalid; ++ } ++ ++ rc = spdk_json_decode_object(params, g_rpc_delete_ssam_scsi_ctrlr_decoder, ++ SPDK_COUNTOF(g_rpc_delete_ssam_scsi_ctrlr_decoder), &req); ++ if (rc != 0) { ++ SPDK_DEBUGLOG(ssam_rpc, "spdk_json_decode_object failed\n"); ++ rc = -EINVAL; ++ goto invalid; ++ } ++ ++ gfunc_id = spdk_ssam_get_gfunc_id_by_name(req.name); ++ rc = spdk_ssam_rpc_para_check(gfunc_id); ++ if (rc != 0) { ++ goto invalid; ++ } ++ ++ spdk_ssam_lock(); ++ smsession = spdk_ssam_session_find(gfunc_id); ++ if (smsession == NULL) { ++ SPDK_ERRLOG("Couldn't find session with function id %d.\n", gfunc_id); ++ rc = -ENODEV; ++ spdk_ssam_unlock(); ++ goto invalid; ++ } ++ ++ rc = rpc_ssam_session_reg_response_cb(smsession, request); ++ if (rc != 0) { ++ SPDK_ERRLOG("The controller is being operated.\n"); ++ rc = -EALREADY; ++ spdk_ssam_unlock(); ++ goto invalid; ++ } ++ ++ rc = spdk_ssam_session_unregister(smsession); ++ if (rc != 0) { ++ /* ++ * Unregitster response cb to avoid use request in the cb function, ++ * because if error happend, request will be responsed immediately ++ */ ++ ssam_session_unreg_response_cb(smsession); ++ spdk_ssam_unlock(); ++ goto invalid; ++ } ++ spdk_ssam_unlock(); ++ ++ free_rpc_delete_ssam_scsi_ctrlrs(&req); ++ return; ++ ++invalid: ++ free_rpc_delete_ssam_scsi_ctrlrs(&req); ++ spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, ++ spdk_strerror(-rc)); ++ return; ++} ++SPDK_RPC_REGISTER("delete_scsi_controller", rpc_ssam_delete_scsi_controller, SPDK_RPC_RUNTIME) ++ ++struct rpc_get_ssam_ctrlrs { ++ uint32_t function_id; ++ char *dbdf; ++}; ++ ++static const struct spdk_json_object_decoder g_rpc_get_ssam_ctrlrs_decoder[] = { ++ {"function_id", offsetof(struct rpc_get_ssam_ctrlrs, function_id), spdk_json_decode_uint32, true}, ++ {"dbdf", offsetof(struct rpc_get_ssam_ctrlrs, dbdf), spdk_json_decode_string, true}, ++}; ++ ++static void ++free_rpc_get_ssam_ctrlrs(struct rpc_get_ssam_ctrlrs *req) ++{ ++ if (req->dbdf != NULL) { ++ free(req->dbdf); ++ req->dbdf = NULL; ++ } ++} ++ ++static void ++_rpc_get_ssam_controller(struct spdk_json_write_ctx *w, ++ struct spdk_ssam_dev *smdev, uint16_t gfunc_id) ++{ ++ spdk_json_write_object_begin(w); ++ ++ spdk_json_write_named_string(w, "ctrlr", spdk_ssam_dev_get_name(smdev)); ++ spdk_json_write_named_string_fmt(w, "cpumask", "0x%s", ++ spdk_cpuset_fmt(spdk_thread_get_cpumask(smdev->thread))); ++ spdk_json_write_named_uint32(w, "session_num", (uint32_t)smdev->active_session_num); ++ ++ spdk_json_write_named_object_begin(w, "backend_specific"); ++ ssam_dump_info_json(smdev, gfunc_id, w); ++ spdk_json_write_object_end(w); ++ ++ spdk_json_write_object_end(w); ++} ++ ++static int ++rpc_ssam_show_controllers(struct spdk_jsonrpc_request *request, uint16_t gfunc_id) ++{ ++ struct spdk_ssam_dev *smdev = NULL; ++ struct spdk_json_write_ctx *w = NULL; ++ struct spdk_ssam_session *smsession = NULL; ++ ++ spdk_ssam_lock(); ++ if (gfunc_id != SPDK_INVALID_GFUNC_ID) { ++ smsession = spdk_ssam_session_find(gfunc_id); ++ if (smsession == NULL) { ++ spdk_ssam_unlock(); ++ return -ENODEV; ++ } ++ ++ smdev = smsession->smdev; ++ ++ w = spdk_jsonrpc_begin_result(request); ++ spdk_json_write_array_begin(w); ++ ++ _rpc_get_ssam_controller(w, smdev, gfunc_id); ++ spdk_ssam_unlock(); ++ ++ spdk_json_write_array_end(w); ++ spdk_jsonrpc_end_result(request, w); ++ return 0; ++ } ++ ++ w = spdk_jsonrpc_begin_result(request); ++ spdk_json_write_array_begin(w); ++ ++ smdev = spdk_ssam_dev_next(NULL); ++ while (smdev != NULL) { ++ _rpc_get_ssam_controller(w, smdev, gfunc_id); ++ smdev = spdk_ssam_dev_next(smdev); ++ } ++ spdk_ssam_unlock(); ++ spdk_json_write_array_end(w); ++ spdk_jsonrpc_end_result(request, w); ++ ++ return 0; ++} ++ ++static int ++rpc_ssam_show_scsi_controllers(struct spdk_jsonrpc_request *request, uint16_t gfunc_id) ++{ ++ struct spdk_ssam_dev *smdev = NULL; ++ struct spdk_json_write_ctx *w = NULL; ++ struct spdk_ssam_session *smsession = NULL; ++ ++ spdk_ssam_lock(); ++ if (gfunc_id != SPDK_INVALID_GFUNC_ID) { ++ smsession = spdk_ssam_session_find(gfunc_id); ++ if (smsession == NULL) { ++ spdk_ssam_unlock(); ++ return -ENODEV; ++ } ++ ++ smdev = smsession->smdev; ++ ++ w = spdk_jsonrpc_begin_result(request); ++ spdk_json_write_array_begin(w); ++ ++ smsession = smdev->smsessions[gfunc_id]; ++ smsession->backend->dump_info_json(smsession, w); ++ spdk_ssam_unlock(); ++ ++ spdk_json_write_array_end(w); ++ spdk_jsonrpc_end_result(request, w); ++ return 0; ++ } ++ ++ w = spdk_jsonrpc_begin_result(request); ++ spdk_json_write_array_begin(w); ++ ++ smdev = spdk_ssam_dev_next(NULL); ++ while (smdev != NULL) { ++ smsession = ssam_sessions_next(smdev->smsessions, NULL); ++ while (smsession != NULL) { ++ smsession->backend->dump_info_json(smsession, w); ++ smsession = ssam_sessions_next(smdev->smsessions, smsession); ++ } ++ smdev = spdk_ssam_dev_next(smdev); ++ } ++ spdk_ssam_unlock(); ++ spdk_json_write_array_end(w); ++ spdk_jsonrpc_end_result(request, w); ++ ++ return 0; ++} ++ ++static void ++rpc_ssam_get_controllers(struct spdk_jsonrpc_request *request, ++ const struct spdk_json_val *params) ++{ ++ struct rpc_get_ssam_ctrlrs req = { ++ .function_id = SPDK_INVALID_GFUNC_ID, ++ .dbdf = NULL, ++ }; ++ uint16_t gfunc_id = SPDK_INVALID_GFUNC_ID; ++ int rc; ++ ++ if (params != NULL) { ++ rc = spdk_json_decode_object(params, g_rpc_get_ssam_ctrlrs_decoder, ++ SPDK_COUNTOF(g_rpc_get_ssam_ctrlrs_decoder), &req); ++ if (rc != 0) { ++ SPDK_DEBUGLOG(ssam_rpc, "spdk_json_decode_object failed\n"); ++ rc = -EINVAL; ++ goto invalid; ++ } ++ } ++ ++ if (req.function_id != SPDK_INVALID_GFUNC_ID && req.dbdf != NULL) { ++ SPDK_ERRLOG("get_controllers can have at most one parameter\n"); ++ rc = -EINVAL; ++ goto invalid; ++ } ++ ++ if (req.function_id != SPDK_INVALID_GFUNC_ID) { ++ gfunc_id = req.function_id; ++ rc = spdk_ssam_rpc_para_check(gfunc_id); ++ if (rc != 0) { ++ goto invalid; ++ } ++ } ++ ++ if (req.dbdf != NULL) { ++ rc = spdk_ssam_rpc_get_gfunc_id_by_dbdf(req.dbdf, &gfunc_id); ++ if (rc != 0) { ++ goto invalid; ++ } ++ rc = spdk_ssam_rpc_para_check(gfunc_id); ++ if (rc != 0) { ++ goto invalid; ++ } ++ } ++ ++ rc = rpc_ssam_show_controllers(request, gfunc_id); ++ if (rc != 0) { ++ goto invalid; ++ } ++ ++ free_rpc_get_ssam_ctrlrs(&req); ++ return; ++ ++invalid: ++ free_rpc_get_ssam_ctrlrs(&req); ++ spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, ++ spdk_strerror(-rc)); ++ return; ++} ++SPDK_RPC_REGISTER("get_controllers", rpc_ssam_get_controllers, SPDK_RPC_RUNTIME) ++ ++struct rpc_get_ssam_scsi_ctrlrs { ++ char *name; ++}; ++ ++static const struct spdk_json_object_decoder g_rpc_get_ssam_scsi_ctrlrs_decoder[] = { ++ {"name", offsetof(struct rpc_get_ssam_scsi_ctrlrs, name), spdk_json_decode_string, true}, ++}; ++ ++static void ++free_rpc_ssam_ctrlrs(struct rpc_get_ssam_scsi_ctrlrs *req) ++{ ++ if (req->name != NULL) { ++ free(req->name); ++ req->name = NULL; ++ } ++} ++ ++static void ++rpc_ssam_get_scsi_controllers(struct spdk_jsonrpc_request *request, ++ const struct spdk_json_val *params) ++{ ++ struct rpc_get_ssam_scsi_ctrlrs req = { ++ .name = NULL, ++ }; ++ uint16_t gfunc_id = SPDK_INVALID_GFUNC_ID; ++ int rc; ++ ++ if (params != NULL) { ++ rc = spdk_json_decode_object(params, g_rpc_get_ssam_scsi_ctrlrs_decoder, ++ SPDK_COUNTOF(g_rpc_get_ssam_scsi_ctrlrs_decoder), &req); ++ if (rc != 0) { ++ SPDK_DEBUGLOG(ssam_rpc, "spdk_json_decode_object failed\n"); ++ rc = -EINVAL; ++ goto invalid; ++ } ++ } ++ ++ if (req.name != NULL) { ++ gfunc_id = spdk_ssam_get_gfunc_id_by_name(req.name); ++ rc = spdk_ssam_rpc_para_check(gfunc_id); ++ if (rc != 0) { ++ goto invalid; ++ } ++ } ++ ++ rc = rpc_ssam_show_scsi_controllers(request, gfunc_id); ++ if (rc != 0) { ++ goto invalid; ++ } ++ ++ free_rpc_ssam_ctrlrs(&req); ++ return; ++ ++invalid: ++ free_rpc_ssam_ctrlrs(&req); ++ spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, ++ spdk_strerror(-rc)); ++ return; ++} ++SPDK_RPC_REGISTER("get_scsi_controllers", rpc_ssam_get_scsi_controllers, SPDK_RPC_RUNTIME) ++ ++struct rpc_ssam_controller_get_iostat { ++ uint32_t function_id; ++ char *dbdf; ++}; ++ ++static const struct spdk_json_object_decoder g_rpc_ssam_controller_get_iostat_decoder[] = { ++ {"function_id", offsetof(struct rpc_ssam_controller_get_iostat, function_id), spdk_json_decode_uint32, true}, ++ {"dbdf", offsetof(struct rpc_ssam_controller_get_iostat, dbdf), spdk_json_decode_string, true}, ++}; ++ ++static void ++free_rpc_ssam_controller_get_iostat(struct rpc_ssam_controller_get_iostat *req) ++{ ++ if (req->dbdf != NULL) { ++ free(req->dbdf); ++ req->dbdf = NULL; ++ } ++} ++ ++static int ++rpc_ssam_show_iostat(struct spdk_jsonrpc_request *request, uint16_t gfunc_id) ++{ ++ struct spdk_ssam_dev *smdev = NULL; ++ struct spdk_json_write_ctx *w = NULL; ++ struct spdk_ssam_session *smsession = NULL; ++ ++ spdk_ssam_lock(); ++ if (gfunc_id != SPDK_INVALID_GFUNC_ID) { ++ smsession = spdk_ssam_session_find(gfunc_id); ++ if (smsession == NULL) { ++ spdk_ssam_unlock(); ++ return -ENODEV; ++ } ++ ++ w = spdk_jsonrpc_begin_result(request); ++ spdk_json_write_object_begin(w); ++ spdk_json_write_named_uint64(w, "tick_rate", spdk_get_ticks_hz()); ++ spdk_json_write_named_array_begin(w, "dbdfs"); ++ ++ if (smsession->backend->show_iostat_json != NULL) { ++ smsession->backend->show_iostat_json(smsession, SPDK_SSAM_SCSI_CTRLR_MAX_DEVS, w); ++ } ++ ++ spdk_ssam_unlock(); ++ ++ spdk_json_write_array_end(w); ++ spdk_json_write_object_end(w); ++ spdk_jsonrpc_end_result(request, w); ++ return 0; ++ } ++ ++ w = spdk_jsonrpc_begin_result(request); ++ spdk_json_write_object_begin(w); ++ spdk_json_write_named_uint64(w, "tick_rate", spdk_get_ticks_hz()); ++ spdk_json_write_named_array_begin(w, "dbdfs"); ++ ++ smdev = spdk_ssam_dev_next(NULL); ++ while (smdev != NULL) { ++ smsession = ssam_sessions_next(smdev->smsessions, NULL); ++ spdk_json_write_object_begin(w); ++ spdk_json_write_named_string(w, "name", smdev->name); ++ spdk_json_write_named_uint64(w, "flight_io", smdev->io_num); ++ spdk_json_write_named_uint64(w, "discard_io_num", smdev->discard_io_num); ++ spdk_json_write_named_uint64(w, "wait_io", smdev->io_wait_cnt); ++ spdk_json_write_named_uint64(w, "wait_io_r", smdev->io_wait_r_cnt); ++ spdk_json_write_object_end(w); ++ while (smsession != NULL) { ++ if (smsession->backend->show_iostat_json != NULL) { ++ smsession->backend->show_iostat_json(smsession, SPDK_SSAM_SCSI_CTRLR_MAX_DEVS, w); ++ } ++ smsession = ssam_sessions_next(smdev->smsessions, smsession); ++ } ++ smdev = spdk_ssam_dev_next(smdev); ++ } ++ ++ spdk_ssam_unlock(); ++ spdk_json_write_array_end(w); ++ spdk_json_write_object_end(w); ++ spdk_jsonrpc_end_result(request, w); ++ return 0; ++} ++ ++static void ++rpc_ssam_controller_get_iostat(struct spdk_jsonrpc_request *request, ++ const struct spdk_json_val *params) ++{ ++ struct rpc_ssam_controller_get_iostat req = { ++ .function_id = SPDK_INVALID_GFUNC_ID, ++ .dbdf = NULL, ++ }; ++ uint16_t gfunc_id = SPDK_INVALID_GFUNC_ID; ++ int rc; ++ ++ if (params != NULL) { ++ rc = spdk_json_decode_object(params, g_rpc_ssam_controller_get_iostat_decoder, ++ SPDK_COUNTOF(g_rpc_ssam_controller_get_iostat_decoder), &req); ++ if (rc != 0) { ++ SPDK_DEBUGLOG(ssam_rpc, "spdk_json_decode_object failed\n"); ++ rc = -EINVAL; ++ goto invalid; ++ } ++ } ++ ++ if (req.function_id != SPDK_INVALID_GFUNC_ID && req.dbdf != NULL) { ++ SPDK_ERRLOG("controller_get_iostat can have at most one parameter\n"); ++ rc = -EINVAL; ++ goto invalid; ++ } ++ ++ if (req.function_id != SPDK_INVALID_GFUNC_ID) { ++ gfunc_id = req.function_id; ++ rc = spdk_ssam_rpc_para_check(gfunc_id); ++ if (rc != 0) { ++ goto invalid; ++ } ++ } ++ ++ if (req.dbdf != NULL) { ++ rc = spdk_ssam_rpc_get_gfunc_id_by_dbdf(req.dbdf, &gfunc_id); ++ if (rc != 0) { ++ goto invalid; ++ } ++ rc = spdk_ssam_rpc_para_check(gfunc_id); ++ if (rc != 0) { ++ goto invalid; ++ } ++ } ++ ++ rc = rpc_ssam_show_iostat(request, gfunc_id); ++ if (rc != 0) { ++ goto invalid; ++ } ++ ++ free_rpc_ssam_controller_get_iostat(&req); ++ return; ++ ++invalid: ++ free_rpc_ssam_controller_get_iostat(&req); ++ spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, ++ spdk_strerror(-rc)); ++ return; ++} ++SPDK_RPC_REGISTER("controller_get_iostat", rpc_ssam_controller_get_iostat, SPDK_RPC_RUNTIME) ++ ++static void ++rpc_ssam_clear_iostat(void) ++{ ++ struct spdk_ssam_dev *smdev = NULL; ++ struct spdk_ssam_session *smsession = NULL; ++ ++ spdk_ssam_lock(); ++ smdev = spdk_ssam_dev_next(NULL); ++ while (smdev != NULL) { ++ smsession = ssam_sessions_next(smdev->smsessions, NULL); ++ while (smsession != NULL) { ++ if (smsession->backend->clear_iostat_json != NULL) { ++ smsession->backend->clear_iostat_json(smsession); ++ } ++ smsession = ssam_sessions_next(smdev->smsessions, smsession); ++ } ++ smdev = spdk_ssam_dev_next(smdev); ++ } ++ spdk_ssam_unlock(); ++} ++ ++static void ++rpc_ssam_controller_clear_iostat(struct spdk_jsonrpc_request *request, ++ const struct spdk_json_val *params) ++{ ++ rpc_ssam_clear_iostat(); ++ spdk_jsonrpc_send_bool_response(request, true); ++ return; ++} ++SPDK_RPC_REGISTER("controller_clear_iostat", rpc_ssam_controller_clear_iostat, SPDK_RPC_RUNTIME) ++ ++struct rpc_bdev_resize { ++ uint32_t function_id; ++ uint64_t new_size_in_mb; ++}; ++ ++static const struct spdk_json_object_decoder g_rpc_bdev_resize[] = { ++ {"function_id", offsetof(struct rpc_bdev_resize, function_id), spdk_json_decode_uint32}, ++ {"new_size_in_mb", offsetof(struct rpc_bdev_resize, new_size_in_mb), spdk_json_decode_uint64}, ++}; ++ ++static int ++ssam_bdev_resize(struct spdk_bdev *bdev, uint64_t new_size_in_mb) ++{ ++ char *bdev_name = bdev->name; ++ int rc; ++ uint64_t current_size_in_mb; ++ uint64_t new_size_in_byte; ++ ++ if (bdev->blocklen == 0) { ++ SPDK_ERRLOG("The blocklen of bdev %s is zero\n", bdev_name); ++ return -EINVAL; ++ } ++ ++ if (UINT64_MAX / bdev->blockcnt < bdev->blocklen) { ++ SPDK_ERRLOG("The old size of bdev is too large, blockcnt: %lu, blocklen: %u\n", ++ bdev->blockcnt, bdev->blocklen); ++ return -EINVAL; ++ } ++ ++ if (new_size_in_mb == 0) { ++ goto end; ++ } ++ ++ current_size_in_mb = bdev->blocklen * bdev->blockcnt / SSAM_MB; ++ if (new_size_in_mb < current_size_in_mb) { ++ SPDK_ERRLOG("The new bdev size must not be smaller than current bdev size\n"); ++ return -EINVAL; ++ } ++ ++ if (UINT64_MAX / new_size_in_mb < SSAM_MB) { ++ SPDK_ERRLOG("The new bdev size is too large\n"); ++ return -EINVAL; ++ } ++ ++end: ++ new_size_in_byte = new_size_in_mb * SSAM_MB; ++ ++ rc = spdk_bdev_notify_blockcnt_change(bdev, new_size_in_byte / bdev->blocklen); ++ if (rc != 0) { ++ SPDK_ERRLOG("failed to notify block cnt change\n"); ++ return -EINVAL; ++ } ++ SPDK_NOTICELOG("bdev %s resize %lu(mb) done.\n", bdev->name, new_size_in_mb); ++ ++ return 0; ++} ++ ++static void ++rpc_ssam_bdev_resize(struct spdk_jsonrpc_request *request, ++ const struct spdk_json_val *params) ++{ ++ struct rpc_bdev_resize req = {0}; ++ uint16_t gfunc_id = SPDK_INVALID_GFUNC_ID; ++ struct spdk_ssam_session *smsession = NULL; ++ struct spdk_bdev *bdev = NULL; ++ int rc; ++ ++ if (params == NULL) { ++ SPDK_ERRLOG("rpc_ssam_bdev_resize params null\n"); ++ rc = -EINVAL; ++ goto invalid; ++ } ++ ++ rc = spdk_json_decode_object(params, g_rpc_bdev_resize, ++ SPDK_COUNTOF(g_rpc_bdev_resize), &req); ++ if (rc != 0) { ++ SPDK_ERRLOG("spdk_json_decode_object failed\n"); ++ rc = -EINVAL; ++ goto invalid; ++ } ++ ++ gfunc_id = req.function_id; ++ rc = spdk_ssam_rpc_para_check_type(gfunc_id, SSAM_DEVICE_VIRTIO_BLK); ++ if (rc != 0) { ++ goto invalid; ++ } ++ ++ spdk_ssam_lock(); ++ smsession = spdk_ssam_session_find(gfunc_id); ++ if (smsession == NULL) { ++ SPDK_ERRLOG("Before resize target, there need to create controller.\n"); ++ rc = -ENODEV; ++ spdk_ssam_unlock(); ++ goto invalid; ++ } ++ ++ if (smsession->backend->get_bdev != NULL) { ++ bdev = smsession->backend->get_bdev(smsession, 0); ++ } ++ if (bdev == NULL) { ++ SPDK_ERRLOG("The controller hasn't correlated to a bdev.\n"); ++ rc = -ENODEV; ++ spdk_ssam_unlock(); ++ goto invalid; ++ } ++ ++ spdk_ssam_unlock(); ++ ++ rc = ssam_bdev_resize(bdev, req.new_size_in_mb); ++ if (rc != 0) { ++ goto invalid; ++ } ++ ++ spdk_jsonrpc_send_bool_response(request, true); ++ return; ++ ++invalid: ++ spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, ++ spdk_strerror(-rc)); ++} ++ ++SPDK_RPC_REGISTER("bdev_resize", rpc_ssam_bdev_resize, SPDK_RPC_RUNTIME) ++ ++struct rpc_scsi_bdev_resize { ++ char *name; ++ uint32_t tgt_id; ++ uint64_t new_size_in_mb; ++}; ++ ++static const struct spdk_json_object_decoder g_rpc_scsi_bdev_resize[] = { ++ {"name", offsetof(struct rpc_scsi_bdev_resize, name), spdk_json_decode_string}, ++ {"tgt_id", offsetof(struct rpc_scsi_bdev_resize, tgt_id), spdk_json_decode_uint32}, ++ {"new_size_in_mb", offsetof(struct rpc_scsi_bdev_resize, new_size_in_mb), spdk_json_decode_uint64}, ++}; ++ ++static void ++free_rpc_scsi_bdev_resize(struct rpc_scsi_bdev_resize *req) ++{ ++ if (req->name != NULL) { ++ free(req->name); ++ req->name = NULL; ++ } ++} ++ ++static void ++rpc_ssam_scsi_bdev_resize(struct spdk_jsonrpc_request *request, ++ const struct spdk_json_val *params) ++{ ++ struct rpc_scsi_bdev_resize req = {0}; ++ uint16_t gfunc_id = SPDK_INVALID_GFUNC_ID; ++ struct spdk_ssam_session *smsession = NULL; ++ struct spdk_bdev *bdev = NULL; ++ int rc; ++ ++ if (params == NULL) { ++ SPDK_ERRLOG("rpc_ssam_bdev_resize params null\n"); ++ rc = -EINVAL; ++ goto invalid; ++ } ++ ++ rc = spdk_json_decode_object(params, g_rpc_scsi_bdev_resize, ++ SPDK_COUNTOF(g_rpc_scsi_bdev_resize), &req); ++ if (rc != 0) { ++ SPDK_ERRLOG("spdk_json_decode_object failed\n"); ++ rc = -EINVAL; ++ goto invalid; ++ } ++ ++ gfunc_id = spdk_ssam_get_gfunc_id_by_name(req.name); ++ rc = spdk_ssam_rpc_para_check_type(gfunc_id, SSAM_DEVICE_VIRTIO_SCSI); ++ if (rc != 0) { ++ goto invalid; ++ } ++ ++ spdk_ssam_lock(); ++ smsession = spdk_ssam_session_find(gfunc_id); ++ if (smsession == NULL) { ++ SPDK_ERRLOG("Before resize target, there need to create controller.\n"); ++ rc = -ENODEV; ++ spdk_ssam_unlock(); ++ goto invalid; ++ } ++ ++ if (smsession->backend->get_bdev != NULL) { ++ bdev = smsession->backend->get_bdev(smsession, req.tgt_id); ++ } ++ if (bdev == NULL) { ++ rc = -ENODEV; ++ spdk_ssam_unlock(); ++ goto invalid; ++ } ++ ++ spdk_ssam_unlock(); ++ ++ rc = ssam_bdev_resize(bdev, req.new_size_in_mb); ++ if (rc != 0) { ++ goto invalid; ++ } ++ ++ free_rpc_scsi_bdev_resize(&req); ++ spdk_jsonrpc_send_bool_response(request, true); ++ return; ++ ++invalid: ++ free_rpc_scsi_bdev_resize(&req); ++ spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, ++ spdk_strerror(-rc)); ++} ++ ++SPDK_RPC_REGISTER("scsi_bdev_resize", rpc_ssam_scsi_bdev_resize, SPDK_RPC_RUNTIME) ++ ++struct rpc_bdev_aio_resize { ++ char *name; ++ uint64_t new_size_in_mb; ++}; ++ ++static const struct spdk_json_object_decoder g_rpc_bdev_aio_resize[] = { ++ {"name", offsetof(struct rpc_bdev_aio_resize, name), spdk_json_decode_string}, ++ {"new_size_in_mb", offsetof(struct rpc_bdev_aio_resize, new_size_in_mb), spdk_json_decode_uint64}, ++}; ++ ++static void ++free_rpc_ssam_bdev_aio_resize(struct rpc_bdev_aio_resize *req) ++{ ++ if (req->name != NULL) { ++ free(req->name); ++ req->name = NULL; ++ } ++} ++ ++static void ++rpc_ssam_bdev_aio_resize(struct spdk_jsonrpc_request *request, ++ const struct spdk_json_val *params) ++{ ++ struct rpc_bdev_aio_resize req = {0}; ++ struct spdk_bdev *bdev = NULL; ++ int rc; ++ ++ if (params == NULL) { ++ SPDK_ERRLOG("rpc_ssam_bdev_resize params null\n"); ++ rc = -EINVAL; ++ goto invalid; ++ } ++ ++ rc = spdk_json_decode_object(params, g_rpc_bdev_aio_resize, ++ SPDK_COUNTOF(g_rpc_bdev_aio_resize), &req); ++ if (rc != 0) { ++ SPDK_ERRLOG("spdk_json_decode_object failed\n"); ++ rc = -EINVAL; ++ goto invalid; ++ } ++ ++ if (req.name) { ++ bdev = spdk_bdev_get_by_name(req.name); ++ if (bdev == NULL) { ++ SPDK_ERRLOG("bdev '%s' does not exist\n", req.name); ++ rc = -EINVAL; ++ goto invalid; ++ } ++ } ++ ++ rc = ssam_bdev_resize(bdev, req.new_size_in_mb); ++ if (rc != 0) { ++ goto invalid; ++ } ++ ++ free_rpc_ssam_bdev_aio_resize(&req); ++ spdk_jsonrpc_send_bool_response(request, true); ++ return; ++ ++invalid: ++ free_rpc_ssam_bdev_aio_resize(&req); ++ spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, ++ spdk_strerror(-rc)); ++} ++ ++SPDK_RPC_REGISTER("bdev_aio_resize", rpc_ssam_bdev_aio_resize, SPDK_RPC_RUNTIME) ++ ++static void ++rpc_os_ready(struct spdk_jsonrpc_request *request, const struct spdk_json_val *params) ++{ ++ int rc = 0; ++ int fd; ++ char *enable = "1"; ++ ++ fd = open(SSAM_STORAGE_READY_FILE, O_RDWR); ++ if (fd < 0) { ++ SPDK_ERRLOG("Open storage ready file failed.\n"); ++ rc = EPERM; ++ goto invalid; ++ } ++ ++ rc = write(fd, enable, strlen(enable)); ++ if (rc < 0) { ++ SPDK_ERRLOG("Write storage ready file failed.\n"); ++ close(fd); ++ goto invalid; ++ } ++ ++ close(fd); ++ spdk_jsonrpc_send_bool_response(request, true); ++ return; ++ ++invalid: ++ spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, ++ spdk_strerror(-rc)); ++} ++ ++SPDK_RPC_REGISTER("os_ready", rpc_os_ready, SPDK_RPC_RUNTIME) ++ ++struct rpc_create_scsi_controller { ++ char *dbdf; ++ char *name; ++}; ++ ++static const struct spdk_json_object_decoder g_rpc_create_scsi_controller[] = { ++ {"dbdf", offsetof(struct rpc_create_scsi_controller, dbdf), spdk_json_decode_string}, ++ {"name", offsetof(struct rpc_create_scsi_controller, name), spdk_json_decode_string}, ++}; ++ ++static void ++free_rpc_ssam_create_scsi_controller(struct rpc_create_scsi_controller *req) ++{ ++ if (req->name != NULL) { ++ free(req->name); ++ req->name = NULL; ++ } ++ if (req->dbdf != NULL) { ++ free(req->dbdf); ++ req->dbdf = NULL; ++ } ++} ++ ++static int ++spdk_ssam_rpc_get_gfunc_id_by_dbdf(char *dbdf, uint16_t *gfunc_id) ++{ ++ int rc; ++ uint32_t dbdf_num; ++ ++ rc = ssam_dbdf_str2num(dbdf, &dbdf_num); ++ if (rc != 0) { ++ SPDK_ERRLOG("convert dbdf(%s) to num failed, rc: %d.\n", dbdf, rc); ++ return -EINVAL; ++ } ++ ++ rc = ssam_get_funcid_by_dbdf(dbdf_num, gfunc_id); ++ if (rc != 0) { ++ SPDK_ERRLOG("find gfuncid by dbdf(%u) failed, rc: %d.\n", dbdf_num, rc); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int ++spdk_ssam_rpc_para_check_name(char *name) ++{ ++ uint16_t gfunc_id = spdk_ssam_get_gfunc_id_by_name(name); ++ if (gfunc_id == SPDK_INVALID_GFUNC_ID) { ++ return 0; ++ } ++ ++ return -EINVAL; ++} ++ ++static void ++rpc_ssam_create_scsi_controller(struct spdk_jsonrpc_request *request, ++ const struct spdk_json_val *params) ++{ ++ struct spdk_ssam_session_reg_info info = {0}; ++ struct rpc_create_scsi_controller req = {0}; ++ uint16_t gfunc_id = SPDK_INVALID_GFUNC_ID; ++ int rc; ++ uint16_t queues; ++ ++ if (params == NULL) { ++ SPDK_ERRLOG("rpc_ssam_create_scsi_controller params null\n"); ++ rc = -EINVAL; ++ goto invalid; ++ } ++ ++ rc = spdk_json_decode_object(params, g_rpc_create_scsi_controller, ++ SPDK_COUNTOF(g_rpc_create_scsi_controller), &req); ++ if (rc != 0) { ++ SPDK_ERRLOG("spdk_json_decode_object failed\n"); ++ rc = -EINVAL; ++ goto invalid; ++ } ++ ++ rc = spdk_ssam_rpc_para_check_name(req.name); ++ if (rc != 0) { ++ SPDK_ERRLOG("controller name(%s) is existed\n", req.name); ++ goto invalid; ++ } ++ ++ rc = spdk_ssam_rpc_get_gfunc_id_by_dbdf(req.dbdf, &gfunc_id); ++ if (rc != 0) { ++ goto invalid; ++ } ++ ++ rc = spdk_ssam_rpc_para_check_type(gfunc_id, SSAM_DEVICE_VIRTIO_SCSI); ++ if (rc != 0) { ++ goto invalid; ++ } ++ ++ queues = spdk_ssam_get_queues(); ++ if (queues > SPDK_SSAM_MAX_VQUEUES) { ++ SPDK_ERRLOG("Queue number out of range, need less or equal than %u, actually %u.\n", ++ SPDK_SSAM_MAX_VQUEUES, queues); ++ rc = -EINVAL; ++ goto invalid; ++ } ++ ++ rpc_init_session_reg_info(&info, queues, gfunc_id, request); ++ ++ info.name = strdup(req.name); ++ if (info.name == NULL) { ++ SPDK_ERRLOG("Failed to create name(%s) for ssam session reg info.\n", req.name); ++ rc = -EINVAL; ++ goto invalid; ++ } ++ ++ info.dbdf = strdup(req.dbdf); ++ if (info.dbdf == NULL) { ++ SPDK_ERRLOG("Failed to create dbdf(%s) for ssam session reg info.\n", req.dbdf); ++ rc = -EINVAL; ++ goto invalid; ++ } ++ ++ rc = spdk_ssam_scsi_construct(&info); ++ if (rc < 0) { ++ goto invalid; ++ } ++ ++ free_rpc_ssam_create_scsi_controller(&req); ++ free_rpc_ssam_session_reg_info(&info); ++ return; ++ ++invalid: ++ free_rpc_ssam_create_scsi_controller(&req); ++ free_rpc_ssam_session_reg_info(&info); ++ spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, ++ spdk_strerror(-rc)); ++ return; ++} ++ ++SPDK_RPC_REGISTER("create_scsi_controller", rpc_ssam_create_scsi_controller, SPDK_RPC_RUNTIME) ++ ++struct rpc_scsi_controller_add_target { ++ char *name; ++ int32_t scsi_tgt_num; ++ char *bdev_name; ++}; ++ ++static const struct spdk_json_object_decoder g_rpc_scsi_controller_add_target[] = { ++ {"name", offsetof(struct rpc_scsi_controller_add_target, name), spdk_json_decode_string}, ++ {"scsi_tgt_num", offsetof(struct rpc_scsi_controller_add_target, scsi_tgt_num), spdk_json_decode_uint32}, ++ {"bdev_name", offsetof(struct rpc_scsi_controller_add_target, bdev_name), spdk_json_decode_string}, ++}; ++ ++static void ++free_rpc_ssam_scsi_ctrlr_add_target(struct rpc_scsi_controller_add_target *req) ++{ ++ if (req->name != NULL) { ++ free(req->name); ++ req->name = NULL; ++ } ++ if (req->bdev_name != NULL) { ++ free(req->bdev_name); ++ req->bdev_name = NULL; ++ } ++} ++ ++static void ++rpc_ssam_scsi_controller_add_target(struct spdk_jsonrpc_request *request, ++ const struct spdk_json_val *params) ++{ ++ struct rpc_scsi_controller_add_target req = {0}; ++ uint16_t gfunc_id = SPDK_INVALID_GFUNC_ID; ++ struct spdk_ssam_session *smsession; ++ int rc; ++ ++ if (params == NULL) { ++ SPDK_ERRLOG("rpc_ssam_scsi_controller_add_target params null\n"); ++ rc = -EINVAL; ++ goto invalid; ++ } ++ ++ rc = spdk_json_decode_object(params, g_rpc_scsi_controller_add_target, ++ SPDK_COUNTOF(g_rpc_scsi_controller_add_target), &req); ++ if (rc != 0) { ++ SPDK_ERRLOG("spdk_json_decode_object failed\n"); ++ rc = -EINVAL; ++ goto invalid; ++ } ++ ++ gfunc_id = spdk_ssam_get_gfunc_id_by_name(req.name); ++ rc = spdk_ssam_rpc_para_check_type(gfunc_id, SSAM_DEVICE_VIRTIO_SCSI); ++ if (rc != 0) { ++ goto invalid; ++ } ++ ++ spdk_ssam_lock(); ++ smsession = spdk_ssam_session_find(gfunc_id); ++ if (smsession == NULL) { ++ SPDK_ERRLOG("Before adding a SCSI target, there should be a SCSI controller.\n"); ++ rc = -ENODEV; ++ spdk_ssam_unlock(); ++ goto invalid; ++ } ++ ++ rc = rpc_ssam_session_reg_response_cb(smsession, request); ++ if (rc != 0) { ++ SPDK_ERRLOG("The controller is being operated.\n"); ++ rc = -EALREADY; ++ spdk_ssam_unlock(); ++ goto invalid; ++ } ++ ++ rc = spdk_ssam_scsi_dev_add_tgt(smsession, req.scsi_tgt_num, req.bdev_name); ++ if (rc != 0) { ++ /* ++ * Unregitster response cb to avoid use request in the cb function, ++ * because if error happend, request will be responsed immediately ++ */ ++ ssam_session_unreg_response_cb(smsession); ++ spdk_ssam_unlock(); ++ goto invalid; ++ } ++ spdk_ssam_unlock(); ++ ++ free_rpc_ssam_scsi_ctrlr_add_target(&req); ++ return; ++ ++invalid: ++ free_rpc_ssam_scsi_ctrlr_add_target(&req); ++ spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, ++ spdk_strerror(-rc)); ++} ++ ++SPDK_RPC_REGISTER("scsi_controller_add_target", rpc_ssam_scsi_controller_add_target, SPDK_RPC_RUNTIME) ++ ++struct rpc_scsi_controller_remove_target { ++ char *name; ++ int32_t scsi_tgt_num; ++}; ++ ++static const struct spdk_json_object_decoder g_rpc_scsi_controller_remove_target[] = { ++ {"name", offsetof(struct rpc_scsi_controller_remove_target, name), spdk_json_decode_string}, ++ {"scsi_tgt_num", offsetof(struct rpc_scsi_controller_remove_target, scsi_tgt_num), spdk_json_decode_int32}, ++}; ++ ++static void ++free_rpc_scsi_controller_remove_target(struct rpc_scsi_controller_remove_target *req) ++{ ++ if (req->name != NULL) { ++ free(req->name); ++ req->name = NULL; ++ } ++} ++ ++static void ++rpc_ssam_scsi_controller_remove_target(struct spdk_jsonrpc_request *request, ++ const struct spdk_json_val *params) ++{ ++ struct rpc_scsi_controller_remove_target req = {0}; ++ uint16_t gfunc_id = SPDK_INVALID_GFUNC_ID; ++ int rc; ++ struct spdk_ssam_session *smsession = NULL; ++ ++ if (params == NULL) { ++ SPDK_ERRLOG("rpc_ssam_scsi_controller_remove_target params null\n"); ++ rc = -EINVAL; ++ goto invalid; ++ } ++ ++ rc = spdk_json_decode_object(params, g_rpc_scsi_controller_remove_target, ++ SPDK_COUNTOF(g_rpc_scsi_controller_remove_target), &req); ++ if (rc != 0) { ++ SPDK_ERRLOG("spdk_json_decode_object failed\n"); ++ rc = -EINVAL; ++ goto invalid; ++ } ++ ++ gfunc_id = spdk_ssam_get_gfunc_id_by_name(req.name); ++ rc = spdk_ssam_rpc_para_check_type(gfunc_id, SSAM_DEVICE_VIRTIO_SCSI); ++ if (rc != 0) { ++ goto invalid; ++ } ++ ++ spdk_ssam_lock(); ++ ++ smsession = spdk_ssam_session_find(gfunc_id); ++ if (smsession == NULL) { ++ rc = -ENODEV; ++ spdk_ssam_unlock(); ++ goto invalid; ++ } ++ ++ rc = rpc_ssam_session_reg_response_cb(smsession, request); ++ if (rc != 0) { ++ SPDK_ERRLOG("The controller is being operated.\n"); ++ rc = -EALREADY; ++ spdk_ssam_unlock(); ++ goto invalid; ++ } ++ ++ rc = spdk_ssam_scsi_dev_remove_tgt(smsession, req.scsi_tgt_num, ++ rpc_ssam_send_response_cb, request); ++ if (rc != 0) { ++ /* ++ * Unregitster response cb to avoid use request in the cb function, ++ * because if error happend, request will be responsed immediately ++ */ ++ ssam_session_unreg_response_cb(smsession); ++ spdk_ssam_unlock(); ++ goto invalid; ++ } ++ spdk_ssam_unlock(); ++ free_rpc_scsi_controller_remove_target(&req); ++ return; ++ ++invalid: ++ free_rpc_scsi_controller_remove_target(&req); ++ spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, ++ spdk_strerror(-rc)); ++} ++ ++SPDK_RPC_REGISTER("scsi_controller_remove_target", rpc_ssam_scsi_controller_remove_target, SPDK_RPC_RUNTIME) ++ ++struct rpc_ssam_scsi_device_iostat { ++ char *name; ++ int32_t scsi_tgt_num; ++}; ++ ++static const struct spdk_json_object_decoder g_rpc_ssam_scsi_device_iostat[] = { ++ {"name", offsetof(struct rpc_ssam_scsi_device_iostat, name), spdk_json_decode_string}, ++ {"scsi_tgt_num", offsetof(struct rpc_ssam_scsi_device_iostat, scsi_tgt_num), spdk_json_decode_int32}, ++}; ++ ++static void ++free_rpc_ssam_scsi_device_iostat(struct rpc_ssam_scsi_device_iostat *req) ++{ ++ if (req->name != NULL) { ++ free(req->name); ++ req->name = NULL; ++ } ++} ++ ++static int ++rpc_ssam_show_scsi_iostat(struct spdk_jsonrpc_request *request, uint16_t gfunc_id, uint16_t scsi_tgt_num) ++{ ++ struct spdk_json_write_ctx *w = NULL; ++ struct spdk_ssam_session *smsession = NULL; ++ ++ spdk_ssam_lock(); ++ smsession = spdk_ssam_session_find(gfunc_id); ++ if (smsession == NULL) { ++ spdk_ssam_unlock(); ++ return -ENODEV; ++ } ++ ++ w = spdk_jsonrpc_begin_result(request); ++ ++ if (smsession->backend->show_iostat_json != NULL) { ++ smsession->backend->show_iostat_json(smsession, scsi_tgt_num, w); ++ } ++ ++ spdk_ssam_unlock(); ++ ++ spdk_jsonrpc_end_result(request, w); ++ return 0; ++} ++ ++static void ++rpc_ssam_scsi_device_iostat(struct spdk_jsonrpc_request *request, ++ const struct spdk_json_val *params) ++{ ++ struct rpc_ssam_scsi_device_iostat req = {0}; ++ uint16_t gfunc_id = SPDK_INVALID_GFUNC_ID; ++ int rc; ++ ++ if (params == NULL) { ++ SPDK_ERRLOG("rpc_ssam_scsi_device_iostat params null\n"); ++ rc = -EINVAL; ++ goto invalid; ++ } ++ ++ rc = spdk_json_decode_object(params, g_rpc_ssam_scsi_device_iostat, ++ SPDK_COUNTOF(g_rpc_ssam_scsi_device_iostat), &req); ++ if (rc != 0) { ++ SPDK_ERRLOG("spdk_json_decode_object failed\n"); ++ rc = -EINVAL; ++ goto invalid; ++ } ++ ++ if (req.scsi_tgt_num < 0 || req.scsi_tgt_num > SPDK_SSAM_SCSI_CTRLR_MAX_DEVS) { ++ SPDK_ERRLOG("scsi_tgt_num is out of range\n"); ++ rc = -EINVAL; ++ goto invalid; ++ } ++ ++ gfunc_id = spdk_ssam_get_gfunc_id_by_name(req.name); ++ rc = spdk_ssam_rpc_para_check(gfunc_id); ++ if (rc != 0) { ++ goto invalid; ++ } ++ ++ rc = rpc_ssam_show_scsi_iostat(request, gfunc_id, req.scsi_tgt_num); ++ if (rc != 0) { ++ goto invalid; ++ } ++ ++ free_rpc_ssam_scsi_device_iostat(&req); ++ return; ++ ++invalid: ++ free_rpc_ssam_scsi_device_iostat(&req); ++ spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, ++ spdk_strerror(-rc)); ++ return; ++} ++SPDK_RPC_REGISTER("scsi_device_iostat", rpc_ssam_scsi_device_iostat, SPDK_RPC_RUNTIME) ++ ++struct rpc_limit_log_interval { ++ int interval; ++}; ++ ++static void ++rpc_ssam_device_pcie_list(struct spdk_jsonrpc_request *request, ++ const struct spdk_json_val *params) ++{ ++ struct spdk_json_write_ctx *w = NULL; ++ int rc; ++ uint32_t size = ssam_get_device_pcie_list_size(); ++ if (size == 0) { ++ rc = ssam_init_device_pcie_list(); ++ if (rc != 0) { ++ SPDK_ERRLOG("init device_pcie_list failed\n"); ++ spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, ++ spdk_strerror(-rc)); ++ return; ++ } ++ } ++ ++ w = spdk_jsonrpc_begin_result(request); ++ spdk_json_write_object_begin(w); ++ ++ ssam_dump_device_pcie_list(w); ++ ++ spdk_json_write_object_end(w); ++ spdk_jsonrpc_end_result(request, w); ++ return; ++} ++ ++SPDK_RPC_REGISTER("device_pcie_list", rpc_ssam_device_pcie_list, SPDK_RPC_RUNTIME) ++ ++static void ++rpc_ssam_config_remove(struct spdk_jsonrpc_request *request, const struct spdk_json_val *params) ++{ ++ int rc; ++ ++ char *json_config_file = ssam_rc_get_recover_json_file_path(); ++ rc = access(json_config_file, F_OK); ++ if (rc != 0) { ++ SPDK_ERRLOG("Json config file not found.\n"); ++ goto invalid; ++ } ++ ++ rc = unlink(json_config_file); ++ if (rc != 0) { ++ SPDK_ERRLOG("Json config file remove failed.\n"); ++ goto invalid; ++ } ++ ++ SPDK_NOTICELOG("Json config file remove successfully.\n"); ++ spdk_jsonrpc_send_bool_response(request, true); ++ return; ++ ++invalid: ++ spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, ++ spdk_strerror(-rc)); ++} ++ ++SPDK_RPC_REGISTER("config_remove", rpc_ssam_config_remove, SPDK_RPC_RUNTIME) ++ ++SPDK_LOG_REGISTER_COMPONENT(ssam_rpc) +diff --git a/lib/ssam/ssam_scsi.c b/lib/ssam/ssam_scsi.c +new file mode 100644 +index 0000000..88d9278 +--- /dev/null ++++ b/lib/ssam/ssam_scsi.c +@@ -0,0 +1,2418 @@ ++/*- ++ * BSD LICENSE ++ * ++ * Copyright (c) Huawei Technologies Co., Ltd. 2021-2022. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * ++ * * Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * * Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in ++ * the documentation and/or other materials provided with the ++ * distribution. ++ * * Neither the name of Intel Corporation nor the names of its ++ * contributors may be used to endorse or promote products derived ++ * from this software without specific prior written permission. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ++ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ++ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ++ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ++ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ++ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ++ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ++ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ++ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ++ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#include "linux/virtio_scsi.h" ++ ++#include ++#include ++ ++ ++#include "spdk/likely.h" ++#include "spdk/scsi_spec.h" ++#include "spdk/env.h" ++#include "spdk/scsi.h" ++#include "spdk/ssam.h" ++#include "spdk/string.h" ++#include "spdk/bdev_module.h" ++ ++#include "ssam_internal.h" ++ ++#define SESSION_STOP_POLLER_PERIOD 1000 ++#define IOV_HEADER_TAIL_NUM 2 ++#define PAYLOAD_SIZE_MAX (2048U * 1024) ++#define VMIO_TYPE_VIRTIO_SCSI_CTRL 4 ++#define SSAM_SPDK_SCSI_DEV_MAX_LUN 1 ++#define SSAM_SENSE_DATE_LEN 32 ++#define PERF_STAT ++ ++/* Features supported by virtio-scsi lib. */ ++#define SPDK_SSAM_SCSI_FEATURES (SPDK_SSAM_FEATURES | \ ++ (1ULL << VIRTIO_SCSI_F_INOUT) | \ ++ (1ULL << VIRTIO_SCSI_F_HOTPLUG) | \ ++ (1ULL << VIRTIO_SCSI_F_CHANGE) | \ ++ (1ULL << VIRTIO_SCSI_F_T10_PI)) ++ ++/* Features that are specified in VIRTIO SCSI but currently not supported: ++ * - Live migration not supported yet ++ * - T10 PI ++ */ ++#define SPDK_SSAM_SCSI_DISABLED_FEATURES (SPDK_SSAM_DISABLED_FEATURES | \ ++ (1ULL << VIRTIO_SCSI_F_T10_PI)) ++ ++/* ssam-user-scsi support protocol features */ ++#define SPDK_SSAM_SCSI_PROTOCOL_FEATURES (1ULL << SSAM_USER_PROTOCOL_F_INFLIGHT_SHMFD) ++ ++enum spdk_scsi_dev_ssam_status { ++ /* Target ID is empty. */ ++ SSAM_SCSI_DEV_EMPTY, ++ ++ /* Target is still being added. */ ++ SSAM_SCSI_DEV_ADDING, ++ ++ /* Target ID occupied. */ ++ SSAM_SCSI_DEV_PRESENT, ++ ++ /* Target ID is occupied but removal is in progress. */ ++ SSAM_SCSI_DEV_REMOVING, ++ ++ /* In session - device (SCSI target) seen but removed. */ ++ SSAM_SCSI_DEV_REMOVED, ++}; ++ ++struct ssam_scsi_stat { ++ uint64_t count; ++ uint64_t total_tsc; // pre_dma <- -> post_return ++ uint64_t dma_tsc; // pre_dma <- -> post_dma ++ uint64_t bdev_tsc; // pre_bdev <- -> post_bdev ++ uint64_t bdev_submit_tsc; // <- spdk_bdev_xxx -> ++ uint64_t complete_tsc; // pre_return <- -> post_return ++ uint64_t internel_tsc; // total_tsc - dma_tsc - bdev_tsc - complete_tsc ++ ++ uint64_t complete_read_ios; // Number of successfully completed read requests ++ uint64_t err_read_ios; // Number of failed completed read requests ++ uint64_t complete_write_ios; // Number of successfully completed write requests ++ uint64_t err_write_ios; // Number of failed completed write requests ++ uint64_t flush_ios; // Total number of flush requests ++ uint64_t complete_flush_ios; // Number of successfully completed flush requests ++ uint64_t err_flush_ios; // Number of failed completed flush requests ++ uint64_t fatal_ios; ++ uint64_t io_retry; ++ ++ uint64_t start_count; ++ uint64_t dma_count; ++ uint64_t dma_complete_count; ++ uint64_t bdev_count; ++ uint64_t bdev_complete_count; ++}; ++ ++struct spdk_scsi_dev_io_state { ++ struct spdk_bdev_io_stat stat; ++ uint64_t submit_tsc; ++ struct ssam_scsi_stat scsi_stat; ++}; ++ ++/** Context for a SCSI target in a ssam device */ ++struct spdk_scsi_dev_ssam_state { ++ struct spdk_scsi_dev_io_state *io_stat[SSAM_SPDK_SCSI_DEV_MAX_LUN]; ++ struct spdk_scsi_dev *dev; ++ ++ enum spdk_scsi_dev_ssam_status status; ++ ++ uint64_t flight_io; ++}; ++ ++struct ssam_scsi_tgt_hotplug_ctx { ++ unsigned scsi_tgt_num; ++}; ++ ++struct spdk_ssam_scsi_session { ++ struct spdk_ssam_session smsession; ++ int ref; ++ bool registered; ++ struct spdk_poller *stop_poller; ++ struct spdk_scsi_dev_ssam_state scsi_dev_state[SPDK_SSAM_SCSI_CTRLR_MAX_DEVS]; ++ char *dbdf; ++}; ++ ++struct ssam_scsi_session_ctx { ++ struct spdk_ssam_scsi_session *ssmsession; ++ void **user_ctx; ++}; ++ ++struct ssam_scsi_task_stat { ++ uint64_t start_tsc; ++ uint64_t dma_start_tsc; ++ uint64_t dma_end_tsc; ++ uint64_t bdev_start_tsc; ++ uint64_t bdev_func_tsc; ++ uint64_t bdev_end_tsc; ++ uint64_t complete_start_tsc; ++ uint64_t complete_end_tsc; ++}; ++ ++struct spdk_ssam_scsi_task { ++ struct spdk_scsi_task scsi_task; ++ /* Returned status of I/O processing, it can be VIRTIO_BLK_S_OK, ++ * VIRTIO_BLK_S_IOERR or VIRTIO_BLK_S_UNSUPP ++ */ ++ union { ++ struct virtio_scsi_cmd_resp resp; ++ struct virtio_scsi_ctrl_tmf_resp tmf_resp; ++ }; ++ ++ /* Number of bytes processed successfully */ ++ uint32_t used_len; ++ ++ /* Records the amount of valid data in the struct iovec iovs array. */ ++ uint32_t iovcnt; ++ struct ssam_iovec iovs; ++ ++ /* If set, the task is currently used for I/O processing. */ ++ bool used; ++ ++ /* For bdev io wait */ ++ struct spdk_ssam_scsi_session *ssmsession; ++ struct spdk_ssam_session_io_wait session_io_wait; ++ ++ /* ssam request data */ ++ struct ssam_request *io_req; ++ ++ uint16_t vq_idx; ++ uint16_t task_idx; ++ int32_t tgt_id; ++ struct spdk_ssam_session *smsession; ++ struct spdk_scsi_dev *scsi_dev; ++ struct ssam_scsi_task_stat task_stat; ++}; ++ ++struct ssam_add_tgt_ev_ctx { ++ char *bdev_name; ++ int tgt_num; ++}; ++ ++static void ssam_scsi_request_worker(struct spdk_ssam_session *smsession, void *arg); ++static void ssam_scsi_destroy_bdev_device(struct spdk_ssam_session *smsession, void *args); ++static void ssam_scsi_response_worker(struct spdk_ssam_session *smsession, void *arg); ++static int ssam_scsi_remove_session(struct spdk_ssam_session *smsession); ++static void ssam_scsi_remove_self(struct spdk_ssam_session *smsession); ++static void ssam_scsi_dump_info_json(struct spdk_ssam_session *smsession, ++ struct spdk_json_write_ctx *w); ++static void ssam_scsi_write_config_json(struct spdk_ssam_session *smsession, ++ struct spdk_json_write_ctx *w); ++static int ssam_scsi_get_config(struct spdk_ssam_session *smsession, uint8_t *config, ++ uint32_t len, uint16_t queues); ++static void ssam_scsi_show_iostat_json(struct spdk_ssam_session *smsession, uint32_t id, ++ struct spdk_json_write_ctx *w); ++static void ssam_scsi_clear_iostat_json(struct spdk_ssam_session *smsession); ++static void ssam_scsi_print_stuck_io_info(struct spdk_ssam_session *smsession); ++static void ssam_scsi_req_complete(struct spdk_ssam_dev *smdev, struct ssam_request *io_req, uint8_t status); ++static struct spdk_bdev *ssam_scsi_get_bdev(struct spdk_ssam_session *smsession, uint32_t id); ++ ++static void ssam_free_scsi_task_pool(struct spdk_ssam_scsi_session *ssmsession); ++static int spdk_ssam_scsi_dev_hot_remove_tgt(struct spdk_ssam_session *smsession, void **_ctx); ++static void ssam_scsi_process_io_task(struct spdk_ssam_session *smsession, struct spdk_ssam_scsi_task *task); ++static int ssam_scsi_task_iovs_memory_get(struct spdk_ssam_scsi_task *task, uint32_t payload_size); ++static void ssam_scsi_submit_io_task(struct spdk_ssam_scsi_task *task); ++static void ssam_scsi_destruct_tgt(struct spdk_ssam_scsi_session *ssmsession, int scsi_tgt_num); ++ ++static const struct spdk_ssam_session_backend g_ssam_scsi_session_backend = { ++ .type = VIRTIO_TYPE_SCSI, ++ .request_worker = ssam_scsi_request_worker, ++ .destroy_bdev_device = ssam_scsi_destroy_bdev_device, ++ .response_worker = ssam_scsi_response_worker, ++ .remove_session = ssam_scsi_remove_session, ++ .remove_self = ssam_scsi_remove_self, ++ .print_stuck_io_info = ssam_scsi_print_stuck_io_info, ++ .dump_info_json = ssam_scsi_dump_info_json, ++ .write_config_json = ssam_scsi_write_config_json, ++ .ssam_get_config = ssam_scsi_get_config, ++ .show_iostat_json = ssam_scsi_show_iostat_json, ++ .clear_iostat_json = ssam_scsi_clear_iostat_json, ++ .get_bdev = ssam_scsi_get_bdev, ++}; ++ ++static void ++ssam_scsi_task_stat_tick(uint64_t *tsc) ++{ ++#ifdef PERF_STAT ++ *tsc = spdk_get_ticks(); ++#endif ++ return; ++} ++ ++static void ++ssam_scsi_stat_statistics(struct spdk_ssam_scsi_task *task) ++{ ++#ifdef PERF_STAT ++ if (task->scsi_task.lun == NULL || task->io_req->type == VMIO_TYPE_VIRTIO_SCSI_CTRL || ++ task->task_stat.bdev_func_tsc == 0 || task->task_stat.bdev_end_tsc == 0) { ++ return; ++ } ++ ++ int32_t lun_id = spdk_scsi_lun_get_id(task->scsi_task.lun); ++ struct ssam_scsi_stat *scsi_stat = &task->ssmsession->scsi_dev_state[task->tgt_id].io_stat[lun_id]->scsi_stat; ++ ++ uint64_t dma_tsc = task->task_stat.dma_end_tsc - task->task_stat.dma_start_tsc; ++ uint64_t bdev_tsc = task->task_stat.bdev_end_tsc - task->task_stat.bdev_start_tsc; ++ uint64_t bdev_submit_tsc = task->task_stat.bdev_func_tsc - task->task_stat.bdev_start_tsc; ++ uint64_t complete_tsc = task->task_stat.complete_end_tsc - task->task_stat.complete_start_tsc; ++ uint64_t total_tsc = task->task_stat.complete_end_tsc - task->task_stat.start_tsc; ++ ++ struct ssam_io_message *io_cmd = &task->io_req->req.cmd; ++ if (io_cmd->writable) { /* read io */ ++ if (task->scsi_task.status == SPDK_SCSI_STATUS_GOOD) { ++ scsi_stat->complete_read_ios++; ++ } else { ++ scsi_stat->err_read_ios++; ++ } ++ } else { ++ if (task->scsi_task.status == SPDK_SCSI_STATUS_GOOD) { ++ scsi_stat->complete_write_ios++; ++ } else { ++ scsi_stat->err_write_ios++; ++ } ++ } ++ ++ scsi_stat->dma_tsc += dma_tsc; ++ scsi_stat->bdev_tsc += bdev_tsc; ++ scsi_stat->bdev_submit_tsc += bdev_submit_tsc; ++ scsi_stat->complete_tsc += complete_tsc; ++ scsi_stat->total_tsc += total_tsc; ++ scsi_stat->internel_tsc += total_tsc - complete_tsc - bdev_tsc - dma_tsc; ++ scsi_stat->count += 1; ++#endif ++} ++ ++static uint32_t ++ssam_scsi_tgtid_to_lunid(uint32_t tgt_id) ++{ ++ return (((tgt_id) << 0x8) | SSAM_VIRTIO_SCSI_LUN_ID); ++} ++ ++static int ++ssam_scsi_get_config(struct spdk_ssam_session *smsession, uint8_t *config, ++ uint32_t len, uint16_t queues) ++{ ++ struct virtio_scsi_config scsi_cfg; ++ scsi_cfg.num_queues = 0x80; ++ scsi_cfg.seg_max = 0x6f; ++ scsi_cfg.max_sectors = 0x1ff; ++ scsi_cfg.cmd_per_lun = 0x80; ++ scsi_cfg.event_info_size = 0; ++ scsi_cfg.sense_size = 0x60; ++ scsi_cfg.cdb_size = 0x20; ++ scsi_cfg.max_channel = 0; ++ scsi_cfg.max_target = SPDK_SSAM_SCSI_CTRLR_MAX_DEVS; ++ scsi_cfg.max_lun = 0xff; ++ ++ memcpy(config, (void*)&scsi_cfg, sizeof(struct virtio_scsi_config)); ++ return 0; ++} ++ ++static int ++ssam_scsi_send_event(struct spdk_ssam_session *smsession, unsigned scsi_dev_num, ++ uint32_t event, uint32_t reason) ++{ ++ struct virtio_scsi_event vscsi_event = {0}; ++ int ret; ++ ++ vscsi_event.event = event; ++ vscsi_event.reason = reason; ++ ++ vscsi_event.lun[0] = 1; ++ vscsi_event.lun[0x1] = (uint8_t)scsi_dev_num; ++ vscsi_event.lun[0x2] = 0; ++ vscsi_event.lun[0x3] = 0; ++ memset(&vscsi_event.lun[0x4], 0, 0x4); ++ ++ ret = ssam_send_action(smsession->gfunc_id, SSAM_FUNCTION_ACTION_SCSI_EVENT, ++ (const void*)&vscsi_event, sizeof(struct virtio_scsi_event)); ++ if (ret < 0) { ++ SPDK_ERRLOG("%s: SCSI target %d send event %u(reason %u) failed: %s.\n", ++ smsession->name, scsi_dev_num, event, reason, strerror(-ret)); ++ } ++ return ret; ++} ++ ++static void ++ssam_scsi_stop_cpl_cb(struct spdk_ssam_session *smsession, void **ctx) ++{ ++ struct spdk_ssam_scsi_session *ssmsession = (struct spdk_ssam_scsi_session *)smsession; ++ spdk_ssam_session_rsp_fn rsp_fn = smsession->rsp_fn; ++ void *rsp_ctx = smsession->rsp_ctx; ++ ++ SPDK_NOTICELOG("SCSI controller %s deleted\n", smsession->name); ++ ++ if (smsession->name != NULL) { ++ free(smsession->name); ++ smsession->name = NULL; ++ } ++ ++ if (ssmsession->dbdf != NULL) { ++ free(ssmsession->dbdf); ++ ssmsession->dbdf = NULL; ++ } ++ ++ ssam_set_session_be_freed(ctx); ++ memset(ssmsession, 0, sizeof(*ssmsession)); ++ free(ssmsession); ++ ++ if (rsp_fn != NULL) { ++ rsp_fn(rsp_ctx, 0); ++ rsp_fn = NULL; ++ } ++} ++ ++static void ++ssam_scsi_destroy_session(struct ssam_scsi_session_ctx *ctx) ++{ ++ struct spdk_ssam_session *smsession = &ctx->ssmsession->smsession; ++ struct spdk_ssam_scsi_session *ssmsession = ctx->ssmsession; ++ ++ if (smsession->task_cnt > 0) { ++ return; ++ } ++ ++ if (ssmsession->ref > 0) { ++ return; ++ } ++ ++ ssam_session_destroy(smsession); ++ ++ ssmsession->registered = false; ++ spdk_poller_unregister(&ssmsession->stop_poller); ++ ssam_free_scsi_task_pool(ssmsession); ++ ssam_session_stop_done(&ssmsession->smsession, 0, ctx->user_ctx); ++ free(ctx); ++ ++ return; ++} ++ ++static int ++ssam_scsi_destroy_session_poller_cb(void *arg) ++{ ++ struct ssam_scsi_session_ctx *ctx = arg; ++ ++ if (spdk_ssam_trylock() != 0) { ++ return SPDK_POLLER_BUSY; ++ } ++ ++ ssam_scsi_destroy_session(ctx); ++ ++ spdk_ssam_unlock(); ++ ++ return SPDK_POLLER_BUSY; ++} ++ ++static int ++ssam_scsi_stop_cb(struct spdk_ssam_session *smsession, void **ctx) ++{ ++ struct spdk_ssam_scsi_session *ssmsession = (struct spdk_ssam_scsi_session *)smsession; ++ struct ssam_scsi_session_ctx *_ctx = ++ (struct ssam_scsi_session_ctx *)calloc(1, sizeof(struct ssam_scsi_session_ctx)); ++ ++ if (_ctx == NULL) { ++ SPDK_ERRLOG("%s: calloc scsi session ctx error.\n", smsession->name); ++ return -ENOMEM; ++ } ++ ++ _ctx->ssmsession = ssmsession; ++ _ctx->user_ctx = ctx; ++ ++ ssmsession->stop_poller = SPDK_POLLER_REGISTER(ssam_scsi_destroy_session_poller_cb, ++ _ctx, SESSION_STOP_POLLER_PERIOD); ++ if (ssmsession->stop_poller == NULL) { ++ SPDK_ERRLOG("%s: ssam_destroy_session_poller_cb start failed.\n", smsession->name); ++ ssam_session_stop_done(smsession, -EBUSY, ctx); ++ free(_ctx); ++ return -EBUSY; ++ } ++ ++ return 0; ++} ++ ++static int ++ssam_scsi_stop(struct spdk_ssam_session *smsession) ++{ ++ struct spdk_ssam_send_event_flag send_event_flag = { ++ .need_async = true, ++ .need_rsp = true, ++ }; ++ return ssam_send_event_to_session(smsession, ssam_scsi_stop_cb, ssam_scsi_stop_cpl_cb, send_event_flag, NULL); ++} ++ ++// sync interface for hot-remove ++static void ++ssam_scsi_remove_self(struct spdk_ssam_session *smsession) ++{ ++ struct spdk_ssam_scsi_session *ssmsession = (struct spdk_ssam_scsi_session*)smsession; ++ // no need error ++ if (ssmsession->ref > 0) { ++ return; // still have targets ++ } ++ ++ SPDK_NOTICELOG("%s: is being freed\n", smsession->name); ++ ++ ssmsession->registered = false; ++ ssam_free_scsi_task_pool(ssmsession); ++ ++ ssam_sessions_remove(smsession->smdev->smsessions, smsession); ++ ++ if (smsession->smdev->active_session_num > 0) { ++ smsession->smdev->active_session_num--; ++ } ++ smsession->smdev = NULL; ++ // free smsession ++ free(smsession->name); ++ free(ssmsession->dbdf); ++ free(ssmsession); ++} ++ ++// async interface ++static int ++ssam_scsi_remove_session(struct spdk_ssam_session *smsession) ++{ ++ struct spdk_ssam_scsi_session *ssmsession = (struct spdk_ssam_scsi_session*)smsession; ++ int ret; ++ ++ if (smsession->registered && ssmsession->ref != 0) { ++ SPDK_ERRLOG("%s: SCSI target %d is still present.\n", smsession->name, ssmsession->ref); ++ return -EBUSY; ++ } ++ ++ ret = ssam_scsi_stop(smsession); ++ if (ret != 0) { ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static struct spdk_scsi_dev * ++spdk_ssam_scsi_dev_get_tgt(struct spdk_ssam_scsi_session *ssmsession, uint8_t num) ++{ ++ if (ssmsession == NULL) { ++ SPDK_ERRLOG("ssmsession is null.\n"); ++ return NULL; ++ } ++ if (num >= SPDK_SSAM_SCSI_CTRLR_MAX_DEVS) { ++ SPDK_ERRLOG("%s: tgt num %u over %u.\n", ssmsession->smsession.name, num, SPDK_SSAM_SCSI_CTRLR_MAX_DEVS); ++ return NULL; ++ } ++ if (ssmsession->scsi_dev_state[num].status != SSAM_SCSI_DEV_PRESENT) { ++ return NULL; ++ } ++ ++ if (ssmsession->scsi_dev_state[num].dev == NULL) { ++ SPDK_ERRLOG("%s: no tgt num %u device.\n", ssmsession->smsession.name, num); ++ return NULL; ++ } ++ return ssmsession->scsi_dev_state[num].dev; ++} ++ ++static void ++ssam_scsi_dump_device_info(struct spdk_ssam_session *smsession, struct spdk_json_write_ctx *w) ++{ ++ struct spdk_ssam_scsi_session *ssmsession = (struct spdk_ssam_scsi_session*)smsession; ++ struct spdk_scsi_dev *sdev; ++ struct spdk_scsi_lun *lun; ++ int32_t tgt_id; ++ ++ spdk_json_write_named_array_begin(w, "scsi_targets"); ++ for (tgt_id = 0; tgt_id < SPDK_SSAM_SCSI_CTRLR_MAX_DEVS; tgt_id++) { ++ sdev = spdk_ssam_scsi_dev_get_tgt(ssmsession, tgt_id); ++ if (!sdev) { ++ continue; ++ } ++ ++ spdk_json_write_object_begin(w); ++ ++ spdk_json_write_named_uint32(w, "scsi_target_num", tgt_id); ++ spdk_json_write_named_uint32(w, "id", spdk_scsi_dev_get_id(sdev)); ++ spdk_json_write_named_string(w, "target_name", spdk_scsi_dev_get_name(sdev)); ++ lun = spdk_scsi_dev_get_lun(sdev, 0); ++ if (!lun) { ++ continue; ++ } ++ spdk_json_write_named_string(w, "bdev_name", spdk_scsi_lun_get_bdev_name(lun)); ++ ++ spdk_json_write_object_end(w); ++ } ++ ++ spdk_json_write_array_end(w); ++} ++ ++static void ++ssam_scsi_dump_info_json(struct spdk_ssam_session *smsession, struct spdk_json_write_ctx *w) ++{ ++ struct spdk_ssam_scsi_session *ssmsession = (struct spdk_ssam_scsi_session*)smsession; ++ spdk_json_write_object_begin(w); ++ ++ spdk_json_write_named_string(w, "dbdf", ssmsession->dbdf); ++ spdk_json_write_named_string(w, "name", spdk_ssam_session_get_name(smsession)); ++ spdk_json_write_named_uint32(w, "function_id", (uint32_t)smsession->gfunc_id); ++ spdk_json_write_named_uint32(w, "queues", (uint32_t)smsession->max_queues); ++ spdk_json_write_named_string(w, "ctrlr", spdk_ssam_dev_get_name(smsession->smdev)); ++ spdk_json_write_named_string_fmt(w, "cpumask", "0x%s", ++ spdk_cpuset_fmt(spdk_thread_get_cpumask(smsession->smdev->thread))); ++ ++ ssam_scsi_dump_device_info(smsession, w); ++ ++ spdk_json_write_object_end(w); ++} ++ ++static void ++ssam_scsi_write_config_json(struct spdk_ssam_session *smsession, ++ struct spdk_json_write_ctx *w) ++{ ++ struct spdk_ssam_scsi_session *ssmsession = (struct spdk_ssam_scsi_session*)smsession; ++ struct spdk_scsi_dev *sdev; ++ struct spdk_scsi_lun *lun; ++ int32_t tgt_id; ++ ++ spdk_json_write_object_begin(w); ++ spdk_json_write_named_string(w, "method", "create_scsi_controller"); ++ ++ spdk_json_write_named_object_begin(w, "params"); ++ spdk_json_write_named_string(w, "dbdf", ssmsession->dbdf); ++ spdk_json_write_named_string(w, "name", smsession->name); ++ spdk_json_write_object_end(w); ++ ++ spdk_json_write_object_end(w); ++ ++ for (tgt_id = 0; tgt_id < SPDK_SSAM_SCSI_CTRLR_MAX_DEVS; tgt_id++) { ++ sdev = spdk_ssam_scsi_dev_get_tgt(ssmsession, tgt_id); ++ if (!sdev) { ++ continue; ++ } ++ ++ lun = spdk_scsi_dev_get_lun(sdev, 0); ++ if (!lun) { ++ SPDK_ERRLOG("%s: no lun, continue.\n", smsession->name); ++ continue; ++ } ++ ++ spdk_json_write_object_begin(w); ++ spdk_json_write_named_string(w, "method", "scsi_controller_add_target"); ++ ++ spdk_json_write_named_object_begin(w, "params"); ++ spdk_json_write_named_string(w, "name", smsession->name); ++ spdk_json_write_named_uint32(w, "scsi_tgt_num", tgt_id); ++ ++ spdk_json_write_named_string(w, "bdev_name", spdk_scsi_lun_get_bdev_name(lun)); ++ spdk_json_write_object_end(w); ++ ++ spdk_json_write_object_end(w); ++ } ++} ++ ++static void ++ssam_scsi_show_tgt_iostat_json(struct spdk_ssam_scsi_session *ssmsession, ++ struct spdk_json_write_ctx *w, int32_t tgt_id, struct spdk_scsi_dev *sdev) ++{ ++ struct spdk_scsi_dev_io_state *io_stat; ++ struct spdk_scsi_lun *lun; ++ struct ssam_scsi_stat scsi_stat; ++ uint64_t ticks_hz = spdk_get_ticks_hz(); ++ uint64_t count; ++ uint64_t poll_count; ++ ++ lun = spdk_scsi_dev_get_lun(sdev, 0); ++ if (lun == NULL) { ++ return; ++ } ++ ++ io_stat = ssmsession->scsi_dev_state[tgt_id].io_stat[0]; ++ if (io_stat == NULL) { ++ SPDK_ERRLOG("No scsi iostat, tgt_id %d\n", tgt_id); ++ return; ++ } ++ ++ spdk_json_write_object_begin(w); ++ ++ spdk_json_write_named_uint32(w, "scsi_dev_num", tgt_id); ++ spdk_json_write_named_uint32(w, "id", spdk_scsi_dev_get_id(sdev)); ++ spdk_json_write_named_string(w, "target_name", spdk_scsi_dev_get_name(sdev)); ++ ++ memcpy(&scsi_stat, &io_stat->scsi_stat, sizeof(struct ssam_scsi_stat)); ++ ++ spdk_json_write_named_int32(w, "id", spdk_scsi_lun_get_id(lun)); ++ spdk_json_write_named_string(w, "bdev_name", spdk_scsi_lun_get_bdev_name(lun)); ++ spdk_json_write_named_uint64(w, "bytes_read", io_stat->stat.bytes_read); ++ spdk_json_write_named_uint64(w, "num_read_ops", io_stat->stat.num_read_ops); ++ spdk_json_write_named_uint64(w, "bytes_written", io_stat->stat.bytes_written); ++ spdk_json_write_named_uint64(w, "num_write_ops", io_stat->stat.num_write_ops); ++ spdk_json_write_named_uint64(w, "read_latency_ticks", io_stat->stat.read_latency_ticks); ++ spdk_json_write_named_uint64(w, "write_latency_ticks", io_stat->stat.write_latency_ticks); ++ ++ spdk_json_write_named_uint64(w, "complete_read_ios", scsi_stat.complete_read_ios); ++ spdk_json_write_named_uint64(w, "err_read_ios", scsi_stat.err_read_ios); ++ spdk_json_write_named_uint64(w, "complete_write_ios", scsi_stat.complete_write_ios); ++ spdk_json_write_named_uint64(w, "err_write_ios", scsi_stat.err_write_ios); ++ spdk_json_write_named_uint64(w, "flush_ios", scsi_stat.flush_ios); ++ spdk_json_write_named_uint64(w, "complete_flush_ios", scsi_stat.complete_flush_ios); ++ spdk_json_write_named_uint64(w, "err_flush_ios", scsi_stat.err_flush_ios); ++ spdk_json_write_named_uint64(w, "fatal_ios", scsi_stat.fatal_ios); ++ spdk_json_write_named_uint64(w, "io_retry", scsi_stat.io_retry); ++ ++ spdk_json_write_named_uint64(w, "start_count", scsi_stat.start_count); ++ spdk_json_write_named_uint64(w, "dma_count", scsi_stat.dma_count); ++ spdk_json_write_named_uint64(w, "dma_complete_count", scsi_stat.dma_complete_count); ++ spdk_json_write_named_uint64(w, "bdev_count", scsi_stat.bdev_count); ++ spdk_json_write_named_uint64(w, "bdev_complete_count", scsi_stat.bdev_complete_count); ++ spdk_json_write_named_uint64(w, "flight_io", ssmsession->scsi_dev_state[tgt_id].flight_io); ++ ++ if (scsi_stat.count == 0) { ++ count = 1; ++ } else { ++ count = scsi_stat.count; ++ } ++ ++ if (ssmsession->smsession.smdev->stat.poll_count == 0) { ++ poll_count = 1; ++ } else { ++ poll_count = ssmsession->smsession.smdev->stat.poll_count; ++ } ++ ++ spdk_json_write_named_string_fmt(w, "poll_lat", "%.9f", ++ (float)ssmsession->smsession.smdev->stat.poll_tsc / poll_count / ticks_hz); ++ spdk_json_write_named_string_fmt(w, "total_lat", "%.9f", (float)scsi_stat.total_tsc / count / ticks_hz); ++ spdk_json_write_named_string_fmt(w, "dma_lat", "%.9f", (float)scsi_stat.dma_tsc / count / ticks_hz); ++ spdk_json_write_named_string_fmt(w, "bdev_lat", "%.9f", (float)scsi_stat.bdev_tsc / count / ticks_hz); ++ spdk_json_write_named_string_fmt(w, "bdev_submit_lat", "%.9f", (float)scsi_stat.bdev_submit_tsc / count / ticks_hz); ++ spdk_json_write_named_string_fmt(w, "complete_lat", "%.9f", (float)scsi_stat.complete_tsc / count / ticks_hz); ++ spdk_json_write_named_string_fmt(w, "internel_lat", "%.9f", (float)scsi_stat.internel_tsc / count / ticks_hz); ++ ++ spdk_json_write_object_end(w); ++} ++ ++static void ++ssam_scsi_show_iostat_json(struct spdk_ssam_session *smsession, uint32_t id, struct spdk_json_write_ctx *w) ++{ ++ struct spdk_ssam_scsi_session *ssmsession = (struct spdk_ssam_scsi_session*)smsession; ++ struct spdk_scsi_dev *sdev; ++ int32_t tgt_id; ++ ++ if (id != SPDK_SSAM_SCSI_CTRLR_MAX_DEVS) { ++ sdev = spdk_ssam_scsi_dev_get_tgt(ssmsession, id); ++ if (sdev != NULL) { ++ ssam_scsi_show_tgt_iostat_json(ssmsession, w, id, sdev); ++ } else { ++ spdk_json_write_object_begin(w); ++ spdk_json_write_object_end(w); ++ } ++ return; ++ } ++ ++ spdk_json_write_object_begin(w); ++ ++ spdk_json_write_named_uint32(w, "function_id", smsession->gfunc_id); ++ ++ spdk_json_write_named_array_begin(w, "scsi_target"); ++ ++ for (tgt_id = 0; tgt_id < SPDK_SSAM_SCSI_CTRLR_MAX_DEVS; tgt_id++) { ++ sdev = spdk_ssam_scsi_dev_get_tgt(ssmsession, tgt_id); ++ if (!sdev) { ++ continue; ++ } ++ ssam_scsi_show_tgt_iostat_json(ssmsession, w, tgt_id, sdev); ++ } ++ ++ spdk_json_write_array_end(w); ++ spdk_json_write_object_end(w); ++} ++ ++static void ++ssam_scsi_clear_iostat_json(struct spdk_ssam_session *smsession) ++{ ++ struct spdk_ssam_scsi_session *ssmsession = (struct spdk_ssam_scsi_session *)smsession; ++ struct spdk_scsi_dev_io_state *io_stat; ++ int32_t tgt_id; ++ int32_t lun_id; ++ for (tgt_id = 0; tgt_id < SPDK_SSAM_SCSI_CTRLR_MAX_DEVS; tgt_id++) { ++ for (lun_id = 0; lun_id < SSAM_SPDK_SCSI_DEV_MAX_LUN; lun_id++) { ++ io_stat = ssmsession->scsi_dev_state[tgt_id].io_stat[lun_id]; ++ if (io_stat == NULL) { ++ continue; ++ } ++ memset(io_stat, 0, sizeof(struct spdk_scsi_dev_io_state)); ++ } ++ } ++ return; ++} ++ ++static struct spdk_bdev * ++ssam_scsi_get_bdev(struct spdk_ssam_session *smsession, uint32_t tgt_id) ++{ ++ struct spdk_ssam_scsi_session *ssmsession = (struct spdk_ssam_scsi_session*)smsession; ++ struct spdk_scsi_dev *scsi_dev; ++ struct spdk_scsi_lun *scsi_lun = NULL; ++ const char *bdev_name = NULL; ++ if (tgt_id >= SPDK_SSAM_SCSI_CTRLR_MAX_DEVS) { ++ SPDK_ERRLOG("%s: tgt %d invalid\n", smsession->name, tgt_id); ++ return NULL; ++ } ++ if (ssmsession->scsi_dev_state[tgt_id].dev == NULL) { ++ SPDK_ERRLOG("%s: tgt %d not be created\n", smsession->name, tgt_id); ++ return NULL; ++ } ++ ++ scsi_dev = ssmsession->scsi_dev_state[tgt_id].dev; ++ // lun id use 0 ++ scsi_lun = spdk_scsi_dev_get_lun(scsi_dev, 0); ++ if (scsi_lun == NULL) { ++ return NULL; ++ } ++ bdev_name = spdk_scsi_lun_get_bdev_name(scsi_lun); ++ if (bdev_name == NULL) { ++ return NULL; ++ } ++ return spdk_bdev_get_by_name(bdev_name); ++} ++ ++static int ++ssam_scsi_iostat_construct(struct spdk_ssam_scsi_session *ssmsession, int32_t tgt_id, ++ int *lun_id_list, int num_luns) ++{ ++ struct spdk_scsi_dev_io_state *io_stat; ++ int32_t lun_id; ++ int i; ++ ++ for (i = 0; i < num_luns; i++) { ++ lun_id = lun_id_list[i]; ++ io_stat = ssmsession->scsi_dev_state[tgt_id].io_stat[lun_id]; ++ if (io_stat != NULL) { ++ SPDK_ERRLOG("io_stat with tgt %d lun %d already exist\n", tgt_id, lun_id); ++ return -EEXIST; ++ } ++ ++ io_stat = calloc(1, sizeof(*io_stat)); ++ if (io_stat == NULL) { ++ SPDK_ERRLOG("Could not allocate io_stat for tgt %d lun %d\n", tgt_id, lun_id); ++ return -ENOMEM; ++ } ++ ssmsession->scsi_dev_state[tgt_id].io_stat[lun_id] = io_stat; ++ } ++ ++ return 0; ++} ++ ++static void ++ssam_scsi_iostat_destruct(struct spdk_scsi_dev_ssam_state *state) ++{ ++ int32_t lun_id; ++ ++ for (lun_id = 0; lun_id < SSAM_SPDK_SCSI_DEV_MAX_LUN; lun_id++) { ++ if (state->io_stat[lun_id] != NULL) { ++ free(state->io_stat[lun_id]); ++ state->io_stat[lun_id] = NULL; ++ } ++ } ++ ++ return; ++} ++ ++static void ++ssam_remove_scsi_tgt(struct spdk_ssam_scsi_session *ssmsession, unsigned scsi_tgt_num) ++{ ++ struct spdk_scsi_dev_ssam_state *state = &ssmsession->scsi_dev_state[scsi_tgt_num]; ++ struct spdk_ssam_session *smsession = &ssmsession->smsession; ++ spdk_ssam_session_rsp_fn rsp_fn = smsession->rsp_fn; ++ void *rsp_ctx = smsession->rsp_ctx; ++ ++ smsession->rsp_fn = NULL; ++ smsession->rsp_ctx = NULL; ++ ++ // delete scsi port ++ spdk_scsi_dev_delete_port(state->dev, 0); ++ ++ // destruct scsi dev ++ spdk_scsi_dev_destruct(state->dev, NULL, NULL); ++ state->dev = NULL; ++ ++ // free iostat ++ ssam_scsi_iostat_destruct(state); ++ state->status = SSAM_SCSI_DEV_EMPTY; ++ ++ // ref-- ++ if (ssmsession->ref > 0) { ++ ssmsession->ref--; ++ } else { ++ SPDK_ERRLOG("%s: ref internel error\n", smsession->name); ++ } ++ if (rsp_fn != NULL) { ++ rsp_fn(rsp_ctx, 0); ++ rsp_fn = NULL; ++ } ++ SPDK_NOTICELOG("%s: target %u is removed\n", smsession->name, scsi_tgt_num); ++} ++ ++static int ++ssam_scsi_get_payload_size(struct ssam_request *io_req, uint32_t *payload_size) ++{ ++ struct ssam_io_message *io_cmd = &io_req->req.cmd; ++ uint32_t payload = 0; ++ uint32_t first_vec; ++ uint32_t end_vec; ++ uint32_t loop; ++ ++ if (io_cmd->writable) { /* read io */ ++ /* FROM_DEV: [req][resp][write_buf]...[write_buf ]*, write_buf start at index 2 */ ++ first_vec = 2; ++ end_vec = io_cmd->iovcnt - 1; ++ } else { /* write io */ ++ first_vec = 1; ++ /* TO_DEV: [req][read_buf]...[read_buf][resp], read_buf last index is iovnt-2 */ ++ end_vec = io_cmd->iovcnt - 2; ++ } ++ ++ for (loop = first_vec; loop <= end_vec; loop++) { ++ if (spdk_unlikely((UINT32_MAX - io_cmd->iovs[loop].iov_len) < payload)) { ++ SPDK_ERRLOG("payload size overflow\n"); ++ return -1; ++ } ++ payload += io_cmd->iovs[loop].iov_len; ++ } ++ ++ if (spdk_unlikely(payload > PAYLOAD_SIZE_MAX)) { ++ SPDK_ERRLOG("payload size larger than %u, payload_size = %u\n", ++ PAYLOAD_SIZE_MAX, payload); ++ return -1; ++ } ++ ++ *payload_size = payload; ++ ++ return 0; ++} ++ ++static void ++ssam_session_io_resubmit(void *arg) ++{ ++ struct spdk_ssam_scsi_task *task = (struct spdk_ssam_scsi_task *)arg; ++ struct spdk_ssam_session *smsession = &task->ssmsession->smsession; ++ uint32_t payload_size = task->scsi_task.transfer_len; ++ int rc; ++ ++ rc = ssam_scsi_task_iovs_memory_get(task, payload_size); ++ if (rc != 0) { ++ ssam_session_insert_io_wait(smsession, &task->session_io_wait); ++ return; ++ } ++ ssam_scsi_process_io_task(smsession, task); ++} ++ ++static void ++ssam_scsi_task_init(struct spdk_ssam_scsi_task *task) ++{ ++ memset(&task->scsi_task, 0, sizeof(struct spdk_scsi_task)); ++ ++ task->used = true; ++ task->iovcnt = 0; ++ task->io_req = NULL; ++ task->session_io_wait.cb_fn = ssam_session_io_resubmit; ++ task->session_io_wait.cb_arg = task; ++} ++ ++static void ++ssam_scsi_task_dma_request_para(struct ssam_dma_request *data_request, struct spdk_ssam_scsi_task *task, ++ uint32_t type, uint8_t status) ++{ ++ struct spdk_scsi_task *scsi_task = &task->scsi_task; ++ struct ssam_io_message *io_cmd = NULL; ++ struct spdk_ssam_dma_cb dma_cb = { ++ .status = status, ++ .req_dir = type, ++ .gfunc_id = task->io_req->gfunc_id, ++ .vq_idx = task->vq_idx, ++ .task_idx = task->task_idx ++ }; ++ ++ io_cmd = &task->io_req->req.cmd; ++ data_request->cb = (void *)*(uint64_t *)&dma_cb; ++ data_request->gfunc_id = task->io_req->gfunc_id; ++ data_request->flr_seq = task->io_req->flr_seq; ++ data_request->direction = type; ++ data_request->data_len = scsi_task->transfer_len; ++ if (type == SSAM_REQUEST_DATA_STORE) { ++ data_request->src = task->iovs.phys.sges; ++ data_request->src_num = task->iovcnt; ++ /* FROM_DEV: [req][resp][write_buf]...[write_buf ]*, write_buf start at index 2 */ ++ data_request->dst = &io_cmd->iovs[2]; ++ /* dma data iovs does not contain header and tail */ ++ data_request->dst_num = io_cmd->iovcnt - IOV_HEADER_TAIL_NUM; ++ } else if (type == SSAM_REQUEST_DATA_LOAD) { ++ data_request->src = &io_cmd->iovs[1]; ++ /* dma data iovs does not contain header and tail */ ++ data_request->src_num = io_cmd->iovcnt - IOV_HEADER_TAIL_NUM; ++ data_request->dst = task->iovs.phys.sges; ++ data_request->dst_num = task->iovcnt; ++ } ++} ++ ++static void ++ssam_scsi_task_finish(struct spdk_ssam_scsi_task *task) ++{ ++ struct spdk_ssam_session *smsession = task->smsession; ++ struct spdk_ssam_virtqueue *vq = &smsession->virtqueue[task->vq_idx]; ++ ++ if (smsession->task_cnt == 0) { ++ SPDK_ERRLOG("%s: task count internel error\n", smsession->name); ++ return; ++ } ++ ++ task->io_req = NULL; ++ ++ if (task->iovs.virt.sges[0].iov_base != NULL) { ++ ssam_mempool_free(smsession->mp, task->iovs.virt.sges[0].iov_base); ++ task->iovs.virt.sges[0].iov_base = NULL; ++ } ++ ++ memset(&task->iovs, 0, sizeof(task->iovs)); ++ ++ task->iovcnt = 0; ++ smsession->task_cnt--; ++ task->used = false; ++ vq->index[vq->index_l] = task->task_idx; ++ vq->index_l = (vq->index_l + 1) & 0xFF; ++ vq->use_num--; ++} ++ ++static int ++ssam_scsi_io_complete(struct spdk_ssam_dev *smdev, struct ssam_request *io_req, void *rsp_buf, uint32_t rsp_len) ++{ ++ struct ssam_io_message *io_cmd = &io_req->req.cmd; ++ struct ssam_virtio_res *virtio_res = NULL; ++ struct ssam_io_response io_resp; ++ struct iovec io_vec; ++ int rc; ++ ++ memset(&io_resp, 0, sizeof(io_resp)); ++ io_resp.gfunc_id = io_req->gfunc_id; ++ io_resp.iocb_id = io_req->iocb_id; ++ io_resp.status = io_req->status; ++ io_resp.req = io_req; ++ io_resp.flr_seq = io_req->flr_seq; ++ ++ virtio_res = (struct ssam_virtio_res*)&io_resp.data; ++ virtio_res->iovs = &io_vec; ++ if (io_cmd->writable) { /* FROM_DEV: [req][resp][write_buf]...[write_buf ] */ ++ virtio_res->iovs->iov_base = io_cmd->iovs[1].iov_base; ++ virtio_res->iovs->iov_len = io_cmd->iovs[1].iov_len; ++ } else { /* TO_DEV: [req][read_buf]...[read_buf][resp] */ ++ virtio_res->iovs->iov_base = io_cmd->iovs[io_cmd->iovcnt - 1].iov_base; ++ virtio_res->iovs->iov_len = io_cmd->iovs[io_cmd->iovcnt - 1].iov_len; ++ } ++ virtio_res->iovcnt = 1; ++ virtio_res->rsp = rsp_buf; ++ virtio_res->rsp_len = rsp_len; ++ ++ rc = ssam_io_complete(smdev->tid, &io_resp); ++ if (rc != 0) { ++ return rc; ++ } ++ ++ ssam_dev_io_dec(smdev); ++ return 0; ++} ++ ++struct ssam_scsi_req_complete_arg { ++ struct spdk_ssam_dev *smdev; ++ struct ssam_request *io_req; ++ uint8_t status; ++}; ++ ++static void ++ssam_scsi_req_complete_cb(void *arg) ++{ ++ struct ssam_scsi_req_complete_arg *cb_arg = (struct ssam_scsi_req_complete_arg *)arg; ++ struct virtio_scsi_cmd_resp resp = {0}; ++ struct virtio_scsi_ctrl_tmf_resp tmf_resp = {0}; ++ int rc; ++ ++ if (spdk_unlikely(cb_arg->io_req->type == VMIO_TYPE_VIRTIO_SCSI_CTRL)) { ++ tmf_resp.response = cb_arg->status; ++ rc = ssam_scsi_io_complete(cb_arg->smdev, cb_arg->io_req, &tmf_resp, sizeof(struct virtio_scsi_ctrl_tmf_resp)); ++ } else { ++ resp.response = cb_arg->status; ++ rc = ssam_scsi_io_complete(cb_arg->smdev, cb_arg->io_req, &resp, sizeof(struct virtio_scsi_cmd_resp)); ++ } ++ ++ if (rc != 0) { ++ struct spdk_ssam_session_io_wait_r *io_wait_r = ++ calloc(1, sizeof(struct spdk_ssam_session_io_wait_r)); ++ if (io_wait_r == NULL) { ++ SPDK_ERRLOG("calloc for io_wait_r failed\n"); ++ sleep(1); ++ raise(SIGTERM); ++ } ++ io_wait_r->cb_fn = ssam_scsi_req_complete_cb; ++ io_wait_r->cb_arg = cb_arg; ++ ssam_session_insert_io_wait_r(cb_arg->smdev, io_wait_r); ++ return; ++ } ++ free(cb_arg); ++ cb_arg = NULL; ++} ++ ++static void ++ssam_scsi_req_complete(struct spdk_ssam_dev *smdev, struct ssam_request *io_req, uint8_t status) ++{ ++ struct virtio_scsi_cmd_resp resp = {0}; ++ struct virtio_scsi_ctrl_tmf_resp tmf_resp = {0}; ++ int rc; ++ ++ if (spdk_unlikely(io_req->type == VMIO_TYPE_VIRTIO_SCSI_CTRL)) { ++ tmf_resp.response = status; ++ rc = ssam_scsi_io_complete(smdev, io_req, &tmf_resp, sizeof(struct virtio_scsi_ctrl_tmf_resp)); ++ } else { ++ resp.response = status; ++ rc = ssam_scsi_io_complete(smdev, io_req, &resp, sizeof(struct virtio_scsi_cmd_resp)); ++ } ++ ++ if (rc != 0) { ++ struct spdk_ssam_session_io_wait_r *io_wait_r = ++ calloc(1, sizeof(struct spdk_ssam_session_io_wait_r)); ++ struct ssam_scsi_req_complete_arg *cb_arg = ++ calloc(1, sizeof(struct ssam_scsi_req_complete_arg)); ++ if (io_wait_r == NULL || cb_arg == NULL) { ++ SPDK_ERRLOG("calloc for io_wait_r failed\n"); ++ sleep(1); ++ raise(SIGTERM); ++ } ++ cb_arg->smdev = smdev; ++ cb_arg->io_req = io_req; ++ cb_arg->status = status; ++ io_wait_r->cb_fn = ssam_scsi_req_complete_cb; ++ io_wait_r->cb_arg = cb_arg; ++ ssam_session_insert_io_wait_r(smdev, io_wait_r); ++ } ++} ++ ++static void ++ssam_scsi_task_put(struct spdk_ssam_scsi_task *task) ++{ ++ memset(&task->resp, 0, sizeof(task->resp)); ++ if (task->io_req->type != VMIO_TYPE_VIRTIO_SCSI_CTRL) { ++ task->ssmsession->scsi_dev_state[task->tgt_id].flight_io--; ++ } ++ spdk_scsi_task_put(&task->scsi_task); ++} ++ ++static void ++ssam_scsi_submit_completion_cb(void *arg) ++{ ++ struct spdk_ssam_scsi_task *task = (struct spdk_ssam_scsi_task*)arg; ++ struct spdk_ssam_session *smsession = task->smsession; ++ int rc; ++ ++ if (spdk_unlikely(task->io_req->type == VMIO_TYPE_VIRTIO_SCSI_CTRL)) { ++ rc = ssam_scsi_io_complete(smsession->smdev, task->io_req, &task->tmf_resp, ++ sizeof(struct virtio_scsi_ctrl_tmf_resp)); ++ } else { ++ rc = ssam_scsi_io_complete(smsession->smdev, task->io_req, &task->resp, ++ sizeof(struct virtio_scsi_cmd_resp)); ++ } ++ ++ if (rc != 0) { ++ struct spdk_ssam_session_io_wait_r *io_wait_r = ++ calloc(1, sizeof(struct spdk_ssam_session_io_wait_r)); ++ if (io_wait_r == NULL) { ++ SPDK_ERRLOG("calloc for io_wait_r failed\n"); ++ sleep(1); ++ raise(SIGTERM); ++ } ++ io_wait_r->cb_fn = ssam_scsi_submit_completion_cb; ++ io_wait_r->cb_arg = task; ++ ssam_session_insert_io_wait_r(smsession->smdev, io_wait_r); ++ return; ++ } ++ ssam_scsi_task_stat_tick(&task->task_stat.complete_end_tsc); ++ ssam_scsi_stat_statistics(task); ++ ++ /* after spdk_task_construct called, put task */ ++ ssam_scsi_task_put(task); ++} ++ ++static void ++ssam_scsi_submit_completion(struct spdk_ssam_scsi_task *task) ++{ ++ struct spdk_ssam_session *smsession = task->smsession; ++ struct ssam_request *io_req = task->io_req; ++ int rc; ++ ++ ssam_scsi_task_stat_tick(&task->task_stat.complete_start_tsc); ++ if (spdk_unlikely(io_req->type == VMIO_TYPE_VIRTIO_SCSI_CTRL)) { ++ rc = ssam_scsi_io_complete(smsession->smdev, io_req, &task->tmf_resp, ++ sizeof(struct virtio_scsi_ctrl_tmf_resp)); ++ } else { ++ rc = ssam_scsi_io_complete(smsession->smdev, io_req, &task->resp, sizeof(struct virtio_scsi_cmd_resp)); ++ } ++ ++ if (rc != 0) { ++ struct spdk_ssam_session_io_wait_r *io_wait_r = ++ calloc(1, sizeof(struct spdk_ssam_session_io_wait_r)); ++ if (io_wait_r == NULL) { ++ SPDK_ERRLOG("calloc for io_wait_r failed\n"); ++ sleep(1); ++ raise(SIGTERM); ++ } ++ io_wait_r->cb_fn = ssam_scsi_submit_completion_cb; ++ io_wait_r->cb_arg = task; ++ ssam_session_insert_io_wait_r(smsession->smdev, io_wait_r); ++ return; ++ } ++ ssam_scsi_task_stat_tick(&task->task_stat.complete_end_tsc); ++ ssam_scsi_stat_statistics(task); ++ ++ /* after spdk_task_construct called, put task */ ++ ssam_scsi_task_put(task); ++} ++ ++struct ssam_scsi_dma_data_request_arg { ++ struct spdk_ssam_dev *smdev; ++ struct spdk_ssam_scsi_task *task; ++ struct ssam_dma_request dma_req; ++}; ++ ++static void ++ssam_scsi_dma_data_request_cb(void *arg) ++{ ++ struct ssam_scsi_dma_data_request_arg *cb_arg = (struct ssam_scsi_dma_data_request_arg *)arg; ++ int ret = ssam_dma_data_request(cb_arg->smdev->tid, &cb_arg->dma_req); ++ if (ret == -ENOMEM || ret == -EIO) { ++ struct spdk_ssam_session_io_wait_r *io_wait_r = ++ calloc(1, sizeof(struct spdk_ssam_session_io_wait_r)); ++ if (io_wait_r == NULL) { ++ SPDK_ERRLOG("calloc for io_wait_r failed\n"); ++ sleep(1); ++ raise(SIGTERM); ++ } ++ io_wait_r->cb_fn = ssam_scsi_dma_data_request_cb; ++ io_wait_r->cb_arg = cb_arg; ++ ssam_session_insert_io_wait_r(cb_arg->smdev, io_wait_r); ++ return; ++ } ++ if (ret < 0) { ++ SPDK_ERRLOG("ssam dma data request failed(%d)\n", ret); ++ cb_arg->task->resp.response = VIRTIO_SCSI_S_FAILURE; ++ ssam_scsi_submit_completion(cb_arg->task); ++ } ++ free(cb_arg); ++ cb_arg = NULL; ++} ++ ++static void ++ssam_scsi_task_dma_request(struct spdk_ssam_scsi_task *task, enum data_request_dma_type data_dir) ++{ ++ struct spdk_ssam_session *smsession = task->smsession; ++ struct ssam_dma_request data_request = {0}; ++ int ret = 0; ++ ++ ssam_scsi_task_stat_tick(&task->task_stat.dma_start_tsc); ++ task->ssmsession->scsi_dev_state[task->tgt_id].io_stat[0]->scsi_stat.dma_count++; ++ ++ switch (data_dir) { ++ case SSAM_REQUEST_DATA_STORE: ++ ssam_scsi_task_dma_request_para(&data_request, task, SSAM_REQUEST_DATA_STORE, 0); ++ ++ /* dma request: ipu -> Host */ ++ ret = ssam_dma_data_request(smsession->smdev->tid, &data_request); ++ break; ++ ++ case SSAM_REQUEST_DATA_LOAD: ++ ssam_scsi_task_dma_request_para(&data_request, task, SSAM_REQUEST_DATA_LOAD, 0); ++ ++ /* dma request: Host -> ipu */ ++ ret = ssam_dma_data_request(smsession->smdev->tid, &data_request); ++ break; ++ ++ default: ++ SPDK_ERRLOG("Invalid data dir: %u.\n", data_dir); ++ break; ++ } ++ ++ if (ret == -ENOMEM || ret == -EIO) { ++ struct spdk_ssam_session_io_wait_r *io_wait_r = ++ calloc(1, sizeof(struct spdk_ssam_session_io_wait_r)); ++ struct ssam_scsi_dma_data_request_arg *cb_arg = ++ calloc(1, sizeof(struct ssam_scsi_dma_data_request_arg)); ++ if (io_wait_r == NULL || cb_arg == NULL) { ++ SPDK_ERRLOG("calloc for io_wait_r failed\n"); ++ sleep(1); ++ raise(SIGTERM); ++ } ++ cb_arg->smdev = smsession->smdev; ++ cb_arg->dma_req = data_request; ++ cb_arg->task = task; ++ io_wait_r->cb_fn = ssam_scsi_dma_data_request_cb; ++ io_wait_r->cb_arg = cb_arg; ++ ssam_session_insert_io_wait_r(smsession->smdev, io_wait_r); ++ return; ++ } ++ ++ if (ret < 0) { ++ SPDK_ERRLOG("ssam dma data request failed(%d)\n", ret); ++ task->resp.response = VIRTIO_SCSI_S_FAILURE; ++ ssam_scsi_submit_completion(task); ++ } ++} ++ ++static void ++ssam_scsi_task_copy_resp(struct spdk_ssam_scsi_task *task) ++{ ++ struct spdk_scsi_task *scsi_task = &task->scsi_task; ++ ++ if (spdk_unlikely(task->io_req->type == VMIO_TYPE_VIRTIO_SCSI_CTRL)) { ++ task->tmf_resp.response = scsi_task->status; ++ } else { ++ task->resp.status = scsi_task->status; ++ if (spdk_unlikely(scsi_task->sense_data_len > SSAM_SENSE_DATE_LEN)) { ++ return; ++ } ++ if (scsi_task->status != SPDK_SCSI_STATUS_GOOD) { ++ memcpy(task->resp.sense, scsi_task->sense_data, scsi_task->sense_data_len); ++ task->resp.sense_len = scsi_task->sense_data_len; ++ } ++ ++ if (scsi_task->transfer_len != scsi_task->length) { ++ SPDK_ERRLOG("task transfer_len(%u) not equal length(%u), internel error.\n", ++ scsi_task->transfer_len, scsi_task->length); ++ } ++ ++ task->resp.resid = scsi_task->length - scsi_task->data_transferred; ++ } ++} ++ ++static void ++ssam_scsi_read_task_cpl_cb(struct spdk_scsi_task *scsi_task) ++{ ++ if (spdk_unlikely(spdk_get_shutdown_sig_received())) { ++ /* ++ * In the hot restart process, when this callback is triggered, ++ * the task and bdev_io memory may have been released. ++ * Therefore, task and bdev_io are not released in this scenario. ++ */ ++ return; ++ } ++ struct spdk_ssam_scsi_task *task = SPDK_CONTAINEROF(scsi_task, struct spdk_ssam_scsi_task, scsi_task); ++ int32_t tgt_id = task->tgt_id; ++ int32_t lun_id = spdk_scsi_lun_get_id(scsi_task->lun); ++ struct spdk_scsi_dev_io_state *io_stat = task->ssmsession->scsi_dev_state[tgt_id].io_stat[lun_id]; ++ ++ /* Second part start of read */ ++ io_stat->submit_tsc = spdk_get_ticks(); ++ ++ ssam_scsi_task_copy_resp(task); ++ ++ ssam_scsi_task_stat_tick(&task->task_stat.bdev_end_tsc); ++ task->ssmsession->scsi_dev_state[task->tgt_id].io_stat[0]->scsi_stat.bdev_complete_count++; ++ ++ /* 1) Read request without data is no need to dma; ++ 2) Read request failed just complete it. ++ */ ++ if (scsi_task->length == 0 || scsi_task->status != SPDK_SCSI_STATUS_GOOD) { ++ ssam_scsi_submit_completion(task); ++ return; ++ } ++ ++ /* Dma data from IPU to HOST */ ++ ssam_scsi_task_dma_request(task, SSAM_REQUEST_DATA_STORE); ++ ++ return; ++} ++ ++static void ++ssam_scsi_write_task_cpl_cb(struct spdk_scsi_task *scsi_task) ++{ ++ if (spdk_unlikely(spdk_get_shutdown_sig_received())) { ++ /* ++ * In the hot restart process, when this callback is triggered, ++ * the task and bdev_io memory may have been released. ++ * Therefore, task and bdev_io are not released in this scenario. ++ */ ++ return; ++ } ++ struct spdk_ssam_scsi_task *task = SPDK_CONTAINEROF(scsi_task, struct spdk_ssam_scsi_task, scsi_task); ++ int32_t tgt_id = task->tgt_id; ++ int32_t lun_id = spdk_scsi_lun_get_id(scsi_task->lun); ++ struct spdk_scsi_dev_io_state *io_stat = task->ssmsession->scsi_dev_state[tgt_id].io_stat[lun_id]; ++ uint32_t payload_size = task->scsi_task.transfer_len; ++ ++ /* Second part start of write */ ++ io_stat->submit_tsc = spdk_get_ticks(); ++ ++ /* copy result from spdk_scsi_task to spdk_ssam_scsi_task->resp */ ++ ssam_scsi_task_copy_resp(task); ++ ++ ssam_scsi_task_stat_tick(&task->task_stat.bdev_end_tsc); ++ task->ssmsession->scsi_dev_state[task->tgt_id].io_stat[0]->scsi_stat.bdev_complete_count++; ++ ++ ssam_scsi_submit_completion(task); ++ /* Second part end of write */ ++ io_stat->stat.write_latency_ticks += ssam_get_diff_tsc(io_stat->submit_tsc); ++ io_stat->stat.bytes_written += payload_size; ++ io_stat->stat.num_write_ops++; ++ ++ return; ++} ++ ++static void ++ssam_scsi_ctl_task_cpl_cb(struct spdk_scsi_task *scsi_task) ++{ ++ struct spdk_ssam_scsi_task *task = SPDK_CONTAINEROF(scsi_task, struct spdk_ssam_scsi_task, scsi_task); ++ ++ ssam_scsi_task_copy_resp(task); ++ ++ ssam_scsi_submit_completion(task); ++} ++ ++static void ++ssam_scsi_task_free_cb(struct spdk_scsi_task *scsi_task) ++{ ++ struct spdk_ssam_scsi_task *task = SPDK_CONTAINEROF(scsi_task, struct spdk_ssam_scsi_task, scsi_task); ++ ++ ssam_scsi_task_finish(task); ++} ++ ++static int ++ssam_scsi_task_init_target(struct spdk_ssam_scsi_task *task, const __u8 *lun) ++{ ++ struct spdk_ssam_scsi_session *ssmsession = task->ssmsession; ++ struct spdk_scsi_dev_ssam_state *state = NULL; ++ int32_t lun_id = (((uint16_t)lun[2] << 8) | lun[3]) & 0x3FFF; ++ int32_t tgt_id = lun[1]; ++ ++ if (lun[0] != 1 || tgt_id >= SPDK_SSAM_SCSI_CTRLR_MAX_DEVS) { ++ SPDK_ERRLOG("First byte must be 1 and second is target\n"); ++ ssmsession->smsession.smdev->discard_io_num++; ++ return -1; ++ } ++ ++ state = &ssmsession->scsi_dev_state[tgt_id]; ++ task->scsi_dev = state->dev; ++ if (state->dev == NULL || state->status != SSAM_SCSI_DEV_PRESENT) { ++ return -1; ++ } ++ ++ task->tgt_id = tgt_id; ++ task->scsi_task.target_port = spdk_scsi_dev_find_port_by_id(task->scsi_dev, 0); ++ task->scsi_task.lun = spdk_scsi_dev_get_lun(state->dev, lun_id); ++ if (task->scsi_task.lun == NULL) { ++ SPDK_ERRLOG("Failed to init scsi task lun by lun_id(%d)\n", lun_id); ++ return -1; ++ } ++ return 0; ++} ++ ++static void ++ssam_scsi_submit_io_task(struct spdk_ssam_scsi_task *task) ++{ ++ task->resp.response = VIRTIO_SCSI_S_OK; ++ ++ ssam_scsi_task_stat_tick(&task->task_stat.bdev_start_tsc); ++ spdk_scsi_dev_queue_task(task->scsi_dev, &task->scsi_task); ++ task->ssmsession->scsi_dev_state[task->tgt_id].io_stat[0]->scsi_stat.bdev_count++; ++ ssam_scsi_task_stat_tick(&task->task_stat.bdev_func_tsc); ++ ++ SPDK_DEBUGLOG(ssam_blk_data, "====== Task: task_idx %u submitted ======\n", task->task_idx); ++} ++ ++static int ++ssam_scsi_task_iovs_memory_get(struct spdk_ssam_scsi_task *task, uint32_t payload_size) ++{ ++ struct ssam_mempool *mp = task->smsession->mp; ++ void *buffer = NULL; ++ uint64_t phys_addr = 0; ++ uint32_t alloc_size; ++ ++ if (payload_size == 0) { /* A little strange */ ++ alloc_size = 1; /* Alloc one iov at least */ ++ } else { ++ alloc_size = payload_size; ++ } ++ ++ buffer = ssam_mempool_alloc(mp, alloc_size, &phys_addr); ++ if (spdk_unlikely(buffer == NULL)) { ++ return -ENOMEM; ++ } ++ ++ /* ssam request max IO size is PAYLOAD_SIZE_MAX, only use one iov to save data */ ++ task->iovs.virt.sges[0].iov_base = buffer; ++ task->iovs.phys.sges[0].iov_base = (void *)phys_addr; ++ task->iovs.virt.sges[0].iov_len = payload_size; ++ task->iovs.phys.sges[0].iov_len = payload_size; ++ task->iovcnt = 1; ++ ++ return 0; ++} ++ ++static void ++scsi_mgmt_task_submit(struct spdk_ssam_scsi_task *task, enum spdk_scsi_task_func func) ++{ ++ task->tmf_resp.response = VIRTIO_SCSI_S_OK; ++ task->scsi_task.function = func; ++ spdk_scsi_dev_queue_mgmt_task(task->scsi_dev, &task->scsi_task); ++} ++ ++static void ++ssam_scsi_process_ctl_task(struct spdk_ssam_session *smsession, struct spdk_ssam_scsi_task *task) ++{ ++ struct virtio_scsi_ctrl_tmf_req *ctrl_req = (struct virtio_scsi_ctrl_tmf_req *)task->io_req->req.cmd.header; ++ int32_t lun_id = spdk_scsi_lun_get_id(task->scsi_task.lun); ++ struct spdk_scsi_dev_io_state *io_stat = task->ssmsession->scsi_dev_state[task->tgt_id].io_stat[lun_id]; ++ int ret = 0; ++ ++ spdk_scsi_task_construct(&task->scsi_task, ssam_scsi_ctl_task_cpl_cb, ssam_scsi_task_free_cb); ++ ret = ssam_scsi_task_init_target(task, ctrl_req->lun); ++ if (ret < 0) { ++ task->tmf_resp.response = VIRTIO_SCSI_S_FAILURE; ++ ssam_scsi_submit_completion(task); ++ return; ++ } ++ ++ switch (ctrl_req->type) { ++ case VIRTIO_SCSI_T_TMF: ++ /* Check if we are processing a valid request */ ++ if (task->scsi_dev == NULL) { ++ task->tmf_resp.response = VIRTIO_SCSI_S_BAD_TARGET; ++ ssam_scsi_submit_completion(task); ++ break; ++ } ++ ++ switch (ctrl_req->subtype) { ++ case VIRTIO_SCSI_T_TMF_LOGICAL_UNIT_RESET: ++ /* Handle LUN reset */ ++ SPDK_DEBUGLOG(ssam_scsi, "%s: LUN reset\n", smsession->name); ++ ++ scsi_mgmt_task_submit(task, SPDK_SCSI_TASK_FUNC_LUN_RESET); ++ return; ++ default: ++ task->tmf_resp.response = VIRTIO_SCSI_S_ABORTED; ++ ssam_scsi_submit_completion(task); ++ /* Unsupported command */ ++ SPDK_DEBUGLOG(ssam_scsi, "%s: unsupported TMF command %x\n", ++ smsession->name, ctrl_req->subtype); ++ break; ++ } ++ break; ++ ++ case VIRTIO_SCSI_T_AN_QUERY: ++ case VIRTIO_SCSI_T_AN_SUBSCRIBE: ++ task->tmf_resp.response = VIRTIO_SCSI_S_ABORTED; ++ ssam_scsi_submit_completion(task); ++ break; ++ ++ default: ++ SPDK_DEBUGLOG(ssam_scsi, "%s: Unsupported control command %x\n", ++ smsession->name, ctrl_req->type); ++ io_stat->scsi_stat.fatal_ios++; ++ break; ++ } ++} ++ ++static void ++ssam_scsi_io_task_construct(struct spdk_ssam_scsi_task *task) ++{ ++ struct spdk_scsi_task *scsi_task = &task->scsi_task; ++ struct ssam_io_message *io_cmd = &task->io_req->req.cmd; ++ ++ if (io_cmd->writable) { /* read io */ ++ spdk_scsi_task_construct(scsi_task, ssam_scsi_read_task_cpl_cb, ssam_scsi_task_free_cb); ++ } else { /* write io */ ++ spdk_scsi_task_construct(scsi_task, ssam_scsi_write_task_cpl_cb, ssam_scsi_task_free_cb); ++ } ++} ++ ++static int32_t ++ssam_scsi_io_task_setup(struct spdk_ssam_scsi_task *task) ++{ ++ struct spdk_scsi_task *scsi_task = &task->scsi_task; ++ struct ssam_io_message *io_cmd = &task->io_req->req.cmd; ++ struct virtio_scsi_cmd_req *req = (struct virtio_scsi_cmd_req *)io_cmd->header; ++ uint32_t payload_size; ++ int ret; ++ ++ ssam_scsi_io_task_construct(task); ++ ++ ret = ssam_scsi_get_payload_size(task->io_req, &payload_size); ++ if (ret != 0) { ++ return ret; ++ } ++ ++ ret = ssam_scsi_task_init_target(task, req->lun); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ scsi_task->dxfer_dir = (io_cmd->writable ? SPDK_SCSI_DIR_FROM_DEV : SPDK_SCSI_DIR_TO_DEV); ++ scsi_task->iovs = task->iovs.virt.sges; ++ scsi_task->cdb = req->cdb; ++ scsi_task->transfer_len = payload_size; ++ scsi_task->length = payload_size; ++ ++ ret = ssam_scsi_task_iovs_memory_get(task, payload_size); ++ if (ret != 0) { ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static void ++ssam_scsi_process_io_task(struct spdk_ssam_session *smsession, struct spdk_ssam_scsi_task *task) ++{ ++ struct spdk_ssam_scsi_session *ssmsession = (struct spdk_ssam_scsi_session*)smsession; ++ struct spdk_scsi_dev_io_state *io_stat; ++ uint64_t cur_tsc; ++ int32_t lun_id; ++ ++ ssmsession->scsi_dev_state[task->tgt_id].flight_io++; ++ ++ if (spdk_unlikely(task->scsi_task.lun == NULL)) { ++ spdk_scsi_task_process_null_lun(&task->scsi_task); ++ task->resp.response = VIRTIO_SCSI_S_OK; ++ ssam_scsi_submit_completion(task); ++ return; ++ } ++ ++ lun_id = spdk_scsi_lun_get_id(task->scsi_task.lun); ++ io_stat = ssmsession->scsi_dev_state[task->tgt_id].io_stat[lun_id]; ++ if (io_stat == NULL) { ++ SPDK_ERRLOG("No io_stat with tgt %d lun %d\n", task->tgt_id, lun_id); ++ task->resp.response = VIRTIO_SCSI_S_FAILURE; ++ ssam_scsi_submit_completion(task); ++ return; ++ } ++ /* First part start of read and write */ ++ cur_tsc = spdk_get_ticks(); ++ io_stat->submit_tsc = cur_tsc; ++ memset(&task->task_stat, 0, sizeof(task->task_stat)); ++ task->task_stat.start_tsc = cur_tsc; ++ io_stat->scsi_stat.start_count++; ++ ++ switch (task->scsi_task.dxfer_dir) { ++ case SPDK_SCSI_DIR_FROM_DEV: /* read: read data from backend to ipu, then dma to host */ ++ ssam_scsi_submit_io_task(task); ++ /* First part end of read */ ++ uint8_t rw_type = task->scsi_task.cdb[0]; ++ if (rw_type == SPDK_SBC_READ_6 || rw_type == SPDK_SBC_READ_10 || ++ rw_type == SPDK_SBC_READ_12 || rw_type == SPDK_SBC_READ_16) { ++ io_stat->stat.read_latency_ticks += ssam_get_diff_tsc(io_stat->submit_tsc); ++ io_stat->stat.bytes_read += task->scsi_task.transfer_len; ++ io_stat->stat.num_read_ops++; ++ } ++ break; ++ ++ case SPDK_SCSI_DIR_TO_DEV: /* write: dma data from host to ipu, then submit to backend */ ++ ssam_scsi_task_dma_request(task, SSAM_REQUEST_DATA_LOAD); ++ break; ++ ++ default: ++ SPDK_ERRLOG("scsi task dxfer dir error, dir is %u.\n", task->scsi_task.dxfer_dir); ++ io_stat->scsi_stat.fatal_ios++; ++ break; ++ } ++} ++ ++static void ++ssam_scsi_pre_process_io_task(struct spdk_ssam_session *smsession, struct spdk_ssam_scsi_task *task) ++{ ++ int ret; ++ struct spdk_ssam_scsi_session *ssmsession = (struct spdk_ssam_scsi_session*)smsession; ++ ++ ret = ssam_scsi_io_task_setup(task); ++ if (ret != 0) { ++ if (ret == -ENOMEM) { ++ ssam_session_insert_io_wait(smsession, &task->session_io_wait); ++ return; ++ } ++ task->resp.response = VIRTIO_SCSI_S_FAILURE; ++ ssmsession->scsi_dev_state[task->tgt_id].flight_io++; ++ ssam_scsi_submit_completion(task); ++ return; ++ } ++ ++ ssam_scsi_process_io_task(smsession, task); ++} ++ ++static void ++ssam_scsi_process_request(struct spdk_ssam_session *smsession, struct ssam_request *io_req, ++ uint16_t vq_idx) ++{ ++ struct spdk_ssam_scsi_task *task = NULL; ++ struct spdk_ssam_virtqueue *vq = &smsession->virtqueue[vq_idx]; ++ ++ if (spdk_unlikely(vq->use_num >= vq->num)) { ++ SPDK_ERRLOG("Session:%s vq(%hu) task_cnt(%u) limit(%u).\n", smsession->name, vq_idx, vq->use_num, vq->num); ++ ssam_scsi_req_complete(smsession->smdev, io_req, VIRTIO_SCSI_S_FAILURE); ++ return; ++ } ++ ++ uint32_t index = vq->index[vq->index_r]; ++ task = &((struct spdk_ssam_scsi_task *)vq->tasks)[index]; ++ if (spdk_unlikely(task->used)) { ++ SPDK_ERRLOG("%s: vq(%hu) task_idx(%hu) is already pending.\n", smsession->name, vq_idx, task->task_idx); ++ ssam_scsi_req_complete(smsession->smdev, io_req, VIRTIO_SCSI_S_FAILURE); ++ return; ++ } ++ ++ smsession->task_cnt++; ++ vq->index_r = (vq->index_r + 1) & 0xFF; ++ vq->use_num++; ++ ssam_scsi_task_init(task); ++ task->io_req = io_req; ++ ++ if (spdk_unlikely(io_req->type == VMIO_TYPE_VIRTIO_SCSI_CTRL)) { ++ ssam_scsi_process_ctl_task(smsession, task); ++ } else { ++ ssam_scsi_pre_process_io_task(smsession, task); ++ } ++ ++ return; ++} ++ ++static void ++ssam_scsi_request_worker(struct spdk_ssam_session *smsession, void *arg) ++{ ++ struct ssam_request *io_req = (struct ssam_request*)arg; ++ struct ssam_io_message *io_cmd = &io_req->req.cmd; ++ struct spdk_ssam_dev *smdev = smsession->smdev; ++ struct virtio_scsi_cmd_req *req = (struct virtio_scsi_cmd_req *)io_cmd->header; ++ struct spdk_ssam_scsi_session *ssmsession = (struct spdk_ssam_scsi_session *)smsession; ++ uint16_t vq_idx = io_cmd->virtio.vq_idx; ++ uint32_t tgt_id = req->lun[1]; ++ ++ smdev->io_num++; ++ ++ if (vq_idx >= smsession->max_queues) { ++ SPDK_ERRLOG("vq_idx out of range, need less than %u, actually %u\n", ++ smsession->max_queues, vq_idx); ++ goto err; ++ } ++ ++ if (io_req->status != SSAM_IO_STATUS_OK) { ++ SPDK_WARNLOG("%s: ssam request status invalid, but still process, status=%d\n", ++ smsession->name, io_req->status); ++ goto err; ++ } ++ ++ if (ssmsession->scsi_dev_state[tgt_id].status != SSAM_SCSI_DEV_PRESENT) { ++ /* If dev has been deleted, return io err */ ++ goto err; ++ } ++ ++ ssam_scsi_process_request(smsession, io_req, vq_idx); ++ ++ return; ++ ++err: ++ ssam_scsi_req_complete(smsession->smdev, io_req, VIRTIO_SCSI_S_FAILURE); ++ return; ++} ++ ++static void ++ssam_scsi_response_worker(struct spdk_ssam_session *smsession, void *arg) ++{ ++ struct ssam_dma_rsp *dma_rsp = (struct ssam_dma_rsp*)arg; ++ const struct spdk_ssam_dma_cb *dma_cb = (const struct spdk_ssam_dma_cb *)&dma_rsp->cb; ++ struct spdk_ssam_scsi_task *task = NULL; ++ uint16_t vq_idx = dma_cb->vq_idx; ++ uint16_t task_idx = dma_cb->task_idx; ++ uint8_t req_dir = dma_cb->req_dir; ++ ++ if (spdk_unlikely(vq_idx >= smsession->max_queues)) { ++ smsession->smdev->discard_io_num++; ++ SPDK_ERRLOG("vq_idx out of range, need less than %u, actually %u\n", ++ smsession->max_queues, vq_idx); ++ return; ++ } ++ ++ task = &((struct spdk_ssam_scsi_task *)smsession->virtqueue[vq_idx].tasks)[task_idx]; ++ if (dma_rsp->status != 0) { ++ task->resp.response = VIRTIO_SCSI_S_FAILURE; ++ ssam_scsi_submit_completion(task); ++ SPDK_ERRLOG("dma data process failed!\n"); ++ return; ++ } ++ if (dma_rsp->last_flag == 0) { ++ task->resp.response = VIRTIO_SCSI_S_FAILURE; ++ ssam_scsi_submit_completion(task); ++ SPDK_ERRLOG("last_flag should not equal 0!\n"); ++ return; ++ } ++ int32_t tgt_id = task->tgt_id; ++ int32_t lun_id = spdk_scsi_lun_get_id(task->scsi_task.lun); ++ struct spdk_scsi_dev_io_state *io_stat = task->ssmsession->scsi_dev_state[tgt_id].io_stat[lun_id]; ++ ++ ssam_scsi_task_stat_tick(&task->task_stat.dma_end_tsc); ++ task->ssmsession->scsi_dev_state[task->tgt_id].io_stat[0]->scsi_stat.dma_complete_count++; ++ ++ if (req_dir == SSAM_REQUEST_DATA_LOAD) { ++ /* Write: write data ready, submit task to backend */ ++ ssam_scsi_submit_io_task(task); ++ /* First part end of write */ ++ io_stat->stat.write_latency_ticks += ssam_get_diff_tsc(io_stat->submit_tsc); ++ } else if (req_dir == SSAM_REQUEST_DATA_STORE) { ++ /* Read: data have been read by user, complete the task */ ++ task->resp.response = VIRTIO_SCSI_S_OK; ++ ssam_scsi_submit_completion(task); ++ /* Second part end of read */ ++ io_stat->stat.read_latency_ticks += ssam_get_diff_tsc(io_stat->submit_tsc); ++ } else { ++ io_stat->scsi_stat.fatal_ios++; ++ } ++} ++ ++static void ++ssam_scsi_destroy_bdev_device(struct spdk_ssam_session *smsession, void *args) ++{ ++ unsigned scsi_tgt_num = (unsigned)(uintptr_t)(args); ++ struct spdk_ssam_scsi_session *ssmsession = (struct spdk_ssam_scsi_session *)smsession; ++ ++ ssam_remove_scsi_tgt(ssmsession, scsi_tgt_num); ++} ++ ++static void ++ssam_free_scsi_task_pool(struct spdk_ssam_scsi_session *ssmsession) ++{ ++ struct spdk_ssam_session *smsession = &ssmsession->smsession; ++ struct spdk_ssam_virtqueue *vq = NULL; ++ uint16_t max_queues = smsession->max_queues; ++ uint16_t i; ++ ++ if (max_queues > SPDK_SSAM_MAX_VQUEUES) { ++ return; ++ } ++ ++ for (i = 0; i < max_queues; i++) { ++ vq = &smsession->virtqueue[i]; ++ if (vq->tasks != NULL) { ++ spdk_free(vq->tasks); ++ vq->tasks = NULL; ++ } ++ ++ if (vq->index != NULL) { ++ spdk_free(vq->index); ++ vq->index = NULL; ++ } ++ } ++} ++ ++static int ++ssam_alloc_scsi_task_pool(struct spdk_ssam_scsi_session *ssmsession) ++{ ++ struct spdk_ssam_session *smsession = &ssmsession->smsession; ++ struct spdk_ssam_virtqueue *vq = NULL; ++ struct spdk_ssam_scsi_task *task = NULL; ++ uint16_t max_queues = smsession->max_queues; ++ uint32_t task_cnt = smsession->queue_size; ++ uint16_t i; ++ uint32_t j; ++ ++ if ((max_queues > SPDK_SSAM_MAX_VQUEUES) || (max_queues == 0)) { ++ SPDK_ERRLOG("%s: max_queues %u invalid\n", smsession->name, max_queues); ++ return -EINVAL; ++ } ++ ++ if ((task_cnt == 0) || (task_cnt > SPDK_SSAM_MAX_VQ_SIZE)) { ++ SPDK_ERRLOG("%s: virtuque size %u invalid\n", smsession->name, task_cnt); ++ return -EINVAL; ++ } ++ ++ for (i = 0; i < max_queues; i++) { ++ vq = &smsession->virtqueue[i]; ++ vq->smsession = smsession; ++ vq->num = task_cnt; ++ vq->use_num = 0; ++ vq->index_l = 0; ++ vq->index_r = 0; ++ vq->tasks = spdk_zmalloc(sizeof(struct spdk_ssam_scsi_task) * task_cnt, ++ SPDK_CACHE_LINE_SIZE, NULL, ++ SPDK_ENV_LCORE_ID_ANY, SPDK_MALLOC_DMA); ++ vq->index = spdk_zmalloc(sizeof(uint32_t) * task_cnt, ++ SPDK_CACHE_LINE_SIZE, NULL, ++ SPDK_ENV_LCORE_ID_ANY, SPDK_MALLOC_DMA); ++ if (vq->tasks == NULL || vq->index == NULL) { ++ SPDK_ERRLOG("%s: failed to allocate %"PRIu32" tasks for virtqueue %"PRIu16"\n", ++ smsession->name, task_cnt, i); ++ ssam_free_scsi_task_pool(ssmsession); ++ return -ENOMEM; ++ } ++ ++ for (j = 0; j < task_cnt; j++) { ++ task = &((struct spdk_ssam_scsi_task *)vq->tasks)[j]; ++ task->ssmsession = ssmsession; ++ task->smsession = &ssmsession->smsession; ++ task->vq_idx = i; ++ task->task_idx = j; ++ vq->index[j] = j; ++ } ++ } ++ ++ return 0; ++} ++ ++static void ++ssam_scsi_print_stuck_io_info(struct spdk_ssam_session *smsession) ++{ ++ struct spdk_ssam_scsi_task *tasks; ++ struct spdk_ssam_scsi_task *task; ++ int i, j; ++ ++ for (i = 0; i < smsession->max_queues; i++) { ++ for (j = 0; j < smsession->queue_size; j++) { ++ tasks = (struct spdk_ssam_scsi_task *)smsession->virtqueue[i].tasks; ++ task = &tasks[j]; ++ if (task == NULL) { ++ continue; ++ } ++ if (task->used) { ++ SPDK_INFOLOG(ssam_scsi, "%s: stuck io payload_size %u, vq_idx %u, task_idx %u\n", ++ smsession->name, task->scsi_task.length, task->vq_idx, task->task_idx); ++ } ++ } ++ } ++} ++ ++static int ++ssam_scsi_start_cb(struct spdk_ssam_session *smsession, void **unused) ++{ ++ SPDK_NOTICELOG("SCSI controller %s created with queues %u\n", ++ smsession->name, smsession->max_queues); ++ ++ ssam_session_start_done(smsession, 0); ++ ++ return 0; ++} ++ ++static int ++ssam_scsi_start(struct spdk_ssam_session *smsession) ++{ ++ struct spdk_ssam_scsi_session *ssmsession = (struct spdk_ssam_scsi_session *)smsession; ++ struct spdk_ssam_send_event_flag send_event_flag = { ++ .need_async = false, ++ .need_rsp = true, ++ }; ++ int rc = ssam_alloc_scsi_task_pool(ssmsession); ++ if (rc != 0) { ++ SPDK_ERRLOG("%s: failed to alloc task pool.\n", smsession->name); ++ return rc; ++ } ++ return ssam_send_event_to_session(smsession, ssam_scsi_start_cb, NULL, send_event_flag, NULL); ++} ++ ++static int ++ssam_scsi_session_connect(struct spdk_ssam_session *smsession, uint16_t queues) ++{ ++ uint16_t queue_cnt = queues; ++ ++ if (queue_cnt == 0) { ++ queue_cnt = SPDK_SSAM_SCSI_DEFAULT_VQUEUES; ++ } ++ ++ smsession->max_queues = queue_cnt; ++ smsession->queue_size = SPDK_SSAM_DEFAULT_VQ_SIZE; ++ ++ return ssam_scsi_start(smsession); ++} ++ ++int ++spdk_ssam_scsi_construct(struct spdk_ssam_session_reg_info *info) ++{ ++ struct spdk_ssam_session *smsession = NULL; ++ struct spdk_ssam_scsi_session *ssmsession = NULL; ++ uint32_t session_ctx_size = sizeof(struct spdk_ssam_scsi_session) - sizeof(struct spdk_ssam_session); ++ uint16_t tid; ++ int rc = 0; ++ ++ spdk_ssam_lock(); ++ ++ tid = spdk_ssam_get_tid(); ++ if (tid == SPDK_INVALID_TID) { ++ spdk_ssam_unlock(); ++ return -EINVAL; ++ } ++ ++ info->tid = tid; ++ info->backend = &g_ssam_scsi_session_backend; ++ info->session_ctx_size = session_ctx_size; ++ strncpy(info->type_name, SPDK_SESSION_TYPE_SCSI, SPDK_SESSION_TYPE_MAX_LEN); ++ rc = spdk_ssam_session_register(info, &smsession); ++ if (rc != 0) { ++ spdk_ssam_unlock(); ++ return rc; ++ } ++ ++ ssmsession = (struct spdk_ssam_scsi_session *)smsession; ++ ssmsession->registered = true; ++ ssmsession->dbdf = strdup(info->dbdf); ++ if (ssmsession->dbdf == NULL) { ++ spdk_ssam_session_unregister(smsession); ++ spdk_ssam_unlock(); ++ return -EINVAL; ++ } ++ ++ rc = ssam_scsi_session_connect(smsession, info->queues); ++ if (rc != 0) { ++ ssam_session_unreg_response_cb(smsession); ++ spdk_ssam_session_unregister(smsession); ++ spdk_ssam_unlock(); ++ return -EINVAL; ++ } ++ ++ spdk_ssam_unlock(); ++ ++ return 0; ++} ++ ++static int ++spdk_ssam_get_scsi_tgt_num(struct spdk_ssam_scsi_session *ssmsession, int *scsi_tgt_num_out) ++{ ++ int scsi_tgt_num = *scsi_tgt_num_out; ++ if (scsi_tgt_num < 0) { ++ for (scsi_tgt_num = 0; scsi_tgt_num < SPDK_SSAM_SCSI_CTRLR_MAX_DEVS; scsi_tgt_num++) { ++ if (ssmsession->scsi_dev_state[scsi_tgt_num].dev == NULL) { ++ break; ++ } ++ } ++ ++ if (scsi_tgt_num == SPDK_SSAM_SCSI_CTRLR_MAX_DEVS) { ++ SPDK_ERRLOG("%s: all SCSI target slots are already in use.\n", ssmsession->smsession.name); ++ return -ENOSPC; ++ } ++ } else { ++ if (scsi_tgt_num >= SPDK_SSAM_SCSI_CTRLR_MAX_DEVS) { ++ SPDK_ERRLOG("%s: SCSI target number is too big (got %d, max %d)\n", ++ ssmsession->smsession.name, scsi_tgt_num, SPDK_SSAM_SCSI_CTRLR_MAX_DEVS - 1); ++ return -EINVAL; ++ } ++ } ++ *scsi_tgt_num_out = scsi_tgt_num; ++ return 0; ++} ++ ++static int ssam_scsi_dev_param_changed(struct spdk_ssam_scsi_session *ssmsession, ++ unsigned scsi_tgt_num) ++{ ++ struct spdk_scsi_dev_ssam_state *state = &ssmsession->scsi_dev_state[scsi_tgt_num]; ++ ++ if (state->dev == NULL) { ++ return 0; ++ } ++ int rc = ssam_scsi_send_event(&ssmsession->smsession, scsi_tgt_num, VIRTIO_SCSI_T_PARAM_CHANGE, 0x2a | (0x09 << 0x8)); ++ if (rc != 0) { ++ SPDK_ERRLOG("%s: tgt %d resize send action failed\n", ssmsession->smsession.name, scsi_tgt_num); ++ return rc; ++ } ++ ++ return 0; ++} ++ ++static unsigned ++ssam_get_scsi_dev_num(const struct spdk_ssam_scsi_session *ssmsession, ++ const struct spdk_scsi_lun *lun) ++{ ++ const struct spdk_scsi_dev *scsi_dev; ++ unsigned scsi_dev_num; ++ ++ scsi_dev = spdk_scsi_lun_get_dev(lun); ++ for (scsi_dev_num = 0; scsi_dev_num < SPDK_SSAM_SCSI_CTRLR_MAX_DEVS; scsi_dev_num++) { ++ if (ssmsession->scsi_dev_state[scsi_dev_num].dev == scsi_dev) { ++ break; ++ } ++ } ++ return scsi_dev_num; ++} ++ ++static void ++ssam_scsi_lun_resize(const struct spdk_scsi_lun *lun, void *arg) ++{ ++ struct spdk_ssam_scsi_session *ssmsession = arg; ++ unsigned scsi_dev_num; ++ ++ scsi_dev_num = ssam_get_scsi_dev_num(ssmsession, lun); ++ if (scsi_dev_num == SPDK_SSAM_SCSI_CTRLR_MAX_DEVS) { ++ /* The entire device has been already removed. */ ++ return; ++ } ++ ++ (void)ssam_scsi_dev_param_changed(ssmsession, scsi_dev_num); ++} ++ ++static void ++ssam_scsi_lun_hotremove(const struct spdk_scsi_lun *lun, void *arg) ++{ ++ struct ssam_scsi_tgt_hotplug_ctx *ctx; ++ struct spdk_ssam_scsi_session *ssmsession = arg; ++ struct spdk_ssam_send_event_flag send_event_flag = { ++ .need_async = false, ++ .need_rsp = false, ++ }; ++ unsigned scsi_dev_num; ++ ++ scsi_dev_num = ssam_get_scsi_dev_num(ssmsession, lun); ++ if (scsi_dev_num == SPDK_SSAM_SCSI_CTRLR_MAX_DEVS) { ++ /* The entire device has been already removed. */ ++ return; ++ } ++ ++ ctx = calloc(1, sizeof(*ctx)); ++ if (ctx == NULL) { ++ SPDK_ERRLOG("calloc failed\n"); ++ return; ++ } ++ ++ ctx->scsi_tgt_num = scsi_dev_num; ++ ssam_send_event_to_session(&ssmsession->smsession, spdk_ssam_scsi_dev_hot_remove_tgt, ++ NULL, send_event_flag, ctx); ++} ++ ++static int ++ssam_scsi_session_add_tgt(struct spdk_ssam_session *smsession, void **ctx) ++{ ++ struct ssam_add_tgt_ev_ctx *args = (struct ssam_add_tgt_ev_ctx *)(*ctx); ++ unsigned scsi_tgt_num = args->tgt_num; ++ struct spdk_ssam_scsi_session *ssmsession = (struct spdk_ssam_scsi_session *)smsession; ++ int rc; ++ ++ rc = spdk_scsi_dev_allocate_io_channels(ssmsession->scsi_dev_state[scsi_tgt_num].dev); ++ if (rc != 0) { ++ SPDK_ERRLOG("%s: Couldn't allocate io channel for SCSI target %u.\n", ++ smsession->name, scsi_tgt_num); ++ } ++ ++ rc = ssam_scsi_send_event(smsession, scsi_tgt_num, VIRTIO_SCSI_T_TRANSPORT_RESET, VIRTIO_SCSI_EVT_RESET_RESCAN); ++ if (rc != 0) { ++ SPDK_WARNLOG("%s: send event %d(reason %d) to target %hu failed, ret: %d, host maynot boot.\n", ++ smsession->name, VIRTIO_SCSI_T_TRANSPORT_RESET, VIRTIO_SCSI_EVT_RESET_RESCAN, scsi_tgt_num, rc); ++ if (rc == -ENOSPC) { ++ spdk_scsi_dev_free_io_channels(ssmsession->scsi_dev_state[scsi_tgt_num].dev); ++ ssam_scsi_destruct_tgt(ssmsession, scsi_tgt_num); ++ return rc; ++ } ++ } ++ ++ ssmsession->scsi_dev_state[scsi_tgt_num].status = SSAM_SCSI_DEV_PRESENT; ++ ssmsession->scsi_dev_state[scsi_tgt_num].flight_io = 0; ++ ++ return 0; ++} ++ ++static void ++ssam_scsi_dev_add_tgt_cpl_cb(struct spdk_ssam_session *smsession, void **ctx) ++{ ++ struct spdk_ssam_scsi_session *ssmsession = (struct spdk_ssam_scsi_session *)smsession; ++ struct ssam_add_tgt_ev_ctx *args = (struct ssam_add_tgt_ev_ctx *)(*ctx); ++ unsigned scsi_tgt_num = args->tgt_num; ++ ssmsession->ref++; ++ ++ SPDK_NOTICELOG("SCSI controller %s target %u added with bdev %s\n", ++ smsession->name, scsi_tgt_num, args->bdev_name); ++ ++ free(args->bdev_name); ++ args->bdev_name = NULL; ++ free(args); ++} ++ ++struct ssam_scsi_session_remove_tgt_arg { ++ struct spdk_ssam_session *smsession; ++ unsigned scsi_tgt_num; ++}; ++ ++static void ++ssam_scsi_session_remove_tgt_cpl(struct spdk_ssam_session *smsession, void **_ctx) ++{ ++ struct ssam_scsi_tgt_hotplug_ctx *ctx = *_ctx; ++ unsigned scsi_tgt_num = ctx->scsi_tgt_num; ++ int rc; ++ rc = ssam_umount_normal(smsession, ssam_scsi_tgtid_to_lunid(scsi_tgt_num)); ++ if (rc != 0) { ++ SPDK_ERRLOG("%s: function umount failed when remove scsi tgt:%s.\n", ++ smsession->name, strerror(-rc)); ++ } ++ free(ctx); ++} ++ ++static int ++ssam_scsi_session_remove_tgt(struct spdk_ssam_session *smsession, void **_ctx) ++{ ++ struct ssam_scsi_tgt_hotplug_ctx *ctx = *_ctx; ++ unsigned scsi_tgt_num = ctx->scsi_tgt_num; ++ struct spdk_ssam_scsi_session *ssmsession = (struct spdk_ssam_scsi_session *)smsession; ++ struct spdk_scsi_dev_ssam_state *state = &ssmsession->scsi_dev_state[scsi_tgt_num]; ++ int rc = 0; ++ ++ if (state->status != SSAM_SCSI_DEV_PRESENT) { ++ SPDK_WARNLOG("%s: SCSI target %u is not present, skip.\n", smsession->name, scsi_tgt_num); ++ rc = -ENODEV; ++ goto out; ++ } ++ ++ if (ssmsession->scsi_dev_state[scsi_tgt_num].flight_io != 0) { ++ SPDK_ERRLOG("%s: SCSI target %u is busy.\n", smsession->name, scsi_tgt_num); ++ rc = -EBUSY; ++ goto out; ++ } ++ ++ state->status = SSAM_SCSI_DEV_REMOVING; ++ ++ SPDK_NOTICELOG("%s: target %d is removing\n", smsession->name, scsi_tgt_num); ++ ++ rc = ssam_scsi_send_event(smsession, scsi_tgt_num, VIRTIO_SCSI_T_TRANSPORT_RESET, ++ VIRTIO_SCSI_EVT_RESET_REMOVED); ++ if (rc != 0) { ++ SPDK_ERRLOG("%s: scsi send remove event failed\n", smsession->name); ++ if (rc == -ENOSPC) { ++ state->status = SSAM_SCSI_DEV_PRESENT; ++ goto out; ++ } ++ } ++ ++ spdk_scsi_dev_free_io_channels(state->dev); ++ ++ spdk_ssam_send_dev_destroy_msg(smsession, (void *)(uintptr_t)scsi_tgt_num); ++ ++ // free ctx see ssam_scsi_session_remove_tgt_cpl() ++ return rc; ++ ++out: ++ free(ctx); ++ ++ return rc; ++} ++ ++static int ++ssam_scsi_construct_tgt(struct spdk_ssam_scsi_session *ssmsession, int scsi_tgt_num, ++ const char *bdev_name) ++{ ++ struct spdk_scsi_dev_ssam_state *state = NULL; ++ char target_name[SPDK_SCSI_DEV_MAX_NAME] = {0}; ++ int lun_id_list[SSAM_SPDK_SCSI_DEV_MAX_LUN]; ++ const char *bdev_names_list[SSAM_SPDK_SCSI_DEV_MAX_LUN]; ++ int rc; ++ ++ state = &ssmsession->scsi_dev_state[scsi_tgt_num]; ++ if (state->dev != NULL) { ++ SPDK_ERRLOG("%s: SCSI target %u already occupied\n", ssmsession->smsession.name, scsi_tgt_num); ++ return -EEXIST; ++ } ++ ++ (void)snprintf(target_name, sizeof(target_name), "Target %u", scsi_tgt_num); ++ lun_id_list[0] = 0; ++ bdev_names_list[0] = (char *)bdev_name; ++ ++ state->status = SSAM_SCSI_DEV_ADDING; ++ rc = ssam_scsi_iostat_construct(ssmsession, scsi_tgt_num, lun_id_list, SSAM_SPDK_SCSI_DEV_MAX_LUN); ++ if (rc != 0) { ++ return rc; ++ } ++ ++ state->dev = spdk_scsi_dev_construct_ext(target_name, bdev_names_list, lun_id_list, ++ SSAM_SPDK_SCSI_DEV_MAX_LUN, ++ SPDK_SPC_PROTOCOL_IDENTIFIER_SAS, ++ ssam_scsi_lun_resize, ssmsession, ++ ssam_scsi_lun_hotremove, ssmsession); ++ if (state->dev == NULL) { ++ SPDK_ERRLOG("%s: couldn't create SCSI target %u using bdev '%s'\n", ++ ssmsession->smsession.name, scsi_tgt_num, bdev_name); ++ rc = -EINVAL; ++ goto dev_fail; ++ } ++ ++ rc = spdk_scsi_dev_add_port(state->dev, 0, "ssam"); ++ if (rc != 0) { ++ goto port_fail; ++ } ++ ++ return rc; ++ ++port_fail: ++ spdk_scsi_dev_destruct(state->dev, NULL, NULL); ++ ++dev_fail: ++ ssam_scsi_iostat_destruct(state); ++ ++ return rc; ++} ++ ++static void ++ssam_scsi_destruct_tgt(struct spdk_ssam_scsi_session *ssmsession, int scsi_tgt_num) ++{ ++ struct spdk_scsi_dev_ssam_state *state = NULL; ++ state = &ssmsession->scsi_dev_state[scsi_tgt_num]; ++ ++ if (state->dev) { ++ spdk_scsi_dev_delete_port(state->dev, 0); ++ spdk_scsi_dev_destruct(state->dev, NULL, NULL); ++ state->dev = NULL; ++ } ++ ssam_scsi_iostat_destruct(state); ++ ++ state->status = SSAM_SCSI_DEV_EMPTY; ++} ++ ++int ++spdk_ssam_scsi_dev_add_tgt(struct spdk_ssam_session *smsession, int scsi_tgt_num, ++ const char *bdev_name) ++{ ++ int rc; ++ struct spdk_ssam_scsi_session *ssmsession = (struct spdk_ssam_scsi_session *)smsession; ++ struct ssam_add_tgt_ev_ctx *ctx = calloc(1, sizeof(struct ssam_add_tgt_ev_ctx)); ++ struct spdk_ssam_send_event_flag send_event_flag = { ++ .need_async = false, ++ .need_rsp = true, ++ }; ++ ++ if (ctx == NULL) { ++ SPDK_ERRLOG("calloc ssam_add_tgt_ev_ctx failed\n"); ++ return -ENOMEM; ++ } ++ ++ if (bdev_name == NULL) { ++ SPDK_ERRLOG("No lun name specified\n"); ++ free(ctx); ++ return -EINVAL; ++ } ++ ++ ctx->bdev_name = spdk_sprintf_alloc("%s", bdev_name); ++ if (ctx->bdev_name == NULL) { ++ SPDK_ERRLOG("calloc ssam_add_tgt_ev_ctx bdev_name failed\n"); ++ free(ctx); ++ return -ENOMEM; ++ } ++ ++ rc = spdk_ssam_get_scsi_tgt_num(ssmsession, &scsi_tgt_num); ++ if (rc < 0) { ++ free(ctx->bdev_name); ++ free(ctx); ++ return rc; ++ } ++ ++ rc = ssam_mount_normal(smsession, ssam_scsi_tgtid_to_lunid(scsi_tgt_num)); ++ if (rc != SSAM_MOUNT_OK) { ++ SPDK_ERRLOG("%s: mount ssam volume failed, tgt id %d\n", smsession->name, scsi_tgt_num); ++ free(ctx->bdev_name); ++ free(ctx); ++ return rc; ++ } ++ ++ rc = ssam_scsi_construct_tgt(ssmsession, scsi_tgt_num, bdev_name); ++ if (rc != 0) { ++ free(ctx->bdev_name); ++ free(ctx); ++ return rc; ++ } ++ ++ ctx->tgt_num = scsi_tgt_num; ++ rc = ssam_send_event_to_session(&ssmsession->smsession, ssam_scsi_session_add_tgt, ++ ssam_scsi_dev_add_tgt_cpl_cb, send_event_flag, (void *)ctx); ++ if (rc != 0) { ++ ssam_scsi_destruct_tgt(ssmsession, scsi_tgt_num); ++ free(ctx->bdev_name); ++ free(ctx); ++ return rc; ++ } ++ ++ SPDK_INFOLOG(ssam_scsi, "%s: added SCSI target %u using bdev '%s'\n", ++ ssmsession->smsession.name, scsi_tgt_num, bdev_name); ++ ++ return 0; ++} ++ ++static int ++spdk_ssam_scsi_dev_hot_remove_tgt(struct spdk_ssam_session *smsession, void **_ctx) ++{ ++ int rc = 0; ++ struct ssam_scsi_tgt_hotplug_ctx *ctx = *_ctx; ++ struct spdk_ssam_scsi_session *ssmsession; ++ ssmsession = (struct spdk_ssam_scsi_session *)smsession; ++ unsigned scsi_tgt_num = ctx->scsi_tgt_num; ++ if (!ssmsession) { ++ SPDK_ERRLOG("invalid SCSI device"); ++ rc = -EINVAL; ++ goto out; ++ } ++ ++ struct spdk_scsi_dev_ssam_state *scsi_dev_state = &ssmsession->scsi_dev_state[scsi_tgt_num]; ++ if (scsi_dev_state->dev == NULL) { ++ /* Nothing to do */ ++ SPDK_WARNLOG("%s: There is no need to remove scsi target\n", smsession->name); ++ rc = -ENODEV; ++ goto out; ++ } ++ ++ if (scsi_dev_state->status != SSAM_SCSI_DEV_PRESENT) { ++ SPDK_INFOLOG(ssam_scsi, "%s: SCSI target %u is being removed\n", smsession->name, scsi_tgt_num); ++ rc = 0; ++ goto out; ++ } ++ ++ scsi_dev_state->status = SSAM_SCSI_DEV_REMOVING; ++ ++ SPDK_NOTICELOG("%s: target %d is hot removing\n", smsession->name, scsi_tgt_num); ++ ++rc = ssam_scsi_send_event(smsession, scsi_tgt_num, VIRTIO_SCSI_T_TRANSPORT_RESET, ++ VIRTIO_SCSI_EVT_RESET_REMOVED); ++if (rc != 0) { ++ SPDK_ERRLOG("%s: scsi send remove event failed\n", smsession->name); ++ if (rc == -ENOSPC) { ++ scsi_dev_state->status = SSAM_SCSI_DEV_PRESENT; ++ goto out; ++ } ++} ++ ++spdk_scsi_dev_free_io_channels(scsi_dev_state->dev); ++ ++spdk_ssam_send_dev_destroy_msg(smsession, (void *)(uintptr_t)scsi_tgt_num); ++ ++out: ++ free(ctx); ++ return rc; ++} ++ ++ int ++ spdk_ssam_scsi_dev_remove_tgt(struct spdk_ssam_session *smsession, unsigned scsi_tgt_num, ++ spdk_ssam_session_rsp_fn cb_fn, void *cb_arg) ++{ ++ struct spdk_ssam_scsi_session *ssmsession; ++ struct ssam_scsi_tgt_hotplug_ctx *ctx; ++ struct spdk_ssam_send_event_flag send_event_flag = { ++ .need_async = false, ++ .need_rsp = true, ++ }; ++ ++ if (scsi_tgt_num >= SPDK_SSAM_SCSI_CTRLR_MAX_DEVS) { ++ SPDK_ERRLOG("%s: invalid SCSI target number %d\n", smsession->name, scsi_tgt_num); ++ return -EINVAL; ++ } ++ ++ ssmsession = (struct spdk_ssam_scsi_session *)smsession; ++ if (!ssmsession) { ++ SPDK_ERRLOG("An invalid SCSI device that removing from a SCSI target."); ++ return -EINVAL; ++ } ++ ++ ctx = calloc(1, sizeof(*ctx)); ++ if (ctx == NULL) { ++ SPDK_ERRLOG("calloc failed\n"); ++ return -ENOMEM; ++ } ++ ++ ctx->scsi_tgt_num = scsi_tgt_num; ++ ++ ssam_send_event_to_session(smsession, ssam_scsi_session_remove_tgt, ++ ssam_scsi_session_remove_tgt_cpl, send_event_flag, ctx); ++ ++ return 0; ++} ++ ++SPDK_LOG_REGISTER_COMPONENT(ssam_scsi) +diff --git a/lib/ssam_adapter/Makefile b/lib/ssam_adapter/Makefile +new file mode 100644 +index 0000000..c76ed1c +--- /dev/null ++++ b/lib/ssam_adapter/Makefile +@@ -0,0 +1,48 @@ ++# ++# BSD LICENSE ++# ++# Copyright (c) Huawei Technologies Co., Ltd. 2021-2022. All rights reserved. ++# ++# Redistribution and use in source and binary forms, with or without ++# modification, are permitted provided that the following conditions ++# are met: ++# ++# * Redistributions of source code must retain the above copyright ++# notice, this list of conditions and the following disclaimer. ++# * Redistributions in binary form must reproduce the above copyright ++# notice, this list of conditions and the following disclaimer in ++# the documentation and/or other materials provided with the ++# distribution. ++# * Neither the name of Intel Corporation nor the names of its ++# contributors may be used to endorse or promote products derived ++# from this software without specific prior written permission. ++# ++# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ++# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ++# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ++# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ++# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ++# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ++# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ++# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ++# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ++# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++# ++ ++SPDK_ROOT_DIR := $(abspath $(CURDIR)/../..) ++include $(SPDK_ROOT_DIR)/mk/spdk.common.mk ++ ++SO_VER := 1 ++SO_MINOR := 0 ++ ++CFLAGS += -I. ++CFLAGS += $(ENV_CFLAGS) ++ ++C_SRCS = ssam_adapter.c ++ ++LIBNAME = ssam_adapter ++ ++SPDK_MAP_FILE = $(abspath $(CURDIR)/spdk_ssam_adapter.map) ++ ++include $(SPDK_ROOT_DIR)/mk/spdk.lib.mk +diff --git a/lib/ssam_adapter/spdk_ssam_adapter.h b/lib/ssam_adapter/spdk_ssam_adapter.h +new file mode 100644 +index 0000000..bc0c3ca +--- /dev/null ++++ b/lib/ssam_adapter/spdk_ssam_adapter.h +@@ -0,0 +1,65 @@ ++/*- ++ * BSD LICENSE ++ * ++ * Copyright (c) Huawei Technologies Co., Ltd. 2021-2022. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * ++ * * Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * * Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in ++ * the documentation and/or other materials provided with the ++ * distribution. ++ * * Neither the name of Intel Corporation nor the names of its ++ * contributors may be used to endorse or promote products derived ++ * from this software without specific prior written permission. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ++ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ++ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ++ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ++ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ++ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ++ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ++ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ++ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ++ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#ifndef SPDK_SSAM_ADAPTER_H ++#define SPDK_SSAM_ADAPTER_H ++ ++enum ssam_log_level { ++ /** All messages will be suppressed. */ ++ SSAMLOG_DISABLED = -1, ++ SSAMLOG_ERROR = 0, ++ SSAMLOG_WARN, ++ SSAMLOG_NOTICE, ++ SSAMLOG_INFO, ++ SSAMLOG_DEBUG, ++}; ++ ++struct ssam_log_para { ++ enum ssam_log_level level; ++ int line; ++ char *file; ++ char *func; ++}; ++ ++struct ssam_log_flag { ++ TAILQ_ENTRY(ssam_log_flag) tailq; ++ const char *name; ++ bool enabled; ++}; ++ ++void spdk_adapt_vlog(struct ssam_log_para *input, const char *format, va_list ap); ++void spdk_adapt_log_register_flag(const char *name, struct ssam_log_flag *flag); ++uint64_t spdk_adapt_vtophys(const void *buf, uint64_t *size); ++void *spdk_adapt_dma_malloc(size_t size, size_t align, uint64_t *phys_addr); ++void spdk_adapt_dma_free(void *buf); ++ ++#endif /* SPDK_SSAM_ADAPTER_H */ +diff --git a/lib/ssam_adapter/spdk_ssam_adapter.map b/lib/ssam_adapter/spdk_ssam_adapter.map +new file mode 100644 +index 0000000..679edf2 +--- /dev/null ++++ b/lib/ssam_adapter/spdk_ssam_adapter.map +@@ -0,0 +1,12 @@ ++{ ++ global: ++ ++ # Public functions in spdk_ssam_adapter.h ++ spdk_adapt_vlog; ++ spdk_adapt_log_register_flag; ++ spdk_adapt_vtophys; ++ spdk_adapt_dma_malloc; ++ spdk_adapt_dma_free; ++ ++ local: *; ++}; +\ No newline at end of file +diff --git a/lib/ssam_adapter/ssam_adapter.c b/lib/ssam_adapter/ssam_adapter.c +new file mode 100644 +index 0000000..d3d37a0 +--- /dev/null ++++ b/lib/ssam_adapter/ssam_adapter.c +@@ -0,0 +1,62 @@ ++/*- ++ * BSD LICENSE ++ * ++ * Copyright (c) Huawei Technologies Co., Ltd. 2021-2022. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * ++ * * Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * * Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in ++ * the documentation and/or other materials provided with the ++ * distribution. ++ * * Neither the name of Intel Corporation nor the names of its ++ * contributors may be used to endorse or promote products derived ++ * from this software without specific prior written permission. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ++ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ++ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ++ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ++ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ++ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ++ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ++ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ++ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ++ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#include "spdk/log.h" ++#include "spdk/stdinc.h" ++#include "spdk/env.h" ++ ++#include "spdk_ssam_adapter.h" ++ ++inline void spdk_adapt_vlog(struct ssam_log_para *input, const char *format, va_list ap) ++{ ++ spdk_vlog((enum spdk_log_level)input->level, input->file, input->line, input->func, format, ap); ++} ++ ++inline uint64_t spdk_adapt_vtophys(const void *buf, uint64_t *size) ++{ ++ return spdk_vtophys(buf, size); ++} ++ ++inline void *spdk_adapt_dma_malloc(size_t size, size_t align, uint64_t *phys_addr) ++{ ++ return spdk_dma_malloc(size, align, phys_addr); ++} ++ ++inline void spdk_adapt_dma_free(void *buf) ++{ ++ spdk_dma_free(buf); ++} ++ ++inline void spdk_adapt_log_register_flag(const char *name, struct ssam_log_flag *flag) ++{ ++ spdk_log_register_flag(name, (struct spdk_log_flag *)flag); ++} +diff --git a/mk/spdk.lib_deps.mk b/mk/spdk.lib_deps.mk +index a6c7825..8328eec 100644 +--- a/mk/spdk.lib_deps.mk ++++ b/mk/spdk.lib_deps.mk +@@ -169,4 +169,5 @@ DEPDIRS-event_scsi := event scsi event_bdev + + DEPDIRS-event_iscsi := event iscsi event_scsi event_sock + DEPDIRS-event_vhost := event vhost event_scsi ++DEPDIRS-event_ssam := event ssam event_scsi + DEPDIRS-event_sock := event sock +diff --git a/module/bdev/iscsi/bdev_iscsi.c b/module/bdev/iscsi/bdev_iscsi.c +index 7516ea9..e4591f1 100644 +--- a/module/bdev/iscsi/bdev_iscsi.c ++++ b/module/bdev/iscsi/bdev_iscsi.c +@@ -42,9 +42,11 @@ + #include "spdk/rpc.h" + #include "spdk/string.h" + #include "spdk/iscsi_spec.h" ++#include "spdk/likely.h" + + #include "spdk/log.h" + #include "spdk/bdev_module.h" ++#include "spdk/event.h" + + #include "iscsi/iscsi.h" + #include "iscsi/scsi-lowlevel.h" +@@ -55,6 +57,7 @@ struct bdev_iscsi_lun; + + #define BDEV_ISCSI_CONNECTION_POLL_US 500 /* 0.5 ms */ + #define BDEV_ISCSI_NO_MAIN_CH_POLL_US 10000 /* 10ms */ ++#define BDEV_ISCSI_TIMEOUT 10 /* 10s */ + + #define DEFAULT_INITIATOR_NAME "iqn.2016-06.io.spdk:init" + +@@ -85,6 +88,8 @@ struct bdev_iscsi_lun { + struct spdk_thread *no_main_ch_poller_td; + bool unmap_supported; + struct spdk_poller *poller; ++ uint32_t unfinished_io_num; ++ uint64_t event_tsc; + }; + + struct bdev_iscsi_io_channel { +@@ -101,6 +106,7 @@ struct bdev_iscsi_conn_req { + bool unmap_supported; + int lun; + int status; ++ int attempts; + TAILQ_ENTRY(bdev_iscsi_conn_req) link; + }; + +@@ -130,6 +136,7 @@ _iscsi_free_lun(void *arg) + struct bdev_iscsi_lun *lun = arg; + + assert(lun != NULL); ++ SPDK_NOTICELOG("iscsi bdev %s removed.\n", lun->bdev.name); + iscsi_destroy_context(lun->context); + pthread_mutex_destroy(&lun->mutex); + free(lun->bdev.name); +@@ -195,6 +202,9 @@ static void + bdev_iscsi_io_complete(struct bdev_iscsi_io *iscsi_io, enum spdk_bdev_io_status status) + { + iscsi_io->status = status; ++ struct spdk_bdev_io *bdev_io = spdk_bdev_io_from_ctx(iscsi_io); ++ struct bdev_iscsi_lun *lun = (struct bdev_iscsi_lun *)bdev_io->bdev->ctxt; ++ lun->unfinished_io_num--; + if (iscsi_io->submit_td != NULL) { + spdk_thread_send_msg(iscsi_io->submit_td, _bdev_iscsi_io_complete, iscsi_io); + } else { +@@ -206,6 +216,16 @@ bdev_iscsi_io_complete(struct bdev_iscsi_io *iscsi_io, enum spdk_bdev_io_status + static void + bdev_iscsi_command_cb(struct iscsi_context *context, int status, void *_task, void *_iscsi_io) + { ++ ++ if (spdk_unlikely(spdk_get_shutdown_sig_received())) { ++ /* ++ * In the hot restart process, when this callback is triggered, ++ * the _task memory may have been released. ++ * Therefore, _task are not released in this scenario. ++ */ ++ return; ++ } ++ + struct scsi_task *task = _task; + struct bdev_iscsi_io *iscsi_io = _iscsi_io; + +@@ -278,6 +298,17 @@ bdev_iscsi_destruct_cb(void *ctx) + { + struct bdev_iscsi_lun *lun = ctx; + ++ /* when main_td and no_main_ch_poller_td have different cores, ++ * ensure that the poller is deregistered before free lun. ++ */ ++ pthread_mutex_lock(&lun->mutex); ++ if (lun->ch_count > 0) { ++ pthread_mutex_unlock(&lun->mutex); ++ spdk_thread_send_msg(lun->no_main_ch_poller_td, bdev_iscsi_destruct_cb, lun); ++ return; ++ } ++ pthread_mutex_unlock(&lun->mutex); ++ + spdk_poller_unregister(&lun->no_main_ch_poller); + spdk_io_device_unregister(lun, _iscsi_free_lun); + } +@@ -379,7 +410,17 @@ bdev_iscsi_poll_lun(void *_lun) + return SPDK_POLLER_IDLE; + } + ++ /* When the default route is deleted, the TCP connection cannot be reconnected. ++ * Therefore, if fd not open, hot restart SSAM. ++ */ ++ if (pfd.revents == POLLNVAL) { ++ SPDK_WARNLOG("fd not open, hot restart SSAM.\n"); ++ sleep(1); ++ raise(SIGTERM); ++ } ++ + if (pfd.revents != 0) { ++ lun->event_tsc = spdk_get_ticks(); + if (iscsi_service(lun->context, pfd.revents) < 0) { + SPDK_ERRLOG("iscsi_service failed: %s\n", iscsi_get_error(lun->context)); + } +@@ -387,6 +428,20 @@ bdev_iscsi_poll_lun(void *_lun) + return SPDK_POLLER_BUSY; + } + ++ /* When the network is disconnected, the revent obtained by the poll() is always 0. ++ * As a result, the I/O cannot be responded, causing IO hang. ++ * Therefore, if no event is obtained within a period of time and there are unfinished I/Os, ++ * iscsi_service() will be called to obtain the disconnection event. ++ */ ++ uint64_t diff_tsc = spdk_get_ticks() - lun->event_tsc; ++ if (spdk_unlikely((diff_tsc / BDEV_ISCSI_TIMEOUT) >= spdk_get_ticks_hz() && lun->unfinished_io_num != 0)) { ++ SPDK_ERRLOG("There is no event while the unfinished io num is not zero(%d)\n", lun->unfinished_io_num); ++ if (iscsi_service(lun->context, pfd.revents) < 0) { ++ SPDK_ERRLOG("iscsi_service failed: %s\n", iscsi_get_error(lun->context)); ++ } ++ lun->event_tsc = spdk_get_ticks(); ++ } ++ + return SPDK_POLLER_IDLE; + } + +@@ -431,7 +486,12 @@ static void _bdev_iscsi_submit_request(void *_bdev_io) + struct spdk_bdev_io *bdev_io = _bdev_io; + struct bdev_iscsi_io *iscsi_io = (struct bdev_iscsi_io *)bdev_io->driver_ctx; + struct bdev_iscsi_lun *lun = (struct bdev_iscsi_lun *)bdev_io->bdev->ctxt; +- ++ if (lun->unfinished_io_num == UINT32_MAX) { ++ bdev_iscsi_io_complete(iscsi_io, SPDK_BDEV_IO_STATUS_FAILED); ++ SPDK_ERRLOG("Too many unfinished io jobs\n"); ++ return; ++ } ++ lun->unfinished_io_num++; + switch (bdev_io->type) { + case SPDK_BDEV_IO_TYPE_READ: + spdk_bdev_io_get_buf(bdev_io, bdev_iscsi_get_buf_cb, +@@ -538,6 +598,7 @@ _iscsi_destroy_cb(void *ctx) + + lun->main_td = NULL; + spdk_poller_unregister(&lun->poller); ++ SPDK_NOTICELOG("iscsi bdev %s unregister main poller\n", lun->bdev.name); + + pthread_mutex_unlock(&lun->mutex); + } +@@ -638,6 +699,7 @@ create_iscsi_lun(struct iscsi_context *context, int lun_id, char *url, char *ini + lun->lun_id = lun_id; + lun->url = url; + lun->initiator_iqn = initiator_iqn; ++ lun->event_tsc = spdk_get_ticks(); + + pthread_mutex_init(&lun->mutex, NULL); + +@@ -770,10 +832,33 @@ iscsi_bdev_conn_poll(void *arg) + + if (pfd.revents != 0) { + if (iscsi_service(context, pfd.revents) < 0) { ++ req->attempts += 1; ++ if (req->attempts >= 10) { ++ SPDK_ERRLOG("iscsi_service failed times: %d\n", req->attempts); ++ iscsi_connect_cb(context, -1, NULL, req); ++ _bdev_iscsi_conn_req_free(req); ++ continue; ++ } + SPDK_ERRLOG("iscsi_service failed: %s\n", iscsi_get_error(context)); + } + } + ++ if ((pfd.revents & POLLHUP) && (req->status == -1)) { ++ SPDK_NOTICELOG("req->bdev_name(%s) reconnect\n", req->bdev_name); ++ iscsi_destroy_context(req->context); ++ req->context = iscsi_create_context(req->initiator_iqn); ++ struct iscsi_url *iscsi_url = iscsi_parse_full_url(req->context, req->url); ++ int rc = iscsi_set_session_type(req->context, ISCSI_SESSION_NORMAL); ++ rc = rc ? rc : iscsi_set_header_digest(req->context, ISCSI_HEADER_DIGEST_NONE); ++ rc = rc ? rc : iscsi_set_targetname(req->context, iscsi_url->target); ++ rc = iscsi_full_connect_async(req->context, iscsi_url->portal, iscsi_url->lun, iscsi_connect_cb, req); ++ if (rc < 0) { ++ SPDK_ERRLOG("Failed to connect provided URL=%s: %s\n", req->url, iscsi_get_error(req->context)); ++ } ++ iscsi_destroy_url(iscsi_url); ++ sleep(3); ++ } ++ + if (req->status == 0) { + /* + * The request completed successfully. +@@ -847,10 +932,12 @@ create_iscsi_disk(const char *bdev_name, const char *url, const char *initiator_ + + iscsi_destroy_url(iscsi_url); + req->status = -1; ++ req->attempts = 0; + TAILQ_INSERT_TAIL(&g_iscsi_conn_req, req, link); + if (!g_conn_poller) { + g_conn_poller = SPDK_POLLER_REGISTER(iscsi_bdev_conn_poll, NULL, BDEV_ISCSI_CONNECTION_POLL_US); + } ++ SPDK_NOTICELOG("iscsi bdev %s created by %s.\n", req->bdev_name, req->initiator_iqn); + + return 0; + +diff --git a/module/event/subsystems/Makefile b/module/event/subsystems/Makefile +index a78985e..0c1822d 100644 +--- a/module/event/subsystems/Makefile ++++ b/module/event/subsystems/Makefile +@@ -41,6 +41,7 @@ DIRS-y += nbd + endif + + DIRS-$(CONFIG_VHOST) += vhost ++DIRS-$(CONFIG_SSAM) += ssam + + # These dependencies are not based specifically on symbols, but rather + # the subsystem dependency tree defined within the event subsystem C files +@@ -52,6 +53,7 @@ DEPDIRS-nbd := bdev + DEPDIRS-nvmf := bdev + DEPDIRS-scsi := bdev + DEPDIRS-vhost := scsi ++DEPDIRS-ssam := scsi + + .PHONY: all clean $(DIRS-y) + +diff --git a/module/event/subsystems/ssam/Makefile b/module/event/subsystems/ssam/Makefile +new file mode 100644 +index 0000000..cf4188f +--- /dev/null ++++ b/module/event/subsystems/ssam/Makefile +@@ -0,0 +1,44 @@ ++# ++# BSD LICENSE ++# ++# Copyright (c) Huawei Technologies Co., Ltd. 2021-2021. All rights reserved. ++# ++# Redistribution and use in source and binary forms, with or without ++# modification, are permitted provided that the following conditions ++# are met: ++# ++# * Redistributions of source code must retain the above copyright ++# notice, this list of conditions and the following disclaimer. ++# * Redistributions in binary form must reproduce the above copyright ++# notice, this list of conditions and the following disclaimer in ++# the documentation and/or other materials provided with the ++# distribution. ++# * Neither the name of Intel Corporation nor the names of its ++# contributors may be used to endorse or promote products derived ++# from this software without specific prior written permission. ++# ++# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ++# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ++# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ++# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ++# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ++# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ++# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ++# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ++# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ++# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++# ++ ++SPDK_ROOT_DIR := $(abspath $(CURDIR)/../../../..) ++include $(SPDK_ROOT_DIR)/mk/spdk.common.mk ++ ++SO_VER := 3 ++SO_MINOR := 0 ++ ++C_SRCS = ssam.c ++LIBNAME = event_ssam ++ ++SPDK_MAP_FILE = $(SPDK_ROOT_DIR)/mk/spdk_blank.map ++ ++include $(SPDK_ROOT_DIR)/mk/spdk.lib.mk +diff --git a/module/event/subsystems/ssam/ssam.c b/module/event/subsystems/ssam/ssam.c +new file mode 100644 +index 0000000..7636b8a +--- /dev/null ++++ b/module/event/subsystems/ssam/ssam.c +@@ -0,0 +1,71 @@ ++/*- ++ * BSD LICENSE ++ * ++ * Copyright (c) Huawei Technologies Co., Ltd. 2021-2021. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * ++ * * Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * * Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in ++ * the documentation and/or other materials provided with the ++ * distribution. ++ * * Neither the name of Intel Corporation nor the names of its ++ * contributors may be used to endorse or promote products derived ++ * from this software without specific prior written permission. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ++ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ++ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ++ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ++ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ++ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ++ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ++ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ++ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ++ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#include "spdk/stdinc.h" ++ ++#include "spdk/ssam.h" ++ ++#include "spdk_internal/event.h" ++ ++static void ++ssam_subsystem_init_done(int rc) ++{ ++ spdk_subsystem_init_next(rc); ++} ++ ++static void ++ssam_subsystem_init(void) ++{ ++ spdk_ssam_subsystem_init(ssam_subsystem_init_done); ++} ++ ++static void ++ssam_subsystem_fini_done(void) ++{ ++ spdk_subsystem_fini_next(); ++} ++ ++static void ++ssam_subsystem_fini(void) ++{ ++ spdk_ssam_subsystem_fini(ssam_subsystem_fini_done); ++} ++ ++static struct spdk_subsystem g_spdk_subsystem_ssam = { ++ .name = SSAM_SERVER_NAME, ++ .init = ssam_subsystem_init, ++ .fini = ssam_subsystem_fini, ++ .write_config_json = spdk_ssam_config_json, ++}; ++ ++SPDK_SUBSYSTEM_REGISTER(g_spdk_subsystem_ssam); ++SPDK_SUBSYSTEM_DEPEND(ssam, scsi) +diff --git a/scripts/generate_patch.sh b/scripts/generate_patch.sh +new file mode 100644 +index 0000000..a11ceb1 +--- /dev/null ++++ b/scripts/generate_patch.sh +@@ -0,0 +1,24 @@ ++#! /bin/bash ++ ++if ! git rev-parse --is-inside-work-tree > /dev/null 2>&1; then ++ echo "Error: This script must be run inside a git repository." ++ exit 1 ++fi ++ ++B_COMMIT="ef3ba74365acd74e9a179c6cb3dadece573f5726" ++ ++OUTPUT="../OUTPUT" ++PATCH_FILE="spdk-21.01.patch" ++ ++if [ ! -d "$OUTPUT_DIR" ]; then ++ mkdir -p "$OUTPUT_DIR" ++fi ++ ++git diff "$B_COMMIT" HEAD > "${OUTPUT_DIR}/${PATCH_FILE}" ++ ++if [ $? -eq 0 ]; then ++ echo "Patch generated and saved to ${OUTPUT_DIR}/${PATCH_FILE}" ++else ++ echo "Error: Failed to generate patch." ++ exit 1 ++fi +\ No newline at end of file +diff --git a/scripts/hw_dpu_rpc.py b/scripts/hw_dpu_rpc.py +new file mode 100644 +index 0000000..f1a35a0 +--- /dev/null ++++ b/scripts/hw_dpu_rpc.py +@@ -0,0 +1,240 @@ ++#!/usr/bin/env python3 ++ ++import argparse ++import logging ++import rpc ++import sys ++from rpc.client import print_dict, JSONRPCException ++from rpc.helpers import deprecated_aliases ++import os ++import stat ++import pwd ++import grp ++ ++def get_parser(): ++ parser = argparse.ArgumentParser( ++ description='SPDK RPC command line interface', usage='%(prog)s [options]') ++ ++ ++ parser.add_argument('-r', dest='conn_retries', ++ help='Retry connecting to the RPC server N times with 0.2s interval. Default: 0', ++ default=0, type=int) ++ parser.add_argument('-t', dest='timeout', ++ help='Timeout as a floating point number expressed in seconds waiting for response. Default: 60.0', ++ default=60.0, type=float) ++ ++ parser.set_defaults(is_server=False) ++ parser.set_defaults(dry_run=False) ++ parser.set_defaults(port=5260) ++ parser.set_defaults(verbose="ERROR") ++ parser.set_defaults(server_addr='/var/tmp/spdk.sock') ++ return parser ++ ++def init_rpc_func(): ++ parser = get_parser() ++ subparsers = parser.add_subparsers(help='RPC methods', dest='called_rpc_name', metavar='') ++ ++ @rpc.ssam.log_info ++ def create_blk_controller(args): ++ rpc.ssam.create_blk_controller(args.client, ++ dev_name=args.dev_name, ++ index=args.index, ++ readonly=args.readonly, ++ serial=args.serial) ++ ++ p = subparsers.add_parser('create_blk_controller', ++ help='Add a new block controller') ++ p.add_argument('dev_name', help='block device name') ++ p.add_argument('index', help='function id or dbdf') ++ p.add_argument("-r", "--readonly", action='store_true', help='Set controller as read-only') ++ p.add_argument("-s", "--serial", help='Set volume id') ++ p.set_defaults(func=create_blk_controller) ++ ++ @rpc.ssam.log_info ++ def get_controllers(args): ++ print_dict(rpc.ssam.get_controllers(args.client, args.function_id, args.dbdf)) ++ ++ p = subparsers.add_parser('get_controllers', ++ help='List all or specific controller(s)') ++ p.add_argument('-f', '--function_id', help="function id of PCI device", type=int, required=False) ++ p.add_argument('-d', '--dbdf', help="dbdf of PCI device", required=False) ++ p.set_defaults(func=get_controllers) ++ ++ @rpc.ssam.log_info ++ def get_scsi_controllers(args): ++ print_dict(rpc.ssam.get_scsi_controllers(args.client, args.name)) ++ ++ p = subparsers.add_parser('get_scsi_controllers', aliases=['scsi_controller_list'], ++ help='List all or specific scsi controller(s)') ++ p.add_argument('-n', '--name', help="name of controller", required=False) ++ p.set_defaults(func=get_scsi_controllers) ++ ++ @rpc.ssam.log_info ++ def delete_controller(args): ++ rpc.ssam.delete_controller(args.client, index=args.index) ++ ++ p = subparsers.add_parser('delete_controller', ++ help='Delete a controller') ++ p.add_argument('index', help='function id or dbdf of PCI device') ++ p.set_defaults(func=delete_controller) ++ ++ @rpc.ssam.log_info ++ def delete_scsi_controller(args): ++ rpc.ssam.delete_scsi_controller(args.client, name=args.name) ++ ++ p = subparsers.add_parser('delete_scsi_controller', aliases=['scsi_controller_delete'], ++ help='Delete a scsi controller') ++ p.add_argument('name', help='controller name to be delete', type=str) ++ p.set_defaults(func=delete_scsi_controller) ++ ++ @rpc.ssam.log_info ++ def bdev_resize(args): ++ rpc.ssam.bdev_resize(args.client, ++ function_id=args.function_id, ++ new_size_in_mb=args.new_size_in_mb) ++ ++ p = subparsers.add_parser('bdev_resize', ++ help='Resize a blk bdev by blk controller') ++ p.add_argument('function_id', help='function id of PCI device', type=int) ++ p.add_argument('new_size_in_mb', help='new bdev size for resize operation. The unit is MiB', type=int) ++ p.set_defaults(func=bdev_resize) ++ ++ @rpc.ssam.log_info ++ def scsi_bdev_resize(args): ++ rpc.ssam.scsi_bdev_resize(args.client, ++ name=args.name, ++ tgt_id=args.tgt_id, ++ new_size_in_mb=args.new_size_in_mb) ++ ++ p = subparsers.add_parser('scsi_bdev_resize', ++ help='Resize a scsi bdev by scsi controller') ++ p.add_argument('name', help='controller name of PCI device', type=str) ++ p.add_argument('tgt_id', help='tgt id of bdev', type=int) ++ p.add_argument('new_size_in_mb', help='new bdev size for resize operation. The unit is MiB', type=int) ++ p.set_defaults(func=scsi_bdev_resize) ++ ++ @rpc.ssam.log_info ++ def bdev_aio_resize(args): ++ rpc.ssam.bdev_aio_resize(args.client, ++ name=args.name, ++ new_size_in_mb=args.new_size_in_mb) ++ ++ p = subparsers.add_parser('bdev_aio_resize', ++ help='Resize a bdev by bdev name') ++ p.add_argument('name', help='aio bdev name', type=str) ++ p.add_argument('new_size_in_mb', help='new bdev size for resize operation. The unit is MiB', type=int) ++ p.set_defaults(func=bdev_aio_resize) ++ ++ @rpc.ssam.log_info ++ def os_ready(args): ++ rpc.ssam.os_ready(args.client) ++ ++ p = subparsers.add_parser('os_ready', ++ help='Write ready flag for booting OS.') ++ p.set_defaults(func=os_ready) ++ ++ @rpc.ssam.log_info ++ def controller_get_iostat(args): ++ print_dict(rpc.ssam.controller_get_iostat(args.client, args.function_id, args.dbdf)) ++ ++ p = subparsers.add_parser('controller_get_iostat', ++ help='Show all or specific controller(s) iostat') ++ p.add_argument('-f', '--function_id', help="function id of PCI device", type=int, required=False) ++ p.add_argument('-d', '--dbdf', help="dbdf of PCI device", required=False) ++ p.set_defaults(func=controller_get_iostat) ++ ++ @rpc.ssam.log_info ++ def controller_clear_iostat(args): ++ rpc.ssam.controller_clear_iostat(args.client) ++ ++ p = subparsers.add_parser('controller_clear_iostat', ++ help='clear all controllers iostat') ++ p.set_defaults(func=controller_clear_iostat) ++ ++ @rpc.ssam.log_info ++ def create_scsi_controller(args): ++ rpc.ssam.create_scsi_controller(args.client, ++ dbdf=args.dbdf, ++ name=args.name) ++ ++ p = subparsers.add_parser('create_scsi_controller', aliases=['scsi_controller_create'], ++ help='Add a new scsi controller') ++ p.add_argument('dbdf', help='the pci dbdf of virtio scsi controller, which got by \'device_pcie_list\'', type=str) ++ p.add_argument('name', help='controller name to be create', type=str) ++ p.set_defaults(func=create_scsi_controller) ++ ++ @rpc.ssam.log_info ++ def scsi_controller_add_target(args): ++ rpc.ssam.scsi_controller_add_target(args.client, ++ name=args.name, ++ scsi_tgt_num=int(args.scsi_tgt_num), ++ bdev_name=args.bdev_name) ++ ++ p = subparsers.add_parser('scsi_controller_add_target', ++ help='Add LUN to ssam scsi controller target') ++ p.add_argument('name', help='controller name where add lun', type=str) ++ p.add_argument('scsi_tgt_num', help='target number to use') ++ p.add_argument('bdev_name', help='name of bdev to add to target') ++ p.set_defaults(func=scsi_controller_add_target) ++ ++ @rpc.ssam.log_info ++ def scsi_controller_remove_target(args): ++ rpc.ssam.scsi_controller_remove_target(args.client, ++ name=args.name, ++ scsi_tgt_num=int(args.scsi_tgt_num)) ++ ++ p = subparsers.add_parser('scsi_controller_remove_target', ++ help='Remove LUN from ssam scsi controller target') ++ p.add_argument('name', help='controller name to remove lun', type=str) ++ p.add_argument('scsi_tgt_num', help='target number to use') ++ p.set_defaults(func=scsi_controller_remove_target) ++ ++ @rpc.ssam.log_info ++ def scsi_device_iostat(args): ++ print_dict(rpc.ssam.scsi_device_iostat(args.client, ++ name=args.name, ++ scsi_tgt_num=int(args.scsi_tgt_num))) ++ ++ p = subparsers.add_parser('scsi_device_iostat', ++ help='Show iostat of scsi device') ++ p.add_argument('name', help='controller name', type=str) ++ p.add_argument('scsi_tgt_num', help='target number', type=int) ++ p.set_defaults(func=scsi_device_iostat) ++ ++ @rpc.ssam.log_info ++ def device_pcie_list(args): ++ print_dict(rpc.ssam.device_pcie_list(args.client)) ++ ++ p = subparsers.add_parser('device_pcie_list', ++ help='Show storage device pcie list') ++ p.set_defaults(func=device_pcie_list) ++ ++ return parser ++ ++if __name__ == "__main__": ++ def call_rpc_func(args): ++ args.func(args) ++ check_called_name(args.called_rpc_name) ++ ++ def check_called_name(name): ++ if name in deprecated_aliases: ++ print("{} is deprecated, use {} instead.".format(name, deprecated_aliases[name]), file=sys.stderr) ++ ++ parser = init_rpc_func() ++ args = parser.parse_args() ++ ++ if sys.stdin.isatty() and not hasattr(args, 'func'): ++ # No arguments and no data piped through stdin ++ parser.print_help() ++ exit(1) ++ ++ if args.called_rpc_name != "get_version": ++ args.client = rpc.client.JSONRPCClient(args.server_addr, args.port, args.timeout, ++ log_level=getattr(logging, args.verbose.upper()), ++ conn_retries=args.conn_retries) ++ ++ try: ++ call_rpc_func(args) ++ except JSONRPCException as ex: ++ print(ex.message) ++ exit(1) +diff --git a/scripts/rpc/__init__.py b/scripts/rpc/__init__.py +index e8fa41e..1c037d9 100644 +--- a/scripts/rpc/__init__.py ++++ b/scripts/rpc/__init__.py +@@ -22,6 +22,7 @@ from . import pmem + from . import subsystem + from . import trace + from . import vhost ++from . import ssam + from . import vmd + from . import sock + from . import client as rpc_client +diff --git a/scripts/rpc/ssam.py b/scripts/rpc/ssam.py +new file mode 100644 +index 0000000..2b7c110 +--- /dev/null ++++ b/scripts/rpc/ssam.py +@@ -0,0 +1,226 @@ ++from .helpers import deprecated_alias ++from getpass import getuser ++ ++def log_command_info(client, event): ++ """log event info. ++ Args: ++ user_name: event user ++ event: function id of PCI device ++ src_addr: queue number of ssam ++ """ ++ params = { ++ 'user_name': getuser(), ++ 'event': event, ++ 'src_addr': "localhost", ++ } ++ return client.call('log_command_info', params) ++ ++def log_info(func): ++ def wrapper_log_info(arg, *args, **kw): ++ log_command_info(arg.client, func.__name__) ++ return func(arg, *args, **kw) ++ return wrapper_log_info ++ ++def create_blk_controller(client, dev_name, index, readonly=None, serial=None): ++ """Create ssam BLK controller. ++ Args: ++ dev_name: device name to add to controller ++ index: function id or dbdf of PCI device ++ queues: queue number of ssam ++ readonly: set controller as read-only ++ serial: set volume id ++ """ ++ params = { ++ 'dev_name': dev_name, ++ 'index': index, ++ } ++ if readonly: ++ params['readonly'] = readonly ++ if serial: ++ params['serial'] = serial ++ return client.call('create_blk_controller', params) ++ ++ ++def get_controllers(client, function_id=None, dbdf=None): ++ """Get information about configured ssam controllers. ++ ++ Args: ++ function_id: function id of PCI device ++ dbdf: dbdf of PCI device ++ ++ Returns: ++ List of ssam controllers. ++ """ ++ params = {} ++ if function_id is not None: ++ params['function_id'] = function_id ++ if dbdf is not None: ++ params['dbdf'] = dbdf ++ return client.call('get_controllers', params) ++ ++def get_scsi_controllers(client, name=None): ++ """Get information about configured ssam controllers. ++ ++ Args: ++ name: name of scsi controller ++ ++ Returns: ++ List of ssam scsi controllers. ++ """ ++ params = {} ++ if name is not None: ++ params['name'] = name ++ return client.call('get_scsi_controllers', params) ++ ++def delete_controller(client, index): ++ """Delete ssam controller from configuration. ++ Args: ++ index: function id or dbdf of PCI device ++ """ ++ params = {'index': index} ++ return client.call('delete_controller', params) ++ ++def delete_scsi_controller(client, name): ++ """Delete ssam controller from configuration. ++ Args: ++ name: scsi controller name to be delete ++ """ ++ params = {'name': name} ++ return client.call('delete_scsi_controller', params) ++ ++def controller_get_iostat(client, function_id=None, dbdf=None): ++ """Get iostat about configured ssam controllers. ++ ++ Args: ++ function_id: function id of PCI device ++ dbdf: dbdf of PCI device ++ ++ Returns: ++ List of iostat of ssam controllers. ++ """ ++ params = {} ++ if function_id is not None: ++ params['function_id'] = function_id ++ if dbdf is not None: ++ params['dbdf'] = dbdf ++ return client.call('controller_get_iostat', params) ++ ++def controller_clear_iostat(client): ++ """Clear iostat about configured ssam controllers. ++ """ ++ return client.call('controller_clear_iostat') ++ ++def bdev_resize(client, function_id, new_size_in_mb): ++ """Resize bdev in the system. ++ Args: ++ function_id: function id of PCI device ++ new_size_in_mb: new bdev size for resize operation. The unit is MiB ++ """ ++ params = { ++ 'function_id': function_id, ++ 'new_size_in_mb': new_size_in_mb, ++ } ++ return client.call('bdev_resize', params) ++ ++def scsi_bdev_resize(client, name, tgt_id, new_size_in_mb): ++ """Resize scsi bdev in the system. ++ Args: ++ name: controller name of PCI device ++ tgt_id: tgt id of bdev ++ new_size_in_mb: new bdev size for resize operation. The unit is MiB ++ """ ++ params = { ++ 'name': name, ++ 'tgt_id': tgt_id, ++ 'new_size_in_mb': new_size_in_mb, ++ } ++ return client.call('scsi_bdev_resize', params) ++ ++def bdev_aio_resize(client, name, new_size_in_mb): ++ """Resize aio bdev in the system. ++ Args: ++ name: aio bdev name ++ new_size_in_mb: new bdev size for resize operation. The unit is MiB ++ """ ++ params = { ++ 'name': name, ++ 'new_size_in_mb': new_size_in_mb, ++ } ++ return client.call('bdev_aio_resize', params) ++ ++def os_ready(client): ++ """Write ready flag for booting OS. ++ ++ """ ++ return client.call('os_ready') ++ ++def create_scsi_controller(client, dbdf, name): ++ """Create ssam scsi controller. ++ Args: ++ dbdf: the pci dbdf of virtio scsi controller ++ name: controller name to be create ++ """ ++ params = { ++ 'dbdf': dbdf, ++ 'name': name, ++ } ++ ++ return client.call('create_scsi_controller', params) ++ ++def scsi_controller_add_target(client, name, scsi_tgt_num, bdev_name): ++ """Add LUN to ssam scsi controller target. ++ Args: ++ name: controller name where add lun ++ scsi_tgt_num: target number to use ++ bdev_name: name of bdev to add to target ++ """ ++ params = { ++ 'name': name, ++ 'scsi_tgt_num': scsi_tgt_num, ++ 'bdev_name': bdev_name, ++ } ++ return client.call('scsi_controller_add_target', params) ++ ++def scsi_controller_remove_target(client, name, scsi_tgt_num): ++ """Remove LUN from ssam scsi controller target. ++ Args: ++ name: controller name to remove lun ++ scsi_tgt_num: target number to use ++ """ ++ params = { ++ 'name': name, ++ 'scsi_tgt_num': scsi_tgt_num, ++ } ++ return client.call('scsi_controller_remove_target', params) ++ ++def scsi_device_iostat(client, name, scsi_tgt_num): ++ """Get iostat about scsi device. ++ ++ Args: ++ name: controller name ++ scsi_tgt_num: target number ++ ++ Returns: ++ List of iostat of ssam controllers. ++ """ ++ params = { ++ 'name': name, ++ 'scsi_tgt_num': scsi_tgt_num, ++ } ++ return client.call('scsi_device_iostat', params) ++ ++def device_pcie_list(client): ++ """Show storage device pcie list. ++ ++ Returns: ++ List of storage device pcie. ++ """ ++ ++ return client.call('device_pcie_list') ++ ++def config_remove(client): ++ """Remove json config file. ++ ++ """ ++ ++ return client.call('config_remove') +\ No newline at end of file diff --git a/patch/spdk-spec.patch b/patch/spdk-spec.patch new file mode 100644 index 0000000000000000000000000000000000000000..1d468813ea1f4c09a7f1ba6187c61b518a8fdc87 --- /dev/null +++ b/patch/spdk-spec.patch @@ -0,0 +1,67 @@ +diff --git a/spdk.spec b/spdk.spec +index 2821730..c9fd663 100644 +--- a/spdk.spec ++++ b/spdk.spec +@@ -37,6 +37,7 @@ Patch26: 0026-lib-nvme-add-mutex-before-submit-admin-request.patch + Patch27: 0027--nvme-cuse-Add-ctrlr_lock-for-cuse-register-and-unreg.patch + Patch28: 0028-fixed-use-after-free-detected-by-Coverity.patch + ++Patch1001: spdk-21.01.patch + %define package_version %{version}-%{release} + + %define install_datadir %{buildroot}/%{_datadir}/%{name} +@@ -113,6 +114,9 @@ BuildArch: noarch + %autosetup -n spdk-%{version} -p1 + + %build ++chmod +x -R scripts/* ++./scripts/pkgdep.sh --docs --pmem --rdma --uring ++ + ./configure --prefix=%{_usr} \ + --disable-tests \ + --without-crypto \ +@@ -125,7 +129,9 @@ BuildArch: noarch + --with-rdma \ + --with-shared \ + --with-iscsi-initiator \ +- --without-vtune ++ --without-vtune \ ++ --with-ssam \ ++ --disable-unit-tests + + make -j`nproc` all + +@@ -136,7 +142,7 @@ make -C doc + %check + sed -i "s/sudo//g" test/common/autotest_common.sh + sed -i '/target_space=/aexport SPDK_TEST_STORAGE=$target_dir;return 0' test/common/autotest_common.sh +-test/unit/unittest.sh ++#test/unit/unittest.sh + + %install + %make_install -j`nproc` prefix=%{_usr} libdir=%{_libdir} datadir=%{_datadir} +@@ -164,6 +170,7 @@ find %{install_datadir}/scripts -type f -regextype egrep -regex '.*([.]py)' \ + mkdir -p %{install_sbindir} + ln -sf -r %{install_datadir}/scripts/rpc.py %{install_sbindir}/%{name}-rpc + ln -sf -r %{install_datadir}/scripts/spdkcli.py %{install_sbindir}/%{name}-cli ++ln -sf -r %{install_datadir}/scripts/hw_dpu_rpc.py %{install_sbindir}/%{name}-hw_dpu_rpc + + %if %{with doc} + # Install doc +@@ -186,7 +193,7 @@ mv doc/output/html/ %{install_docdir} + /opt/spdk/scripts/setup.sh + /opt/spdk/scripts/common.sh + /opt/spdk/include/spdk/pci_ids.h +- ++%{_bindir}/ssam + + %files devel + %{_includedir}/%{name} +@@ -198,6 +205,7 @@ mv doc/output/html/ %{install_docdir} + %{_datadir}/%{name}/scripts + %{_sbindir}/%{name}-rpc + %{_sbindir}/%{name}-cli ++%{_sbindir}/%{name}-hw_dpu_rpc + + %if %{with doc} + %files doc diff --git a/spdk.spec b/spdk.spec deleted file mode 100644 index 282173098964ca97515f7d8a8231eeeb51d338de..0000000000000000000000000000000000000000 --- a/spdk.spec +++ /dev/null @@ -1,275 +0,0 @@ -# Build documentation package -%bcond_with doc - -Name: spdk -Version: 21.01.1 -Release: 15 -Summary: Set of libraries and utilities for high performance user-mode storage -License: BSD and MIT -URL: http://spdk.io -Source0: https://github.com/spdk/spdk/archive/refs/tags/v%{version}.tar.gz -Patch1: 0001-blobstore-fix-memleak-problem-in-blob_load_cpl.patch -Patch2: 0002-blobstore-fix-potential-memleak-problem-in-blob_seri.patch -Patch3: 0003-idxd-fix-memleak-problem-in-spdk_idxd_configure_chan.patch -Patch4: 0004-idxd-fix-one-memleak-problem-in-spdk_idxd_get_channe.patch -Patch5: 0005-ioat-fix-potential-double-free-problem-in-ioat_chann.patch -Patch6: 0006-nvmf-check-return-value-of-strdup-in-spdk_nvmf_subsy.patch -Patch7: 0007-nvmf-check-return-value-of-strdup-in-spdk_nvmf_subsy.patch -Patch8: 0008-nvmf-fix-fd-leakage-problem-in-nvmf_vfio_user_listen.patch -Patch9: 0009-posix-set-fd-to-1-after-close-fd-in-posix_sock_creat.patch -Patch10: 0010-spdk_top-check-return-value-of-strdup-in-store_last_.patch -Patch11: 0011-uring-set-fd-to-1-after-close-fd-in-uring_sock_creat.patch -Patch12: 0012-spdk-use-fstack-protector-strong-instead-of-fstack-p.patch -Patch13: 0013-lib-vhost-Fix-compilation-with-dpdk-21.11.patch -Patch14: 0014-mk-Fix-debug-build-error-on-ARM-ThunderX2-and-neoverse_N1_platform.patch -Patch15: 0015-configure-add-gcc-version-check-for-ARM-Neoverse-N1_platform.patch -Patch16: 0016-Enhance-security-for-share-library.patch -Patch17: 0017-barrier-LOONGARCH-memory-barriers.patch -Patch18: 0018-nvme-pcie-add-memory-barrier-for-LOONGARCH.patch -Patch19: 0019-build-Specify-the-target-build-architecture-for-LOON.patch -Patch20: 0020-configure-add-CONFIG_HAVE_ARC4RANDOM.patch -Patch21: 0021-lib-bdev-return-error-when-failing-to-get-resource.patch -Patch22: 0022-Fix-the-build-error-ppc64le-gnu-gcc-does-not-support.patch -Patch23: 0023-Fix-probe-core-dump-while-admin-cmd-timeout.patch -Patch24: 0024-Fix-build-warning.patch -Patch25: 0025-ut-rdma-Fix-GCC-10.2.0-warning.patch -Patch26: 0026-lib-nvme-add-mutex-before-submit-admin-request.patch -Patch27: 0027--nvme-cuse-Add-ctrlr_lock-for-cuse-register-and-unreg.patch -Patch28: 0028-fixed-use-after-free-detected-by-Coverity.patch - -%define package_version %{version}-%{release} - -%define install_datadir %{buildroot}/%{_datadir}/%{name} -%define install_sbindir %{buildroot}/%{_sbindir} -%define install_docdir %{buildroot}/%{_docdir}/%{name} - -# Distros that don't support python3 will use python2 -%if "%{dist}" == ".el7" -%define use_python2 1 -%else -%define use_python2 0 -%endif - -ExclusiveArch: x86_64 aarch64 loongarch64 ppc64le - -BuildRequires: gcc gcc-c++ make -BuildRequires: dpdk-devel >= 21.11, numactl-devel, ncurses-devel -BuildRequires: libiscsi-devel, libaio-devel, openssl-devel, libuuid-devel -BuildRequires: libibverbs-devel, librdmacm-devel -BuildRequires: CUnit, CUnit-devel -%if %{with doc} -BuildRequires: doxygen mscgen graphviz -%endif - -# Install dependencies -Requires: dpdk >= 21.11, numactl-libs, openssl-libs -Requires: libiscsi, libaio, libuuid -# NVMe over Fabrics -Requires: librdmacm, librdmacm -Requires(post): /sbin/ldconfig -Requires(postun): /sbin/ldconfig - -%description -The Storage Performance Development Kit provides a set of tools -and libraries for writing high performance, scalable, user-mode storage -applications. - - -%package devel -Summary: Storage Performance Development Kit development files -Requires: %{name}%{?_isa} = %{package_version} -Provides: %{name}-static%{?_isa} = %{package_version} - -%description devel -This package contains the headers and other files needed for -developing applications with the Storage Performance Development Kit. - - -%package tools -Summary: Storage Performance Development Kit tools files -%if "%{use_python2}" == "0" -Requires: %{name}%{?_isa} = %{package_version} python3 python3-configshell python3-pexpect -%else -Requires: %{name}%{?_isa} = %{package_version} python python-configshell pexpect -%endif -BuildArch: noarch - -%description tools -%{summary} - - -%if %{with doc} -%package doc -Summary: Storage Performance Development Kit documentation -BuildArch: noarch - -%description doc -%{summary} -%endif - - -%prep -# add -q -%autosetup -n spdk-%{version} -p1 - -%build -./configure --prefix=%{_usr} \ - --disable-tests \ - --without-crypto \ - --without-isal \ - --with-dpdk=/usr/lib64/dpdk/pmds-22.0 \ - --without-fio \ - --with-vhost \ - --without-pmdk \ - --without-rbd \ - --with-rdma \ - --with-shared \ - --with-iscsi-initiator \ - --without-vtune - -make -j`nproc` all - -%if %{with doc} -make -C doc -%endif - -%check -sed -i "s/sudo//g" test/common/autotest_common.sh -sed -i '/target_space=/aexport SPDK_TEST_STORAGE=$target_dir;return 0' test/common/autotest_common.sh -test/unit/unittest.sh - -%install -%make_install -j`nproc` prefix=%{_usr} libdir=%{_libdir} datadir=%{_datadir} -install -d $RPM_BUILD_ROOT/opt/spdk/scripts -install -d $RPM_BUILD_ROOT/opt/spdk/include/spdk -install -m 0744 ./scripts/setup.sh $RPM_BUILD_ROOT/opt/spdk/scripts/setup.sh -install -m 0744 ./scripts/common.sh $RPM_BUILD_ROOT/opt/spdk/scripts/common.sh -install -m 0644 ./include/spdk/pci_ids.h $RPM_BUILD_ROOT/opt/spdk/include/spdk/pci_ids.h - -# Install tools -mkdir -p %{install_datadir} -find scripts -type f -regextype egrep -regex '.*(spdkcli|rpc).*[.]py' \ - -exec cp --parents -t %{install_datadir} {} ";" - -# env is banned - replace '/usr/bin/env anything' with '/usr/bin/anything' -find %{install_datadir}/scripts -type f -regextype egrep -regex '.*([.]py|[.]sh)' \ - -exec sed -i -E '1s@#!/usr/bin/env (.*)@#!/usr/bin/\1@' {} + - -%if "%{use_python2}" == "1" -find %{install_datadir}/scripts -type f -regextype egrep -regex '.*([.]py)' \ - -exec sed -i -E '1s@#!/usr/bin/python3@#!/usr/bin/python2@' {} + -%endif - -# synlinks to tools -mkdir -p %{install_sbindir} -ln -sf -r %{install_datadir}/scripts/rpc.py %{install_sbindir}/%{name}-rpc -ln -sf -r %{install_datadir}/scripts/spdkcli.py %{install_sbindir}/%{name}-cli - -%if %{with doc} -# Install doc -mkdir -p %{install_docdir} -mv doc/output/html/ %{install_docdir} -%endif - - -%post -p /sbin/ldconfig -%postun -p /sbin/ldconfig - - -%files -%{_bindir}/spdk_* -%{_libdir}/*.so.* -%dir /opt/spdk -%dir /opt/spdk/scripts -%dir /opt/spdk/include -%dir /opt/spdk/include/spdk -/opt/spdk/scripts/setup.sh -/opt/spdk/scripts/common.sh -/opt/spdk/include/spdk/pci_ids.h - - -%files devel -%{_includedir}/%{name} -%{_libdir}/*.a -%{_libdir}/*.so - - -%files tools -%{_datadir}/%{name}/scripts -%{_sbindir}/%{name}-rpc -%{_sbindir}/%{name}-cli - -%if %{with doc} -%files doc -%{_docdir}/%{name} -%endif - - -%changelog -* Mon May 20 2024 yanshuai - 21.01.1-15 -- lib/nvme: fixed use-after-free detected by Coverity - -* Mon May 20 2024 Hongtao Zhang - 21.01.1-14 -- nvme/cuse: Add ctrlr_lock for cuse register and unregister - -* Mon Apr 29 2024 Hongtao Zhang - 21.01.1-13 -- lib/nvme: add mutex before submit admin request - -* Fri Mar 15 2024 wangxiaomeng - 21.01.1-12 -- Fix build warning - -* Wed Mar 6 2024 Hongtao Zhang - 21.01.1-11 -- Fix probe core dump while admin cmd timeout - -* Mon Mar 4 2024 Ren Zhijie - 21.01.1-10 -- Add support for ppc64le - -* Thu Dec 14 2023 Hongtao Zhang - 21.01.1-9 -- Return error when failing to get resource - -* Tue Dec 12 2023 Hongtao Zhang - 21.01.1-8 -- configure add CONFIG_HAVE_ARC4RANDOM - -* Tue Jul 4 2023 Xue Liu - 21.01.1-7 -- Add support for LOONGARCH. - -* Sat Nov 5 2022 Weifeng Su - 21.01.1-6 -- Enable unittest - -* Mon Oct 24 2022 Hongtao Zhang - 21.01.1-5 -- Add the setup.sh script during installation - -* Tue Mar 15 2022 Weifeng Su - 21.01.1-4 -- Remove rpath link option, Due to it's easy for attacher to - construct 'rpath' attacks - -* Fri Feb 25 2022 Hongtao Zhang - 21.01.1-3 -- Fix build error on ARM ThunderX2 and neoverse N1 platform - -* Mon Jan 10 2022 Weifeng Su - 21.01.1-2 -- Adapt for dpdk 21.11 - -* Tue Nov 23 2021 Weifeng Su - 21.01.1-1 -- rebase to v21.01.1 Maintenance LTS Version - -* Mon Sep 13 2021 Zhiqiang Liu - 21.01-5 -- use -fstack-protector-strong instead of -fstack-protector for -stronger security. - -* Sat Jul 24 2021 Zhiqiang Liu - 21.01-4 -- backport 13 bugfix from upstream - -* Tue Jul 13 2021 Xiaokeng Li - 21.01-3 -- backport bugfix from upstream - -* Wed Mar 10 2021 Shihao Sun - 21.01-2 -- use --without-isal to avoid build failed in arm. - -* Thu Feb 4 2021 Shihao Sun - 21.01-1 -- update spdk to 21.01 LTS version. -* Thu Nov 26 2020 Shihao Sun - 20.01.1-2 -- modify license -* Sat Nov 7 2020 Feilong Lin - 20.01.1-1 -- Support aarch64 -* Tue Sep 18 2018 Pawel Wodkowski - 0:18.07-3 -- Initial RPM release diff --git a/spdk.yaml b/spdk.yaml deleted file mode 100644 index e1bd99b52cb93b826310110a6549c912890bc5f6..0000000000000000000000000000000000000000 --- a/spdk.yaml +++ /dev/null @@ -1,4 +0,0 @@ -version_control: github -src_repo: spdk/spdk -tag_prefix: "v" -separator: "." diff --git a/v21.01.1.tar.gz b/v21.01.1.tar.gz deleted file mode 100644 index 901e2becb004fd9903860927e7494dfbd150fdfd..0000000000000000000000000000000000000000 Binary files a/v21.01.1.tar.gz and /dev/null differ