From ddfaaf6e01ba245d1941a955e61488a730dce09a Mon Sep 17 00:00:00 2001
From: sunshihao <sunshihao@huawei.com>
Date: Thu, 25 Feb 2021 18:21:53 +0800
Subject: [PATCH 21/27] module/bdev: Add bdev module support for HSAK

Signed-off-by: sunshihao <sunshihao@huawei.com>
---
 app/spdk_lspci/Makefile            |   2 +-
 examples/accel/perf/Makefile       |   2 +-
 examples/interrupt_tgt/Makefile    |   2 +-
 examples/sock/hello_world/Makefile |   2 +-
 include/spdk/bdev.h                |  20 +-
 include/spdk/bdev_module.h         |  28 +-
 include/spdk/nvme.h                |  72 +---
 lib/bdev/bdev.c                    |  10 +-
 lib/nvme/nvme_ctrlr_self.c         |  75 ++--
 lib/nvme/nvme_ns.c                 |   5 -
 lib/nvme/nvme_ns_cmd.c             |   1 +
 lib/nvme/nvme_ns_self.c            |  11 +-
 lib/nvme/nvme_pcie.c               |   5 +-
 lib/nvme/nvme_pcie_common.c        |   1 +
 lib/nvme/nvme_rebind.c             |  86 ++--
 lib/rpc/rpc.c                      |   2 +
 lib/thread/thread.c                |  38 +-
 mk/nvme.libtest.mk                 |   2 +-
 mk/spdk.common.mk                  |   5 +-
 mk/spdk.modules.mk                 |   2 +-
 module/bdev/nvme/Makefile          |   1 +
 module/bdev/nvme/bdev_nvme.c       | 285 ++++++++++++-
 module/bdev/nvme/bdev_nvme.h       |  42 ++
 module/bdev/nvme/bdev_nvme_self.c  | 661 +++++++++++++++++++++++++++++
 module/bdev/nvme/bdev_nvme_self.h  |  43 ++
 module/bdev/nvme/common.h          |   4 +
 scripts/setup_self.sh              | 347 +++++++++++++++
 27 files changed, 1538 insertions(+), 216 deletions(-)
 create mode 100644 module/bdev/nvme/bdev_nvme_self.c
 create mode 100644 module/bdev/nvme/bdev_nvme_self.h
 create mode 100755 scripts/setup_self.sh

diff --git a/app/spdk_lspci/Makefile b/app/spdk_lspci/Makefile
index 5efb95f..c4f11be 100644
--- a/app/spdk_lspci/Makefile
+++ b/app/spdk_lspci/Makefile
@@ -39,6 +39,6 @@ APP = spdk_lspci
 
 C_SRCS := spdk_lspci.c
 
-SPDK_LIB_LIST = $(SOCK_MODULES_LIST) nvme vmd
+SPDK_LIB_LIST = $(SOCK_MODULES_LIST) nvme vmd trace
 
 include $(SPDK_ROOT_DIR)/mk/spdk.app.mk
diff --git a/examples/accel/perf/Makefile b/examples/accel/perf/Makefile
index 53b9ae6..555ccf0 100644
--- a/examples/accel/perf/Makefile
+++ b/examples/accel/perf/Makefile
@@ -39,6 +39,6 @@ APP = accel_perf
 
 C_SRCS := accel_perf.c
 
-SPDK_LIB_LIST = $(ACCEL_MODULES_LIST) event_accel
+SPDK_LIB_LIST = $(ACCEL_MODULES_LIST) event_accel conf
 
 include $(SPDK_ROOT_DIR)/mk/spdk.app.mk
diff --git a/examples/interrupt_tgt/Makefile b/examples/interrupt_tgt/Makefile
index 90a2b8a..c27a2c7 100644
--- a/examples/interrupt_tgt/Makefile
+++ b/examples/interrupt_tgt/Makefile
@@ -41,7 +41,7 @@ C_SRCS := interrupt_tgt.c
 
 SPDK_LIB_LIST = $(INTR_BLOCKDEV_MODULES_LIST) event_bdev conf
 
-SPDK_LIB_LIST += event_nbd
+SPDK_LIB_LIST += event_nbd bdev_nvme
 SPDK_LIB_LIST += event_vhost
 
 ifeq ($(SPDK_ROOT_DIR)/lib/env_dpdk,$(CONFIG_ENV))
diff --git a/examples/sock/hello_world/Makefile b/examples/sock/hello_world/Makefile
index f86df44..ea5d552 100644
--- a/examples/sock/hello_world/Makefile
+++ b/examples/sock/hello_world/Makefile
@@ -38,6 +38,6 @@ APP = hello_sock
 C_SRCS := hello_sock.c
 
 SPDK_LIB_LIST = $(SOCK_MODULES_LIST)
-SPDK_LIB_LIST += event_net sock
+SPDK_LIB_LIST += event_net sock conf
 
 include $(SPDK_ROOT_DIR)/mk/spdk.app.mk
diff --git a/include/spdk/bdev.h b/include/spdk/bdev.h
index 22b87ec..d0284d9 100644
--- a/include/spdk/bdev.h
+++ b/include/spdk/bdev.h
@@ -119,7 +119,6 @@ enum spdk_bdev_status {
 };
 
 #ifdef SPDK_CONFIG_APP_RW
-/** ns status */
 enum spdk_bdev_ns_status {
 	SPDK_BDEV_NS_STATUS_INVALID,
 	SPDK_BDEV_NS_STATUS_READY,
@@ -128,7 +127,6 @@ enum spdk_bdev_ns_status {
 };
 
 typedef void (*LIBSTORAGE_CALLBACK_FUNC)(int32_t cb_status, int32_t sct_code, void *cb_arg);
-
 typedef struct libstorage_io {
 	uint8_t *buf;
 	struct iovec *iovs;	/* array of iovecs to transfer */
@@ -1411,19 +1409,13 @@ int spdk_bdev_unmap(struct spdk_bdev_desc *desc, struct spdk_io_channel *ch,
  * be called (even if the request ultimately failed). Return
  * negated errno on failure, in which case the callback will not be called.
  */
-int
-spdk_bdev_unmap_multiblocks(struct spdk_bdev_desc *desc, struct spdk_io_channel *ch,
-			    void *unmap_d, uint16_t unmap_count,
-			    spdk_bdev_io_completion_cb cb, void *cb_arg);
-
-void *
-spdk_bdev_get_channel_group(struct spdk_io_channel *io_ch);
-
-void *
-spdk_bdev_io_get_pool(size_t nbytes);
+int spdk_bdev_unmap_multiblocks(struct spdk_bdev_desc *desc, struct spdk_io_channel *ch,
+				void *unmap_d, uint16_t unmap_count,
+				spdk_bdev_io_completion_cb cb, void *cb_arg);
 
-bool
-spdk_bdev_have_io_in_channel(struct spdk_io_channel *bdevIoCh);
+void *spdk_bdev_get_channel_group(struct spdk_io_channel *io_ch);
+void *spdk_bdev_io_get_pool(size_t nbytes);
+bool spdk_bdev_have_io_in_channel(struct spdk_io_channel *bdevIoCh);
 #endif
 
 /**
diff --git a/include/spdk/bdev_module.h b/include/spdk/bdev_module.h
index 3ff7e28..55dc980 100644
--- a/include/spdk/bdev_module.h
+++ b/include/spdk/bdev_module.h
@@ -225,21 +225,12 @@ struct spdk_bdev_fn_table {
 
 #ifdef SPDK_CONFIG_APP_RW
 	uint16_t (*get_io_channel_id)(struct spdk_io_channel *ch);
-
 	int (*bdev_poll_rsp)(void *pollCh);
-
 	uint64_t (*get_timeout_count)(struct spdk_io_channel *ch);
 #endif
 };
 
 #ifdef SPDK_CONFIG_APP_RW
-static inline void spdk_bdev_set_io_location(void *bdev_ctx, uint8_t location)
-{
-	struct spdk_bdev_io *bdev_io = spdk_bdev_io_from_ctx(bdev_ctx);
-	uint8_t *ioLoc = (uint8_t *)bdev_io->internal.caller_ctx;
-	*ioLoc = location;
-}
-
 enum spdk_bdev_driver_ctx {
 	SPDK_BDEV_IO_ACTION_PI,
 	SPDK_BDEV_IO_ACTION_FUA,
@@ -262,12 +253,6 @@ enum spdk_bdev_io_fua {
 	IO_FUA_YES	= 1
 };
 
-void spdk_bdev_nvme_remove_cb(void *cb_ctx, void *ctrlr);
-
-void spdk_bdev_fail_ctrlr(const char *traddr);
-
-void *nvme_channel_get_group(void *io_ch);
-
 enum reqLocation_E {
 	LOCAL_RECEIVE_APP = 1,
 	LOCAL_LIBSTORAGE_SUBMIT = 2,
@@ -280,6 +265,10 @@ enum reqLocation_E {
 	LOCAL_LIBSTORAGE_SUBMIT_RETRY = 9,
 	LOCAL_LIBSTORAGE_BDEV_NOMEM = 10,
 };
+
+void spdk_bdev_nvme_remove_cb(void *cb_ctx, void *ctrlr);
+void spdk_bdev_fail_ctrlr(const char *traddr);
+void *nvme_channel_get_group(void *io_ch);
 #endif
 
 /** bdev I/O completion status */
@@ -1351,6 +1340,15 @@ int spdk_bdev_push_media_events(struct spdk_bdev *bdev, const struct spdk_bdev_m
  */
 void spdk_bdev_notify_media_management(struct spdk_bdev *bdev);
 
+#ifdef SPDK_CONFIG_APP_RW
+static inline void spdk_bdev_set_io_location(void *bdev_ctx, uint8_t location)
+{
+	struct spdk_bdev_io *bdev_io = spdk_bdev_io_from_ctx(bdev_ctx);
+	uint8_t *ioLoc = (uint8_t *)bdev_io->internal.caller_ctx;
+	*ioLoc = location;
+}
+#endif
+
 /*
  *  Macro used to register module for later initialization.
  */
diff --git a/include/spdk/nvme.h b/include/spdk/nvme.h
index adda642..6393db3 100644
--- a/include/spdk/nvme.h
+++ b/include/spdk/nvme.h
@@ -3436,6 +3436,7 @@ struct spdk_nvme_transport_ops {
 void spdk_nvme_transport_register(const struct spdk_nvme_transport_ops *ops);
 
 #ifdef SPDK_CONFIG_APP_RW
+#define NVME_MAX_CONTROLLERS 1024
 struct nvme_ctrlr_info {
 	char		ctrlName[16];
 	char		pciAddr[24];
@@ -3454,10 +3455,10 @@ struct nvme_ctrlr_info {
 	uint16_t	ssvid;			/* Subsystem vendor id */
 	uint16_t	ctrlid;			/* Controller id */
 	uint16_t	trtype;			/* Transport type */
-	uint16_t	support_ns  : 1;		/* Supports the Namespace Management and Namespace Attachment commands */
-	uint16_t	directives  : 1;		/* Supports Directives */
-	uint16_t	streams     : 1;		/* Supports Streams Directives */
-	uint16_t	dsm         : 1;		/* Supports the controller supports the Dataset Management command */
+	uint16_t	support_ns  : 1;	/* Supports the Namespace Management and Namespace Attachment commands */
+	uint16_t	directives  : 1;	/* Supports Directives */
+	uint16_t	streams     : 1;	/* Supports Streams Directives */
+	uint16_t	dsm         : 1;	/* Supports the controller supports the Dataset Management command */
 	uint16_t	reserved    : 12;
 	uint16_t	reserved2[3];
 };
@@ -3494,7 +3495,7 @@ bool spdk_nvme_ctrlr_is_format_supported(struct spdk_nvme_ctrlr *ctrlr);
 bool spdk_nvme_ctrlr_is_format_all_ns(struct spdk_nvme_ctrlr *ctrlr);
 bool spdk_nvme_ctrlr_is_directive_supported(struct spdk_nvme_ctrlr *ctrlr);
 bool spdk_nvme_ctrlr_is_streams_supported(struct spdk_nvme_ctrlr *ctrlr);
-int32_t spdk_nvme_ctrlr_identify_directives(struct spdk_nvme_ctrlr *ctrlr, uint16_t nsid,
+int32_t spdk_nvme_ctrlr_identify_directives(struct spdk_nvme_ctrlr *ctrlr, uint32_t nsid,
 		void *payload);
 int32_t spdk_nvme_ctrlr_enable_streams(struct spdk_nvme_ctrlr *ctrlr, uint32_t nsid);
 int32_t spdk_nvme_ctrlr_ret_streams_param(struct spdk_nvme_ctrlr *ctrlr, void *payload);
@@ -3540,38 +3541,13 @@ struct spdk_uevent {
 	char traddr[SPDK_NVMF_TRADDR_MAX_LEN + 1];
 };
 
-/* make a socket to get uevent */
 int nvme_uevent_connect(void);
-
-/* get uevent from socket fd */
 int nvme_get_uevent(int fd, struct spdk_uevent *uevent);
-
-/* blocked to get uevent from socket fd */
 int nvme_get_uevent_block(int fd, struct spdk_uevent *uevent);
-
-/**
- * @Description: bind device with pci_addr to driver
- * @param pci_addr: device's pci_addr,like "0000:08:00.0"
- * @param driver: driver name which device bind to
- */
 int32_t spdk_rebind_driver(char *pci_addr, char *driver_name);
-
-/**
- * \brief True if the protection information transferred at the start of metadata
- *        when end-to-end data protection enabled.
- *
- * This function is thread safe and can be called at any point while the controller is attached to
- *  the SPDK NVMe driver.
- */
 bool spdk_nvme_ns_pi_md_start(struct spdk_nvme_ns *ns);
-
-/**
- * \brief True if the namespace supports Dataset Management command.
- *
- * This function is thread safe and can be called at any point while the controller is attached to
- *  the SPDK NVMe driver.
- */
 bool spdk_nvme_ns_is_dataset_mng_supported(struct spdk_nvme_ns *ns);
+uint16_t spdk_nvme_get_qpair_id(struct spdk_nvme_qpair *qpair);
 
 /**
  * Submit a data set management request to the specified NVMe namespace. Data set
@@ -3632,40 +3608,6 @@ int spdk_nvme_ns_cmd_writev_stream(struct spdk_nvme_ns *ns, struct spdk_nvme_qpa
 				   spdk_nvme_cmd_cb cb_fn, void *cb_arg, uint32_t io_flags,
 				   spdk_nvme_req_reset_sgl_cb reset_sgl_fn,
 				   spdk_nvme_req_next_sge_cb next_sge_fn);
-
-/**
- * \brief Send comman to NVMe controller to start or abort a self-test operation.
- *
- * \param ctrlr NVMe controller to operate self-test command.
- * \param nsid Depending on the log page, this may be 0, a namespace identifier, or SPDK_NVME_GLOBAL_NS_TAG.
- * \param stc self-test code, which specifies the action taken by the Device Self-test command.
- * \param payload The pointer to the payload buffer. it doesn't work actually.
- * \param payload_size The size of payload buffer. it doesn't work actually.
- * \param cb_fn Callback function to invoke when the feature has been retrieved.
- * \param cb_arg Argument to pass to the callback function.
- *
- * \return 0 if successfully submitted, ENOMEM if resources could not be allocated for this request
- *
- * This function is thread safe and can be called at any point while the controller is attached to
- *  the SPDK NVMe driver.
- *
- * Call \ref spdk_nvme_ctrlr_process_admin_completions() to poll for completion
- * of commands submitted through this function.
- *
- * \sa spdk_nvme_ctrlr_cmd_self_test_operation()
- */
-int spdk_nvme_ctrlr_cmd_self_test_operation(struct spdk_nvme_ctrlr *ctrlr, uint32_t nsid,
-		uint32_t stc,
-		void *payload, uint32_t payload_size,
-		spdk_nvme_cmd_cb cb_fn, void *cb_arg);
-
-/**
- *\get I/O queue pair id
- *\param qpair I/O queue pair to submit the request
- *\
- *\return I/O queue pair id
- */
-uint16_t spdk_nvme_get_qpair_id(struct spdk_nvme_qpair *qpair);
 #endif
 
 /*
diff --git a/lib/bdev/bdev.c b/lib/bdev/bdev.c
index bf102bb..1d8ce99 100644
--- a/lib/bdev/bdev.c
+++ b/lib/bdev/bdev.c
@@ -3568,6 +3568,7 @@ _bdev_io_check_md_buf(const struct iovec *iovs, const void *md_buf)
 	return _is_buf_allocated(iovs) == (md_buf != NULL);
 }
 
+#ifdef SPDK_CONFIG_APP_RW
 static void
 bdev_build_contig_io(uint8_t type, void *buf, void *md_buf, uint64_t offset_blocks,
 		     uint64_t num_blocks,
@@ -3587,6 +3588,7 @@ bdev_build_contig_io(uint8_t type, void *buf, void *md_buf, uint64_t offset_bloc
 		bdev_io->driver_ctx[SPDK_BDEV_IO_STREAM_ID_1] = (io->streamId >> 8) & 0xFF;
 	}
 }
+#endif
 
 static int
 bdev_read_blocks_with_md(struct spdk_bdev_desc *desc, struct spdk_io_channel *ch, void *buf,
@@ -3664,11 +3666,11 @@ spdk_bdev_read_blocks_with_md(struct spdk_bdev_desc *desc, struct spdk_io_channe
 	if (!spdk_bdev_is_md_separate(spdk_bdev_desc_get_bdev(desc))) {
 		return -EINVAL;
 	}
-
+#endif
 	if (!_bdev_io_check_md_buf(&iov, md_buf)) {
 		return -EINVAL;
 	}
-#endif
+
 	return bdev_read_blocks_with_md(desc, ch, buf, md_buf, offset_blocks, num_blocks,
 					cb, cb_arg);
 }
@@ -3841,11 +3843,11 @@ spdk_bdev_write_blocks_with_md(struct spdk_bdev_desc *desc, struct spdk_io_chann
 	if (!spdk_bdev_is_md_separate(spdk_bdev_desc_get_bdev(desc))) {
 		return -EINVAL;
 	}
-
+#endif
 	if (!_bdev_io_check_md_buf(&iov, md_buf)) {
 		return -EINVAL;
 	}
-#endif
+
 	return bdev_write_blocks_with_md(desc, ch, buf, md_buf, offset_blocks, num_blocks,
 					 cb, cb_arg);
 }
diff --git a/lib/nvme/nvme_ctrlr_self.c b/lib/nvme/nvme_ctrlr_self.c
index d3937d9..4ac1925 100644
--- a/lib/nvme/nvme_ctrlr_self.c
+++ b/lib/nvme/nvme_ctrlr_self.c
@@ -14,18 +14,16 @@
 #include "spdk/stdinc.h"
 #include "nvme_internal.h"
 
-void
-spdk_nvme_ctrlr_set_shutdown(struct spdk_nvme_ctrlr *ctrlr, bool is_shutdown)
+void spdk_nvme_ctrlr_set_shutdown(struct spdk_nvme_ctrlr *ctrlr, bool is_shutdown)
 {
 	nvme_robust_mutex_lock(&ctrlr->ctrlr_lock);
-	ctrlr-> is_destructed= is_shutdown;
+	ctrlr->is_destructed = is_shutdown;
 	nvme_robust_mutex_unlock(&ctrlr->ctrlr_lock);
 }
 
-bool
-spdk_nvme_ctrlr_is_smart_per_namespace_supported(struct spdk_nvme_ctrlr *ctrlr)
+bool spdk_nvme_ctrlr_is_smart_per_namespace_supported(struct spdk_nvme_ctrlr *ctrlr)
 {
-	if(NULL == ctrlr) {
+	if (NULL == ctrlr) {
 		SPDK_ERRLOG("spdk_nvme_ctrlr_is_smart_per_namespace_supported: Invalid Parameters!\n");
 		return false;
 	}
@@ -33,7 +31,7 @@ spdk_nvme_ctrlr_is_smart_per_namespace_supported(struct spdk_nvme_ctrlr *ctrlr)
 	/* check Bit 0 of Log Page Attributes(LPA),
 	to find out whether the controller supports namespace basis or not. */
 
-	if(0 == ctrlr->cdata.lpa.ns_smart) {
+	if (0 == ctrlr->cdata.lpa.ns_smart) {
 		SPDK_NOTICELOG("This controller does not support the SMART information on a per namespace basis.\n");
 		return false;
 	}
@@ -42,14 +40,14 @@ spdk_nvme_ctrlr_is_smart_per_namespace_supported(struct spdk_nvme_ctrlr *ctrlr)
 }
 
 static int nvme_get_log_info(struct spdk_nvme_ctrlr *ctrlr, uint8_t log_page, uint32_t nsid,
-			     void *payload,  uint32_t payload_size)
+			     			 void *payload,  uint32_t payload_size)
 {
 	struct nvme_completion_poll_status status = {0x0};
 	int ret;
 
 	status.done = false;
 	ret = spdk_nvme_ctrlr_cmd_get_log_page(ctrlr, log_page, nsid, payload, payload_size, 0,
-						nvme_completion_poll_cb, &status);
+					       nvme_completion_poll_cb, &status);
 	if (ret) {
 		return ret;
 	}
@@ -61,31 +59,30 @@ static int nvme_get_log_info(struct spdk_nvme_ctrlr *ctrlr, uint8_t log_page, ui
 	}
 	if (spdk_nvme_cpl_is_error(&status.cpl)) {
 		SPDK_ERRLOG("spdk_nvme_ctrlr_get_smart_info failed! sc[0x%x], sct[0x%x]\n",
-			     status.cpl.status.sc, status.cpl.status.sct);
+			    status.cpl.status.sc, status.cpl.status.sct);
 		return -ENXIO;
 	}
 	return 0;
 }
 
-int
-spdk_nvme_ctrlr_get_smart_info(struct spdk_nvme_ctrlr *ctrlr, uint32_t nsid,
-		struct spdk_nvme_health_information_page *smart_info)
+int spdk_nvme_ctrlr_get_smart_info(struct spdk_nvme_ctrlr *ctrlr, uint32_t nsid,
+			       				   struct spdk_nvme_health_information_page *smart_info)
 {
 	struct spdk_nvme_ns *ns = NULL;
 
-	if(NULL == ctrlr || NULL == smart_info) {
+	if (NULL == ctrlr || NULL == smart_info) {
 		SPDK_ERRLOG("Invalid parameters!\n");
 		return -EINVAL;
 	}
 
 	/* if controller does not support namespase basis, then set the nsid to 0xFFFFFFFF, and continue the process.
 	and if nsid is 0, set the nsid to 0xFFFFFFFF too. */
-	if(!spdk_nvme_ctrlr_is_smart_per_namespace_supported(ctrlr) || 0 == nsid) {
+	if (!spdk_nvme_ctrlr_is_smart_per_namespace_supported(ctrlr) || 0 == nsid) {
 		nsid = SPDK_NVME_GLOBAL_NS_TAG;
 	}
 
 	/* nsid should be 0xffffffff or on a per namespace basis. */
-	if(nsid != SPDK_NVME_GLOBAL_NS_TAG) {
+	if (nsid != SPDK_NVME_GLOBAL_NS_TAG) {
 		ns = spdk_nvme_ctrlr_get_ns(ctrlr, nsid);
 		if (NULL == ns) {
 			SPDK_ERRLOG("Invalid NS %u\n", nsid);
@@ -100,18 +97,17 @@ spdk_nvme_ctrlr_get_smart_info(struct spdk_nvme_ctrlr *ctrlr, uint32_t nsid,
 	}
 
 	return nvme_get_log_info(ctrlr, SPDK_NVME_LOG_HEALTH_INFORMATION, nsid, smart_info,
-				sizeof(struct spdk_nvme_health_information_page));
+				 sizeof(struct spdk_nvme_health_information_page));
 }
 
-int
-spdk_nvme_ctrlr_get_error_info(struct spdk_nvme_ctrlr *ctrlr, uint32_t err_entries,
-			       struct spdk_nvme_error_information_entry *error_info)
+int spdk_nvme_ctrlr_get_error_info(struct spdk_nvme_ctrlr *ctrlr, uint32_t err_entries,
+			       				   struct spdk_nvme_error_information_entry *error_info)
 {
 	const struct spdk_nvme_ctrlr_data *cdata = NULL;
 	uint32_t nsid = SPDK_NVME_GLOBAL_NS_TAG;
 	int ret;
 
-	if(NULL == ctrlr || NULL == error_info) {
+	if (NULL == ctrlr || NULL == error_info) {
 		SPDK_ERRLOG("Invalid parameters!\n");
 		return -EINVAL;
 	}
@@ -121,7 +117,7 @@ spdk_nvme_ctrlr_get_error_info(struct spdk_nvme_ctrlr *ctrlr, uint32_t err_entri
 	if (err_entries > (cdata->elpe + 1u)) {
 		/* if the parameter is bigger,  then cut it into the maximum number supported. */
 		SPDK_WARNLOG("Cannot get %d error log entries, the controller only support %d errors.\n",
-			      err_entries, cdata->elpe + 1);
+			     err_entries, cdata->elpe + 1);
 		err_entries = cdata->elpe + 1;
 	}
 
@@ -134,38 +130,32 @@ spdk_nvme_ctrlr_get_error_info(struct spdk_nvme_ctrlr *ctrlr, uint32_t err_entri
 	return err_entries;
 }
 
-struct spdk_nvme_ctrlr_opts *
-spdk_nvme_ctrlr_get_opts(struct spdk_nvme_ctrlr *ctrlr)
+struct spdk_nvme_ctrlr_opts * spdk_nvme_ctrlr_get_opts(struct spdk_nvme_ctrlr *ctrlr)
 {
 	return &ctrlr->opts;
 }
 
-bool
-spdk_nvme_ctrlr_is_ns_manage_supported(struct spdk_nvme_ctrlr *ctrlr)
+bool spdk_nvme_ctrlr_is_ns_manage_supported(struct spdk_nvme_ctrlr *ctrlr)
 {
 	return ctrlr->cdata.oacs.ns_manage != 0;
 }
 
-bool
-spdk_nvme_ctrlr_is_format_supported(struct spdk_nvme_ctrlr *ctrlr)
+bool spdk_nvme_ctrlr_is_format_supported(struct spdk_nvme_ctrlr *ctrlr)
 {
 	return ctrlr->cdata.oacs.format != 0;
 }
 
-bool
-spdk_nvme_ctrlr_is_format_all_ns(struct spdk_nvme_ctrlr *ctrlr)
+bool spdk_nvme_ctrlr_is_format_all_ns(struct spdk_nvme_ctrlr *ctrlr)
 {
 	return ctrlr->cdata.fna.format_all_ns != 0;
 }
 
-bool
-spdk_nvme_ctrlr_is_directive_supported(struct spdk_nvme_ctrlr *ctrlr)
+bool spdk_nvme_ctrlr_is_directive_supported(struct spdk_nvme_ctrlr *ctrlr)
 {
 	return ctrlr->cdata.oacs.directives != 0;
 }
 
-void
-spdk_nvme_ctrlr_update_unvmcap(struct spdk_nvme_ctrlr *ctrlr)
+void spdk_nvme_ctrlr_update_unvmcap(struct spdk_nvme_ctrlr *ctrlr)
 {
 	int rc;
 	struct nvme_completion_poll_status  status;
@@ -192,8 +182,7 @@ spdk_nvme_ctrlr_update_unvmcap(struct spdk_nvme_ctrlr *ctrlr)
 	ctrlr->cdata.unvmcap[1] = cdata.unvmcap[1];
 }
 
-int32_t
-spdk_nvme_ctrlr_identify_directives(struct spdk_nvme_ctrlr *ctrlr, uint16_t nsid, void *payload)
+int32_t spdk_nvme_ctrlr_identify_directives(struct spdk_nvme_ctrlr *ctrlr, uint16_t nsid, void *payload)
 {
 	struct nvme_completion_poll_status status;
 	int32_t res;
@@ -209,10 +198,11 @@ spdk_nvme_ctrlr_identify_directives(struct spdk_nvme_ctrlr *ctrlr, uint16_t nsid
 	}
 
 	status.done = false;
-	res = nvme_ctrlr_cmd_directive_receive(ctrlr, nsid, SPDK_NVME_ID_RECV_OP_RET_PARA,
-					       SPDK_NVME_DIR_TYPE_IDENTIFY, 0, payload,
-					       sizeof(struct spdk_nvme_identify_recv_ret_para),
-					       0, nvme_completion_poll_cb, &status);
+	res = spdk_nvme_ctrlr_cmd_directive_receive(ctrlr, nsid,
+			SPDK_NVME_IDENTIFY_DIRECTIVE_RECEIVE_RETURN_PARAM,
+			SPDK_NVME_DIRECTIVE_TYPE_IDENTIFY, 0, payload,
+			sizeof(struct spdk_nvme_ns_identify_directive_param),
+			0, 0, nvme_completion_poll_cb, &status);
 	if (res != 0) {
 		return res;
 	}
@@ -225,15 +215,14 @@ spdk_nvme_ctrlr_identify_directives(struct spdk_nvme_ctrlr *ctrlr, uint16_t nsid
 
 	if (spdk_nvme_cpl_is_error(&status.cpl)) {
 		SPDK_ERRLOG("Failed to Identify directive! sc[0x%x], sct[0x%x]\n",
-		status.cpl.status.sc, status.cpl.status.sct);
+			    status.cpl.status.sc, status.cpl.status.sct);
 		return -ENXIO;
 	}
 
 	return 0;
 }
 
-uint16_t
-spdk_nvme_get_qpair_id(struct spdk_nvme_qpair *qpair)
+uint16_t spdk_nvme_get_qpair_id(struct spdk_nvme_qpair *qpair)
 {
 	return qpair->id;
 }
diff --git a/lib/nvme/nvme_ns.c b/lib/nvme/nvme_ns.c
index 458d32f..f5cf75b 100644
--- a/lib/nvme/nvme_ns.c
+++ b/lib/nvme/nvme_ns.c
@@ -108,11 +108,6 @@ nvme_ns_set_identify_data(struct spdk_nvme_ns *ns)
 		ns->flags |= SPDK_NVME_NS_DPS_PI_SUPPORTED;
 		ns->pi_type = nsdata->dps.pit;
 	}
-#ifdef SPDK_CONFIG_APP_RW
-	if (nsdata->dps.md_start) {
-		ns->flags |= SPDK_NVME_NS_DPS_PI_MDSTART;
-	}
-#endif
 }
 
 static int
diff --git a/lib/nvme/nvme_ns_cmd.c b/lib/nvme/nvme_ns_cmd.c
index 4d706bc..37dcdc2 100644
--- a/lib/nvme/nvme_ns_cmd.c
+++ b/lib/nvme/nvme_ns_cmd.c
@@ -462,6 +462,7 @@ _nvme_ns_cmd_rw(struct spdk_nvme_ns *ns, struct spdk_nvme_qpair *qpair,
 	uint32_t		sector_size = _nvme_get_host_buffer_sector_size(ns, io_flags);
 	uint32_t		sectors_per_max_io = ns->sectors_per_max_io;
 	uint32_t		sectors_per_stripe = ns->sectors_per_stripe;
+	int			rc;
 
 	req = nvme_allocate_request(qpair, payload, lba_count * sector_size, lba_count * ns->md_size,
 				    cb_fn, cb_arg);
diff --git a/lib/nvme/nvme_ns_self.c b/lib/nvme/nvme_ns_self.c
index 5aabbaa..9e9def8 100644
--- a/lib/nvme/nvme_ns_self.c
+++ b/lib/nvme/nvme_ns_self.c
@@ -14,12 +14,13 @@
 
 bool spdk_nvme_ns_pi_md_start(struct spdk_nvme_ns *ns)
 {
-	return (ns->flags & SPDK_NVME_NS_DPS_PI_MDSTART) ? true : false;
+	struct spdk_nvme_ns_data *nsdata = &ns->ctrlr->nsdata[ns->id - 1];
+	return nsdata->dps.md_start == 1;
 }
 
 bool spdk_nvme_ns_is_dataset_mng_supported(struct spdk_nvme_ns *ns)
 {
-	return (ns->flags & SPDK_NVME_NS_DEALLOCATE_SUPPORTED) ? true : false;
+	return (ns->flags & SPDK_NVME_NS_DEALLOCATE_SUPPORTED) == 1;
 }
 
 int nvme_ns_get_common_data(struct spdk_nvme_ctrlr *ctrlr, struct spdk_nvme_ns_data *nsdata)
@@ -36,8 +37,7 @@ int nvme_ns_get_common_data(struct spdk_nvme_ctrlr *ctrlr, struct spdk_nvme_ns_d
 	if (spdk_nvme_ctrlr_is_ns_manage_supported(ctrlr)) {
 		rc = nvme_ctrlr_cmd_identify(ctrlr, SPDK_NVME_IDENTIFY_NS, 0, SPDK_NVME_GLOBAL_NS_TAG, 0,
 					     nsdata, sizeof(*nsdata), nvme_completion_poll_cb, &status);
-	}
-	else {
+	} else {
 		rc = nvme_ctrlr_cmd_identify(ctrlr, SPDK_NVME_IDENTIFY_NS, 0, 1, 0,
 					     nsdata, sizeof(*nsdata), nvme_completion_poll_cb, &status);
 	}
@@ -46,7 +46,8 @@ int nvme_ns_get_common_data(struct spdk_nvme_ctrlr *ctrlr, struct spdk_nvme_ns_d
 	}
 
 	if (nvme_wait_for_completion_robust_lock(ctrlr->adminq, &status, &ctrlr->ctrlr_lock)) {
-		SPDK_ERRLOG("Failed to identify nsdata, sct[%x], sc[%x]\n", status.cpl.status.sct, status.cpl.status.sc);
+		SPDK_ERRLOG("Failed to identify nsdata, sct[%x], sc[%x]\n", status.cpl.status.sct,
+			    status.cpl.status.sc);
 		return -1;
 	}
 
diff --git a/lib/nvme/nvme_pcie.c b/lib/nvme/nvme_pcie.c
index 08fe344..0e9e24d 100644
--- a/lib/nvme/nvme_pcie.c
+++ b/lib/nvme/nvme_pcie.c
@@ -51,9 +51,10 @@ struct nvme_pcie_enum_ctx {
 	bool has_pci_addr;
 };
 
+#ifndef SPDK_CONFIG_APP_RW
 static int nvme_pcie_ctrlr_attach(struct spdk_nvme_probe_ctx *probe_ctx,
 				  struct spdk_pci_addr *pci_addr);
-
+#endif
 static uint16_t g_signal_lock;
 static bool g_sigset = false;
 
@@ -594,6 +595,7 @@ nvme_pcie_ctrlr_scan(struct spdk_nvme_probe_ctx *probe_ctx,
 	}
 }
 
+#ifndef SPDK_CONFIG_APP_RW
 static int
 nvme_pcie_ctrlr_attach(struct spdk_nvme_probe_ctx *probe_ctx, struct spdk_pci_addr *pci_addr)
 {
@@ -605,6 +607,7 @@ nvme_pcie_ctrlr_attach(struct spdk_nvme_probe_ctx *probe_ctx, struct spdk_pci_ad
 
 	return spdk_pci_enumerate(spdk_pci_nvme_get_driver(), pcie_nvme_enum_cb, &enum_ctx);
 }
+#endif
 
 static struct spdk_nvme_ctrlr *nvme_pcie_ctrlr_construct(const struct spdk_nvme_transport_id *trid,
 		const struct spdk_nvme_ctrlr_opts *opts,
diff --git a/lib/nvme/nvme_pcie_common.c b/lib/nvme/nvme_pcie_common.c
index b0b14f6..564f81b 100644
--- a/lib/nvme/nvme_pcie_common.c
+++ b/lib/nvme/nvme_pcie_common.c
@@ -36,6 +36,7 @@
 
 #include "spdk/stdinc.h"
 #include "spdk/likely.h"
+#include "spdk/bdev_module.h"
 #include "spdk/string.h"
 #include "nvme_internal.h"
 #include "nvme_pcie_internal.h"
diff --git a/lib/nvme/nvme_rebind.c b/lib/nvme/nvme_rebind.c
index 5836fa3..1d8dadf 100644
--- a/lib/nvme/nvme_rebind.c
+++ b/lib/nvme/nvme_rebind.c
@@ -11,13 +11,8 @@
  * GNU General Public License for more details.
  */
 
-#include <string.h>
-#include <sys/types.h>
-#include <fcntl.h>
-#include <stdlib.h>
-#include <errno.h>
+#include "spdk/stdinc.h"
 #include <securec.h>
-#include <unistd.h>
 #include <sys/queue.h>
 #include "spdk/log.h"
 #include "spdk/nvme.h"
@@ -25,7 +20,7 @@
 #define PATH_LEN 4096
 #define ID_LEN 16
 
-// nvme that fails to bind uio
+/* nvme that fails to bind uio */
 struct failed_nvme {
 	char *pci_addr;
 	TAILQ_ENTRY(failed_nvme) tailq;
@@ -35,25 +30,29 @@ struct failed_nvme {
   * failed nvmes list, failed nvme will send a "nvme add uevent" when we bind it back to nvme driver
   * in spdk_rebind_driver, we should ignore this event or we wouldn't stop binding this nvme to uio.
   */
-static TAILQ_HEAD(failed_nvme_list, failed_nvme) g_failed_nvmes = TAILQ_HEAD_INITIALIZER(g_failed_nvmes);
+static TAILQ_HEAD(failed_nvme_list,
+		  failed_nvme) g_failed_nvmes = TAILQ_HEAD_INITIALIZER(g_failed_nvmes);
 
-// get vendor id from /sys/bus/pci/devices/pci_addr/vendor
-// get device id from /sys/bus/pci/devices/pci_addr/device
-static int32_t get_id_from_sysfs(const char *pci_addr, const char *id_type, char *ret_id, uint8_t ret_id_len)
+/* get vendor id from /sys/bus/pci/devices/pci_addr/vendor
+ * get device id from /sys/bus/pci/devices/pci_addr/device
+ */
+static int32_t get_id_from_sysfs(const char *pci_addr, const char *id_type, char *ret_id,
+				 uint8_t ret_id_len)
 {
 	int32_t fd = -1;
 	char sysfs_path[PATH_LEN];
 	char tmp_id[ID_LEN] = {0};
 	char *tmp = NULL;
 
-	// id's length is 5 byte,like XXXX'\0'
+	/* id's length is 5 byte,like XXXX'\0' */
 	if (ret_id_len < 5) {
 		SPDK_ERRLOG("ret_id_len is less than 5 bytes\n");
 		return -1;
 	}
 
-	// construct path in sysfs which stores id
-	if (snprintf_s(sysfs_path, PATH_LEN, PATH_LEN - 1, "/sys/bus/pci/devices/%s/%s", pci_addr, id_type) > 0) {
+	/* construct path in sysfs which stores i */
+	if (snprintf_s(sysfs_path, PATH_LEN, PATH_LEN - 1, "/sys/bus/pci/devices/%s/%s", pci_addr,
+		       id_type) > 0) {
 		fd = open(sysfs_path, O_RDONLY);
 	}
 	if (fd < 0) {
@@ -61,16 +60,16 @@ static int32_t get_id_from_sysfs(const char *pci_addr, const char *id_type, char
 		return -1;
 	}
 
-	// id in sysfs is like 0xDDDD
+	/* id in sysfs is like 0xDDDD */
 	if (read(fd, tmp_id, ID_LEN - 1) <= 0) {
 		SPDK_ERRLOG("fail to read id from %s, errno(%d): %s\n", sysfs_path, errno, strerror(errno));
 		close(fd);
 		return -1;
 	}
 
-	// 2 means skipping prefix "0x" of id read from sysfs
+	/* 2 means skipping prefix "0x" of id read from sysfs */
 	tmp = tmp_id + 2;
-	// 4 means the value of id read from sysfs, not including prefix "0x"
+	/* 4 means the value of id read from sysfs, not including prefix "0x" */
 	if (snprintf_s(ret_id, ret_id_len, 4, "%s", tmp) <= 0) {
 		SPDK_ERRLOG("string copy failed\n");
 	}
@@ -79,24 +78,24 @@ static int32_t get_id_from_sysfs(const char *pci_addr, const char *id_type, char
 	return 0;
 }
 
-// get ven_dev_id which combines vendor id and device id
+/* get ven_dev_id which combines vendor id and device id */
 static int32_t get_ven_dev_id(const char *pci_addr, char *ven_dev_id, uint8_t ven_dev_id_len)
 {
 	char ven_id[ID_LEN], dev_id[ID_LEN];
 
-	// ven_dev_id combines with vendor id and device id,like "DDDD XXXX'\0'",length is 10 bytes
+	/* ven_dev_id combines with vendor id and device id,like "DDDD XXXX'\0'",length is 10 bytes */
 	if (ven_dev_id_len < 10) {
 		SPDK_ERRLOG("ven_dev_id_len is less than 10 bytes\n");
 		return -1;
 	}
 
-	// get vendor id from sysfs,format is like "DDDD"
+	/* get vendor id from sysfs,format is like "DDDD" */
 	if (get_id_from_sysfs(pci_addr, "vendor", ven_id, ID_LEN) < 0) {
 		SPDK_ERRLOG("fail to get vendor id\n");
 		return -1;
 	}
 
-	// get device id from sysfs,format is like "XXXX"
+	/* get device id from sysfs,format is like "XXXX" */
 	if (get_id_from_sysfs(pci_addr, "device", dev_id, ID_LEN) < 0) {
 		SPDK_ERRLOG("fail to get device id\n");
 		return -1;
@@ -109,13 +108,13 @@ static int32_t get_ven_dev_id(const char *pci_addr, char *ven_dev_id, uint8_t ve
 	return 0;
 }
 
-// unbind driver by writing remove_id and unbind files in sysfs
+/* unbind driver by writing remove_id and unbind files in sysfs */
 static int32_t unbind_driver(char *pci_addr, const char *ven_dev_id)
 {
-	char sysfs_dev_remove_id[PATH_LEN];	// remove_id file path in sysfs
-	char sysfs_dev_unbind[PATH_LEN];	// unbind file path in sysfs
-	int32_t remove_id_fd = -1;		// file description of remove_id file
-	int32_t unbind_fd = -1;			// file description of unbind file
+	char sysfs_dev_remove_id[PATH_LEN];	/* remove_id file path in sysfs */
+	char sysfs_dev_unbind[PATH_LEN];	/* unbind file path in sysfs */
+	int32_t remove_id_fd = -1;		/* file description of remove_id file */
+	int32_t unbind_fd = -1;			/* file description of unbind file */
 	int32_t ret;
 
 	ret = snprintf_s(sysfs_dev_remove_id, PATH_LEN, PATH_LEN - 1,
@@ -140,7 +139,7 @@ static int32_t unbind_driver(char *pci_addr, const char *ven_dev_id)
 	(void)write(remove_id_fd, ven_dev_id, strlen(ven_dev_id) + 1);
 	close(remove_id_fd);
 
-	// unbind driver by wrting unbind file
+	/* unbind driver by wrting unbind file */
 	unbind_fd = open(sysfs_dev_unbind, O_WRONLY);
 	if (unbind_fd < 0) {
 		SPDK_ERRLOG("fail to open %s, errno(%d): %s\n", sysfs_dev_unbind, errno, strerror(errno));
@@ -149,7 +148,8 @@ static int32_t unbind_driver(char *pci_addr, const char *ven_dev_id)
 
 	ret = write(unbind_fd, pci_addr, strlen(pci_addr) + 1);
 	if (ret < 0) {
-		SPDK_ERRLOG("write %s to %s fail, errno(%d): %s\n",pci_addr, sysfs_dev_unbind, errno, strerror(errno));
+		SPDK_ERRLOG("write %s to %s fail, errno(%d): %s\n", pci_addr, sysfs_dev_unbind, errno,
+			    strerror(errno));
 		close(unbind_fd);
 		return -1;
 	}
@@ -159,25 +159,27 @@ static int32_t unbind_driver(char *pci_addr, const char *ven_dev_id)
 	return 0;
 }
 
-// bind device to new driver by writing new_id and bind files in sysfs
+/* bind device to new driver by writing new_id and bind files in sysfs */
 static int32_t bind_driver(const char *pci_addr, const char *ven_dev_id, const char *driver_name)
 {
-	char sysfs_driver_new_id[PATH_LEN];	// new_id file path in sysfs
-	char sysfs_driver_bind[PATH_LEN];	// bind file path in sysfs
-	int32_t new_id_fd = -1;			// file description of new_id file
-	int32_t bind_fd = -1;			// file descriptoin of bind file
+	char sysfs_driver_new_id[PATH_LEN];	/* new_id file path in sysfs */
+	char sysfs_driver_bind[PATH_LEN];	/* bind file path in sysfs */
+	int32_t new_id_fd = -1;			/* file description of new_id file */
+	int32_t bind_fd = -1;			/* file descriptoin of bind file */
 	int rc;
 
-	rc = snprintf_s(sysfs_driver_new_id, PATH_LEN, PATH_LEN - 1, "/sys/bus/pci/drivers/%s/new_id", driver_name);
+	rc = snprintf_s(sysfs_driver_new_id, PATH_LEN, PATH_LEN - 1, "/sys/bus/pci/drivers/%s/new_id",
+			driver_name);
 	if (rc > 0) {
-		rc = snprintf_s(sysfs_driver_bind, PATH_LEN, PATH_LEN - 1, "/sys/bus/pci/drivers/%s/bind", driver_name);
+		rc = snprintf_s(sysfs_driver_bind, PATH_LEN, PATH_LEN - 1, "/sys/bus/pci/drivers/%s/bind",
+				driver_name);
 	}
 	if (rc <= 0) {
 		SPDK_ERRLOG("string copy failed\n");
 		return -1;
 	}
 
-	// try to bind driver by write ven_dev_id to new_id file
+	/* try to bind driver by write ven_dev_id to new_id file */
 	new_id_fd = open(sysfs_driver_new_id, O_WRONLY);
 	if (new_id_fd < 0) {
 		SPDK_ERRLOG("fail to open %s, errno(%d): %s\n", sysfs_driver_new_id, errno, strerror(errno));
@@ -187,7 +189,7 @@ static int32_t bind_driver(const char *pci_addr, const char *ven_dev_id, const c
 	(void)write(new_id_fd, ven_dev_id, strlen(ven_dev_id) + 1);
 	close(new_id_fd);
 
-	// bind driver by writing pci_addr to bind file if writing new_id file failed
+	/* bind driver by writing pci_addr to bind file if writing new_id file failed */
 	bind_fd = open(sysfs_driver_bind, O_WRONLY);
 	if (bind_fd < 0) {
 		SPDK_ERRLOG("fail to open %s, errno(%d): %s\n", sysfs_driver_bind, errno, strerror(errno));
@@ -210,10 +212,10 @@ int32_t spdk_rebind_driver(char *pci_addr, char *driver_name)
 		return -1;
 	}
 
-	// ignore event from binding pci back to nvme driver
+	/* ignore event from binding pci back to nvme driver */
 	TAILQ_FOREACH(iter, &g_failed_nvmes, tailq) {
 		if (strncmp(iter->pci_addr, pci_addr, strlen(iter->pci_addr)) == 0) {
-			// oncely ignore nvme add event from binding back to nvme,so do rebind when next hotplug of this pci happen
+			/* oncely ignore nvme add event from binding back to nvme,so do rebind when next hotplug of this pci happen */
 			TAILQ_REMOVE(&g_failed_nvmes, iter, tailq);
 			free(iter->pci_addr);
 			free(iter);
@@ -237,10 +239,10 @@ int32_t spdk_rebind_driver(char *pci_addr, char *driver_name)
 	}
 
 	if (bind_driver(pci_addr, ven_dev_id, driver_name) < 0) {
-		// retry
+		/* retry */
 		if (bind_driver(pci_addr, ven_dev_id, driver_name) < 0) {
 			SPDK_ERRLOG("fail to bind %s to %s\n", pci_addr, driver_name);
-			// add fialed nvme to g_failed_nvmes
+			/* add fialed nvme to g_failed_nvmes */
 			struct failed_nvme *failed_nvme = (struct failed_nvme *)malloc(sizeof(struct failed_nvme));
 			if (failed_nvme == NULL) {
 				SPDK_ERRLOG("failed to malloc for failed_nvme,can't bind %s back to nvme\n", pci_addr);
@@ -254,7 +256,7 @@ int32_t spdk_rebind_driver(char *pci_addr, char *driver_name)
 			}
 			TAILQ_INSERT_TAIL(&g_failed_nvmes, failed_nvme, tailq);
 
-			// bind device back to nvme driver if failed to bind uio
+			/* bind device back to nvme driver if failed to bind uio */
 			bind_driver(pci_addr, ven_dev_id, "nvme");
 		}
 	}
diff --git a/lib/rpc/rpc.c b/lib/rpc/rpc.c
index 9662b88..9b2caed 100644
--- a/lib/rpc/rpc.c
+++ b/lib/rpc/rpc.c
@@ -110,6 +110,8 @@ jsonrpc_handler(struct spdk_jsonrpc_request *request,
 
 	assert(method != NULL);
 
+	SPDK_NOTICELOG("[spdk] jsonrpc handle request: %p, handling method: %s\n", request,
+		       (char *)method->start);
 	m = _get_rpc_method(method);
 	if (m == NULL) {
 		spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_METHOD_NOT_FOUND, "Method not found");
diff --git a/lib/thread/thread.c b/lib/thread/thread.c
index 08a1284..1ab822b 100644
--- a/lib/thread/thread.c
+++ b/lib/thread/thread.c
@@ -52,6 +52,12 @@
 #define SPDK_MSG_BATCH_SIZE		8
 #define SPDK_MAX_DEVICE_NAME_LEN	256
 #define SPDK_THREAD_EXIT_TIMEOUT_SEC	5
+#ifdef SPDK_CONFIG_APP_RW
+void spdk_set_thread_exited(struct spdk_thread *thread)
+{
+	thread->state = SPDK_THREAD_STATE_EXITED;
+}
+#endif
 
 static pthread_mutex_t g_devlist_mutex = PTHREAD_MUTEX_INITIALIZER;
 
@@ -296,7 +302,6 @@ spdk_thread_create(const char *name, struct spdk_cpuset *cpumask)
 			thread->msg_cache_count++;
 		}
 	}
-
 	if (name) {
 		snprintf(thread->name, sizeof(thread->name), "%s", name);
 	} else {
@@ -315,8 +320,8 @@ spdk_thread_create(const char *name, struct spdk_cpuset *cpumask)
 	g_thread_count++;
 	pthread_mutex_unlock(&g_devlist_mutex);
 
-	SPDK_DEBUGLOG(thread, "Allocating new thread (%" PRIu64 ", %s)\n",
-		      thread->id, thread->name);
+	SPDK_NOTICELOG("Allocating new thread (%" PRIu64 ", %s)\n",
+		       thread->id, thread->name);
 
 	if (spdk_interrupt_mode_is_enabled()) {
 		thread->interrupt_mode = true;
@@ -894,7 +899,6 @@ spdk_thread_send_msg(const struct spdk_thread *thread, spdk_msg_fn fn, void *ctx
 	int rc;
 
 	assert(thread != NULL);
-
 	if (spdk_unlikely(thread->state == SPDK_THREAD_STATE_EXITED)) {
 		SPDK_ERRLOG("Thread %s is marked as exited.\n", thread->name);
 		return -EIO;
@@ -1143,6 +1147,11 @@ spdk_poller_unregister(struct spdk_poller **ppoller)
 	struct spdk_thread *thread;
 	struct spdk_poller *poller;
 
+	if (!g_bRunReactor) {
+		*ppoller = NULL;
+		return;
+	}
+
 	poller = *ppoller;
 	if (poller == NULL) {
 		return;
@@ -1427,8 +1436,12 @@ io_device_free(struct io_device *dev)
 		assert(dev->unregister_thread != NULL);
 		SPDK_DEBUGLOG(thread, "io_device %s (%p) needs to unregister from thread %s\n",
 			      dev->name, dev->io_device, dev->unregister_thread->name);
+#ifndef SPDK_CONFIG_APP_RW
 		rc = spdk_thread_send_msg(dev->unregister_thread, _finish_unregister, dev);
 		assert(rc == 0);
+#else
+		_finish_unregister((void *)dev);
+#endif
 	}
 }
 
@@ -1779,8 +1792,13 @@ spdk_for_each_channel(void *io_device, spdk_channel_msg fn, void *ctx,
 				i->cur_thread = thread;
 				i->ch = ch;
 				pthread_mutex_unlock(&g_devlist_mutex);
+#ifndef SPDK_CONFIG_APP_RW
 				rc = spdk_thread_send_msg(thread, _call_channel, i);
 				assert(rc == 0);
+#else
+				_call_channel(i);
+#endif
+				assert(rc == 0);
 				return;
 			}
 		}
@@ -1788,8 +1806,12 @@ spdk_for_each_channel(void *io_device, spdk_channel_msg fn, void *ctx,
 
 	pthread_mutex_unlock(&g_devlist_mutex);
 
+#ifndef SPDK_CONFIG_APP_RW
 	rc = spdk_thread_send_msg(i->orig_thread, _call_completion, i);
 	assert(rc == 0);
+#else
+	_call_completion(i);
+#endif
 }
 
 void
@@ -1814,8 +1836,12 @@ spdk_for_each_channel_continue(struct spdk_io_channel_iter *i, int status)
 				i->cur_thread = thread;
 				i->ch = ch;
 				pthread_mutex_unlock(&g_devlist_mutex);
+#ifndef SPDK_CONFIG_APP_RW
 				rc = spdk_thread_send_msg(thread, _call_channel, i);
 				assert(rc == 0);
+#else
+				_call_channel(i);
+#endif
 				return;
 			}
 		}
@@ -1827,8 +1853,12 @@ end:
 	i->ch = NULL;
 	pthread_mutex_unlock(&g_devlist_mutex);
 
+#ifndef SPDK_CONFIG_APP_RW
 	rc = spdk_thread_send_msg(i->orig_thread, _call_completion, i);
 	assert(rc == 0);
+#else
+	_call_completion(i);
+#endif
 }
 
 struct spdk_interrupt {
diff --git a/mk/nvme.libtest.mk b/mk/nvme.libtest.mk
index 201db50..03f4fe4 100644
--- a/mk/nvme.libtest.mk
+++ b/mk/nvme.libtest.mk
@@ -38,6 +38,6 @@ include $(SPDK_ROOT_DIR)/mk/spdk.modules.mk
 
 C_SRCS := $(APP:%=%.c)
 
-SPDK_LIB_LIST = $(SOCK_MODULES_LIST) nvme vmd
+SPDK_LIB_LIST = $(SOCK_MODULES_LIST) nvme vmd trace
 
 include $(SPDK_ROOT_DIR)/mk/spdk.app.mk
diff --git a/mk/spdk.common.mk b/mk/spdk.common.mk
index 8569687..6bdc1dd 100644
--- a/mk/spdk.common.mk
+++ b/mk/spdk.common.mk
@@ -81,7 +81,7 @@ else ifeq ($(TARGET_MACHINE),aarch64)
 COMMON_CFLAGS += -march=$(TARGET_ARCHITECTURE)
 COMMON_CFLAGS += -DPAGE_SIZE=$(shell getconf PAGESIZE)
 else
-COMMON_CFLAGS += -march=$(TARGET_ARCHITECTURE)
+COMMON_CFLAGS += -march=core-avx-i
 endif
 
 ifeq ($(CONFIG_WERROR), y)
@@ -248,12 +248,13 @@ endif
 COMMON_CFLAGS += -pthread
 LDFLAGS += -pthread
 
-CFLAGS   += $(COMMON_CFLAGS) -Wno-pointer-sign -Wstrict-prototypes -Wold-style-definition -std=gnu99
+CFLAGS   += $(COMMON_CFLAGS) -Wno-pointer-sign -Wstrict-prototypes -Wold-style-definition -std=gnu99 -include spdk/config.h
 CXXFLAGS += $(COMMON_CFLAGS)
 
 SYS_LIBS += -lrt
 SYS_LIBS += -luuid
 SYS_LIBS += -lcrypto
+SYS_LIBS += -lsecurec
 
 ifneq ($(CONFIG_NVME_CUSE)$(CONFIG_FUSE),nn)
 SYS_LIBS += -lfuse3
diff --git a/mk/spdk.modules.mk b/mk/spdk.modules.mk
index 415a3b2..d45702c 100644
--- a/mk/spdk.modules.mk
+++ b/mk/spdk.modules.mk
@@ -34,7 +34,7 @@
 BLOCKDEV_MODULES_LIST = bdev_malloc bdev_null bdev_nvme bdev_passthru bdev_lvol
 BLOCKDEV_MODULES_LIST += bdev_raid bdev_error bdev_gpt bdev_split bdev_delay
 BLOCKDEV_MODULES_LIST += bdev_zone_block
-BLOCKDEV_MODULES_LIST += blobfs blobfs_bdev blob_bdev blob lvol vmd nvme
+BLOCKDEV_MODULES_LIST += blobfs blobfs_bdev blob_bdev blob lvol vmd nvme conf
 
 # Some bdev modules don't have pollers, so they can directly run in interrupt mode
 INTR_BLOCKDEV_MODULES_LIST = bdev_malloc bdev_passthru bdev_error bdev_gpt bdev_split bdev_raid
diff --git a/module/bdev/nvme/Makefile b/module/bdev/nvme/Makefile
index f9ddb23..9ad93ef 100644
--- a/module/bdev/nvme/Makefile
+++ b/module/bdev/nvme/Makefile
@@ -39,6 +39,7 @@ SO_MINOR := 0
 
 C_SRCS = bdev_nvme.c bdev_nvme_rpc.c nvme_rpc.c common.c bdev_ocssd.c bdev_ocssd_rpc.c
 C_SRCS-$(CONFIG_NVME_CUSE) += bdev_nvme_cuse_rpc.c
+C_SRCS-$(CONFIG_APP_RW) += bdev_nvme_self.c
 
 ifeq ($(OS),Linux)
 C_SRCS += vbdev_opal.c vbdev_opal_rpc.c
diff --git a/module/bdev/nvme/bdev_nvme.c b/module/bdev/nvme/bdev_nvme.c
index e9d730d..01d0238 100644
--- a/module/bdev/nvme/bdev_nvme.c
+++ b/module/bdev/nvme/bdev_nvme.c
@@ -48,8 +48,14 @@
 
 #include "spdk/bdev_module.h"
 #include "spdk/log.h"
+#include "spdk/conf.h"
 
+#ifdef SPDK_CONFIG_APP_RW
+#include "bdev_nvme_self.h"
+#define SPDK_BDEV_NVME_DEFAULT_DELAY_CMD_SUBMIT false
+#else
 #define SPDK_BDEV_NVME_DEFAULT_DELAY_CMD_SUBMIT true
+#endif
 #define SPDK_BDEV_NVME_DEFAULT_KEEP_ALIVE_TIMEOUT_IN_MS	(10000)
 
 static int bdev_nvme_config_json(struct spdk_json_write_ctx *w);
@@ -170,7 +176,7 @@ static int bdev_nvme_abort(struct nvme_io_channel *nvme_ch,
 			   struct nvme_bdev_io *bio, struct nvme_bdev_io *bio_to_abort);
 static int bdev_nvme_reset(struct nvme_io_channel *nvme_ch, struct nvme_bdev_io *bio);
 static int bdev_nvme_failover(struct nvme_bdev_ctrlr *nvme_bdev_ctrlr, bool remove);
-static void remove_cb(void *cb_ctx, struct spdk_nvme_ctrlr *ctrlr);
+void remove_cb(void *cb_ctx, struct spdk_nvme_ctrlr *ctrlr);
 
 typedef void (*populate_namespace_fn)(struct nvme_bdev_ctrlr *nvme_bdev_ctrlr,
 				      struct nvme_bdev_ns *nvme_ns, struct nvme_async_probe_ctx *ctx);
@@ -256,6 +262,10 @@ bdev_nvme_poll(void *arg)
 		group->start_ticks = spdk_get_ticks();
 	}
 
+#ifdef SPDK_CONFIG_APP_RW
+	bdev_update_ch_timeout(group);
+#endif
+
 	num_completions = spdk_nvme_poll_group_process_completions(group->group, 0,
 			  bdev_nvme_disconnected_qpair_cb);
 	if (group->collect_spin_stat) {
@@ -270,9 +280,13 @@ bdev_nvme_poll(void *arg)
 		}
 	}
 
+	if (!spdk_get_reactor_type()) {
+		return num_completions;
+	}
 	return num_completions > 0 ? SPDK_POLLER_BUSY : SPDK_POLLER_IDLE;
 }
 
+#ifndef SPDK_CONFIG_APP_RW
 static int
 bdev_nvme_poll_adminq(void *arg)
 {
@@ -288,6 +302,7 @@ bdev_nvme_poll_adminq(void *arg)
 
 	return rc == 0 ? SPDK_POLLER_IDLE : SPDK_POLLER_BUSY;
 }
+#endif
 
 static int
 bdev_nvme_destruct(void *ctx)
@@ -330,6 +345,7 @@ bdev_nvme_create_qpair(struct nvme_io_channel *nvme_ch)
 	g_opts.io_queue_requests = opts.io_queue_requests;
 
 	nvme_ch->qpair = spdk_nvme_ctrlr_alloc_io_qpair(ctrlr, &opts, sizeof(opts));
+	syslog(LOG_INFO, "open a new qpair=%p, thread=%lu.\n", nvme_ch->qpair, pthread_self());
 	if (nvme_ch->qpair == NULL) {
 		return -1;
 	}
@@ -791,7 +807,11 @@ _bdev_nvme_submit_request(struct spdk_io_channel *ch, struct spdk_bdev_io *bdev_
 static void
 bdev_nvme_submit_request(struct spdk_io_channel *ch, struct spdk_bdev_io *bdev_io)
 {
+#ifdef SPDK_CONFIG_APP_RW
+	int rc = _bdev_nvme_submit_request_self(ch, bdev_io);
+#else
 	int rc = _bdev_nvme_submit_request(ch, bdev_io);
+#endif
 
 	if (spdk_unlikely(rc != 0)) {
 		if (rc == -ENOMEM) {
@@ -824,6 +844,12 @@ bdev_nvme_io_type_supported(void *ctx, enum spdk_bdev_io_type io_type)
 	case SPDK_BDEV_IO_TYPE_NVME_ADMIN:
 	case SPDK_BDEV_IO_TYPE_NVME_IO:
 	case SPDK_BDEV_IO_TYPE_ABORT:
+#ifdef SPDK_CONFIG_APP_RW
+	case SPDK_BDEV_IO_TYPE_READ_NVME:
+	case SPDK_BDEV_IO_TYPE_WRITE_NVME:
+	case SPDK_BDEV_IO_TYPE_READV_NVME:
+	case SPDK_BDEV_IO_TYPE_WRITEV_NVME:
+#endif
 		return true;
 
 	case SPDK_BDEV_IO_TYPE_COMPARE:
@@ -944,7 +970,7 @@ bdev_nvme_poll_group_create_cb(void *io_device, void *ctx_buf)
 
 	group->poller = SPDK_POLLER_REGISTER(bdev_nvme_poll, group, g_opts.nvme_ioq_poll_period_us);
 
-	if (group->poller == NULL) {
+	if (group->poller == NULL && spdk_get_reactor_type()) {
 		spdk_nvme_poll_group_destroy(group->group);
 		return -1;
 	}
@@ -980,6 +1006,7 @@ bdev_nvme_get_module_ctx(void *ctx)
 	return bdev_nvme_get_ctrlr(&nvme_bdev->disk);
 }
 
+#ifndef SPDK_CONFIG_APP_RW
 static int
 bdev_nvme_dump_info_json(void *ctx, struct spdk_json_write_ctx *w)
 {
@@ -1093,6 +1120,7 @@ bdev_nvme_dump_info_json(void *ctx, struct spdk_json_write_ctx *w)
 
 	return 0;
 }
+#endif
 
 static void
 bdev_nvme_write_config_json(struct spdk_bdev *bdev, struct spdk_json_write_ctx *w)
@@ -1128,10 +1156,17 @@ static const struct spdk_bdev_fn_table nvmelib_fn_table = {
 	.submit_request		= bdev_nvme_submit_request,
 	.io_type_supported	= bdev_nvme_io_type_supported,
 	.get_io_channel		= bdev_nvme_get_io_channel,
-	.dump_info_json		= bdev_nvme_dump_info_json,
 	.write_config_json	= bdev_nvme_write_config_json,
 	.get_spin_time		= bdev_nvme_get_spin_time,
 	.get_module_ctx		= bdev_nvme_get_module_ctx,
+#ifdef SPDK_CONFIG_APP_RW
+	.dump_info_json		= bdev_nvme_dump_info_json_self,
+	.bdev_poll_rsp		= bdev_nvme_poll,
+	.get_io_channel_id	= bdev_nvme_get_io_channel_id,
+	.get_timeout_count	= bdev_nvme_get_timeout_count,
+#else
+	.dump_info_json		= bdev_nvme_dump_info_json,
+#endif
 };
 
 static int
@@ -1157,7 +1192,12 @@ nvme_disk_create(struct spdk_bdev *disk, const char *base_name,
 		/* Enable if the Volatile Write Cache exists */
 		disk->write_cache = 1;
 	}
+
+#ifdef SPDK_CONFIG_APP_RW
+	disk->blocklen = spdk_nvme_ns_get_sector_size(ns);
+#else
 	disk->blocklen = spdk_nvme_ns_get_extended_sector_size(ns);
+#endif
 	disk->blockcnt = spdk_nvme_ns_get_num_sectors(ns);
 	disk->optimal_io_boundary = spdk_nvme_ns_get_optimal_io_boundary(ns);
 
@@ -1356,14 +1396,14 @@ nvme_ctrlr_depopulate_standard_namespace(struct nvme_bdev_ns *nvme_ns)
 	nvme_ctrlr_depopulate_namespace_done(nvme_ns);
 }
 
-static void
+void
 nvme_ctrlr_populate_namespace(struct nvme_bdev_ctrlr *ctrlr, struct nvme_bdev_ns *nvme_ns,
 			      struct nvme_async_probe_ctx *ctx)
 {
 	g_populate_namespace_fn[nvme_ns->type](ctrlr, nvme_ns, ctx);
 }
 
-static void
+void
 nvme_ctrlr_depopulate_namespace(struct nvme_bdev_ctrlr *ctrlr, struct nvme_bdev_ns *nvme_ns)
 {
 	g_depopulate_namespace_fn[nvme_ns->type](nvme_ns);
@@ -1579,8 +1619,10 @@ nvme_bdev_ctrlr_create(struct spdk_nvme_ctrlr *ctrlr,
 				sizeof(struct nvme_io_channel),
 				name);
 
+#ifndef SPDK_CONFIG_APP_RW
 	nvme_bdev_ctrlr->adminq_timer_poller = SPDK_POLLER_REGISTER(bdev_nvme_poll_adminq, nvme_bdev_ctrlr,
 					       g_opts.nvme_adminq_poll_period_us);
+#endif
 
 	TAILQ_INSERT_TAIL(&g_nvme_bdev_ctrlrs, nvme_bdev_ctrlr, tailq);
 
@@ -1618,7 +1660,7 @@ err_alloc_namespaces:
 	return rc;
 }
 
-static void
+void
 attach_cb(void *cb_ctx, const struct spdk_nvme_transport_id *trid,
 	  struct spdk_nvme_ctrlr *ctrlr, const struct spdk_nvme_ctrlr_opts *opts)
 {
@@ -1669,7 +1711,7 @@ _nvme_bdev_ctrlr_destruct(void *ctx)
 	nvme_bdev_ctrlr_destruct(nvme_bdev_ctrlr);
 }
 
-static void
+void
 remove_cb(void *cb_ctx, struct spdk_nvme_ctrlr *ctrlr)
 {
 	struct nvme_bdev_ctrlr *nvme_bdev_ctrlr = cb_ctx;
@@ -2174,6 +2216,9 @@ bdev_nvme_library_init(void)
 				bdev_nvme_poll_group_destroy_cb,
 				sizeof(struct nvme_bdev_poll_group),  "bdev_nvme_poll_groups");
 
+#ifdef SPDK_CONFIG_APP_RW
+	return bdev_probe_ctrlr();
+#endif
 	return 0;
 }
 
@@ -2363,11 +2408,14 @@ bdev_nvme_comparev_and_writev_done(void *ref, const struct spdk_nvme_cpl *cpl)
 	}
 }
 
-static void
+void
 bdev_nvme_queued_done(void *ref, const struct spdk_nvme_cpl *cpl)
 {
 	struct spdk_bdev_io *bdev_io = spdk_bdev_io_from_ctx((struct nvme_bdev_io *)ref);
 
+#ifdef SPDK_CONFIG_APP_RW
+	spdk_bdev_set_io_location(ref, (uint8_t)LOCAL_LIBSTORAGE_FROM_DISK);
+#endif
 	spdk_bdev_io_complete_nvme_status(bdev_io, cpl->cdw0, cpl->status.sct, cpl->status.sc);
 }
 
@@ -2412,7 +2460,7 @@ bdev_nvme_admin_passthru_done(void *ref, const struct spdk_nvme_cpl *cpl)
 	spdk_thread_send_msg(bio->orig_thread, bdev_nvme_admin_passthru_completion, bio);
 }
 
-static void
+void
 bdev_nvme_queued_reset_sgl(void *ref, uint32_t sgl_offset)
 {
 	struct nvme_bdev_io *bio = ref;
@@ -2429,7 +2477,7 @@ bdev_nvme_queued_reset_sgl(void *ref, uint32_t sgl_offset)
 	}
 }
 
-static int
+int
 bdev_nvme_queued_next_sge(void *ref, void **address, uint32_t *length)
 {
 	struct nvme_bdev_io *bio = ref;
@@ -2979,4 +3027,221 @@ bdev_nvme_get_ctrlr(struct spdk_bdev *bdev)
 	return SPDK_CONTAINEROF(bdev, struct nvme_bdev, disk)->nvme_ns->ctrlr->ctrlr;
 }
 
+#ifdef SPDK_CONFIG_APP_RW
+void *
+nvme_channel_get_group(void *io_ch)
+{
+	struct nvme_io_channel *nvme_io_ch = io_ch;
+	return nvme_io_ch->group;
+}
+struct nvme_bdev_io *nvme_bdev_io_update_args(struct nvme_bdev_io *bio, struct iovec *iov,
+		int iovcnt)
+{
+	bio->iovs = iov;
+	bio->iovcnt = iovcnt;
+	bio->iovpos = 0;
+	bio->iov_offset = 0;
+	return bio;
+}
+
+struct nvme_probe_ctx *bdev_nvme_create_probe_ctx(struct spdk_nvme_transport_id *trid,
+		const char *base_name, const char *hostnqn)
+{
+	struct nvme_probe_ctx *probe_ctx = calloc(1, sizeof(*probe_ctx));
+	if (probe_ctx == NULL) {
+		SPDK_ERRLOG("Failed to allocate probe_ctx\n");
+		return NULL;
+	}
+
+	probe_ctx->count = 1;
+	probe_ctx->trids[0] = *trid;
+	probe_ctx->names[0] = base_name;
+	probe_ctx->hostnqn = hostnqn;
+	return probe_ctx;
+}
+
+bool
+probe_cb(void *cb_ctx, const struct spdk_nvme_transport_id *trid,
+	 struct spdk_nvme_ctrlr_opts *opts)
+{
+	struct nvme_probe_ctx *ctx = cb_ctx;
+
+	SPDK_DEBUGLOG(nvme, "Probing device %s\n", trid->traddr);
+
+	if (nvme_bdev_ctrlr_get(trid)) {
+		SPDK_ERRLOG("A controller with the provided trid (traddr: %s) already exists.\n",
+			    trid->traddr);
+		return false;
+	}
+
+	if (trid->trtype == SPDK_NVME_TRANSPORT_PCIE) {
+		bool claim_device = false;
+		size_t i;
+
+		for (i = 0; i < ctx->count; i++) {
+			if (spdk_nvme_transport_id_compare(trid, &ctx->trids[i]) == 0) {
+				claim_device = true;
+				break;
+			}
+		}
+
+		if (!claim_device) {
+			SPDK_DEBUGLOG(nvme, "Not claiming device at %s\n", trid->traddr);
+			return false;
+		}
+	}
+
+	if (ctx->hostnqn) {
+		snprintf(opts->hostnqn, sizeof(opts->hostnqn), "%s", ctx->hostnqn);
+	}
+
+	opts->arbitration_burst = (uint8_t)g_opts.arbitration_burst;
+	opts->low_priority_weight = (uint8_t)g_opts.low_priority_weight;
+	opts->medium_priority_weight = (uint8_t)g_opts.medium_priority_weight;
+	opts->high_priority_weight = (uint8_t)g_opts.high_priority_weight;
+
+	return true;
+}
+
+int bdev_probe_ctrlr(void)
+{
+	struct spdk_conf_section *sp;
+	const char *val;
+	int rc = 0;
+	int64_t intval = 0;
+	size_t i;
+	struct nvme_probe_ctx *probe_ctx = NULL;
+	int retry_count;
+	uint32_t local_nvme_num = 0;
+
+	sp = spdk_conf_find_section(NULL, "Nvme");
+	if (sp == NULL) {
+		SPDK_ERRLOG("config file does not contain [Nvme] section, which need to be provided\n");
+		goto end;
+	}
+
+	probe_ctx = calloc(1, sizeof(*probe_ctx));
+	if (probe_ctx == NULL) {
+		SPDK_ERRLOG("Failed to allocate probe_ctx\n");
+		rc = -1;
+		goto end;
+	}
+
+	retry_count = spdk_conf_section_get_intval(sp, "RetryCount");
+	if (retry_count >= 0) {
+		g_opts.retry_count = retry_count;
+	}
+	if (retry_count > 255) {
+		SPDK_WARNLOG("RetryCount:%d should not be greater than 255, set it to 255 this time\n",
+			     retry_count);
+		retry_count = 255;
+	}
+	syslog(LOG_INFO, "RetryCount is set to %d\n", retry_count);
+
+	val = spdk_conf_section_get_val(sp, "TimeoutUsec");
+	if (val != NULL) {
+		intval = spdk_strtoll(val, 10);
+		if (intval < 0) {
+			SPDK_ERRLOG("Invalid TimeoutUsec value\n");
+			rc = -1;
+			goto end;
+		}
+	}
+	syslog(LOG_INFO, "TimeoutUsec is set to %ld\n", intval);
+	g_opts.timeout_us = intval;
+
+	if (g_opts.timeout_us > 0) {
+		val = spdk_conf_section_get_val(sp, "ActionOnTimeout");
+		if (val != NULL) {
+			if (!strcasecmp(val, "Reset")) {
+				g_opts.action_on_timeout = SPDK_BDEV_NVME_TIMEOUT_ACTION_RESET;
+			} else if (!strcasecmp(val, "Abort")) {
+				g_opts.action_on_timeout = SPDK_BDEV_NVME_TIMEOUT_ACTION_ABORT;
+			}
+		}
+	}
+
+	intval = spdk_conf_section_get_intval(sp, "AdminPollRate");
+	if (intval > 0) {
+		g_opts.nvme_adminq_poll_period_us = intval;
+	}
+	syslog(LOG_INFO, "AdminPollRate is set to %lu\n", g_opts.nvme_adminq_poll_period_us);
+	intval = spdk_conf_section_get_intval(sp, "IOPollRate");
+	if (intval > 0) {
+		g_opts.nvme_ioq_poll_period_us = intval;
+	}
+
+	g_opts.delay_cmd_submit = spdk_conf_section_get_boolval(sp, "DelayCmdSubmit",
+				  SPDK_BDEV_NVME_DEFAULT_DELAY_CMD_SUBMIT);
+
+	for (i = 0; i < NVME_MAX_CONTROLLERS; i++) {
+		val = spdk_conf_section_get_nmval(sp, "TransportID", i, 0);
+		if (val == NULL) {
+			break;
+		}
+
+		rc = spdk_nvme_transport_id_parse(&probe_ctx->trids[i], val);
+		if (rc < 0) {
+			SPDK_ERRLOG("Unable to parse TransportID: %s\n", val);
+			rc = -1;
+			goto end;
+		}
+
+		rc = spdk_nvme_host_id_parse(&probe_ctx->hostids[i], val);
+		if (rc < 0) {
+			SPDK_ERRLOG("Unable to parse HostID: %s\n", val);
+			rc = -1;
+			goto end;
+		}
+
+		val = spdk_conf_section_get_nmval(sp, "TransportID", i, 1);
+		if (val == NULL) {
+			SPDK_ERRLOG("No name provided for TransportID\n");
+			rc = -1;
+			goto end;
+		}
+
+		probe_ctx->names[i] = val;
+
+		val = spdk_conf_section_get_nmval(sp, "TransportID", i, 2);
+		if (val != NULL) {
+			rc = spdk_nvme_prchk_flags_parse(&probe_ctx->prchk_flags[i], val);
+			if (rc < 0) {
+				SPDK_ERRLOG("Unable to parse prchk: %s\n", val);
+				rc = -1;
+				goto end;
+			}
+		}
+
+		probe_ctx->count++;
+
+		if (probe_ctx->trids[i].trtype == SPDK_NVME_TRANSPORT_PCIE) {
+			local_nvme_num++;
+		}
+	}
+
+	if (local_nvme_num > 0) {
+		/* used to probe local NVMe device */
+		if (spdk_nvme_probe(NULL, probe_ctx, probe_cb, attach_cb, remove_cb)) {
+			rc = -1;
+			goto end;
+		}
+
+		for (i = 0; i < probe_ctx->count; i++) {
+			if (probe_ctx->trids[i].trtype != SPDK_NVME_TRANSPORT_PCIE) {
+				continue;
+			}
+
+			if (!nvme_bdev_ctrlr_get(&probe_ctx->trids[i])) {
+				SPDK_ERRLOG("NVMe SSD \"%s\" could not be found.\n", probe_ctx->trids[i].traddr);
+				SPDK_ERRLOG("Check PCIe BDF and that it is attached to UIO/VFIO driver.\n");
+			}
+		}
+	}
+end:
+	free(probe_ctx);
+	return rc;
+}
+#endif
+
 SPDK_LOG_REGISTER_COMPONENT(bdev_nvme)
diff --git a/module/bdev/nvme/bdev_nvme.h b/module/bdev/nvme/bdev_nvme.h
index e789371..4c81466 100644
--- a/module/bdev/nvme/bdev_nvme.h
+++ b/module/bdev/nvme/bdev_nvme.h
@@ -42,6 +42,9 @@
 
 #include "common.h"
 
+struct nvme_bdev_io;
+struct nvme_probe_ctx;
+
 enum spdk_bdev_timeout_action {
 	SPDK_BDEV_NVME_TIMEOUT_ACTION_NONE = 0,
 	SPDK_BDEV_NVME_TIMEOUT_ACTION_RESET,
@@ -89,4 +92,43 @@ struct spdk_nvme_ctrlr *bdev_nvme_get_ctrlr(struct spdk_bdev *bdev);
  */
 int bdev_nvme_delete(const char *name);
 
+#ifdef SPDK_CONFIG_APP_RW
+void
+bdev_nvme_queued_done(void *ref, const struct spdk_nvme_cpl *cpl);
+
+void
+bdev_nvme_queued_reset_sgl(void *ref, uint32_t sgl_offset);
+
+int
+bdev_nvme_queued_next_sge(void *ref, void **address, uint32_t *length);
+
+bool
+probe_cb(void *cb_ctx, const struct spdk_nvme_transport_id *trid,
+	 struct spdk_nvme_ctrlr_opts *opts);
+
+void
+attach_cb(void *cb_ctx, const struct spdk_nvme_transport_id *trid,
+	  struct spdk_nvme_ctrlr *ctrlr, const struct spdk_nvme_ctrlr_opts *opts);
+
+void
+remove_cb(void *cb_ctx, struct spdk_nvme_ctrlr *ctrlr);
+
+void
+nvme_ctrlr_populate_namespace(struct nvme_bdev_ctrlr *ctrlr, struct nvme_bdev_ns *ns,
+			      struct nvme_async_probe_ctx *ctx);
+
+void
+nvme_ctrlr_depopulate_namespace(struct nvme_bdev_ctrlr *ctrlr, struct nvme_bdev_ns *ns);
+
+int
+bdev_probe_ctrlr(void);
+
+struct nvme_bdev_io *
+nvme_bdev_io_update_args(struct nvme_bdev_io *bio, struct iovec *iov, int iovcnt);
+
+struct nvme_probe_ctx *
+bdev_nvme_create_probe_ctx(struct spdk_nvme_transport_id *trid, const char *base_name,
+			   const char *hostnqn);
+#endif
+
 #endif /* SPDK_BDEV_NVME_H */
diff --git a/module/bdev/nvme/bdev_nvme_self.c b/module/bdev/nvme/bdev_nvme_self.c
new file mode 100644
index 0000000..7371ecb
--- /dev/null
+++ b/module/bdev/nvme/bdev_nvme_self.c
@@ -0,0 +1,661 @@
+/*
+ * Copyright (C) 2021. Huawei Technologies Co., Ltd. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include "bdev_nvme.h"
+
+#include "spdk/json.h"
+#include "spdk/likely.h"
+#include "spdk/bdev_module.h"
+#include "spdk/nvme_ocssd.h"
+#include "spdk/nvme.h"
+
+#include "spdk_internal/bdev_stat.h"
+#include "bdev_nvme_self.h"
+#include "common.h"
+#include <securec.h>
+
+enum data_direction {
+	BDEV_DISK_READ = 0,
+	BDEV_DISK_WRITE = 1
+};
+
+void bdev_update_ch_timeout(struct nvme_bdev_poll_group *group)
+{
+	uint64_t current_ticks = 0;
+	uint64_t poll_ticks = 0;
+	int64_t poll_time = 0;
+
+	current_ticks = spdk_get_ticks();
+
+	if (spdk_unlikely(g_polltime_threshold)) {
+		if (group->save_start_ticks) {
+			poll_ticks = current_ticks - group->save_start_ticks;
+			poll_time = (poll_ticks * 1000ULL) / spdk_get_ticks_hz();
+			if (poll_time >= g_polltime_threshold) {
+				group->num_poll_timeout++;
+				SPDK_WARNLOG("group[%p] poll timeout in %ldms", group, poll_time);
+			}
+		}
+		group->save_start_ticks = current_ticks;
+	}
+}
+
+int
+_bdev_nvme_submit_request_self(struct spdk_io_channel *ch, struct spdk_bdev_io *bdev_io)
+{
+	struct nvme_io_channel *nvme_ch = spdk_io_channel_get_ctx(ch);
+
+	if (nvme_ch->qpair == NULL) {
+		/* The device is currently resetting */
+		return -1;
+	}
+
+	switch (bdev_io->type) {
+	case SPDK_BDEV_IO_TYPE_READ_NVME:
+		SPDK_DEBUGLOG(bdev_nvme, "read %lu lbas with offset %#lx\n", bdev_io->u.contig.num_blocks,
+			      bdev_io->u.contig.offset_blocks);
+		return bdev_nvme_queue_cmd_with_md((struct nvme_bdev *)bdev_io->bdev->ctxt, nvme_ch->qpair,
+						   bdev_io->driver_ctx, bdev_io->u.contig.buf,
+						   bdev_io->u.contig.md_buf, BDEV_DISK_READ,
+						   bdev_io->u.contig.num_blocks, bdev_io->u.contig.offset_blocks);
+	case SPDK_BDEV_IO_TYPE_WRITE_NVME:
+		SPDK_DEBUGLOG(bdev_nvme, "write %lu lbas with offset %#lx\n", bdev_io->u.contig.num_blocks,
+			      bdev_io->u.contig.offset_blocks);
+		return bdev_nvme_queue_cmd_with_md((struct nvme_bdev *)bdev_io->bdev->ctxt, nvme_ch->qpair,
+						   bdev_io->driver_ctx, bdev_io->u.contig.buf,
+						   bdev_io->u.contig.md_buf, BDEV_DISK_WRITE,
+						   bdev_io->u.contig.num_blocks, bdev_io->u.contig.offset_blocks);
+	case SPDK_BDEV_IO_TYPE_READV_NVME:
+		SPDK_DEBUGLOG(bdev_nvme, "readv %lu lbas with offset %#lx\n", bdev_io->u.bdev.num_blocks,
+			      bdev_io->u.bdev.offset_blocks);
+		return bdev_nvme_queue_cmd_v_with_md((struct nvme_bdev *)bdev_io->bdev->ctxt, nvme_ch->qpair,
+						     bdev_io->driver_ctx, BDEV_DISK_READ,
+						     bdev_io->u.bdev.iovs, bdev_io->u.bdev.iovcnt,
+						     bdev_io->u.bdev.num_blocks, bdev_io->u.bdev.offset_blocks);
+	case SPDK_BDEV_IO_TYPE_WRITEV_NVME:
+		SPDK_DEBUGLOG(bdev_nvme, "writev %lu lbas with offset %#lx\n", bdev_io->u.bdev.num_blocks,
+			      bdev_io->u.bdev.offset_blocks);
+		return bdev_nvme_queue_cmd_v_with_md((struct nvme_bdev *)bdev_io->bdev->ctxt, nvme_ch->qpair,
+						     bdev_io->driver_ctx, BDEV_DISK_WRITE,
+						     bdev_io->u.bdev.iovs, bdev_io->u.bdev.iovcnt,
+						     bdev_io->u.bdev.num_blocks, bdev_io->u.bdev.offset_blocks);
+	case SPDK_BDEV_IO_TYPE_UNMAP_BLOCKS:
+		return bdev_nvme_unmap_blocks((struct nvme_bdev *)bdev_io->bdev->ctxt,
+					      ch,
+					      (void *)bdev_io->driver_ctx,
+					      (struct spdk_nvme_dsm_range *)bdev_io->u.contig.buf,
+					      bdev_io->u.contig.num_blocks);
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+int
+bdev_nvme_dump_info_json_self(void *ctx, struct spdk_json_write_ctx *w)
+{
+	return 0;
+}
+
+uint16_t
+bdev_nvme_get_io_channel_id(struct spdk_io_channel *ch)
+{
+	struct nvme_io_channel *nvme_ch = spdk_io_channel_get_ctx(ch);
+	uint16_t channel_id;
+	struct spdk_nvme_qpair	*qpair = nvme_ch->qpair;
+	channel_id = spdk_nvme_get_qpair_id(qpair);
+	return channel_id;
+}
+
+uint64_t
+bdev_nvme_get_timeout_count(struct spdk_io_channel *ch)
+{
+	struct nvme_io_channel *nvme_ch = spdk_io_channel_get_ctx(ch);
+	return nvme_ch->group->num_poll_timeout;
+}
+
+int32_t
+nvme_ctrlr_get_info(const char *ctrlName, struct nvme_ctrlr_info **ppCtrlr)
+{
+	uint32_t num_ctrlr = 0, i = 0;
+	struct nvme_bdev_ctrlr *nvme_bdev_ctrlr = NULL;
+	struct nvme_ctrlr_info *pCtrlrInfo = NULL;
+	const struct spdk_nvme_ctrlr_data *cdata = NULL;
+	struct spdk_nvme_ctrlr_opts *opts = NULL;
+
+	struct spdk_pci_device *pci_dev = NULL;
+	int rc;
+
+	TAILQ_FOREACH(nvme_bdev_ctrlr, &g_nvme_bdev_ctrlrs, tailq) {
+		num_ctrlr++;
+	}
+	if (num_ctrlr == 0) {
+		SPDK_NOTICELOG("No any nvme controller.\n");
+		return 0;
+	}
+	if (ctrlName != NULL) {
+		num_ctrlr = 1;
+	}
+	pCtrlrInfo = calloc(num_ctrlr, sizeof(struct nvme_ctrlr_info));
+	if (pCtrlrInfo == NULL) {
+		SPDK_ERRLOG("Failed to alloc memory for getting controller infomation.\n");
+		return -1;
+	}
+	TAILQ_FOREACH(nvme_bdev_ctrlr, &g_nvme_bdev_ctrlrs, tailq) {
+		if (i >= num_ctrlr) { /* prevent having controllers be added or deleted */
+			i++;
+			continue;
+		}
+		if (ctrlName != NULL) {
+			if (strcmp(nvme_bdev_ctrlr->name, ctrlName) != 0) {
+				continue;
+			}
+		}
+		cdata = spdk_nvme_ctrlr_get_data(nvme_bdev_ctrlr->ctrlr);
+		opts = spdk_nvme_ctrlr_get_opts(nvme_bdev_ctrlr->ctrlr);
+		pci_dev = spdk_nvme_ctrlr_get_pci_device(nvme_bdev_ctrlr->ctrlr);
+		if (pci_dev == NULL) {
+			SPDK_ERRLOG("Failed to get pci device\n");
+			break;
+		}
+		rc = strcpy_s(pCtrlrInfo[i].ctrlName, sizeof(pCtrlrInfo[i].ctrlName), nvme_bdev_ctrlr->name);
+		if (rc != 0) {
+			SPDK_ERRLOG("String copy failed\n");
+		}
+		rc = strcpy_s(pCtrlrInfo[i].pciAddr, sizeof(pCtrlrInfo[i].pciAddr),
+			      nvme_bdev_ctrlr->connected_trid->traddr);
+		if (rc != 0) {
+			SPDK_ERRLOG("String copy failed\n");
+		}
+
+		rc = memcpy_s(pCtrlrInfo[i].sn, sizeof(pCtrlrInfo[i].sn), cdata->sn, 20);
+		if (rc != 0) {
+			SPDK_ERRLOG("Memory copy failed\n");
+		}
+
+		rc = memcpy_s(pCtrlrInfo[i].fr, sizeof(pCtrlrInfo[i].fr), cdata->fr, 8);
+		if (rc != 0) {
+			SPDK_ERRLOG("Memory copy failed\n");
+		}
+
+		rc = memcpy_s(pCtrlrInfo[i].mn, sizeof(pCtrlrInfo[i].mn), cdata->mn, 40);
+		if (rc != 0) {
+			SPDK_ERRLOG("Memory copy failed\n");
+		}
+
+		pCtrlrInfo[i].trtype = (uint16_t)nvme_bdev_ctrlr->connected_trid->trtype;
+		pCtrlrInfo[i].tnvmcap = cdata->tnvmcap[0];
+		pCtrlrInfo[i].unvmcap = cdata->unvmcap[0];
+		pCtrlrInfo[i].support_ns = cdata->oacs.ns_manage;
+		pCtrlrInfo[i].directives = cdata->oacs.directives;
+		pCtrlrInfo[i].dsm = cdata->oncs.dsm;
+		pCtrlrInfo[i].max_num_ns = cdata->nn;
+		pCtrlrInfo[i].num_io_queues = opts->num_io_queues;
+		pCtrlrInfo[i].io_queue_size = opts->io_queue_size;
+		pCtrlrInfo[i].device_id = spdk_pci_device_get_device_id(pci_dev);
+		pCtrlrInfo[i].subdevice_id = spdk_pci_device_get_subdevice_id(pci_dev);
+		pCtrlrInfo[i].vid = cdata->vid;
+		pCtrlrInfo[i].ssvid = cdata->ssvid;
+		pCtrlrInfo[i].ctrlid = cdata->cntlid;
+		pCtrlrInfo[i].version = spdk_nvme_ctrlr_get_regs_vs(nvme_bdev_ctrlr->ctrlr).raw;
+		i++;
+		if (ctrlName != NULL) {
+			break;
+		}
+	}
+	if (i != num_ctrlr) {
+		SPDK_ERRLOG("It has controller been added or deleted when fetched infomation, please try again later.\n");
+		free(pCtrlrInfo);
+		return -1;
+	}
+	*ppCtrlr = pCtrlrInfo;
+	return num_ctrlr;
+}
+
+struct nvme_bdev_ctrlr *
+nvme_ctrlr_get_by_name(const char *name)
+{
+	struct nvme_bdev_ctrlr *nvme_bdev_ctrlr = NULL;
+
+	if (name == NULL) {
+		return NULL;
+	}
+
+	TAILQ_FOREACH(nvme_bdev_ctrlr, &g_nvme_bdev_ctrlrs, tailq) {
+		if (strcmp(name, nvme_bdev_ctrlr->name) == 0) {
+			return nvme_bdev_ctrlr;
+		}
+	}
+
+	return NULL;
+}
+
+struct spdk_nvme_ctrlr *
+spdk_nvme_ctrlr_get_by_name(const char *ctrlname)
+{
+	struct nvme_bdev_ctrlr	*nvme_bdev_ctrlr = NULL;
+
+	TAILQ_FOREACH(nvme_bdev_ctrlr, &g_nvme_bdev_ctrlrs, tailq) {
+		if (strcmp(nvme_bdev_ctrlr->name, ctrlname) == 0) {
+			return nvme_bdev_ctrlr->ctrlr;
+		}
+	}
+
+	return NULL;
+}
+
+struct spdk_nvme_ctrlr *
+spdk_nvme_ctrlr_get_by_ctrlr(const struct nvme_bdev_ctrlr *nvme_bdev_ctrlr)
+{
+	if (nvme_bdev_ctrlr == NULL) {
+		return NULL;
+	}
+	return nvme_bdev_ctrlr->ctrlr;
+}
+
+void
+nvme_ctrlr_clear_iostat_by_name(const char *ctrlname)
+{
+	int i;
+	size_t size = strnlen(ctrlname, 24);
+
+	for (i = 0; i < STAT_MAX_NUM; i++) {
+		if (strncmp(g_io_stat_map[i].bdev_name, ctrlname, size) == 0) {
+			if ((g_io_stat_map[i].bdev_name[size] == 'n') && isdigit(g_io_stat_map[i].bdev_name[size + 1])) {
+				g_io_stat_map[i].channel_id = 0;
+				memset(g_io_stat_map[i].bdev_name, 0, sizeof(g_io_stat_map[i].bdev_name));
+				g_io_stat_map[i].num_read_ops = 0;
+				g_io_stat_map[i].num_write_ops = 0;
+				g_io_stat_map[i].bytes_read = 0;
+				g_io_stat_map[i].bytes_written = 0;
+				g_io_stat_map[i].io_outstanding = 0;
+				g_io_stat_map[i].read_latency_ticks = 0;
+				g_io_stat_map[i].write_latency_ticks = 0;
+				g_io_stat_map[i].io_ticks = 0;
+				/* used flag set false in last avoid race in channel create */
+				g_io_stat_map[i].used = false;
+			}
+		}
+	}
+}
+
+void
+nvme_ctrlr_clear_iostat_all(void)
+{
+	struct nvme_bdev_ctrlr	*nvme_bdev_ctrlr = NULL;
+
+	TAILQ_FOREACH(nvme_bdev_ctrlr, &g_nvme_bdev_ctrlrs, tailq) {
+		nvme_ctrlr_clear_iostat_by_name(nvme_bdev_ctrlr->name);
+	}
+}
+
+struct spdk_nvme_ns *
+bdev_nvme_get_ns(struct nvme_bdev *nbdev)
+{
+	return nbdev->nvme_ns->ns;
+}
+
+void bdev_nvme_update_block_by_nvme_ctrlr(struct spdk_nvme_ctrlr *ctrlr)
+{
+	uint32_t i;
+	struct nvme_bdev_ctrlr *nvme_bdev_ctrlr = NULL;
+	struct nvme_bdev_ns	*ns = NULL;
+	struct nvme_bdev	*nvme_bdev = NULL, *tmp = NULL;
+
+
+	pthread_mutex_lock(&g_bdev_nvme_mutex);
+	TAILQ_FOREACH(nvme_bdev_ctrlr, &g_nvme_bdev_ctrlrs, tailq) {
+		if (nvme_bdev_ctrlr->ctrlr != ctrlr) {
+			continue;
+		}
+
+		pthread_mutex_unlock(&g_bdev_nvme_mutex);
+		for (i = 0; i < nvme_bdev_ctrlr->num_ns; i++) {
+			ns = nvme_bdev_ctrlr->namespaces[i];
+			TAILQ_FOREACH_SAFE(nvme_bdev, &ns->bdevs, tailq, tmp) {
+				nvme_bdev->disk.blocklen = spdk_nvme_ns_get_sector_size(nvme_bdev->nvme_ns->ns);
+				nvme_bdev->disk.blockcnt = spdk_nvme_ns_get_num_sectors(nvme_bdev->nvme_ns->ns);
+			}
+		}
+		return;
+	}
+	pthread_mutex_unlock(&g_bdev_nvme_mutex);
+}
+
+int
+bdev_nvme_update_ns(struct nvme_bdev_ctrlr *nvme_bdev_ctrlr, uint32_t nsid)
+{
+	struct spdk_nvme_ctrlr	*ctrlr = nvme_bdev_ctrlr->ctrlr;
+	struct nvme_bdev_ns	*ns = NULL;
+
+	if (nvme_bdev_ctrlr == NULL || nsid > nvme_bdev_ctrlr->num_ns) {
+		SPDK_ERRLOG("Parameter error. nsid[%u], the max nsid is[%u]\n", nsid, nvme_bdev_ctrlr->num_ns);
+		return -1;
+	}
+
+	ns = nvme_bdev_ctrlr->namespaces[nsid - 1];
+
+	if (spdk_nvme_ctrlr_is_ocssd_supported(ctrlr)) {
+		ns->type = NVME_BDEV_NS_OCSSD;
+	} else {
+		ns->type = NVME_BDEV_NS_STANDARD;
+	}
+
+	if (!ns->populated && spdk_nvme_ctrlr_is_active_ns(nvme_bdev_ctrlr->ctrlr, nsid)) {
+		SPDK_NOTICELOG("NSID %u to be added\n", nsid);
+		ns->id = nsid;
+		ns->ctrlr = nvme_bdev_ctrlr;
+		TAILQ_INIT(&ns->bdevs);
+		/* add a new bdev device in this ns */
+		nvme_ctrlr_populate_namespace(nvme_bdev_ctrlr, ns, NULL);
+		return 0;
+	}
+
+	if (ns->populated && !spdk_nvme_ctrlr_is_active_ns(nvme_bdev_ctrlr->ctrlr, nsid)) {
+		SPDK_NOTICELOG("NSID %u is removed\n", nsid);
+		nvme_ctrlr_depopulate_namespace(nvme_bdev_ctrlr, ns);
+		return 0;
+	}
+	return -1;
+}
+
+bool
+spdk_bdev_can_remove(struct nvme_bdev_ctrlr *nvme_bdev_ctrlr, uint32_t nsid)
+{
+	struct nvme_bdev_ns *ns = NULL;
+	struct nvme_bdev *bdev = NULL, *tmp = NULL;
+	bool empty = false;
+
+	ns = nvme_bdev_ctrlr->namespaces[nsid - 1];
+	if (ns == NULL) {
+		return true;
+	}
+
+	TAILQ_FOREACH_SAFE(bdev, &ns->bdevs, tailq, tmp) {
+		pthread_mutex_lock(&bdev->disk.internal.mutex);
+		empty = TAILQ_EMPTY(&bdev->disk.internal.open_descs);
+		/* for each bdev in ns, we need to check if any descs is in tailq */
+		if (empty) {
+			/* one bdev is empty, check next until all bdev is checked */
+			bdev->disk.internal.ns_status = SPDK_BDEV_NS_STATUS_REMOVING;
+			pthread_mutex_unlock(&bdev->disk.internal.mutex);
+		} else {
+			/* means at least one bdev is used, so we just quit this process
+			  and mark the status is false. */
+			pthread_mutex_unlock(&bdev->disk.internal.mutex);
+			break;
+		}
+	}
+	return empty;
+}
+
+void
+spdk_bdev_set_ns_normal(struct nvme_bdev_ctrlr *nvme_bdev_ctrlr, uint32_t nsid)
+{
+	struct nvme_bdev_ns *ns = NULL;
+	struct nvme_bdev *bdev = NULL, *tmp = NULL;
+
+	ns = nvme_bdev_ctrlr->namespaces[nsid - 1];
+	if (ns == NULL) {
+		return;
+	}
+
+	TAILQ_FOREACH_SAFE(bdev, &ns->bdevs, tailq, tmp) {
+		pthread_mutex_lock(&bdev->disk.internal.mutex);
+		/* set the ns_status to ready case ns delete fail */
+		if (bdev->disk.internal.ns_status == SPDK_BDEV_NS_STATUS_REMOVING) {
+			bdev->disk.internal.ns_status = SPDK_BDEV_NS_STATUS_READY;
+		}
+		pthread_mutex_unlock(&bdev->disk.internal.mutex);
+	}
+}
+
+int
+bdev_nvme_queue_cmd_with_md(struct nvme_bdev *bdev, struct spdk_nvme_qpair *qpair, void *driver_ctx,
+			    void *buffer, void *metadata, int direction, uint64_t lba_count, uint64_t lba)
+{
+	int rc;
+	uint32_t io_flags = 0;
+	uint8_t *bdev_io_action = (uint8_t *)driver_ctx;
+	/* filter bit 0&1 of io->pi_action to get pi_action */
+	uint8_t pi_action = bdev_io_action[SPDK_BDEV_IO_ACTION_PI] & 0x03;
+	uint8_t dif_flag = bdev_io_action[SPDK_BDEV_IO_ACTION_PI];
+	uint8_t fua = bdev_io_action[SPDK_BDEV_IO_ACTION_FUA];
+	uint32_t pi_type;
+
+	spdk_bdev_set_io_location(driver_ctx, (uint8_t)LOCAL_LIBSTORAGE_BDEV_NVME);
+
+	if (pi_action > IO_NO_PROTECTION) {
+		pi_type = spdk_nvme_ns_get_pi_type(bdev->nvme_ns->ns);
+		if (dif_flag & FLAG_PRCHK) {
+			io_flags |= SPDK_NVME_IO_FLAGS_PRCHK_GUARD;
+		}
+		/* type3 not support ref tag */
+		if (!(dif_flag & FLAG_NO_REF) && (pi_type != SPDK_NVME_FMT_NVM_PROTECTION_TYPE3)) {
+			io_flags |= SPDK_NVME_IO_FLAGS_PRCHK_REFTAG;
+		}
+		if (pi_action == IO_HALF_WAY_PROTECTION) {
+			io_flags |= SPDK_NVME_IO_FLAGS_PRACT;
+		}
+	}
+
+	if (fua) {
+		io_flags |= SPDK_NVME_IO_FLAGS_FORCE_UNIT_ACCESS;
+	}
+
+	if (direction == BDEV_DISK_READ) {
+		rc = spdk_nvme_ns_cmd_read_with_md(bdev->nvme_ns->ns, qpair, buffer, metadata, lba,
+						   lba_count, bdev_nvme_queued_done, driver_ctx, io_flags, 0, 0);
+	} else {
+		rc = spdk_nvme_ns_cmd_write_with_md(bdev->nvme_ns->ns, qpair, buffer, metadata, lba,
+						    lba_count, bdev_nvme_queued_done, driver_ctx, io_flags, 0, 0);
+	}
+
+	if (rc != 0) {
+		if (rc == -ENOMEM) {
+			SPDK_NOTICELOG("%s failed: rc = %d\n", direction == BDEV_DISK_READ ? "read" : "write", rc);
+		} else {
+			SPDK_ERRLOG("%s failed: rc = %d, qpair is %p\n", direction == BDEV_DISK_READ ? "read" : "write",
+				    rc, qpair);
+		}
+	}
+	return rc;
+}
+
+int
+bdev_nvme_queue_cmd_v_with_md(struct nvme_bdev *bdev, struct spdk_nvme_qpair *qpair,
+			      void *driver_ctx,
+			      int direction, struct iovec *iov, int iovcnt, uint64_t lba_count, uint64_t lba)
+{
+	int rc;
+	struct nvme_bdev_io *bio = NULL;
+	uint32_t io_flags = 0;
+	uint8_t *bdev_io_action = (uint8_t *)driver_ctx;
+	/* filter bit 0&1 of io->pi_action to get pi_action */
+	uint8_t pi_action = bdev_io_action[SPDK_BDEV_IO_ACTION_PI] & 0x03;
+	uint8_t dif_flag = bdev_io_action[SPDK_BDEV_IO_ACTION_PI];
+	uint8_t fua = bdev_io_action[SPDK_BDEV_IO_ACTION_FUA];
+	uint32_t pi_type;
+
+	spdk_bdev_set_io_location(driver_ctx, (uint8_t)LOCAL_LIBSTORAGE_BDEV_NVME);
+
+	if (pi_action > IO_NO_PROTECTION) {
+		pi_type = spdk_nvme_ns_get_pi_type(bdev->nvme_ns->ns);
+		if (dif_flag & FLAG_PRCHK) {
+		    io_flags |= SPDK_NVME_IO_FLAGS_PRCHK_GUARD;
+		}
+		/* type3 not support ref tag */
+		if (!(dif_flag & FLAG_NO_REF) && (pi_type != SPDK_NVME_FMT_NVM_PROTECTION_TYPE3)) {
+		    io_flags |= SPDK_NVME_IO_FLAGS_PRCHK_REFTAG;
+		}
+		if (pi_action == IO_HALF_WAY_PROTECTION) {
+			io_flags |= SPDK_NVME_IO_FLAGS_PRACT;
+		}
+	}
+
+	if (fua) {
+		io_flags |= SPDK_NVME_IO_FLAGS_FORCE_UNIT_ACCESS;
+	}
+
+	bio = nvme_bdev_io_update_args((struct nvme_bdev_io *)driver_ctx, iov, iovcnt);
+
+	if (direction == BDEV_DISK_READ) {
+		rc = spdk_nvme_ns_cmd_readv(bdev->nvme_ns->ns, qpair, lba,
+					    lba_count, bdev_nvme_queued_done, bio, io_flags,
+					    bdev_nvme_queued_reset_sgl, bdev_nvme_queued_next_sge);
+	} else {
+		rc = spdk_nvme_ns_cmd_writev(bdev->nvme_ns->ns, qpair, lba, lba_count,
+						    0, bdev_nvme_queued_done, bio, io_flags,
+						    bdev_nvme_queued_reset_sgl, bdev_nvme_queued_next_sge);
+	}
+
+	if (rc != 0) {
+		if (rc == -ENOMEM) {
+			SPDK_NOTICELOG("%s failed: rc = %d\n", direction == BDEV_DISK_READ ? "readv" : "writev", rc);
+		} else {
+			SPDK_ERRLOG("%s failed: rc = %d, qpair is %p\n", direction == BDEV_DISK_READ ? "read" : "write", rc,
+				    qpair);
+		}
+	}
+	return rc;
+}
+
+struct nvme_bdev_ctrlr *
+bdev_nvme_get_ctrlr_by_bdev_desc(void *bdev_desc)
+{
+	struct spdk_bdev *bdev = spdk_bdev_desc_get_bdev(bdev_desc);
+	struct nvme_bdev *nbdev = (struct nvme_bdev *)bdev->ctxt;
+	if (nbdev == NULL) {
+		return NULL;
+	}
+	return nbdev->nvme_ns->ctrlr;
+}
+
+int
+bdev_nvme_unmap_blocks(struct nvme_bdev *nbdev, struct spdk_io_channel *ch, void *driver_ctx,
+		       struct spdk_nvme_dsm_range *unmap_d, uint16_t unmap_count)
+{
+	struct nvme_io_channel *nvme_ch = spdk_io_channel_get_ctx(ch);
+	int i;
+
+	if (unmap_count == 0 || unmap_count > SPDK_NVME_DATASET_MANAGEMENT_MAX_RANGES) {
+		SPDK_ERRLOG("Invalid parameter, unmap count: %u\n", unmap_count);
+		return -EINVAL;
+	}
+
+	if (unmap_d == NULL) {
+		return -EINVAL;
+	}
+
+	for (i = 0; i < unmap_count; i++) {
+		if (unmap_d[i].length > SPDK_NVME_DATASET_MANAGEMENT_RANGE_MAX_BLOCKS) {
+			SPDK_ERRLOG("Invalid parameter, unmap block count: %u\n", unmap_d[i].length);
+			return -EINVAL;
+		}
+		unmap_d[i].attributes.raw = 0;
+	}
+
+	spdk_bdev_set_io_location(driver_ctx, (uint8_t)LOCAL_LIBSTORAGE_BDEV_NVME);
+	return spdk_nvme_ns_cmd_unmap_blocks(nbdev->nvme_ns->ns, nvme_ch->qpair,
+					     SPDK_NVME_DSM_ATTR_DEALLOCATE,
+					     unmap_d, unmap_count,
+					     bdev_nvme_queued_done, driver_ctx);
+}
+
+void
+spdk_bdev_nvme_remove_cb(void *cb_ctx, void *ctrlr)
+{
+	remove_cb(cb_ctx, (struct spdk_nvme_ctrlr *)ctrlr);
+}
+
+void spdk_bdev_fail_ctrlr(const char *traddr)
+{
+	struct nvme_bdev_ctrlr *nvme_bdev_ctrlr;
+
+	pthread_mutex_lock(&g_bdev_nvme_mutex);
+	TAILQ_FOREACH(nvme_bdev_ctrlr, &g_nvme_bdev_ctrlrs, tailq) {
+		if (strcmp(nvme_bdev_ctrlr->connected_trid->traddr, traddr) == 0) {
+			spdk_nvme_ctrlr_fail(nvme_bdev_ctrlr->ctrlr);
+			remove_cb(NULL, nvme_bdev_ctrlr->ctrlr);
+			return;
+		}
+	}
+}
+
+int
+spdk_bdev_nvme_create_self(struct spdk_nvme_transport_id *trid,
+			   const char *base_name,
+			   const char **names, size_t *count,
+			   const char *hostnqn)
+{
+	struct nvme_probe_ctx	*probe_ctx;
+	struct nvme_bdev_ctrlr	*nvme_bdev_ctrlr;
+	struct nvme_bdev_ns	*ns;
+	struct nvme_bdev	*nvme_bdev;
+	struct nvme_bdev	*tmp = NULL;
+	uint32_t		i, nsid;
+	size_t			j;
+
+	if (nvme_bdev_ctrlr_get(trid) != NULL) {
+		SPDK_ERRLOG("A controller with the trid (traddr: %s) already exists.\n", trid->traddr);
+		return -1;
+	}
+
+	probe_ctx = bdev_nvme_create_probe_ctx(trid, base_name, hostnqn);
+	if (probe_ctx == NULL) {
+		SPDK_ERRLOG("Failed to create probe_ctx\n");
+		return -1;
+	}
+
+	if (spdk_nvme_probe(trid, probe_ctx, probe_cb, attach_cb, NULL)) {
+		SPDK_ERRLOG("Failed to probe for new devices\n");
+		free(probe_ctx);
+		return -1;
+	}
+
+	nvme_bdev_ctrlr = nvme_bdev_ctrlr_get(trid);
+	if (!nvme_bdev_ctrlr) {
+		SPDK_ERRLOG("Failed to find new NVMe controller\n");
+		free(probe_ctx);
+		return -1;
+	}
+
+	/*
+	 * Report the new bdevs that were created in this call.
+	 * There can be more than one bdev per NVMe controller since one bdev is created per namespace.
+	 */
+	j = 0;
+	for (i = 0; i < nvme_bdev_ctrlr->num_ns; i++) {
+		nsid = i + 1;
+		ns = nvme_bdev_ctrlr->namespaces[nsid - 1];
+
+		if (!ns->populated) {
+			continue;
+		}
+		assert(ns->id == nsid);
+		TAILQ_FOREACH_SAFE(nvme_bdev, &ns->bdevs, tailq, tmp) {
+			if (j < *count) {
+				j++;
+				names[j] = nvme_bdev->disk.name;
+			} else {
+				SPDK_ERRLOG("Maximum number of namespaces is %zu.", *count);
+				free(probe_ctx);
+				return -1;
+			}
+		}
+	}
+
+	*count = j;
+
+	free(probe_ctx);
+	return 0;
+}
diff --git a/module/bdev/nvme/bdev_nvme_self.h b/module/bdev/nvme/bdev_nvme_self.h
new file mode 100644
index 0000000..d7cc587
--- /dev/null
+++ b/module/bdev/nvme/bdev_nvme_self.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2021. Huawei Technologies Co., Ltd. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+void
+bdev_update_ch_timeout(struct nvme_bdev_poll_group *group);
+
+int
+_bdev_nvme_submit_request_self(struct spdk_io_channel *ch, struct spdk_bdev_io *bdev_io);
+
+int
+bdev_nvme_dump_info_json_self(void *ctx, struct spdk_json_write_ctx *w);
+
+uint16_t
+bdev_nvme_get_io_channel_id(struct spdk_io_channel *ch);
+
+uint64_t
+bdev_nvme_get_timeout_count(struct spdk_io_channel *ch);
+
+int
+bdev_nvme_queue_cmd_with_md(struct nvme_bdev *bdev, struct spdk_nvme_qpair *qpair, void *driver_ctx,
+			    void *buffer, void *metadata, int direction, uint64_t lba_count, uint64_t lba);
+
+int
+bdev_nvme_queue_cmd_v_with_md(struct nvme_bdev *bdev, struct spdk_nvme_qpair *qpair,
+			      void *driver_ctx,
+			      int direction, struct iovec *iov, int iovcnt, uint64_t lba_count, uint64_t lba);
+
+struct nvme_bdev_ctrlr *
+bdev_nvme_get_ctrlr_by_bdev_desc(void *bdev_desc);
+
+int
+bdev_nvme_unmap_blocks(struct nvme_bdev *nbdev, struct spdk_io_channel *ch, void *driver_ctx,
+		       struct spdk_nvme_dsm_range *unmap_d, uint16_t unmap_count);
diff --git a/module/bdev/nvme/common.h b/module/bdev/nvme/common.h
index b7555d5..81b4009 100644
--- a/module/bdev/nvme/common.h
+++ b/module/bdev/nvme/common.h
@@ -127,6 +127,10 @@ struct nvme_bdev_poll_group {
 	uint64_t				spin_ticks;
 	uint64_t				start_ticks;
 	uint64_t				end_ticks;
+#ifdef	SPDK_CONFIG_APP_RW
+	uint64_t				save_start_ticks;
+	uint64_t				num_poll_timeout;
+#endif
 };
 
 typedef void (*spdk_bdev_create_nvme_fn)(void *ctx, size_t bdev_count, int rc);
diff --git a/scripts/setup_self.sh b/scripts/setup_self.sh
new file mode 100755
index 0000000..9e77c29
--- /dev/null
+++ b/scripts/setup_self.sh
@@ -0,0 +1,347 @@
+#!/usr/bin/env bash
+
+set -e
+
+rootdir=$(readlink -f $(dirname $0))/..
+
+function linux_iter_pci {
+	# Argument is the class code
+	# TODO: More specifically match against only class codes in the grep
+	# step.
+	lspci -mm -n -D | grep $1 | tr -d '"' | awk -F " " '{print $1}'
+}
+
+function linux_bind_driver() {
+	bdf="$1"
+	driver_name="$2"
+	old_driver_name="no driver"
+	ven_dev_id=$(lspci -n -s $bdf | cut -d' ' -f3 | sed 's/:/ /')
+
+	if [ -e "/sys/bus/pci/devices/$bdf/driver" ]; then
+		old_driver_name=$(basename $(readlink /sys/bus/pci/devices/$bdf/driver))
+
+		if [ "$driver_name" = "$old_driver_name" ]; then
+			return 0
+		fi
+
+		echo "$ven_dev_id" > "/sys/bus/pci/devices/$bdf/driver/remove_id" 2> /dev/null || true
+		echo "$bdf" > "/sys/bus/pci/devices/$bdf/driver/unbind"
+	fi
+
+	echo "$bdf ($ven_dev_id): $old_driver_name -> $driver_name"
+
+	echo "$ven_dev_id" > "/sys/bus/pci/drivers/$driver_name/new_id" 2> /dev/null || true
+	echo "$bdf" > "/sys/bus/pci/drivers/$driver_name/bind" 2> /dev/null || true
+}
+
+function linux_hugetlbfs_mount() {
+	mount | grep ' type hugetlbfs ' | awk '{ print $3 }'
+}
+
+function is_device_in_except_device_list() {
+	exists_flag=0
+	if [ $# -gt 1 ]; then
+		except_dev_list=$2
+	fi
+
+	for dev in ${except_dev_list[@]}
+	do
+		if [ "$dev" == "$1" ]; then
+			exists_flag=1
+		fi
+	done
+	echo ${exists_flag}
+}
+
+function config_linux_device {
+	if [ $# -gt 0 ]; then
+		configlist=$*
+		echo configure devices $configlist
+	else
+		echo "need to specify at least one device to bind uio driver."
+		exit 1
+	fi
+	driver_name=uio_pci_generic
+
+	# NVMe
+	modprobe $driver_name || true
+	for bdf in ${configlist[@]}; do
+		existflag=0
+		for confbdf in $(linux_iter_pci 0108); do
+			if [ "$bdf" == "$confbdf" ]; then
+				linux_bind_driver "$bdf" "$driver_name"
+				existflag=1
+				break
+			fi
+		done
+		if [ $existflag -eq 0 ]; then
+			echo "nvme device \"$bdf\" is not in present"
+		fi
+	done
+	config_linux_hugepage
+}
+
+function configure_linux {
+	if [ $# -gt 0 ]; then
+		exceptdevlist=$*
+		echo configure devices except $exceptdevlist
+	fi
+	# Use uio, Not IOMMU.
+	driver_name=uio_pci_generic
+
+	# NVMe
+	modprobe $driver_name || true
+	for bdf in $(linux_iter_pci 0108); do
+		need_configure=`is_device_in_except_device_list ${bdf} "${exceptdevlist}"`
+		if [ $need_configure -ne 0 ]; then
+			continue
+		fi
+		linux_bind_driver "$bdf" "$driver_name"
+	done
+
+	echo "1" > "/sys/bus/pci/rescan"
+
+	config_linux_hugepage
+}
+
+function config_linux_hugepage {
+	hugetlbfs_mount=$(linux_hugetlbfs_mount)
+
+	if [ -z "$hugetlbfs_mount" ]; then
+		hugetlbfs_mount=/mnt/huge
+		echo "Mounting hugetlbfs at $hugetlbfs_mount"
+		mkdir -p "$hugetlbfs_mount"
+		mount -t hugetlbfs nodev "$hugetlbfs_mount"
+	fi
+	echo "$NRHUGE" > /proc/sys/vm/nr_hugepages
+}
+
+function reset_linux {
+	# NVMe
+	modprobe nvme || true
+	for bdf in $(linux_iter_pci 0108); do
+		linux_bind_driver "$bdf" nvme
+	done
+
+	echo "1" > "/sys/bus/pci/rescan"
+
+	hugetlbfs_mount=$(linux_hugetlbfs_mount)
+	rm -f "$hugetlbfs_mount"/spdk*map_*
+}
+
+function status_linux {
+	echo "NVMe devices"
+
+	echo -e "BDF\t\tNuma Node\tDriver name\t\tDevice name"
+	for bdf in $(linux_iter_pci 0108); do
+		driver=`grep DRIVER /sys/bus/pci/devices/$bdf/uevent |awk -F"=" '{print $2}'`
+		node=`cat /sys/bus/pci/devices/$bdf/numa_node`;
+		if [ "$driver" = "nvme" ]; then
+			if [ -d "/sys/bus/pci/devices/$bdf/nvme" ]; then
+				name="\t"`ls /sys/bus/pci/devices/$bdf/nvme`;
+			else
+				name="\t"`ls /sys/bus/pci/devices/$bdf/misc`;
+			fi
+		else
+			name="-";
+		fi
+		echo -e "$bdf\t$node\t\t$driver\t\t$name";
+	done
+}
+
+function reset_device_linux {
+	#NVMe
+	if [ $# -gt 0 ]; then
+		resetdevlist=$*
+		echo reset nvme devices $resetdevlist
+	else
+		echo no devices to reset
+		return
+	fi
+
+	for bdf in ${resetdevlist[@]}; do
+		exist=0
+		for existbdf in $(linux_iter_pci 0108); do
+			if [[ "$existbdf" == "$bdf" ]]; then
+				exist=1
+			fi
+		done
+
+		if [ $exist -eq 0 ]; then
+			echo nvme device \"$bdf\" is not in present
+			continue
+		fi
+
+		linux_bind_driver "$bdf" nvme
+	done
+}
+
+function reset_all_linux {
+	# NVMe
+	echo "1" > "/sys/bus/pci/rescan"
+	reset_device_linux $(linux_iter_pci 0108)
+
+	hugetlbfs_mount=$(linux_hugetlbfs_mount)
+	rm -f "$hugetlbfs_mount"/spdk*map_*
+}
+
+function help_linux {
+	# NVMe
+	echo ""
+	echo "setup.sh"
+	echo "setup.sh config"
+	echo "setup.sh status"
+	echo "setup.sh reset"
+	echo "setup.sh hugepage"
+	echo "setup.sh config except_device=\"pci_addr\""
+	echo "setup.sh config except_device=\"pci_addr1,pci_addr2,pci_addr3,...\""
+	echo "setup.sh config_device \"pci_addr\""
+	echo "setup.sh config_device \"pci_addr1,pci_addr2,pci_addr3,...\""
+	echo "setup.sh reset_device \"pci_addr\""
+	echo "setup.sh reset_device \"pci_addr1,pci_addr2,pci_addr3,...\""
+	echo "setup.sh reset_all"
+	echo ""
+}
+
+function configure_freebsd {
+	TMP=`mktemp`
+
+	# NVMe
+	GREP_STR="class=0x010802"
+
+	AWK_PROG="{if (count > 0) printf \",\"; printf \"%s:%s:%s\",\$2,\$3,\$4; count++}"
+	echo $AWK_PROG > $TMP
+
+	BDFS=`pciconf -l | grep "${GREP_STR}" | awk -F: -f $TMP`
+
+	kldunload nic_uio.ko || true
+	kenv hw.nic_uio.bdfs=$BDFS
+	kldload nic_uio.ko
+	rm $TMP
+
+	kldunload contigmem.ko || true
+	kenv hw.contigmem.num_buffers=$((NRHUGE * 2 / 256))
+	kenv hw.contigmem.buffer_size=$((256 * 1024 * 1024))
+	kldload contigmem.ko
+}
+
+function reset_freebsd {
+	kldunload contigmem.ko || true
+	kldunload nic_uio.ko || true
+}
+
+function get_slot_id {
+	pciaddr=$1
+
+	return_msg=`lspci -vvv -xxx -s "$pciaddr"  | grep -i "Slot:"`
+	slot_id=${return_msg##* }
+
+	echo $slot_id
+}
+
+function get_except_device_linux {
+	param=$1
+	if [[ $param == except_device=* ]]; then
+		devstr=${param#*=}
+		OLD_IFS="$IFS"
+		IFS=","
+		expdev=($devstr)
+		IFS=$OLD_IFS
+	fi
+	if [ ${#expdev[@]} -ne 0 ]; then
+		echo ${expdev[@]}
+	fi
+}
+
+function get_device_linux {
+	devstr=$1
+	OLD_IFS="$IFS"
+	IFS=","
+	resetdev=($devstr)
+	IFS=$OLD_IFS
+
+	if [ ${#resetdev[@]} -ne 0 ]; then
+		echo ${resetdev[@]}
+	fi
+}
+
+: ${NRHUGE:=1024}
+
+username=$1
+mode=$2
+
+if [ "$username" = "reset" -o "$username" = "config" -o "$username" = "status" ]; then
+	mode="$username"
+	username=""
+fi
+
+if [ "$username" = "reset_device" -o "$username" = "reset_all" -o "$username" = "help" ]; then
+	mode="$username"
+	username=""
+fi
+
+if [ "$username" = "config_device" -o "$username" = "hugepage" ]; then
+	mode="$username"
+	username=""
+fi
+
+if [ "$mode" == "" ]; then
+	mode="config"
+fi
+
+if [ "$username" = "" ]; then
+	username="$SUDO_USER"
+	if [ "$username" = "" ]; then
+		username=`logname 2>/dev/null` || true
+	fi
+fi
+
+if [ "$mode" == "config" ]; then
+	paramcnt=$#
+	if [ $paramcnt -eq 2 ]; then
+		paramstr=$2
+		exceptdev=`get_except_device_linux $paramstr`
+	fi
+fi
+
+if [ "$mode" == "reset_device" ]; then
+	paramcnt=$#
+	if [ $paramcnt -eq 2 ]; then
+		paramstr=$2
+		resetdev=`get_device_linux $paramstr`
+	fi
+fi
+
+if [ "$mode" == "config_device" ]; then
+	paramcnt=$#
+	if [ $paramcnt -eq 2 ]; then
+		paramstr=$2
+		configdev=`get_device_linux $paramstr`
+	fi
+fi
+
+if [ `uname` = Linux ]; then
+	if [ "$mode" == "config" ]; then
+       configure_linux $exceptdev
+	elif [ "$mode" == "reset" ]; then
+		reset_linux
+	elif [ "$mode" == "status" ]; then
+		status_linux
+	elif [ "$mode" == "reset_device" ]; then
+		reset_device_linux $resetdev
+	elif [ "$mode" == "reset_all" ]; then
+		reset_all_linux
+	elif [ "$mode" == "help" ]; then
+		help_linux
+	elif [ "$mode" == "config_device" ]; then
+		config_linux_device $configdev
+	elif [ "$mode" == "hugepage" ]; then
+		config_linux_hugepage
+	fi
+else
+	if [ "$mode" == "config" ]; then
+		configure_freebsd
+	elif [ "$mode" == "reset" ]; then
+		reset_freebsd
+	fi
+fi
-- 
2.33.0

