Compare commits
75 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
752ceb0c12 | ||
|
ca470ff844 | ||
|
10807f35f9 | ||
|
64b08f6ea3 | ||
|
e2025217a7 | ||
|
a1570edd1d | ||
|
cb37376ef6 | ||
|
6b740d2c10 | ||
|
5cf63f81be | ||
|
f94c640130 | ||
|
1ea7b29ff8 | ||
|
18936662df | ||
|
1cf67849c8 | ||
|
49e5142b3c | ||
|
91c22965cc | ||
|
73c1c75f23 | ||
|
8aafde8856 | ||
|
199f14e3fd | ||
|
93b2a5420a | ||
|
731887d713 | ||
|
6f6fced5a5 | ||
|
a56704a0e1 | ||
|
a37bb7d4a5 | ||
|
dc09422cd3 | ||
|
9b420e8c00 | ||
|
d28343f894 | ||
|
b7a6b5f559 | ||
|
fce34b065d | ||
|
023a6b0b0d | ||
|
d56a2b7214 | ||
|
99379a07f1 | ||
|
14c153e67f | ||
|
27acba1262 | ||
|
b0cdf76635 | ||
|
beb7cd3829 | ||
|
4d419d55b8 | ||
|
512fcf8e58 | ||
|
2879f9d2da | ||
|
06bceceac8 | ||
|
edf5f3de0e | ||
|
f04c67984b | ||
|
c8607790cb | ||
|
08b6e24735 | ||
|
a5b0bc0cd8 | ||
|
0896f85b59 | ||
|
94abf2d8c2 | ||
|
f4c28506d5 | ||
|
53603323e5 | ||
|
64f1ea8ea4 | ||
|
1f0dd58a43 | ||
|
049e9290cf | ||
|
82c0c8ca03 | ||
|
1cf1b00b1b | ||
|
1d839c1ac3 | ||
|
9bdc45693a | ||
|
c7a0275edf | ||
|
a5a51c7b56 | ||
|
f2a113d144 | ||
|
b4ed65d26f | ||
|
26e589c3a4 | ||
|
14eeeea669 | ||
|
3f732d80d3 | ||
|
e02a868dd3 | ||
|
d02ff4c422 | ||
|
4b87c7259a | ||
|
02f9f45524 | ||
|
84c4530e9b | ||
|
0811dda8bf | ||
|
9b37f63fe9 | ||
|
edb5cd988a | ||
|
68eb748759 | ||
|
bbe2ed83dc | ||
|
d53d7a5723 | ||
|
2a1fe02d98 | ||
|
081cd04ea2 |
5
.gitignore
vendored
5
.gitignore
vendored
@ -2,17 +2,12 @@
|
||||
*.a
|
||||
*.cmd
|
||||
*.d
|
||||
*.dll
|
||||
*.exe
|
||||
*.gcda
|
||||
*.gcno
|
||||
*.kdev4
|
||||
*.ko
|
||||
*.lib
|
||||
*.log
|
||||
*.o
|
||||
*.obj
|
||||
*.pdb
|
||||
*.pyc
|
||||
*.so
|
||||
*.so.*
|
||||
|
2
.gitmodules
vendored
2
.gitmodules
vendored
@ -1,6 +1,6 @@
|
||||
[submodule "dpdk"]
|
||||
path = dpdk
|
||||
url = https://git.quacker.org/d/numam-dpdk.git
|
||||
url = https://github.com/spdk/dpdk.git
|
||||
[submodule "intel-ipsec-mb"]
|
||||
path = intel-ipsec-mb
|
||||
url = https://github.com/spdk/intel-ipsec-mb.git
|
||||
|
200
CHANGELOG.md
200
CHANGELOG.md
@ -1,66 +1,14 @@
|
||||
# Changelog
|
||||
|
||||
## v21.04.1: (Upcoming Release)
|
||||
## v21.01.2: (Upcoming Release)
|
||||
|
||||
## v21.04: ZNS NVMe bdev, PMR, ADQ initiator, RPM
|
||||
## v21.01.1:
|
||||
|
||||
### accel
|
||||
### dpdk
|
||||
|
||||
Two new accelerated crc32 functions `spdk_accel_submit_crc32cv` and
|
||||
`spdk_accel_batch_prep_crc32cv` are added in order to provide the
|
||||
chained accelerated CRC32 computation support.
|
||||
Added `rte_ethdev` and `rte_net` dependencies for all builds with DPDK.
|
||||
|
||||
### bdev
|
||||
|
||||
For `bdev_ocssd_create` RPC, the optional parameter `range` was removed.
|
||||
Only one OCSSD bdev can be created for one OCSSD namespace.
|
||||
|
||||
Removed the `spdk_bdev_open` from bdev library API.
|
||||
Removed the `spdk_vbdev_register` and `spdk_bdev_part_base_construct` from bdev module API.
|
||||
Removed the `config_text` function for bdev modules to report legacy config.
|
||||
|
||||
Added `spdk_bdev_get_max_active_zones` API to display maximum number active zones of a given bdev.
|
||||
|
||||
Added `spdk_bdev_get_max_zone_append_size` API to display maximum zone append data transfer size.
|
||||
|
||||
### bdev_nvme
|
||||
|
||||
Added support for zoned namespaces.
|
||||
|
||||
### blobstore
|
||||
|
||||
Removed the `spdk_bdev_create_bs_dev_from_desc` and `spdk_bdev_create_bs_dev` API.
|
||||
|
||||
### env
|
||||
|
||||
Added `spdk_pci_device_allow` API to allow applications to add PCI addresses to
|
||||
the allowed list after the application has started.
|
||||
|
||||
Removed the `pci_whitelist`, `pci_blacklist` and `master_core` members of struct `spdk_env_opts`.
|
||||
|
||||
Added hotplug support based on uevent in `pci_event.c`. Added usage of this functionality in
|
||||
nvme, virtio-scsi and virtio_blk libraries. Please see the new API `spdk_pci_event_listen`,
|
||||
`spdk_pci_get_event`, `spdk_pci_register_error_handler` and `spdk_pci_unregister_error_handler`.
|
||||
|
||||
### event
|
||||
|
||||
Removed the `config_file`, `max_delay_us`, `pci_whitelist`
|
||||
and `pci_blacklist` members of struct `spdk_app_opts`.
|
||||
|
||||
### idxd
|
||||
|
||||
A new API `spdk_idxd_device_needs_rebalance` was added so that users of the library
|
||||
can know whether they need to rebalance the flow control for the channel
|
||||
that was just added/removed. This is based on how the low level library
|
||||
shares devices amongst channels.
|
||||
|
||||
The API `spdk_idxd_reconfigure_chan` had the `num_channels` removed as this
|
||||
is now tracked in the library. The app makes use the new API above to
|
||||
determine whether to rebalance or not. This applies to `spdk_idxd_configure_chan`
|
||||
as well.
|
||||
|
||||
The API `spdk_idxd_put_channel` now returns the rebalance state for the
|
||||
underlying device.
|
||||
Fixed compatibility issues with DPDK 19.11.
|
||||
|
||||
### iscsi
|
||||
|
||||
@ -69,147 +17,23 @@ A TEXT PDU with no data, but CONTINUE flag set, would result in a NULL pointer d
|
||||
and crash the SPDK iSCSI target process. All users of the SPDK iSCSI target
|
||||
are recommended to update. All SPDK versions <= v21.01 are affected.
|
||||
|
||||
### net
|
||||
### nbd
|
||||
|
||||
The net library is deprecated and will be removed in the 21.07 release.
|
||||
Fixed kernel hang when bdev is removed by always setting NBD_SET_TIMEOUT.
|
||||
|
||||
### nvme
|
||||
|
||||
Added a new function `spdk_nvme_ctrlr_get_regs_pmrcap` to get the PMR capabilities.
|
||||
Fixed segfault when removing qpair when transport connection fails (issue #1777).
|
||||
|
||||
Added an accelerated table pointer in `spdk_nvme_poll_group`
|
||||
which can be used to provide the accelerated functions by users with
|
||||
hardware engine, such as crc32c accelerated function.
|
||||
### ocssd
|
||||
|
||||
Added new functions `spdk_nvme_ctrlr_get_pmrsz`, `spdk_nvme_ctrlr_enable_pmr`,
|
||||
`spdk_nvme_ctrlr_disable_pmr`, `spdk_nvme_ctrlr_map_pmr` and `spdk_nvme_ctrlr_unmap_pmr`.
|
||||
|
||||
Added NVMe transport operations to enable, disable, map and unmap the PMR.
|
||||
|
||||
Added `spdk_nvme_qpair_get_optimal_poll_group` function and `qpair_get_optimal_poll_group`
|
||||
function pointer in spdk_nvmf_transport_ops structure in order to add the qpair to the most
|
||||
suitable polling group.
|
||||
|
||||
Added OPTPERF and namespace optimal performance fields to nvme_spec.h.
|
||||
|
||||
Added `spdk_nvme_set_hotplug_filter` API to allow applications to choose which
|
||||
hot-inserted SSDs should be probed. This is useful for use cases where multiple
|
||||
independent SPDK processes are running on one node. The filter function can
|
||||
then be implemented in these processes to decide which SSDs to probe based on
|
||||
the new SSD's PCI address.
|
||||
|
||||
New functions `spdk_nvme_poll_group_get_stats` and `spdk_nvme_poll_group_free_stats`
|
||||
were added. These functions allow to get transport statistics per NVME poll group.
|
||||
|
||||
Added `spdk_nvme_map_cmd` API to map the NVMe command with SGL cases.
|
||||
|
||||
Added support for vector variant of ZNS zone append commands with new API
|
||||
`spdk_nvme_zns_zone_appendv` and `spdk_nvme_zns_zone_appendv_with_md`.
|
||||
|
||||
Added `spdk_nvme_zns_ns_get_max_open_zones` and `spdk_nvme_zns_ns_get_max_active_zones` API,
|
||||
to display maximum number of open and active zones of the given namespace.
|
||||
|
||||
Added `spdk_nvme_zns_ns_get_zone_size_sectors` API to provide size of zone in number of
|
||||
sectors.
|
||||
|
||||
Added `spdk_nvme_qpair_get_id` API to display the ID of the specified qpair.
|
||||
|
||||
### nvmf
|
||||
|
||||
Removed the `spdk_nvmf_tgt_listen` and `spdk_nvmf_subsystem_add_ns` API.
|
||||
|
||||
Added new APIs:
|
||||
|
||||
- `spdk_nvmf_poll_group_dump_stat` (function in `nvmf.h`).
|
||||
- `poll_group_dump_stat` (transport op in `nvmf_transport.h`).
|
||||
|
||||
The following APIs have been deprecated and will be removed in SPDK 21.07:
|
||||
|
||||
- `spdk_nvmf_poll_group_get_stat` (function in `nvmf.h`),
|
||||
- `spdk_nvmf_transport_poll_group_get_stat` (function in `nvmf.h`),
|
||||
- `spdk_nvmf_transport_poll_group_free_stat`(function in `nvmf.h`),
|
||||
- `spdk_nvmf_rdma_device_stat` (struct in `nvmf.h`),
|
||||
- `spdk_nvmf_transport_poll_group_stat` (struct in `nvmf.h`),
|
||||
- `poll_group_get_stat` (transport op in `nvmf_transport.h`),
|
||||
- `poll_group_free_stat` (transport op in `nvmf_transport.h`).
|
||||
|
||||
See header files for details.
|
||||
|
||||
The `trtype` field in JSON returned by `nvmf_get_stats` RPC contains now the name of the transport,
|
||||
which is the same as the type for defined transports and more informative for a custom transport.
|
||||
|
||||
Added `hdgst` and `ddgst` parameters to `bdev_nvme_attach_controller` RPC in order change
|
||||
state of TCP header and data digest.
|
||||
|
||||
Added `num_cqe` parameter to `nvmf_create_transport` RPC to set number of completion queues (CQ)
|
||||
for RDMA transport. Useful when CQ resize operation is not supported, for example iWARP.
|
||||
|
||||
### ocf
|
||||
|
||||
Updated OCF submodule to v20.12.2
|
||||
|
||||
Added `bdev_ocf_set_cache_mode` RPC to dynamically switch cache mode of OCF bdev.
|
||||
|
||||
### opal
|
||||
|
||||
Removed the `spdk_opal_supported` API.
|
||||
|
||||
### raid
|
||||
|
||||
For `bdev_raid_create` RPC, the deprecated parameter `strip_size` was removed.
|
||||
|
||||
### rpc
|
||||
|
||||
New RPC `bdev_nvme_get_transport_statistics` was added, it allows to get transport statistics
|
||||
of nvme poll groups.
|
||||
|
||||
Parameter `enable-zerocopy-send` of RPC `sock_impl_set_options` is deprecated and will be removed in SPDK 21.07,
|
||||
use `enable-zerocopy-send-server` or `enable-zerocopy-send-client` instead.
|
||||
Parameter `disable-zerocopy-send` of RPC `sock_impl_set_options` is deprecated and will be removed in SPDK 21.07,
|
||||
use `disable-zerocopy-send-server` or `disable-zerocopy-send-client` instead.
|
||||
|
||||
### rpm
|
||||
|
||||
Added support for new RPM spec, rpmbuild/spdk.spec, which can be used for packaging the
|
||||
SPDK. The pkg/spdk.spec is considered to be deprecated and scheduled for removal in SPDK 21.07.
|
||||
See [RPM documentation](https://spdk.io/doc/rpm.html) for more details.
|
||||
Fixed the bug that no media event is pushed to the target bdev.
|
||||
|
||||
### sock
|
||||
|
||||
The type of `enable_placement_id` in struct `spdk_sock_impl_opts` is changed from
|
||||
bool to int. We can use RPC to configure different value of `enable_placement_id`.
|
||||
Then we can leverage SO_INCOMING_CPU to get placement_id, which aims to utilize
|
||||
CPU cache locality, enabled by setting enable_placement_id=2.
|
||||
Added `enable_quickack` and `enable_placement_id` when saving JSON configuration.
|
||||
|
||||
A new socket placement mode called PLACEMENT_MARK has been added. Some NICs allow
|
||||
sockets to be marked using the SO_MARK socket option as a hint for which hardware
|
||||
queue they should be associated with. This mode leverages that by setting the same
|
||||
value for all sockets within a poll group.
|
||||
|
||||
New parameters `enable_zerocopy_send_server` and `enable_zerocopy_send_client` were added
|
||||
to struct spdk_sock_impl_opts, these parameters enable or disable zero copy send for server
|
||||
and client sockets which are created using `spdk_sock_listen` and `spdk_sock_listen_ext` (server);
|
||||
`spdk_sock_connect` and `spdk_sock_connect_ext` (client) functions. Existing parameter
|
||||
`enable_zerocopy_send` enables or disables zero copy send for both server and client sockets.
|
||||
|
||||
### thread
|
||||
|
||||
A new API `spdk_io_channel_get_io_device` was added to get the io_device for the specified
|
||||
I/O channel.
|
||||
|
||||
Added `spdk_thread_set_interrupt_mode` function in order to set present spdk_thread into
|
||||
interrupt mode or back to poll mode. It is valid only when thread interrupt facility is
|
||||
enabled by `spdk_interrupt_mode_enable`.
|
||||
|
||||
Added `spdk_poller_register_interrupt` function to mark that the poller is capable of
|
||||
entering interrupt mode. Callback function will be called when the poller must transition
|
||||
into or out of interrupt mode.
|
||||
|
||||
### virtio
|
||||
|
||||
Added the `bdev_virtio_blk_set_hotplug` RPC for the virtio blk pci device.
|
||||
|
||||
## v21.01:
|
||||
## v21.01: Dynamic scheduler, vfio-user, ZNS Zone Append
|
||||
|
||||
### bdev
|
||||
|
||||
|
4
CONFIG
4
CONFIG
@ -87,9 +87,6 @@ CONFIG_ENV=
|
||||
# installation.
|
||||
CONFIG_DPDK_DIR=
|
||||
|
||||
# This directory should contain 'include' and 'lib' directories for WPDK.
|
||||
CONFIG_WPDK_DIR=
|
||||
|
||||
# Build SPDK FIO plugin. Requires CONFIG_FIO_SOURCE_DIR set to a valid
|
||||
# fio source code directory.
|
||||
CONFIG_FIO_PLUGIN=n
|
||||
@ -125,7 +122,6 @@ CONFIG_VIRTIO=y
|
||||
|
||||
# Build custom vfio-user transport for NVMf target and NVMe initiator.
|
||||
CONFIG_VFIO_USER=n
|
||||
CONFIG_VFIO_USER_DIR=
|
||||
|
||||
# Build with PMDK backends
|
||||
CONFIG_PMDK=n
|
||||
|
13
LICENSE
13
LICENSE
@ -1,16 +1,3 @@
|
||||
The SPDK repo contains multiple git submodules each with its own
|
||||
license info. Unless otherwise noted all other code in this repo
|
||||
is BSD as stated below.
|
||||
|
||||
Submodule license info:
|
||||
dpdk: see dpdk/license
|
||||
intel-ipsec-mb: see intel-ipsec-mb/LICENSE
|
||||
isa-l: see isa-l/LICENSE
|
||||
libvfio-user: see libvfio-user/LICENSE
|
||||
ocf: see ocf/LICENSE
|
||||
|
||||
The rest of the SPDK repo:
|
||||
|
||||
BSD LICENSE
|
||||
|
||||
Copyright (c) Intel Corporation.
|
||||
|
14
Makefile
14
Makefile
@ -63,13 +63,6 @@ endif
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(OS),Windows)
|
||||
ifeq ($(CURDIR)/wpdk/build,$(CONFIG_WPDK_DIR))
|
||||
WPDK = wpdk
|
||||
DIRS-y += wpdk
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_SHARED),y)
|
||||
LIB = shared_lib
|
||||
else
|
||||
@ -108,11 +101,10 @@ uninstall: $(DIRS-y)
|
||||
$(Q)echo "Uninstalled spdk"
|
||||
|
||||
ifneq ($(SKIP_DPDK_BUILD),1)
|
||||
dpdkdeps $(DPDK_DEPS): $(WPDK)
|
||||
dpdkbuild: $(WPDK) $(DPDK_DEPS)
|
||||
dpdkbuild: $(DPDK_DEPS)
|
||||
endif
|
||||
|
||||
lib: $(WPDK) $(DPDKBUILD) $(VFIOUSERBUILD)
|
||||
lib: $(DPDKBUILD) $(VFIOUSERBUILD)
|
||||
module: lib
|
||||
shared_lib: module
|
||||
app: $(LIB)
|
||||
@ -128,7 +120,7 @@ mk/cc.mk:
|
||||
false
|
||||
|
||||
build_dir: mk/cc.mk
|
||||
$(Q)mkdir -p build/lib/pkgconfig/tmp
|
||||
$(Q)mkdir -p build/lib/pkgconfig
|
||||
$(Q)mkdir -p build/bin
|
||||
$(Q)mkdir -p build/fio
|
||||
$(Q)mkdir -p build/examples
|
||||
|
@ -41,10 +41,7 @@ DIRS-y += iscsi_top
|
||||
DIRS-y += iscsi_tgt
|
||||
DIRS-y += spdk_tgt
|
||||
DIRS-y += spdk_lspci
|
||||
ifneq ($(OS),Windows)
|
||||
# TODO - currently disabled on Windows due to lack of support for curses
|
||||
DIRS-y += spdk_top
|
||||
endif
|
||||
ifeq ($(OS),Linux)
|
||||
DIRS-$(CONFIG_VHOST) += vhost
|
||||
DIRS-y += spdk_dd
|
||||
|
@ -37,7 +37,6 @@
|
||||
#include "spdk/event.h"
|
||||
#include "spdk/fd.h"
|
||||
#include "spdk/string.h"
|
||||
#include "spdk/util.h"
|
||||
#include "spdk/vmd.h"
|
||||
|
||||
#include <libaio.h>
|
||||
|
@ -84,7 +84,6 @@
|
||||
#define MAX_POLLER_IND_STR_LEN 8
|
||||
#define MAX_CORE_MASK_STR_LEN 16
|
||||
#define MAX_CORE_STR_LEN 6
|
||||
#define MAX_CORE_FREQ_STR_LEN 18
|
||||
#define MAX_TIME_STR_LEN 12
|
||||
#define MAX_POLLER_RUN_COUNT 20
|
||||
#define MAX_PERIOD_STR_LEN 12
|
||||
@ -92,14 +91,16 @@
|
||||
#define FROM_HEX 16
|
||||
#define THREAD_WIN_WIDTH 69
|
||||
#define THREAD_WIN_HEIGHT 9
|
||||
#define THREAD_WIN_HOR_POS 75
|
||||
#define THREAD_WIN_FIRST_COL 2
|
||||
#define CORE_WIN_FIRST_COL 16
|
||||
#define CORE_WIN_WIDTH 48
|
||||
#define CORE_WIN_HEIGHT 11
|
||||
#define CORE_WIN_HEIGHT 9
|
||||
#define CORE_WIN_HOR_POS 60
|
||||
#define POLLER_WIN_HEIGHT 8
|
||||
#define POLLER_WIN_WIDTH 64
|
||||
#define POLLER_WIN_FIRST_COL 14
|
||||
#define FIRST_DATA_ROW 7
|
||||
#define POLLER_WIN_HOR_POS 59
|
||||
|
||||
enum tabs {
|
||||
THREADS_TAB,
|
||||
@ -142,6 +143,7 @@ struct core_info {
|
||||
uint8_t g_sleep_time = 1;
|
||||
uint16_t g_selected_row;
|
||||
uint16_t g_max_selected_row;
|
||||
struct rpc_thread_info *g_thread_info[MAX_THREADS];
|
||||
const char *poller_type_str[SPDK_POLLER_TYPES_COUNT] = {"Active", "Timed", "Paused"};
|
||||
const char *g_tab_title[NUMBER_OF_TABS] = {"[1] THREADS", "[2] POLLERS", "[3] CORES"};
|
||||
struct spdk_jsonrpc_client *g_rpc_client;
|
||||
@ -178,7 +180,6 @@ static struct col_desc g_col_desc[NUMBER_OF_TABS][TABS_COL_COUNT] = {
|
||||
{.name = "Poller count", .max_data_string = MAX_POLLER_COUNT_STR_LEN},
|
||||
{.name = "Idle [us]", .max_data_string = MAX_TIME_STR_LEN},
|
||||
{.name = "Busy [us]", .max_data_string = MAX_TIME_STR_LEN},
|
||||
{.name = "Frequency [MHz]", .max_data_string = MAX_CORE_FREQ_STR_LEN},
|
||||
{.name = (char *)NULL}
|
||||
}
|
||||
};
|
||||
@ -257,7 +258,6 @@ struct rpc_core_info {
|
||||
uint32_t lcore;
|
||||
uint64_t busy;
|
||||
uint64_t idle;
|
||||
uint32_t core_freq;
|
||||
struct rpc_core_threads threads;
|
||||
};
|
||||
|
||||
@ -275,6 +275,7 @@ struct rpc_threads_stats g_threads_stats;
|
||||
struct rpc_pollers_stats g_pollers_stats;
|
||||
struct rpc_cores_stats g_cores_stats;
|
||||
struct rpc_poller_info g_pollers_history[RPC_MAX_POLLERS];
|
||||
struct rpc_thread_info g_thread_history[RPC_MAX_THREADS];
|
||||
|
||||
static void
|
||||
init_str_len(void)
|
||||
@ -474,7 +475,6 @@ static const struct spdk_json_object_decoder rpc_core_info_decoders[] = {
|
||||
{"lcore", offsetof(struct rpc_core_info, lcore), spdk_json_decode_uint32},
|
||||
{"busy", offsetof(struct rpc_core_info, busy), spdk_json_decode_uint64},
|
||||
{"idle", offsetof(struct rpc_core_info, idle), spdk_json_decode_uint64},
|
||||
{"core_freq", offsetof(struct rpc_core_info, core_freq), spdk_json_decode_uint32, true},
|
||||
{"lw_threads", offsetof(struct rpc_core_info, threads), rpc_decode_cores_lw_threads},
|
||||
};
|
||||
|
||||
@ -544,60 +544,12 @@ rpc_send_req(char *rpc_name, struct spdk_jsonrpc_client_response **resp)
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
sort_threads(const void *p1, const void *p2)
|
||||
{
|
||||
const struct rpc_thread_info thread_info1 = *(struct rpc_thread_info *)p1;
|
||||
const struct rpc_thread_info thread_info2 = *(struct rpc_thread_info *)p2;
|
||||
uint64_t count1, count2;
|
||||
|
||||
switch (g_current_sort_col[THREADS_TAB]) {
|
||||
case 0: /* Sort by name */
|
||||
return strcmp(thread_info1.name, thread_info2.name);
|
||||
case 1: /* Sort by core */
|
||||
count2 = thread_info1.core_num;
|
||||
count1 = thread_info2.core_num;
|
||||
break;
|
||||
case 2: /* Sort by active pollers number */
|
||||
count1 = thread_info1.active_pollers_count;
|
||||
count2 = thread_info2.active_pollers_count;
|
||||
break;
|
||||
case 3: /* Sort by timed pollers number */
|
||||
count1 = thread_info1.timed_pollers_count;
|
||||
count2 = thread_info2.timed_pollers_count;
|
||||
break;
|
||||
case 4: /* Sort by paused pollers number */
|
||||
count1 = thread_info1.paused_pollers_count;
|
||||
count2 = thread_info2.paused_pollers_count;
|
||||
break;
|
||||
case 5: /* Sort by idle time */
|
||||
count1 = thread_info1.idle - thread_info1.last_idle;
|
||||
count2 = thread_info2.idle - thread_info2.last_idle;
|
||||
break;
|
||||
case 6: /* Sort by busy time */
|
||||
count1 = thread_info1.busy - thread_info1.last_busy;
|
||||
count2 = thread_info2.busy - thread_info2.last_busy;
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (count2 > count1) {
|
||||
return 1;
|
||||
} else if (count2 < count1) {
|
||||
return -1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
get_data(void)
|
||||
{
|
||||
struct spdk_jsonrpc_client_response *json_resp = NULL;
|
||||
struct rpc_thread_info *thread_info;
|
||||
struct rpc_core_info *core_info;
|
||||
struct rpc_threads_stats threads_stats;
|
||||
uint64_t i, j;
|
||||
int rc = 0;
|
||||
|
||||
@ -607,37 +559,24 @@ get_data(void)
|
||||
}
|
||||
|
||||
/* Decode json */
|
||||
memset(&threads_stats, 0, sizeof(threads_stats));
|
||||
if (spdk_json_decode_object(json_resp->result, rpc_threads_stats_decoders,
|
||||
SPDK_COUNTOF(rpc_threads_stats_decoders), &threads_stats)) {
|
||||
SPDK_COUNTOF(rpc_threads_stats_decoders), &g_threads_stats)) {
|
||||
rc = -EINVAL;
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* This is to free allocated char arrays with old thread names */
|
||||
free_rpc_threads_stats(&g_threads_stats);
|
||||
spdk_jsonrpc_client_free_response(json_resp);
|
||||
|
||||
for (i = 0; i < threads_stats.threads.threads_count; i++) {
|
||||
for (j = 0; j < g_threads_stats.threads.threads_count; j++) {
|
||||
if (g_threads_stats.threads.thread_info[j].id == threads_stats.threads.thread_info[i].id) {
|
||||
threads_stats.threads.thread_info[i].last_busy = g_threads_stats.threads.thread_info[j].busy;
|
||||
threads_stats.threads.thread_info[i].last_idle = g_threads_stats.threads.thread_info[j].idle;
|
||||
}
|
||||
}
|
||||
for (i = 0; i < g_threads_stats.threads.threads_count; i++) {
|
||||
thread_info = &g_threads_stats.threads.thread_info[i];
|
||||
g_thread_info[thread_info->id] = thread_info;
|
||||
}
|
||||
memcpy(&g_threads_stats, &threads_stats, sizeof(struct rpc_threads_stats));
|
||||
qsort(&g_threads_stats.threads.thread_info, threads_stats.threads.threads_count,
|
||||
sizeof(g_threads_stats.threads.thread_info[0]), sort_threads);
|
||||
|
||||
rc = rpc_send_req("thread_get_pollers", &json_resp);
|
||||
if (rc) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Free old pollers values before allocating memory for new ones */
|
||||
free_rpc_pollers_stats(&g_pollers_stats);
|
||||
|
||||
/* Decode json */
|
||||
memset(&g_pollers_stats, 0, sizeof(g_pollers_stats));
|
||||
if (spdk_json_decode_object(json_resp->result, rpc_pollers_stats_decoders,
|
||||
@ -653,9 +592,6 @@ get_data(void)
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Free old cores values before allocating memory for new ones */
|
||||
free_rpc_cores_stats(&g_cores_stats);
|
||||
|
||||
/* Decode json */
|
||||
memset(&g_cores_stats, 0, sizeof(g_cores_stats));
|
||||
if (spdk_json_decode_object(json_resp->result, rpc_cores_stats_decoders,
|
||||
@ -668,7 +604,7 @@ get_data(void)
|
||||
core_info = &g_cores_stats.cores.core[i];
|
||||
|
||||
for (j = 0; j < core_info->threads.threads_count; j++) {
|
||||
g_threads_stats.threads.thread_info[j].core_num = core_info->lcore;
|
||||
g_thread_info[core_info->threads.thread[j].id]->core_num = core_info->lcore;
|
||||
}
|
||||
}
|
||||
|
||||
@ -858,6 +794,53 @@ get_time_str(uint64_t ticks, char *time_str)
|
||||
snprintf(time_str, MAX_TIME_STR_LEN, "%" PRIu64, time);
|
||||
}
|
||||
|
||||
static int
|
||||
sort_threads(const void *p1, const void *p2)
|
||||
{
|
||||
const struct rpc_thread_info *thread_info1 = *(struct rpc_thread_info **)p1;
|
||||
const struct rpc_thread_info *thread_info2 = *(struct rpc_thread_info **)p2;
|
||||
uint64_t count1, count2;
|
||||
|
||||
switch (g_current_sort_col[THREADS_TAB]) {
|
||||
case 0: /* Sort by name */
|
||||
return strcmp(thread_info1->name, thread_info2->name);
|
||||
case 1: /* Sort by core */
|
||||
count2 = thread_info1->core_num;
|
||||
count1 = thread_info2->core_num;
|
||||
break;
|
||||
case 2: /* Sort by active pollers number */
|
||||
count1 = thread_info1->active_pollers_count;
|
||||
count2 = thread_info2->active_pollers_count;
|
||||
break;
|
||||
case 3: /* Sort by timed pollers number */
|
||||
count1 = thread_info1->timed_pollers_count;
|
||||
count2 = thread_info2->timed_pollers_count;
|
||||
break;
|
||||
case 4: /* Sort by paused pollers number */
|
||||
count1 = thread_info1->paused_pollers_count;
|
||||
count2 = thread_info2->paused_pollers_count;
|
||||
break;
|
||||
case 5: /* Sort by idle time */
|
||||
count1 = thread_info1->idle - thread_info1->last_idle;
|
||||
count2 = thread_info2->idle - thread_info2->last_idle;
|
||||
break;
|
||||
case 6: /* Sort by busy time */
|
||||
count1 = thread_info1->busy - thread_info1->last_busy;
|
||||
count2 = thread_info2->busy - thread_info2->last_busy;
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (count2 > count1) {
|
||||
return 1;
|
||||
} else if (count2 < count1) {
|
||||
return -1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
draw_row_background(uint8_t item_index, uint8_t tab)
|
||||
{
|
||||
@ -876,7 +859,7 @@ refresh_threads_tab(uint8_t current_page)
|
||||
{
|
||||
struct col_desc *col_desc = g_col_desc[THREADS_TAB];
|
||||
uint64_t i, threads_count;
|
||||
uint16_t j;
|
||||
uint16_t j, k;
|
||||
uint16_t col;
|
||||
uint8_t max_pages, item_index;
|
||||
static uint8_t last_page = 0;
|
||||
@ -897,16 +880,30 @@ refresh_threads_tab(uint8_t current_page)
|
||||
g_last_threads_count = threads_count;
|
||||
}
|
||||
|
||||
for (i = 0; i < threads_count; i++) {
|
||||
thread_info[i] = &g_threads_stats.threads.thread_info[i];
|
||||
}
|
||||
/* Thread IDs starts from '1', so we have to take this into account when copying.
|
||||
* TODO: In future we can have gaps in ID list, so we will need to change the way we
|
||||
* handle copying threads list below */
|
||||
memcpy(thread_info, &g_thread_info[1], sizeof(struct rpc_thread_info *) * threads_count);
|
||||
|
||||
if (last_page != current_page) {
|
||||
for (i = 0; i < threads_count; i++) {
|
||||
/* Thread IDs start from 1, so we have to do i + 1 */
|
||||
g_threads_stats.threads.thread_info[i].last_idle = g_thread_info[i + 1]->idle;
|
||||
g_threads_stats.threads.thread_info[i].last_busy = g_thread_info[i + 1]->busy;
|
||||
}
|
||||
|
||||
last_page = current_page;
|
||||
}
|
||||
|
||||
max_pages = (threads_count + g_max_data_rows - 1) / g_max_data_rows;
|
||||
|
||||
qsort(thread_info, threads_count, sizeof(thread_info[0]), sort_threads);
|
||||
|
||||
for (k = 0; k < threads_count; k++) {
|
||||
g_thread_history[thread_info[k]->id].busy = thread_info[k]->busy - thread_info[k]->last_busy;
|
||||
g_thread_history[thread_info[k]->id].idle = thread_info[k]->idle - thread_info[k]->last_idle;
|
||||
}
|
||||
|
||||
for (i = current_page * g_max_data_rows;
|
||||
i < spdk_min(threads_count, (uint64_t)((current_page + 1) * g_max_data_rows));
|
||||
i++) {
|
||||
@ -950,6 +947,7 @@ refresh_threads_tab(uint8_t current_page)
|
||||
col += col_desc[4].max_data_string + 2;
|
||||
}
|
||||
|
||||
g_thread_history[thread_info[i]->id].idle = thread_info[i]->idle - thread_info[i]->last_idle;
|
||||
if (!col_desc[5].disabled) {
|
||||
if (g_interval_data == true) {
|
||||
get_time_str(thread_info[i]->idle - thread_info[i]->last_idle, idle_time);
|
||||
@ -961,6 +959,7 @@ refresh_threads_tab(uint8_t current_page)
|
||||
col += col_desc[5].max_data_string;
|
||||
}
|
||||
|
||||
g_thread_history[thread_info[i]->id].busy = thread_info[i]->busy - thread_info[i]->last_busy;
|
||||
if (!col_desc[6].disabled) {
|
||||
if (g_interval_data == true) {
|
||||
get_time_str(thread_info[i]->busy - thread_info[i]->last_busy, busy_time);
|
||||
@ -976,6 +975,11 @@ refresh_threads_tab(uint8_t current_page)
|
||||
}
|
||||
}
|
||||
|
||||
for (k = 0; k < threads_count; k++) {
|
||||
thread_info[k]->last_idle = thread_info[k]->idle;
|
||||
thread_info[k]->last_busy = thread_info[k]->busy;
|
||||
}
|
||||
|
||||
g_max_selected_row = i - current_page * g_max_data_rows - 1;
|
||||
|
||||
return max_pages;
|
||||
@ -1346,8 +1350,7 @@ refresh_cores_tab(uint8_t current_page)
|
||||
uint8_t max_pages, item_index;
|
||||
static uint8_t last_page = 0;
|
||||
char core[MAX_CORE_STR_LEN], threads_number[MAX_THREAD_COUNT_STR_LEN],
|
||||
pollers_number[MAX_POLLER_COUNT_STR_LEN], idle_time[MAX_TIME_STR_LEN],
|
||||
busy_time[MAX_TIME_STR_LEN], core_freq[MAX_CORE_FREQ_STR_LEN];
|
||||
pollers_number[MAX_POLLER_COUNT_STR_LEN], idle_time[MAX_TIME_STR_LEN], busy_time[MAX_TIME_STR_LEN];
|
||||
struct core_info cores[RPC_MAX_CORES];
|
||||
|
||||
memset(&cores, 0, sizeof(cores));
|
||||
@ -1433,18 +1436,6 @@ refresh_cores_tab(uint8_t current_page)
|
||||
}
|
||||
print_max_len(g_tabs[CORES_TAB], TABS_DATA_START_ROW + item_index, offset,
|
||||
col_desc[4].max_data_string, ALIGN_RIGHT, busy_time);
|
||||
offset += col_desc[4].max_data_string + 2;
|
||||
}
|
||||
|
||||
if (!col_desc[5].disabled) {
|
||||
if (!g_cores_stats.cores.core[core_num].core_freq) {
|
||||
snprintf(core_freq, MAX_CORE_FREQ_STR_LEN, "%s", "N/A");
|
||||
} else {
|
||||
snprintf(core_freq, MAX_CORE_FREQ_STR_LEN, "%" PRIu32,
|
||||
g_cores_stats.cores.core[core_num].core_freq);
|
||||
}
|
||||
print_max_len(g_tabs[CORES_TAB], TABS_DATA_START_ROW + item_index, offset,
|
||||
col_desc[5].max_data_string, ALIGN_RIGHT, core_freq);
|
||||
}
|
||||
|
||||
store_core_last_stats(cores[core_num].core, cores[core_num].idle, cores[core_num].busy);
|
||||
@ -1610,7 +1601,6 @@ filter_columns(uint8_t tab)
|
||||
ITEM *cur;
|
||||
void (*p)(enum tabs tab);
|
||||
uint8_t current_index, len = 0;
|
||||
bool disabled[TABS_COL_COUNT];
|
||||
|
||||
for (i = 0; col_desc[i].name != NULL; ++i) {
|
||||
len = spdk_max(col_desc[i].name_len, len);
|
||||
@ -1641,10 +1631,6 @@ filter_columns(uint8_t tab)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
for (int i = 0; i < TABS_COL_COUNT; i++) {
|
||||
disabled[i] = col_desc[i].disabled;
|
||||
}
|
||||
|
||||
while (!stop_loop) {
|
||||
c = wgetch(filter_win);
|
||||
|
||||
@ -1657,17 +1643,6 @@ filter_columns(uint8_t tab)
|
||||
break;
|
||||
case 27: /* ESC */
|
||||
case 'q':
|
||||
for (int i = 0; i < TABS_COL_COUNT; i++) {
|
||||
cur = current_item(my_menu);
|
||||
col_desc[i].disabled = disabled[i];
|
||||
|
||||
my_items = refresh_filtering_menu(&my_menu, filter_win, tab, my_items, elements,
|
||||
item_index(cur) + 1);
|
||||
if (my_items == NULL || my_menu == NULL) {
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
stop_loop = true;
|
||||
break;
|
||||
case ' ': /* Space */
|
||||
@ -1922,17 +1897,6 @@ free_resources(void)
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t
|
||||
get_position_for_window(uint64_t window_size, uint64_t max_size)
|
||||
{
|
||||
/* This function calculates position for pop-up detail window.
|
||||
* Since horizontal and vertical positions are calculated the same way
|
||||
* there is no need for separate functions. */
|
||||
window_size = spdk_min(window_size, max_size);
|
||||
|
||||
return (max_size - window_size) / 2;
|
||||
}
|
||||
|
||||
static void
|
||||
display_thread(struct rpc_thread_info *thread_info)
|
||||
{
|
||||
@ -1951,8 +1915,7 @@ display_thread(struct rpc_thread_info *thread_info)
|
||||
thread_info->paused_pollers_count;
|
||||
|
||||
thread_win = newwin(pollers_count + THREAD_WIN_HEIGHT, THREAD_WIN_WIDTH,
|
||||
get_position_for_window(THREAD_WIN_HEIGHT + pollers_count, g_max_row),
|
||||
get_position_for_window(THREAD_WIN_WIDTH, g_max_col));
|
||||
(g_max_row - pollers_count) / 2, (g_max_col - THREAD_WIN_HOR_POS) / 2);
|
||||
keypad(thread_win, TRUE);
|
||||
thread_panel = new_panel(thread_win);
|
||||
|
||||
@ -1973,9 +1936,9 @@ display_thread(struct rpc_thread_info *thread_info)
|
||||
thread_info->core_num);
|
||||
|
||||
if (g_interval_data) {
|
||||
get_time_str(thread_info->idle - thread_info->last_idle, idle_time);
|
||||
get_time_str(g_thread_history[thread_info->id].idle, idle_time);
|
||||
mvwprintw(thread_win, 3, THREAD_WIN_FIRST_COL + 32, idle_time);
|
||||
get_time_str(thread_info->busy - thread_info->last_busy, busy_time);
|
||||
get_time_str(g_thread_history[thread_info->id].busy, busy_time);
|
||||
mvwprintw(thread_win, 3, THREAD_WIN_FIRST_COL + 54, busy_time);
|
||||
} else {
|
||||
get_time_str(thread_info->idle, idle_time);
|
||||
@ -2042,6 +2005,7 @@ display_thread(struct rpc_thread_info *thread_info)
|
||||
c = wgetch(thread_win);
|
||||
|
||||
switch (c) {
|
||||
case 10: /* ENTER */
|
||||
case 27: /* ESC */
|
||||
stop_loop = true;
|
||||
break;
|
||||
@ -2057,13 +2021,22 @@ display_thread(struct rpc_thread_info *thread_info)
|
||||
static void
|
||||
show_thread(uint8_t current_page)
|
||||
{
|
||||
struct rpc_thread_info thread_info;
|
||||
struct rpc_thread_info *thread_info[g_threads_stats.threads.threads_count];
|
||||
uint64_t thread_number = current_page * g_max_data_rows + g_selected_row;
|
||||
uint64_t i;
|
||||
|
||||
get_data();
|
||||
|
||||
assert(thread_number < g_threads_stats.threads.threads_count);
|
||||
thread_info = g_threads_stats.threads.thread_info[thread_number];
|
||||
for (i = 0; i < g_threads_stats.threads.threads_count; i++) {
|
||||
thread_info[i] = &g_threads_stats.threads.thread_info[i];
|
||||
}
|
||||
|
||||
display_thread(&thread_info);
|
||||
qsort(thread_info, g_threads_stats.threads.threads_count, sizeof(thread_info[0]), sort_threads);
|
||||
|
||||
display_thread(thread_info[thread_number]);
|
||||
|
||||
free_data();
|
||||
}
|
||||
|
||||
static void
|
||||
@ -2093,6 +2066,8 @@ show_core(uint8_t current_page)
|
||||
bool stop_loop = false;
|
||||
char idle_time[MAX_TIME_STR_LEN], busy_time[MAX_TIME_STR_LEN];
|
||||
|
||||
get_data();
|
||||
|
||||
assert(core_number < g_cores_stats.cores.cores_count);
|
||||
for (i = 0; i < g_cores_stats.cores.cores_count; i++) {
|
||||
core_info[i] = &g_cores_stats.cores.core[i];
|
||||
@ -2100,8 +2075,7 @@ show_core(uint8_t current_page)
|
||||
|
||||
threads_count = g_cores_stats.cores.core->threads.threads_count;
|
||||
core_win = newwin(threads_count + CORE_WIN_HEIGHT, CORE_WIN_WIDTH,
|
||||
get_position_for_window(CORE_WIN_HEIGHT + threads_count, g_max_row),
|
||||
get_position_for_window(CORE_WIN_WIDTH, g_max_col));
|
||||
(g_max_row - threads_count) / 2, (g_max_col - CORE_WIN_HOR_POS) / 2);
|
||||
|
||||
keypad(core_win, TRUE);
|
||||
core_panel = new_panel(core_win);
|
||||
@ -2117,20 +2091,9 @@ show_core(uint8_t current_page)
|
||||
mvwaddch(core_win, -1, 0, ACS_LTEE);
|
||||
mvwhline(core_win, 2, 1, ACS_HLINE, CORE_WIN_WIDTH - 2);
|
||||
mvwaddch(core_win, 2, CORE_WIN_WIDTH, ACS_RTEE);
|
||||
print_in_middle(core_win, 3, 0, CORE_WIN_WIDTH - (CORE_WIN_WIDTH / 3), "Frequency:", COLOR_PAIR(5));
|
||||
if (core_info[core_number]->core_freq) {
|
||||
mvwprintw(core_win, 3, CORE_WIN_FIRST_COL + 15, "%" PRIu32,
|
||||
core_info[core_number]->core_freq);
|
||||
} else {
|
||||
mvwprintw(core_win, 3, CORE_WIN_FIRST_COL + 15, "%s", "N/A");
|
||||
}
|
||||
print_left(core_win, 3, 1, CORE_WIN_WIDTH, "Thread count: Idle time:", COLOR_PAIR(5));
|
||||
|
||||
mvwaddch(core_win, -1, 0, ACS_LTEE);
|
||||
mvwhline(core_win, 4, 1, ACS_HLINE, CORE_WIN_WIDTH - 2);
|
||||
mvwaddch(core_win, 4, CORE_WIN_WIDTH, ACS_RTEE);
|
||||
print_left(core_win, 5, 1, CORE_WIN_WIDTH, "Thread count: Idle time:", COLOR_PAIR(5));
|
||||
|
||||
mvwprintw(core_win, 5, CORE_WIN_FIRST_COL, "%" PRIu64,
|
||||
mvwprintw(core_win, 3, CORE_WIN_FIRST_COL, "%" PRIu64,
|
||||
g_cores_history[core_number].threads_count);
|
||||
|
||||
if (g_interval_data == true) {
|
||||
@ -2140,20 +2103,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, 5, CORE_WIN_FIRST_COL + 20, idle_time);
|
||||
mvwprintw(core_win, 3, CORE_WIN_FIRST_COL + 20, idle_time);
|
||||
|
||||
print_left(core_win, 7, 1, CORE_WIN_WIDTH, "Poller count: Busy time:", COLOR_PAIR(5));
|
||||
mvwprintw(core_win, 7, CORE_WIN_FIRST_COL, "%" PRIu64,
|
||||
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, 7, CORE_WIN_FIRST_COL + 20, busy_time);
|
||||
mvwprintw(core_win, 5, CORE_WIN_FIRST_COL + 20, busy_time);
|
||||
|
||||
mvwhline(core_win, 4, 1, ACS_HLINE, CORE_WIN_WIDTH - 2);
|
||||
mvwhline(core_win, 6, 1, ACS_HLINE, CORE_WIN_WIDTH - 2);
|
||||
mvwhline(core_win, 8, 1, ACS_HLINE, CORE_WIN_WIDTH - 2);
|
||||
print_left(core_win, 9, 1, CORE_WIN_WIDTH, "Threads on this core", COLOR_PAIR(5));
|
||||
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 + 10, 1, core_info[core_number]->threads.thread[j].name);
|
||||
mvwprintw(core_win, j + 8, 1, core_info[core_number]->threads.thread[j].name);
|
||||
}
|
||||
|
||||
refresh();
|
||||
@ -2164,9 +2127,9 @@ 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 + 10, 1, core_info[core_number]->threads.thread[j].name);
|
||||
mvwprintw(core_win, j + 8, 1, core_info[core_number]->threads.thread[j].name);
|
||||
} else {
|
||||
print_left(core_win, j + 10, 1, CORE_WIN_WIDTH - 2,
|
||||
print_left(core_win, j + 8, 1, CORE_WIN_WIDTH - 2,
|
||||
core_info[core_number]->threads.thread[j].name, COLOR_PAIR(2));
|
||||
}
|
||||
}
|
||||
@ -2198,6 +2161,8 @@ show_core(uint8_t current_page)
|
||||
|
||||
del_panel(core_panel);
|
||||
delwin(core_win);
|
||||
|
||||
free_data();
|
||||
}
|
||||
|
||||
static void
|
||||
@ -2205,19 +2170,20 @@ show_poller(uint8_t current_page)
|
||||
{
|
||||
PANEL *poller_panel;
|
||||
WINDOW *poller_win;
|
||||
uint64_t count = 0;
|
||||
uint64_t poller_counter = 0, count = 0;
|
||||
uint64_t poller_number = current_page * g_max_data_rows + g_selected_row;
|
||||
struct rpc_poller_info *pollers[RPC_MAX_POLLERS];
|
||||
bool stop_loop = false;
|
||||
char poller_period[MAX_TIME_STR_LEN];
|
||||
int c;
|
||||
|
||||
get_data();
|
||||
|
||||
prepare_poller_data(current_page, pollers, &count, current_page);
|
||||
assert(poller_number < count);
|
||||
|
||||
poller_win = newwin(POLLER_WIN_HEIGHT, POLLER_WIN_WIDTH,
|
||||
get_position_for_window(POLLER_WIN_HEIGHT, g_max_row),
|
||||
get_position_for_window(POLLER_WIN_WIDTH, g_max_col));
|
||||
(g_max_row - poller_counter) / 2, (g_max_col - POLLER_WIN_HOR_POS) / 2);
|
||||
|
||||
keypad(poller_win, TRUE);
|
||||
poller_panel = new_panel(poller_win);
|
||||
@ -2267,6 +2233,7 @@ show_poller(uint8_t current_page)
|
||||
while (!stop_loop) {
|
||||
c = wgetch(poller_win);
|
||||
switch (c) {
|
||||
case 10: /* ENTER */
|
||||
case 27: /* ESC */
|
||||
stop_loop = true;
|
||||
break;
|
||||
@ -2277,6 +2244,8 @@ show_poller(uint8_t current_page)
|
||||
|
||||
del_panel(poller_panel);
|
||||
delwin(poller_win);
|
||||
|
||||
free_data();
|
||||
}
|
||||
|
||||
static void
|
||||
@ -2291,15 +2260,12 @@ show_stats(void)
|
||||
uint8_t active_tab = THREADS_TAB;
|
||||
uint8_t current_page = 0;
|
||||
uint8_t max_pages = 1;
|
||||
uint16_t required_size = WINDOW_HEADER + 1;
|
||||
char current_page_str[CURRENT_PAGE_STR_LEN];
|
||||
bool force_refresh = true;
|
||||
|
||||
clock_gettime(CLOCK_REALTIME, &time_now);
|
||||
time_last = time_now.tv_sec;
|
||||
|
||||
memset(&g_threads_stats, 0, sizeof(g_threads_stats));
|
||||
|
||||
switch_tab(THREADS_TAB);
|
||||
|
||||
while (1) {
|
||||
@ -2307,34 +2273,13 @@ show_stats(void)
|
||||
getmaxyx(stdscr, max_row, max_col);
|
||||
|
||||
if (max_row != g_max_row || max_col != g_max_col) {
|
||||
g_max_row = spdk_max(max_row, required_size);
|
||||
g_max_row = max_row;
|
||||
g_max_col = max_col;
|
||||
g_data_win_size = g_max_row - required_size + 1;
|
||||
g_data_win_size = g_max_row - MENU_WIN_HEIGHT - TAB_WIN_HEIGHT - TABS_DATA_START_ROW;
|
||||
g_max_data_rows = g_max_row - WINDOW_HEADER;
|
||||
resize_interface(active_tab);
|
||||
}
|
||||
|
||||
clock_gettime(CLOCK_REALTIME, &time_now);
|
||||
time_dif = time_now.tv_sec - time_last;
|
||||
if (time_dif < 0) {
|
||||
time_dif = g_sleep_time;
|
||||
}
|
||||
|
||||
if (time_dif >= g_sleep_time || force_refresh) {
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
refresh();
|
||||
}
|
||||
|
||||
c = getch();
|
||||
if (c == 'q') {
|
||||
free_resources();
|
||||
@ -2413,19 +2358,39 @@ show_stats(void)
|
||||
force_refresh = false;
|
||||
break;
|
||||
}
|
||||
|
||||
clock_gettime(CLOCK_REALTIME, &time_now);
|
||||
time_dif = time_now.tv_sec - time_last;
|
||||
if (time_dif < 0) {
|
||||
time_dif = g_sleep_time;
|
||||
}
|
||||
|
||||
if (time_dif >= g_sleep_time || force_refresh) {
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
free_data();
|
||||
|
||||
refresh();
|
||||
}
|
||||
}
|
||||
free_data();
|
||||
}
|
||||
|
||||
static void
|
||||
draw_interface(void)
|
||||
{
|
||||
int i;
|
||||
uint16_t required_size = WINDOW_HEADER + 1;
|
||||
|
||||
getmaxyx(stdscr, g_max_row, g_max_col);
|
||||
g_max_row = spdk_max(g_max_row, required_size);
|
||||
g_data_win_size = g_max_row - required_size;
|
||||
g_data_win_size = g_max_row - MENU_WIN_HEIGHT - TAB_WIN_HEIGHT - TABS_DATA_START_ROW;
|
||||
g_max_data_rows = g_max_row - WINDOW_HEADER;
|
||||
|
||||
g_menu_win = newwin(MENU_WIN_HEIGHT, g_max_col, g_max_row - MENU_WIN_HEIGHT - 1,
|
||||
@ -2495,40 +2460,13 @@ usage(const char *program_name)
|
||||
printf("%s [options]", program_name);
|
||||
printf("\n");
|
||||
printf("options:\n");
|
||||
printf(" -r <path> RPC connect address (default: /var/tmp/spdk.sock)\n");
|
||||
printf(" -r <path> RPC listen address (default: /var/tmp/spdk.sock\n");
|
||||
printf(" -h show this usage\n");
|
||||
}
|
||||
|
||||
static int
|
||||
wait_init(void)
|
||||
{
|
||||
struct spdk_jsonrpc_client_response *json_resp = NULL;
|
||||
char *uninit_log = "Waiting for SPDK target application to initialize...",
|
||||
*uninit_error = "Unable to read SPDK application state!";
|
||||
int c, max_col, rc = 0;
|
||||
|
||||
max_col = getmaxx(stdscr);
|
||||
print_in_middle(stdscr, FIRST_DATA_ROW, 1, max_col, uninit_log, COLOR_PAIR(5));
|
||||
rc = rpc_send_req("framework_wait_init", &json_resp);
|
||||
if (rc) {
|
||||
spdk_jsonrpc_client_free_response(json_resp);
|
||||
|
||||
while (1) {
|
||||
print_in_middle(stdscr, FIRST_DATA_ROW, 1, max_col, uninit_error, COLOR_PAIR(8));
|
||||
c = getch();
|
||||
if (c == 'q') {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
spdk_jsonrpc_client_free_response(json_resp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int op, rc;
|
||||
int op;
|
||||
char *socket = SPDK_DEFAULT_RPC_ADDR;
|
||||
|
||||
while ((op = getopt(argc, argv, "r:h")) != -1) {
|
||||
@ -2536,9 +2474,10 @@ int main(int argc, char **argv)
|
||||
case 'r':
|
||||
socket = optarg;
|
||||
break;
|
||||
case 'H':
|
||||
default:
|
||||
usage(argv[0]);
|
||||
return op == 'h' ? 0 : 1;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2552,11 +2491,7 @@ int main(int argc, char **argv)
|
||||
init_str_len();
|
||||
setup_ncurses();
|
||||
draw_interface();
|
||||
|
||||
rc = wait_init();
|
||||
if (!rc) {
|
||||
show_stats();
|
||||
}
|
||||
show_stats();
|
||||
|
||||
finish(0);
|
||||
|
||||
|
@ -613,8 +613,6 @@ int main(int argc, char **argv)
|
||||
file_name = optarg;
|
||||
break;
|
||||
case 'h':
|
||||
usage();
|
||||
exit(EXIT_SUCCESS);
|
||||
default:
|
||||
usage();
|
||||
exit(1);
|
||||
|
19
autobuild.sh
19
autobuild.sh
@ -51,7 +51,10 @@ function ocf_precompile() {
|
||||
function build_native_dpdk() {
|
||||
local external_dpdk_dir
|
||||
local external_dpdk_base_dir
|
||||
local gcc_version
|
||||
|
||||
gcc_version=$(gcc -dumpversion)
|
||||
gcc_version=${gcc_version//./}
|
||||
external_dpdk_dir="$SPDK_RUN_EXTERNAL_DPDK"
|
||||
external_dpdk_base_dir="$(dirname $external_dpdk_dir)"
|
||||
|
||||
@ -65,9 +68,17 @@ function build_native_dpdk() {
|
||||
git clone --branch $SPDK_TEST_NATIVE_DPDK --depth 1 http://dpdk.org/git/dpdk "$external_dpdk_base_dir"
|
||||
git -C "$external_dpdk_base_dir" log --oneline -n 5
|
||||
|
||||
dpdk_cflags="-fPIC -g -Werror -fcommon"
|
||||
dpdk_cflags="-fPIC -g -fcommon"
|
||||
dpdk_ldflags=""
|
||||
|
||||
if [[ $gcc_version -ge 5 ]]; then
|
||||
dpdk_cflags+=" -Werror"
|
||||
fi
|
||||
|
||||
if [[ $gcc_version -ge 10 ]]; then
|
||||
dpdk_cflags+=" -Wno-stringop-overflow"
|
||||
fi
|
||||
|
||||
# the drivers we use
|
||||
# net/i40e driver is not really needed by us, but it's built as a workaround
|
||||
# for DPDK issue: https://bugs.dpdk.org/show_bug.cgi?id=576
|
||||
@ -113,7 +124,6 @@ function build_native_dpdk() {
|
||||
|
||||
cd $external_dpdk_base_dir
|
||||
if [ "$(uname -s)" = "Linux" ]; then
|
||||
dpdk_cflags+=" -Wno-stringop-overflow"
|
||||
# Fix for freeing device if not kernel driver configured.
|
||||
# TODO: Remove once this is merged in upstream DPDK
|
||||
if grep "20.08.0" $external_dpdk_base_dir/VERSION; then
|
||||
@ -123,12 +133,13 @@ function build_native_dpdk() {
|
||||
wget https://github.com/karlatec/dpdk/commit/3219c0cfc38803aec10c809dde16e013b370bda9.patch -O dpdk-pci.patch
|
||||
wget https://github.com/karlatec/dpdk/commit/adf8f7638de29bc4bf9ba3faf12bbdae73acda0c.patch -O dpdk-qat.patch
|
||||
else
|
||||
wget https://github.com/karlatec/dpdk/commit/f95e331be3a1f856b816948990dd2afc67ea4020.patch -O dpdk-pci.patch
|
||||
wget https://github.com/karlatec/dpdk/commit/6fd2fa906ffdcee04e6ce5da40e61cb841be9827.patch -O dpdk-qat.patch
|
||||
fi
|
||||
git config --local user.name "spdk"
|
||||
git config --local user.email "nomail@all.com"
|
||||
git am dpdk-pci.patch
|
||||
if [[ -f dpdk-pci.patch ]]; then
|
||||
git am dpdk-pci.patch
|
||||
fi
|
||||
git am dpdk-qat.patch
|
||||
fi
|
||||
|
||||
|
@ -13,37 +13,6 @@ source "$1"
|
||||
rootdir=$(readlink -f $(dirname $0))
|
||||
source "$rootdir/test/common/autotest_common.sh"
|
||||
|
||||
function build_rpms() (
|
||||
local version rpms
|
||||
|
||||
# Make sure linker will not attempt to look under DPDK's repo dir to get the libs
|
||||
unset -v LD_LIBRARY_PATH
|
||||
|
||||
install_uninstall_rpms() {
|
||||
rpms=("$HOME/rpmbuild/RPMS/x86_64/"spdk{,-devel,{,-dpdk}-libs}-$version-1.x86_64.rpm)
|
||||
|
||||
sudo rpm -i "${rpms[@]}"
|
||||
rpms=("${rpms[@]##*/}") rpms=("${rpms[@]%.rpm}")
|
||||
# Check if we can find one of the apps in the PATH now and verify if it doesn't miss
|
||||
# any libs.
|
||||
LIST_LIBS=yes "$rootdir/rpmbuild/rpm-deps.sh" "${SPDK_APP[@]##*/}"
|
||||
sudo rpm -e "${rpms[@]}"
|
||||
}
|
||||
|
||||
build_rpm() {
|
||||
MAKEFLAGS="$MAKEFLAGS" SPDK_VERSION="$version" DEPS=no "$rootdir/rpmbuild/rpm.sh" "$@"
|
||||
install_uninstall_rpms
|
||||
}
|
||||
|
||||
version="test_shared"
|
||||
run_test "build_shared_rpm" build_rpm --with-shared
|
||||
|
||||
if [[ -n $SPDK_TEST_NATIVE_DPDK ]]; then
|
||||
version="test_shared_native_dpdk"
|
||||
run_test "build_shared_native_dpdk_rpm" build_rpm --with-shared --with-dpdk="$SPDK_RUN_EXTERNAL_DPDK"
|
||||
fi
|
||||
)
|
||||
|
||||
out=$PWD
|
||||
|
||||
MAKEFLAGS=${MAKEFLAGS:--j16}
|
||||
@ -59,11 +28,6 @@ if [ $(git status --porcelain --ignore-submodules | wc -l) -ne 0 ]; then
|
||||
fi
|
||||
timing_exit porcelain_check
|
||||
|
||||
if [[ $SPDK_TEST_RELEASE_BUILD -eq 1 ]]; then
|
||||
run_test "build_rpms" build_rpms
|
||||
$MAKE clean
|
||||
fi
|
||||
|
||||
if [[ $RUN_NIGHTLY -eq 0 ]]; then
|
||||
timing_finish
|
||||
exit 0
|
||||
|
28
autotest.sh
28
autotest.sh
@ -60,6 +60,8 @@ src=$(readlink -f $(dirname $0))
|
||||
out=$output_dir
|
||||
cd $src
|
||||
|
||||
./scripts/setup.sh status
|
||||
|
||||
freebsd_update_contigmem_mod
|
||||
|
||||
# lcov takes considerable time to process clang coverage.
|
||||
@ -125,11 +127,8 @@ if [ $(uname -s) = Linux ]; then
|
||||
# Export our blocked list so it will take effect during next setup.sh
|
||||
export PCI_BLOCKED
|
||||
fi
|
||||
run_test "setup.sh" "$rootdir/test/setup/test-setup.sh"
|
||||
fi
|
||||
|
||||
./scripts/setup.sh status
|
||||
|
||||
if [[ $(uname -s) == Linux ]]; then
|
||||
# Revert NVMe namespaces to default state
|
||||
nvme_namespace_revert
|
||||
@ -152,11 +151,6 @@ timing_enter afterboot
|
||||
timing_exit afterboot
|
||||
|
||||
if [[ $SPDK_TEST_CRYPTO -eq 1 || $SPDK_TEST_REDUCE -eq 1 ]]; then
|
||||
# Make sure that memory is distributed across all NUMA nodes - by default, all goes to
|
||||
# node0, but if QAT devices are attached to a different node, all of their VFs will end
|
||||
# up under that node too and memory needs to be available there for the tests.
|
||||
CLEAR_HUGE=yes HUGE_EVEN_ALLOC=yes ./scripts/setup.sh
|
||||
./scripts/setup.sh status
|
||||
if [[ $SPDK_TEST_USE_IGB_UIO -eq 1 ]]; then
|
||||
./scripts/qat_setup.sh igb_uio
|
||||
else
|
||||
@ -193,9 +187,18 @@ if [ $SPDK_RUN_FUNCTIONAL_TEST -eq 1 ]; then
|
||||
run_test "blockdev_general" test/bdev/blockdev.sh
|
||||
run_test "bdev_raid" test/bdev/bdev_raid.sh
|
||||
run_test "bdevperf_config" test/bdev/bdevperf/test_config.sh
|
||||
if [[ $(uname -s) == Linux ]]; then
|
||||
run_test "spdk_dd" test/dd/dd.sh
|
||||
run_test "reactor_set_interrupt" test/interrupt/reactor_set_interrupt.sh
|
||||
fi
|
||||
|
||||
if [[ $(uname -s) == Linux ]]; then
|
||||
if [[ $SPDK_TEST_BLOCKDEV -eq 1 || $SPDK_TEST_URING -eq 1 ]]; then
|
||||
# The crypto job also includes the SPDK_TEST_BLOCKDEV in its configuration hence the
|
||||
# dd tests are executed there as well. However, these tests can take a significant
|
||||
# amount of time to complete (up to 4min) on a physical system leading to a potential
|
||||
# job timeout. Avoid that by skipping these tests - this should not affect the coverage
|
||||
# since dd tests are still run as part of the vg jobs.
|
||||
if [[ $SPDK_TEST_CRYPTO -eq 0 ]]; then
|
||||
run_test "spdk_dd" test/dd/dd.sh
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
@ -207,9 +210,6 @@ if [ $SPDK_RUN_FUNCTIONAL_TEST -eq 1 ]; then
|
||||
run_test "blockdev_nvme" test/bdev/blockdev.sh "nvme"
|
||||
run_test "blockdev_nvme_gpt" test/bdev/blockdev.sh "gpt"
|
||||
run_test "nvme" test/nvme/nvme.sh
|
||||
if [[ $SPDK_TEST_NVME_PMR -eq 1 ]]; then
|
||||
run_test "nvme_pmr" test/nvme/nvme_pmr.sh
|
||||
fi
|
||||
if [[ $SPDK_TEST_NVME_CUSE -eq 1 ]]; then
|
||||
run_test "nvme_cuse" test/nvme/cuse/nvme_cuse.sh
|
||||
fi
|
||||
|
215
configure
vendored
215
configure
vendored
@ -7,7 +7,8 @@ trap 'echo -e "\n\nConfiguration failed\n\n" >&2' ERR
|
||||
rootdir=$(readlink -f $(dirname $0))
|
||||
source "$rootdir/scripts/common.sh"
|
||||
|
||||
function usage() {
|
||||
function usage()
|
||||
{
|
||||
echo "'configure' configures SPDK to compile on supported platforms."
|
||||
echo ""
|
||||
echo "Usage: ./configure [OPTION]..."
|
||||
@ -59,7 +60,7 @@ function usage() {
|
||||
echo " virtio Build vhost initiator and virtio-pci bdev modules."
|
||||
echo " No path required."
|
||||
echo " vfio-user Build custom vfio-user transport for NVMf target and NVMe initiator."
|
||||
echo " example: /usr/src/libvfio-user"
|
||||
echo " No path required."
|
||||
echo " pmdk Build persistent memory bdev."
|
||||
echo " example: /usr/share/pmdk"
|
||||
echo " reduce Build vbdev compression module."
|
||||
@ -96,8 +97,6 @@ function usage() {
|
||||
echo " No path required."
|
||||
echo " raid5 Build with bdev_raid module RAID5 support."
|
||||
echo " No path required."
|
||||
echo " wpdk Build using WPDK to provide support for Windows (experimental)."
|
||||
echo " The argument must be a directory containing lib and include."
|
||||
echo ""
|
||||
echo "Environment variables:"
|
||||
echo ""
|
||||
@ -133,18 +132,14 @@ for i in "$@"; do
|
||||
done
|
||||
|
||||
# Detect the compiler toolchain
|
||||
$rootdir/scripts/detect_cc.sh --cc="$CC" --cxx="$CXX" --lto="${CONFIG[LTO]}" --ld="$LD" --cross-prefix="${CONFIG[CROSS_PREFIX]}" > $rootdir/mk/cc.mk
|
||||
$rootdir/scripts/detect_cc.sh --cc="$CC" --cxx="$CXX" --lto="${CONFIG[LTO]}" --ld="$LD" --cross-prefix="${CONFIG[CROSS_PREFIX]}" > $rootdir/mk/cc.mk
|
||||
|
||||
CC=$(grep "DEFAULT_CC=" "$rootdir/mk/cc.mk" | sed s/DEFAULT_CC=//)
|
||||
CC_TYPE=$(grep "CC_TYPE=" "$rootdir/mk/cc.mk" | cut -d "=" -f 2)
|
||||
CC=$(cat $rootdir/mk/cc.mk | grep "DEFAULT_CC=" | sed s/DEFAULT_CC=//)
|
||||
CC_TYPE=$(cat $rootdir/mk/cc.mk | grep "CC_TYPE=" | cut -d "=" -f 2)
|
||||
|
||||
arch=$($CC -dumpmachine)
|
||||
sys_name=$(uname -s)
|
||||
|
||||
if [[ $arch == *mingw* ]] || [[ $arch == *windows* ]]; then
|
||||
sys_name=Windows
|
||||
fi
|
||||
|
||||
# Sanitize default configuration. All parameters set by user explicit should fail
|
||||
# Force no ISA-L if non-x86 or non-aarch64 architecture
|
||||
if [[ "${CONFIG[ISAL]}" = "y" ]]; then
|
||||
@ -154,7 +149,7 @@ if [[ "${CONFIG[ISAL]}" = "y" ]]; then
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ $sys_name != "Linux" ]]; then
|
||||
if [[ $sys_name == "FreeBSD" ]]; then
|
||||
# Vhost, rte_vhost library and virtio are only supported on Linux.
|
||||
CONFIG[VHOST]="n"
|
||||
CONFIG[VIRTIO]="n"
|
||||
@ -163,7 +158,7 @@ fi
|
||||
|
||||
#check nasm only on x86
|
||||
if [[ $arch == x86_64* ]]; then
|
||||
ver=$(nasm -v 2> /dev/null | awk '{print $3}')
|
||||
ver=$(nasm -v 2>/dev/null | awk '{print $3}')
|
||||
if lt "$ver" 2.14; then
|
||||
# ISA-L, compression & crypto require NASM version 2.14 or newer.
|
||||
CONFIG[ISAL]=n
|
||||
@ -188,7 +183,7 @@ function check_dir() {
|
||||
|
||||
for i in "$@"; do
|
||||
case "$i" in
|
||||
-h | --help)
|
||||
-h|--help)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
@ -282,10 +277,6 @@ for i in "$@"; do
|
||||
--without-dpdk)
|
||||
CONFIG[DPDK_DIR]=
|
||||
;;
|
||||
--with-wpdk=*)
|
||||
check_dir "$i"
|
||||
CONFIG[WPDK_DIR]=$(readlink -f ${i#*=})
|
||||
;;
|
||||
--with-env=*)
|
||||
CONFIG[ENV]="${i#*=}"
|
||||
;;
|
||||
@ -350,12 +341,6 @@ for i in "$@"; do
|
||||
;;
|
||||
--with-vfio-user)
|
||||
CONFIG[VFIO_USER]=y
|
||||
CONFIG[VFIO_USER_DIR]=""
|
||||
;;
|
||||
--with-vfio-user=*)
|
||||
CONFIG[VFIO_USER]=y
|
||||
check_dir "$i"
|
||||
CONFIG[VFIO_USER_DIR]=$(readlink -f ${i#*=})
|
||||
;;
|
||||
--without-vfio-user)
|
||||
CONFIG[VFIO_USER]=n
|
||||
@ -459,14 +444,13 @@ for i in "$@"; do
|
||||
echo "Unrecognized option $i"
|
||||
usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ $arch == x86_64* ]]; then
|
||||
BUILD_CMD=("$CC" -o /dev/null -x c $CPPFLAGS $CFLAGS $LDFLAGS "-march=native")
|
||||
BUILD_CMD=($CC -o /dev/null -x c $CPPFLAGS $CFLAGS $LDFLAGS -march=native)
|
||||
else
|
||||
BUILD_CMD=("$CC" -o /dev/null -x c $CPPFLAGS $CFLAGS $LDFLAGS)
|
||||
BUILD_CMD=($CC -o /dev/null -x c $CPPFLAGS $CFLAGS $LDFLAGS)
|
||||
fi
|
||||
BUILD_CMD+=(-I/usr/local/include -L/usr/local/lib)
|
||||
|
||||
@ -496,7 +480,7 @@ if [[ "${CONFIG[IDXD]}" = "y" ]]; then
|
||||
cpu_vendor=$(sysctl -a | grep hw.model | cut -c 1-15)
|
||||
else
|
||||
intel="GenuineIntel"
|
||||
cpu_vendor=$(grep -i 'vendor' /proc/cpuinfo --max-count=1)
|
||||
cpu_vendor=$(grep -i 'vendor' /proc/cpuinfo --max-count=1)
|
||||
fi
|
||||
if [[ "$cpu_vendor" != *"$intel"* ]]; then
|
||||
echo "ERROR: IDXD cannot be used due to CPU incompatiblity."
|
||||
@ -513,8 +497,8 @@ if [[ "${CONFIG[ISAL]}" = "y" ]]; then
|
||||
fi
|
||||
|
||||
if [[ "${CONFIG[ISAL]}" = "n" ]] && [[ "${CONFIG[REDUCE]}" = "y" ]]; then
|
||||
echo "ERROR Conflicting options: --with-reduce is not compatible with --without-isal."
|
||||
exit 1
|
||||
echo "ERROR Conflicting options: --with-reduce is not compatible with --without-isal."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "${CONFIG[ENV]}" ]; then
|
||||
@ -549,23 +533,6 @@ else
|
||||
CONFIG[VIRTIO]="n"
|
||||
fi
|
||||
|
||||
if [[ $sys_name == "Windows" ]]; then
|
||||
if [ -z "${CONFIG[WPDK_DIR]}" ]; then
|
||||
if [ ! -f "$rootdir"/wpdk/Makefile ]; then
|
||||
echo "WPDK not found; please specify --with-wpdk=<path>. See https://wpdk.github.io."
|
||||
exit 1
|
||||
else
|
||||
CONFIG[WPDK_DIR]="${rootdir}/wpdk/build"
|
||||
echo "Using default WPDK in ${CONFIG[WPDK_DIR]}"
|
||||
fi
|
||||
fi
|
||||
else
|
||||
if [ -n "${CONFIG[WPDK_DIR]}" ]; then
|
||||
echo "ERROR: --with-wpdk is only supported for Windows"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "${CONFIG[VTUNE]}" = "y" ]; then
|
||||
if [ -z "${CONFIG[VTUNE_DIR]}" ]; then
|
||||
echo "When VTune is enabled, you must specify the VTune directory using --with-vtune=path"
|
||||
@ -573,7 +540,7 @@ if [ "${CONFIG[VTUNE]}" = "y" ]; then
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ "${CONFIG[ASAN]}" = "y" && "${CONFIG[TSAN]}" = "y" ]]; then
|
||||
if [ "${CONFIG[ASAN]}" = "y" -a "${CONFIG[TSAN]}" = "y" ]; then
|
||||
echo "ERROR: ASAN and TSAN cannot be enabled at the same time."
|
||||
exit 1
|
||||
fi
|
||||
@ -586,7 +553,7 @@ if [[ $sys_name == "FreeBSD" ]]; then
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ $sys_name != "Linux" ]]; then
|
||||
if [[ $sys_name == "FreeBSD" ]]; then
|
||||
if [[ "${CONFIG[VHOST]}" == "y" ]]; then
|
||||
echo "Vhost is only supported on Linux."
|
||||
exit 1
|
||||
@ -598,22 +565,22 @@ if [[ $sys_name != "Linux" ]]; then
|
||||
fi
|
||||
|
||||
if [ "${CONFIG[RDMA]}" = "y" ]; then
|
||||
if [[ ! "${CONFIG[RDMA_PROV]}" == "verbs" ]] && [[ ! "${CONFIG[RDMA_PROV]}" == "mlx5_dv" ]]; then
|
||||
echo "Invalid RDMA provider specified, must be \"verbs\" or \"mlx5_dv\""
|
||||
exit 1
|
||||
fi
|
||||
if [[ ! "${CONFIG[RDMA_PROV]}" == "verbs" ]] && [[ ! "${CONFIG[RDMA_PROV]}" == "mlx5_dv" ]]; then
|
||||
echo "Invalid RDMA provider specified, must be \"verbs\" or \"mlx5_dv\""
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! echo -e '#include <infiniband/verbs.h>\n#include <rdma/rdma_verbs.h>\n' \
|
||||
'int main(void) { return 0; }\n' \
|
||||
| "${BUILD_CMD[@]}" -libverbs -lrdmacm - 2> /dev/null; then
|
||||
echo "--with-rdma requires libverbs and librdmacm."
|
||||
echo "Please install then re-run this script."
|
||||
| ${BUILD_CMD[@]} -libverbs -lrdmacm - 2>/dev/null; then
|
||||
echo --with-rdma requires libverbs and librdmacm.
|
||||
echo Please install then re-run this script.
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if echo -e '#include <infiniband/verbs.h>\n' \
|
||||
'int main(void) { return !!IBV_WR_SEND_WITH_INV; }\n' \
|
||||
| "${BUILD_CMD[@]}" -c - 2> /dev/null; then
|
||||
| ${BUILD_CMD[@]} -c - 2>/dev/null; then
|
||||
CONFIG[RDMA_SEND_WITH_INVAL]="y"
|
||||
else
|
||||
CONFIG[RDMA_SEND_WITH_INVAL]="n"
|
||||
@ -635,7 +602,7 @@ than or equal to 4.14 will see significantly reduced performance.
|
||||
|
||||
if echo -e '#include <rdma/rdma_cma.h>\n' \
|
||||
'int main(void) { return !!RDMA_OPTION_ID_ACK_TIMEOUT; }\n' \
|
||||
| "${BUILD_CMD[@]}" -c - 2> /dev/null; then
|
||||
| ${BUILD_CMD[@]} -c - 2>/dev/null; then
|
||||
CONFIG[RDMA_SET_ACK_TIMEOUT]="y"
|
||||
else
|
||||
CONFIG[RDMA_SET_ACK_TIMEOUT]="n"
|
||||
@ -643,18 +610,18 @@ than or equal to 4.14 will see significantly reduced performance.
|
||||
fi
|
||||
|
||||
if [ "${CONFIG[RDMA_PROV]}" == "mlx5_dv" ]; then
|
||||
if ! echo -e '#include <spdk/stdinc.h>\n' \
|
||||
'#include <infiniband/mlx5dv.h>\n' \
|
||||
'#include <rdma/rdma_cma.h>\n' \
|
||||
'int main(void) { return rdma_establish(NULL) || ' \
|
||||
'!!IBV_QP_INIT_ATTR_SEND_OPS_FLAGS || !!MLX5_OPCODE_RDMA_WRITE; }\n' \
|
||||
| "${BUILD_CMD[@]}" -lmlx5 -I${rootdir}/include -c - 2> /dev/null; then
|
||||
echo "mlx5_dv provider is not supported"
|
||||
exit 1
|
||||
fi
|
||||
if ! echo -e '#include <spdk/stdinc.h>\n' \
|
||||
'#include <infiniband/mlx5dv.h>\n' \
|
||||
'#include <rdma/rdma_cma.h>\n' \
|
||||
'int main(void) { return rdma_establish(NULL) || ' \
|
||||
'!!IBV_QP_INIT_ATTR_SEND_OPS_FLAGS || !!MLX5_OPCODE_RDMA_WRITE; }\n' \
|
||||
| ${BUILD_CMD[@]} -lmlx5 -I${rootdir}/include -c - 2>/dev/null; then
|
||||
echo "mlx5_dv provider is not supported"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "Using '${CONFIG[RDMA_PROV]}' RDMA provider"
|
||||
echo "Using "${CONFIG[RDMA_PROV]}" RDMA provider"
|
||||
fi
|
||||
|
||||
if [[ "${CONFIG[FC]}" = "y" ]]; then
|
||||
@ -678,29 +645,45 @@ if [[ "${CONFIG[ISAL]}" = "y" ]] || [[ "${CONFIG[CRYPTO]}" = "y" ]]; then
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ "${CONFIG[ISAL]}" = "y" ]]; then
|
||||
if [ ! -f "$rootdir"/isa-l/autogen.sh ]; then
|
||||
echo "ISA-L was not found; To install ISA-L run:"
|
||||
echo " git submodule update --init"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cd $rootdir/isa-l
|
||||
ISAL_LOG=$rootdir/isa-l/spdk-isal.log
|
||||
echo -n "Configuring ISA-L (logfile: $ISAL_LOG)..."
|
||||
./autogen.sh &> $ISAL_LOG
|
||||
./configure CFLAGS="-fPIC -g -O2" --enable-shared=no >> $ISAL_LOG 2>&1
|
||||
echo "done."
|
||||
cd $rootdir
|
||||
fi
|
||||
|
||||
if [[ "${CONFIG[PMDK]}" = "y" ]]; then
|
||||
if ! echo -e '#include <libpmemblk.h>\nint main(void) { return 0; }\n' \
|
||||
| "${BUILD_CMD[@]}" -lpmemblk - 2> /dev/null; then
|
||||
echo "--with-pmdk requires libpmemblk."
|
||||
echo "Please install then re-run this script."
|
||||
| ${BUILD_CMD[@]} -lpmemblk - 2>/dev/null; then
|
||||
echo --with-pmdk requires libpmemblk.
|
||||
echo Please install then re-run this script.
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ "${CONFIG[REDUCE]}" = "y" ]]; then
|
||||
if ! echo -e '#include <libpmem.h>\nint main(void) { return 0; }\n' \
|
||||
| "${BUILD_CMD[@]}" -lpmem - 2> /dev/null; then
|
||||
echo "--with-reduce requires libpmem."
|
||||
echo "Please install then re-run this script."
|
||||
| ${BUILD_CMD[@]} -lpmem - 2>/dev/null; then
|
||||
echo --with-reduce requires libpmem.
|
||||
echo Please install then re-run this script.
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ "${CONFIG[NVME_CUSE]}" = "y" ]]; then
|
||||
if ! echo -e '#define FUSE_USE_VERSION 31\n#include <fuse3/cuse_lowlevel.h>\n#include <fuse3/fuse_lowlevel.h>\n#include <fuse3/fuse_opt.h>\nint main(void) { return 0; }\n' \
|
||||
| "${BUILD_CMD[@]}" -lfuse3 -D_FILE_OFFSET_BITS=64 - 2> /dev/null; then
|
||||
echo "--with-cuse requires libfuse3."
|
||||
echo "Please install then re-run this script."
|
||||
| ${BUILD_CMD[@]} -lfuse3 -D_FILE_OFFSET_BITS=64 - 2>/dev/null; then
|
||||
echo --with-cuse requires libfuse3.
|
||||
echo Please install then re-run this script.
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
@ -708,9 +691,9 @@ fi
|
||||
if [[ "${CONFIG[RBD]}" = "y" ]]; then
|
||||
if ! echo -e '#include <rbd/librbd.h>\n#include <rados/librados.h>\n' \
|
||||
'int main(void) { return 0; }\n' \
|
||||
| "${BUILD_CMD[@]}" -lrados -lrbd - 2> /dev/null; then
|
||||
echo "--with-rbd requires librados and librbd."
|
||||
echo "Please install then re-run this script."
|
||||
| ${BUILD_CMD[@]} -lrados -lrbd - 2>/dev/null; then
|
||||
echo --with-rbd requires librados and librbd.
|
||||
echo Please install then re-run this script.
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
@ -722,39 +705,39 @@ if [[ "${CONFIG[ISCSI_INITIATOR]}" = "y" ]]; then
|
||||
'#error\n' \
|
||||
'#endif\n' \
|
||||
'int main(void) { return 0; }\n' \
|
||||
| "${BUILD_CMD[@]}" -L/usr/lib64/iscsi -liscsi - 2> /dev/null; then
|
||||
echo "--with-iscsi-initiator requires libiscsi with"
|
||||
echo "LIBISCSI_API_VERSION >= 20150621."
|
||||
echo "Please install then re-run this script."
|
||||
| ${BUILD_CMD[@]} -L/usr/lib64/iscsi -liscsi - 2>/dev/null; then
|
||||
echo --with-iscsi-initiator requires libiscsi with
|
||||
echo 'LIBISCSI_API_VERSION >= 20150621.'
|
||||
echo Please install then re-run this script.
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ "${CONFIG[ASAN]}" = "y" ]]; then
|
||||
if ! echo -e 'int main(void) { return 0; }\n' \
|
||||
| "${BUILD_CMD[@]}" -fsanitize=address - 2> /dev/null; then
|
||||
echo "--enable-asan requires libasan."
|
||||
echo "Please install then re-run this script."
|
||||
| ${BUILD_CMD[@]} -fsanitize=address - 2>/dev/null; then
|
||||
echo --enable-asan requires libasan.
|
||||
echo Please install then re-run this script.
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ "${CONFIG[UBSAN]}" = "y" ]]; then
|
||||
if ! echo -e 'int main(void) { return 0; }\n' \
|
||||
| "${BUILD_CMD[@]}" -fsanitize=undefined - 2> /dev/null; then
|
||||
echo "--enable-ubsan requires libubsan."
|
||||
echo "Please install then re-run this script."
|
||||
echo "If installed, please check that the GCC version is at least 6.4"
|
||||
echo "and synchronize CC accordingly."
|
||||
| ${BUILD_CMD[@]} -fsanitize=undefined - 2>/dev/null; then
|
||||
echo --enable-ubsan requires libubsan.
|
||||
echo Please install then re-run this script.
|
||||
echo If installed, please check that the GCC version is at least 6.4 \
|
||||
and synchronize CC accordingly.
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ "${CONFIG[TSAN]}" = "y" ]]; then
|
||||
if ! echo -e 'int main(void) { return 0; }\n' \
|
||||
| "${BUILD_CMD[@]}" -fsanitize=thread - 2> /dev/null; then
|
||||
echo "--enable-tsan requires libtsan."
|
||||
echo "Please install then re-run this script."
|
||||
| ${BUILD_CMD[@]} -fsanitize=thread - 2>/dev/null; then
|
||||
echo --enable-tsan requires libtsan.
|
||||
echo Please install then re-run this script.
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
@ -785,11 +768,6 @@ if [[ "${CONFIG[URING]}" = "y" ]]; then
|
||||
echo "${CONFIG[URING_PATH]}: directory not found"
|
||||
exit 1
|
||||
fi
|
||||
elif ! echo -e '#include <liburing.h>\nint main(void) { return 0; }\n' \
|
||||
| "${BUILD_CMD[@]}" -luring - 2> /dev/null; then
|
||||
echo "--with-uring requires liburing."
|
||||
echo "Please build and install then re-run this script."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
@ -802,50 +780,29 @@ if [[ "${CONFIG[FUSE]}" = "y" ]]; then
|
||||
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."
|
||||
echo "Please install then re-run this script."
|
||||
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.
|
||||
echo Please install then re-run this script.
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ "${CONFIG[ISAL]}" = "y" ]]; then
|
||||
if [ ! -f "$rootdir"/isa-l/autogen.sh ]; then
|
||||
echo "ISA-L was not found; To install ISA-L run:"
|
||||
echo " git submodule update --init"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cd $rootdir/isa-l
|
||||
ISAL_LOG=$rootdir/isa-l/spdk-isal.log
|
||||
if [[ -n "${CONFIG[CROSS_PREFIX]}" ]]; then
|
||||
ISAL_OPTS=("--host=${CONFIG[CROSS_PREFIX]}")
|
||||
else
|
||||
ISAL_OPTS=()
|
||||
fi
|
||||
echo -n "Configuring ISA-L (logfile: $ISAL_LOG)..."
|
||||
./autogen.sh &> $ISAL_LOG
|
||||
./configure CFLAGS="-fPIC -g -O2" "${ISAL_OPTS[@]}" --enable-shared=no >> $ISAL_LOG 2>&1
|
||||
echo "done."
|
||||
cd $rootdir
|
||||
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 (($(grep -cE "^\s*CONFIG_[[:alnum:]_]+=" "$rootdir/CONFIG") != ${#CONFIG[@]})); then
|
||||
if [ $(egrep -c "^\s*CONFIG_[[:alnum:]_]+=" $rootdir/CONFIG) -ne ${#CONFIG[@]} ]; then
|
||||
echo ""
|
||||
echo "BUG: Some configuration options are not present in CONFIG file. Please update this file."
|
||||
echo "Missing options in CONFIG (+) file and in current config (-): "
|
||||
diff -u --label "CONFIG file" --label "CONFIG[@]" \
|
||||
<(sed -r -e '/^\s*$/d; /^\s*#.*/d; s/(CONFIG_[[:alnum:]_]+)=.*/\1/g' CONFIG | sort) \
|
||||
<(printf "CONFIG_%s\n" "${!CONFIG[@]}" | sort)
|
||||
<(printf "CONFIG_%s\n" ${!CONFIG[@]} | sort)
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -n "Creating mk/config.mk..."
|
||||
cp -f $rootdir/CONFIG $rootdir/mk/config.mk
|
||||
for key in "${!CONFIG[@]}"; do
|
||||
sed -i.bak -r "s#[[:space:]]*CONFIG_${key}=.*#CONFIG_${key}\?=${CONFIG[$key]}#g" $rootdir/mk/config.mk
|
||||
for key in ${!CONFIG[@]}; do
|
||||
sed -i.bak -r "s#^\s*CONFIG_${key}=.*#CONFIG_${key}\?=${CONFIG[$key]}#g" $rootdir/mk/config.mk
|
||||
done
|
||||
# On FreeBSD sed -i 'SUFFIX' - SUFFIX is mandatory. So no way but to delete the backed file.
|
||||
rm -f $rootdir/mk/config.mk.bak
|
||||
@ -863,7 +820,7 @@ echo "done."
|
||||
# Create .sh with build config for easy sourcing|lookup during the tests.
|
||||
for conf in "${!CONFIG[@]}"; do
|
||||
echo "CONFIG_$conf=${CONFIG[$conf]}"
|
||||
done > "$rootdir/test/common/build_config.sh"
|
||||
done >"$rootdir/test/common/build_config.sh"
|
||||
|
||||
if [[ $sys_name == "FreeBSD" ]]; then
|
||||
echo "Type 'gmake' to build."
|
||||
|
@ -1,42 +0,0 @@
|
||||
# ABI and API Deprecation {#deprecation}
|
||||
|
||||
This document details the policy for maintaining stability of SPDK ABI and API.
|
||||
|
||||
Major ABI version can change at most once for each quarterly SPDK release.
|
||||
ABI versions are managed separately for each library and follow [Semantic Versoning](https://semver.org/).
|
||||
|
||||
API and ABI deprecation notices shall be posted in the next section.
|
||||
Each entry must describe what will be removed and can suggest the future use or alternative.
|
||||
Specific future SPDK release for the removal must be provided.
|
||||
ABI cannot be removed without providing deprecation notice for at least single SPDK release.
|
||||
|
||||
# Deprecation Notices {#deprecation-notices}
|
||||
|
||||
## net
|
||||
|
||||
The net library is deprecated and will be removed in the 21.07 release.
|
||||
|
||||
## nvmf
|
||||
|
||||
The following APIs have been deprecated and will be removed in SPDK 21.07:
|
||||
- `spdk_nvmf_poll_group_get_stat` (function in `nvmf.h`),
|
||||
- `spdk_nvmf_transport_poll_group_get_stat` (function in `nvmf.h`),
|
||||
- `spdk_nvmf_transport_poll_group_free_stat`(function in `nvmf.h`),
|
||||
- `spdk_nvmf_rdma_device_stat` (struct in `nvmf.h`),
|
||||
- `spdk_nvmf_transport_poll_group_stat` (struct in `nvmf.h`),
|
||||
- `poll_group_get_stat` (transport op in `nvmf_transport.h`),
|
||||
- `poll_group_free_stat` (transport op in `nvmf_transport.h`).
|
||||
Please use `spdk_nvmf_poll_group_dump_stat` and `poll_group_dump_stat` instead.
|
||||
|
||||
## rpc
|
||||
|
||||
Parameter `enable-zerocopy-send` of RPC `sock_impl_set_options` is deprecated and will be removed in SPDK 21.07,
|
||||
use `enable-zerocopy-send-server` or `enable-zerocopy-send-client` instead.
|
||||
Parameter `disable-zerocopy-send` of RPC `sock_impl_set_options` is deprecated and will be removed in SPDK 21.07,
|
||||
use `disable-zerocopy-send-server` or `disable-zerocopy-send-client` instead.
|
||||
|
||||
## rpm
|
||||
|
||||
`pkg/spdk.spec` is considered to be deprecated and scheduled for removal in SPDK 21.07.
|
||||
Please use `rpmbuild/spdk.spec` instead and see
|
||||
[RPM documentation](https://spdk.io/doc/rpm.html) for more details.
|
12
doc/Doxyfile
12
doc/Doxyfile
@ -234,7 +234,7 @@ ALIASES =
|
||||
# A mapping has the form "name=value". For example adding "class=itcl::class"
|
||||
# will allow you to use the command class in the itcl::class meaning.
|
||||
|
||||
# TCL_SUBST =
|
||||
TCL_SUBST =
|
||||
|
||||
# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
|
||||
# only. Doxygen will then generate output that is more tailored for C. For
|
||||
@ -813,7 +813,6 @@ INPUT += \
|
||||
compression.md \
|
||||
concurrency.md \
|
||||
containers.md \
|
||||
../deprecation.md \
|
||||
event.md \
|
||||
ftl.md \
|
||||
gdb_macros.md \
|
||||
@ -836,7 +835,6 @@ INPUT += \
|
||||
peer_2_peer.md \
|
||||
pkgconfig.md \
|
||||
porting.md \
|
||||
rpm.md \
|
||||
scheduler.md \
|
||||
shfmt.md \
|
||||
spdkcli.md \
|
||||
@ -1105,7 +1103,7 @@ ALPHABETICAL_INDEX = YES
|
||||
# Minimum value: 1, maximum value: 20, default value: 5.
|
||||
# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
|
||||
|
||||
# COLS_IN_ALPHA_INDEX = 5
|
||||
COLS_IN_ALPHA_INDEX = 5
|
||||
|
||||
# In case all classes in a project start with a common prefix, all classes will
|
||||
# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag
|
||||
@ -1666,7 +1664,7 @@ EXTRA_SEARCH_MAPPINGS =
|
||||
# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output.
|
||||
# The default value is: YES.
|
||||
|
||||
GENERATE_LATEX = NO
|
||||
GENERATE_LATEX = YES
|
||||
|
||||
# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a
|
||||
# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
|
||||
@ -2170,7 +2168,7 @@ EXTERNAL_PAGES = YES
|
||||
# interpreter (i.e. the result of 'which perl').
|
||||
# The default file (with absolute path) is: /usr/bin/perl.
|
||||
|
||||
# PERL_PATH = /usr/bin/perl
|
||||
PERL_PATH = /usr/bin/perl
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to the dot tool
|
||||
@ -2192,7 +2190,7 @@ CLASS_DIAGRAMS = YES
|
||||
# the mscgen tool resides. If left empty the tool is assumed to be found in the
|
||||
# default search path.
|
||||
|
||||
# MSCGEN_PATH =
|
||||
MSCGEN_PATH =
|
||||
|
||||
# You can include diagrams made with dia in doxygen documentation. Doxygen will
|
||||
# then run dia to produce the diagram and insert it in the documentation. The
|
||||
|
@ -66,14 +66,14 @@ To use the IOAT engine, use the RPC [`ioat_scan_accel_engine`](https://spdk.io/d
|
||||
To use the DSA engine, use the RPC [`idxd_scan_accel_engine`](https://spdk.io/doc/jsonrpc.html) with an optional parameter of `-c` and provide a configuration number of either 0 or 1. These pre-defined configurations determine how the DSA engine will be setup in terms
|
||||
of work queues and engines. The DSA engine is very flexible allowing for various configurations of these elements to either account for different quality of service requirements or to isolate hardware paths where the back end media is of varying latency (i.e. persistent memory vs DRAM). The pre-defined configurations are as follows:
|
||||
|
||||
0: A single work queue backed with four DSA engines. This is a generic configuration
|
||||
that enables the hardware to best determine which engine to use as it pulls in new
|
||||
operations.
|
||||
0: Four separate work queues each backed with one DSA engine. This is a generic
|
||||
configuration that provides 4 portals to submit operations to each with a
|
||||
single engine behind it providing some level of isolation as operations are
|
||||
submitted round-robin.
|
||||
|
||||
1: Two separate work queues each backed with two DSA engines. This is another
|
||||
generic configuration that is documented in the specification and allows the
|
||||
application to partition submissions across two work queues. This would be useful
|
||||
when different priorities might be desired per group.
|
||||
1: Two separate work queues each backed with two DSA engines. This is another
|
||||
generic configuration that provides 2 portals to submit operations to and
|
||||
lets the DSA hardware decide which engine to select based on loading.
|
||||
|
||||
There are several other configurations that are possible that include quality
|
||||
of service parameters on the work queues that are not currently utilized by
|
||||
|
23
doc/bdev.md
23
doc/bdev.md
@ -312,8 +312,10 @@ To remove `Cache1`:
|
||||
|
||||
During removal OCF-cache will be stopped and all cached data will be written to the core device.
|
||||
|
||||
Note that OCF has a per-device RAM requirement. More details can be found in the
|
||||
[OCF documentation](https://open-cas.github.io/guide_system_requirements.html).
|
||||
Note that OCF has a per-device RAM requirement
|
||||
of about 56000 + _cache device size_ * 58 / _cache line size_ (in bytes).
|
||||
To get more information on OCF
|
||||
please visit [OCF documentation](https://open-cas.github.io/).
|
||||
|
||||
# Malloc bdev {#bdev_config_malloc}
|
||||
|
||||
@ -369,22 +371,15 @@ This command will remove NVMe bdev named Nvme0.
|
||||
|
||||
## NVMe bdev character device {#bdev_config_nvme_cuse}
|
||||
|
||||
This feature is considered as experimental. You must configure with --with-nvme-cuse
|
||||
option to enable this RPC.
|
||||
This feature is considered as experimental.
|
||||
|
||||
Example commands
|
||||
|
||||
`rpc.py bdev_nvme_cuse_register -n Nvme3
|
||||
`rpc.py bdev_nvme_cuse_register -n Nvme0 -p spdk/nvme0`
|
||||
|
||||
This command will register a character device under /dev/spdk associated with Nvme3
|
||||
controller. If there are namespaces created on Nvme3 controller, a namespace
|
||||
character device is also created for each namespace.
|
||||
|
||||
For example, the first controller registered will have a character device path of
|
||||
/dev/spdk/nvmeX, where X is replaced with a unique integer to differentiate it from
|
||||
other controllers. Note that this 'nvmeX' name here has no correlation to the name
|
||||
associated with the controller in SPDK. Namespace character devices will have a path
|
||||
of /dev/spdk/nvmeXnY, where Y is the namespace ID.
|
||||
This command will register /dev/spdk/nvme0 character device associated with Nvme0
|
||||
controller. If there are namespaces created on Nvme0 controller, for each namespace
|
||||
device /dev/spdk/nvme0nX is created.
|
||||
|
||||
Cuse devices are removed from system, when NVMe controller is detached or unregistered
|
||||
with command:
|
||||
|
@ -14,24 +14,24 @@ make
|
||||
~~~
|
||||
|
||||
Clone the RocksDB repository from the SPDK GitHub fork into a separate directory.
|
||||
Make sure you check out the `6.15.fb` branch.
|
||||
Make sure you check out the `spdk-v5.14.3` branch.
|
||||
|
||||
~~~{.sh}
|
||||
cd ..
|
||||
git clone -b 6.15.fb https://github.com/spdk/rocksdb.git
|
||||
git clone -b spdk-v5.14.3 https://github.com/spdk/rocksdb.git
|
||||
~~~
|
||||
|
||||
Build RocksDB. Only the `db_bench` benchmarking tool is integrated with BlobFS.
|
||||
|
||||
~~~{.sh}
|
||||
cd rocksdb
|
||||
make db_bench SPDK_DIR=relative_path/to/spdk
|
||||
make db_bench SPDK_DIR=path/to/spdk
|
||||
~~~
|
||||
|
||||
Or you can also add `DEBUG_LEVEL=0` for a release build (need to turn on `USE_RTTI`).
|
||||
|
||||
~~~{.sh}
|
||||
export USE_RTTI=1 && make db_bench DEBUG_LEVEL=0 SPDK_DIR=relative_path/to/spdk
|
||||
export USE_RTTI=1 && make db_bench DEBUG_LEVEL=0 SPDK_DIR=path/to/spdk
|
||||
~~~
|
||||
|
||||
Create an NVMe section in the configuration file using SPDK's `gen_nvme.sh` script.
|
||||
|
BIN
doc/img/spdk_top_page1_threads.png
Normal file
BIN
doc/img/spdk_top_page1_threads.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 21 KiB |
BIN
doc/img/spdk_top_page2_pollers.png
Normal file
BIN
doc/img/spdk_top_page2_pollers.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 52 KiB |
BIN
doc/img/spdk_top_page3_cores.png
Normal file
BIN
doc/img/spdk_top_page3_cores.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
@ -1,4 +1,4 @@
|
||||
# Storage Performance Development Kit {#mainpage}
|
||||
# Storage Performance Development Kit {#index}
|
||||
|
||||
# Introduction
|
||||
|
||||
|
@ -4,5 +4,4 @@
|
||||
- @subpage getting_started
|
||||
- @subpage vagrant
|
||||
- @subpage changelog
|
||||
- @subpage deprecation
|
||||
- [Source Code (GitHub)](https://github.com/spdk/spdk)
|
||||
|
980
doc/jsonrpc.md
980
doc/jsonrpc.md
File diff suppressed because it is too large
Load Diff
@ -191,23 +191,3 @@ shim/implementation library system.
|
||||
# two libraries
|
||||
gcc -o my_app ./my_app.c -lspdk -lcustom_env_shim -lcustom_env_implementation
|
||||
~~~
|
||||
|
||||
# SPDK Static Objects {#static_objects}
|
||||
|
||||
SPDK static objects are compiled by default even when no parameters are supplied to the build system.
|
||||
Unlike SPDK shared objects, the filename does not contain any versioning semantics. Linking against
|
||||
static objects is similar to shared objects but will always require the use of `-Wl,--whole-archive`
|
||||
as argument. This is due to the use of constructor functions in SPDK such as those to register
|
||||
NVMe transports.
|
||||
|
||||
Due to the lack of versioning semantics, it is not recommended to install static libraries system wide.
|
||||
Instead the path to these static libraries should be added as argument at compile time using
|
||||
`-L/path/to/static/libs`. The use of static objects instead of shared objects can also be forced
|
||||
through `-Wl,-Bsatic`, otherwise some compilers might prefer to use the shared objects if both
|
||||
are available.
|
||||
|
||||
~~~{.sh}
|
||||
gcc -o my_app ./my_app.c -L/path/to/static/libs -Wl,--whole-archive -Wl,-Bstatic -lpassthru_external
|
||||
-lspdk_event_bdev -lspdk_bdev -lspdk_bdev_malloc -lspdk_log -lspdk_thread -lspdk_util -lspdk_event
|
||||
-lspdk_env_dpdk -Wl,--no-whole-archive -Wl,-Bdynamic -pthread -ldpdk
|
||||
~~~
|
||||
|
16
doc/nvmf.md
16
doc/nvmf.md
@ -106,14 +106,16 @@ using 1GB hugepages or by pre-reserving memory at application startup with `--me
|
||||
option. All pre-reserved memory will be registered as a single region, but won't be returned to the
|
||||
system until the SPDK application is terminated.
|
||||
|
||||
Another known issue occurs when using the E810 NICs in RoCE mode. Specifically, the NVMe-oF target
|
||||
sometimes cannot destroy a qpair, because its posted work requests don't get flushed. It can cause
|
||||
the NVMe-oF target application unable to terminate cleanly.
|
||||
|
||||
## TCP transport support {#nvmf_tcp_transport}
|
||||
|
||||
The transport is built into the nvmf_tgt by default, and it does not need any special libraries.
|
||||
|
||||
## Configuring the SPDK NVMe over Fabrics Target {#nvmf_config}
|
||||
|
||||
An NVMe over Fabrics target can be configured using JSON RPCs.
|
||||
The basic RPCs needed to configure the NVMe-oF subsystem are detailed below. More information about
|
||||
working with NVMe over Fabrics specific RPCs can be found on the @ref jsonrpc_components_nvmf_tgt RPC page.
|
||||
|
||||
## FC transport support {#nvmf_fc_transport}
|
||||
|
||||
To build nvmf_tgt with the FC transport, there is an additional FC LLD (Low Level Driver) code dependency.
|
||||
@ -141,12 +143,6 @@ cd ../spdk
|
||||
make
|
||||
~~~
|
||||
|
||||
## Configuring the SPDK NVMe over Fabrics Target {#nvmf_config}
|
||||
|
||||
An NVMe over Fabrics target can be configured using JSON RPCs.
|
||||
The basic RPCs needed to configure the NVMe-oF subsystem are detailed below. More information about
|
||||
working with NVMe over Fabrics specific RPCs can be found on the @ref jsonrpc_components_nvmf_tgt RPC page.
|
||||
|
||||
### Using RPCs {#nvmf_config_rpc}
|
||||
|
||||
Start the nvmf_tgt application with elevated privileges. Once the target is started,
|
||||
|
@ -68,7 +68,7 @@ system. This is used for access control.
|
||||
|
||||
A user of the NVMe-oF target library begins by creating a target using
|
||||
spdk_nvmf_tgt_create(), setting up a set of addresses on which to accept
|
||||
connections by calling spdk_nvmf_tgt_listen_ext(), then creating a subsystem
|
||||
connections by calling spdk_nvmf_tgt_listen(), then creating a subsystem
|
||||
using spdk_nvmf_subsystem_create().
|
||||
|
||||
Subsystems begin in an inactive state and must be activated by calling
|
||||
@ -78,7 +78,7 @@ calling spdk_nvmf_subsystem_pause() and resumed by calling
|
||||
spdk_nvmf_subsystem_resume().
|
||||
|
||||
Namespaces may be added to the subsystem by calling
|
||||
spdk_nvmf_subsystem_add_ns_ext() when the subsystem is inactive or paused.
|
||||
spdk_nvmf_subsystem_add_ns() when the subsystem is inactive or paused.
|
||||
Namespaces are bdevs. See @ref bdev for more information about the SPDK bdev
|
||||
layer. A bdev may be obtained by calling spdk_bdev_get_by_name().
|
||||
|
||||
|
@ -1,12 +1,5 @@
|
||||
# Performance Reports {#performance_reports}
|
||||
|
||||
## Release 21.01
|
||||
|
||||
- [SPDK 21.01 NVMe Bdev Performance Report](https://ci.spdk.io/download/performance-reports/SPDK_nvme_bdev_perf_report_2101.pdf)
|
||||
- [SPDK 21.01 NVMe-oF TCP Performance Report](https://ci.spdk.io/download/performance-reports/SPDK_tcp_perf_report_2101.pdf)
|
||||
- [SPDK 21.01 NVMe-oF RDMA Performance Report](https://ci.spdk.io/download/performance-reports/SPDK_rdma_perf_report_2101.pdf)
|
||||
- [SPDK 21.01 Vhost Performance Report](https://ci.spdk.io/download/performance-reports/SPDK_vhost_perf_report_2101.pdf)
|
||||
|
||||
## Release 20.10
|
||||
|
||||
- [SPDK 20.10 NVMe Bdev Performance Report](https://ci.spdk.io/download/performance-reports/SPDK_nvme_bdev_perf_report_2010.pdf)
|
||||
|
@ -28,10 +28,9 @@ PKG_CONFIG_PATH=/path/to/spdk/build/lib/pkgconfig pkg-config --libs spdk_syslibs
|
||||
|
||||
Note that SPDK libraries use constructor functions liberally, so you must surround
|
||||
the library list with extra linker options to ensure these functions are not dropped
|
||||
from the resulting application binary. With shared libraries this is achieved through
|
||||
the `-Wl,--no-as-needed` parameters while with static libraries `-Wl,--whole-archive`
|
||||
is used. Here is an example Makefile snippet that shows how to use pkg-config to link
|
||||
an application that uses the SPDK nvme shared library:
|
||||
from the resulting application binary. Here is an example Makefile snippet that
|
||||
shows how to use pkg-config to link an application that uses the SPDK nvme shared
|
||||
library:
|
||||
|
||||
~~~
|
||||
PKG_CONFIG_PATH = $(SPDK_DIR)/build/lib/pkgconfig
|
||||
|
49
doc/rpm.md
49
doc/rpm.md
@ -1,49 +0,0 @@
|
||||
# RPMs {#rpms}
|
||||
|
||||
# In this document {#rpms_toc}
|
||||
|
||||
* @ref building_rpms
|
||||
|
||||
# Building SPDK RPMs {#building_rpms}
|
||||
|
||||
To build basic set of RPM packages out of the SPDK repo simply run:
|
||||
|
||||
~~~{.sh}
|
||||
# rpmbuild/rpm.sh
|
||||
~~~
|
||||
|
||||
Additional configuration options can be passed directly as arguments:
|
||||
|
||||
~~~{.sh}
|
||||
# rpmbuild/rpm.sh --with-shared --with-dpdk=/path/to/dpdk/build
|
||||
~~~
|
||||
|
||||
There are several options that may be passed via environment as well:
|
||||
|
||||
- DEPS - Install all needed dependencies for building RPM packages.
|
||||
Default: "yes"
|
||||
- MAKEFLAGS - Flags passed to make
|
||||
- RPM_RELEASE - Target release version of the RPM packages. Default: 1
|
||||
- REQUIREMENTS - Extra set of RPM dependencies if deemed as needed
|
||||
- SPDK_VERSION - SPDK version. Default: currently checked out tag
|
||||
|
||||
~~~{.sh}
|
||||
# DEPS=no MAKEFLAGS="-d -j1" rpmbuild/rpm.sh --with-shared
|
||||
~~~
|
||||
|
||||
By default, all RPM packages should be created under $HOME directory of the
|
||||
target user:
|
||||
|
||||
~~~{.sh}
|
||||
# printf '%s\n' /root/rpmbuild/RPMS/x86_64/*
|
||||
/root/rpmbuild/RPMS/x86_64/spdk-devel-v21.01-1.x86_64.rpm
|
||||
/root/rpmbuild/RPMS/x86_64/spdk-dpdk-libs-v21.01-1.x86_64.rpm
|
||||
/root/rpmbuild/RPMS/x86_64/spdk-libs-v21.01-1.x86_64.rpm
|
||||
/root/rpmbuild/RPMS/x86_64/spdk-v21.01-1.x86_64.rpm
|
||||
#
|
||||
~~~
|
||||
|
||||
- spdk - provides all the binaries, common tooling, etc.
|
||||
- spdk-devel - provides development files
|
||||
- spdk-libs - provides target lib, .pc files (--with-shared)
|
||||
- spdk-dpdk-libs - provides dpdk lib files (--with-shared|--with-dpdk)
|
@ -5,7 +5,7 @@ The spdk_top application is designed to resemble the standard top in that it pro
|
||||
Why doesn't the classic top utility work for SPDK? SPDK uses a polled-mode design; a reactor thread running on each CPU core assigned to an SPDK application schedules SPDK lightweight threads and pollers to run on the CPU core. Therefore, the standard Linux top utility is not effective for analyzing the CPU usage for polled-mode applications like SPDK because it just reports that they are using 100% of the CPU resources assigned to them. The spdk_top utility was developed to analyze and report the CPU cycles used to do real work vs just polling for work. The utility relies on instrumentation added to pollers to track when they are doing work vs. polling for work. The spdk_top utility gets the fine grained metrics from the pollers, analyzes and report the metrics on a per poller, thread and core basis. This information enables users to identify CPU cores that are busy doing real work so that they can determine if the application needs more or less CPU resources.
|
||||
|
||||
# Run spdk_top
|
||||
Before running spdk_top you need to run the SPDK application whose performance you want to analyze using spdk_top.
|
||||
Before running spdk_top you need to run the SPDK application whose performance you want to analyze using spdk_top. For example, the nvmf_tgt application was running when we used the spdk_top to capture the screen shots in this documentation.
|
||||
|
||||
Run the spdk_top application
|
||||
|
||||
@ -13,53 +13,33 @@ Run the spdk_top application
|
||||
./build/bin/spdk_top
|
||||
~~~
|
||||
|
||||
# Bottom menu
|
||||
Menu at the bottom of SPDK top window shows many options for changing displayed data. Each menu item has a key associated with it in square brackets.
|
||||
|
||||
* Quit - quits the SPDK top application.
|
||||
* TAB selection - allows to select THREADS/POLLERS/CORES tabs.
|
||||
* Previous page/Next page - scrolls up/down to the next set of rows displayed. Indicator in the bottom-left corner shows current page and number of all available pages.
|
||||
* Columns - enables/disables chosen columns in a column pop-up window.
|
||||
* Sorting - allows to sort displayed data by column in a sorting pop-up.
|
||||
* Refresh rate - takes user input from 0 to 255 and changes refresh rate to that value in seconds.
|
||||
* Item details - displays details pop-up window for highlighted data row. Selection is changed by pressing UP and DOWN arrow keys.
|
||||
* Total/Interval - changes displayed values in all tabs to either Total time (measured since start of SPDK application) or Interval time (measured since last refresh).
|
||||
The spdk_top application has 3 tabs: the cores, threads and pollers tabs.
|
||||
|
||||
# Threads Tab
|
||||
The threads tab displays a line item for each spdk thread. The information displayed shows:
|
||||
The threads tab displays a line item for each spdk thread that includes information such as which CPU core the spdk thread is running on, how many pollers the thread is running and how many microseconds was the thread busy/idle. The pollers are grouped into active, timed and pause pollers. To learn more about spdk threads see @ref concurrency.
|
||||
|
||||
* Thread name - name of SPDK thread.
|
||||
* Core - core on which the thread is currently running.
|
||||
* Active/Timed/Paused pollers - number of pollers grouped by type on this thread.
|
||||
* Idle/Busy - how many microseconds the thread was idle/busy.
|
||||
|
||||
\n
|
||||
By pressing ENTER key a pop-up window appears, showing above and a list of pollers running on selected thread (with poller name, type, run count and period).
|
||||
Pop-up then can be closed by pressing ESC key.
|
||||
|
||||
To learn more about spdk threads see @ref concurrency.
|
||||
![Threads Tab](img/spdk_top_page1_threads.png)
|
||||
|
||||
# Pollers Tab
|
||||
The pollers tab displays a line item for each poller. The information displayed shows:
|
||||
The pollers tab displays a line item for each poller and a running counter of the number of times the poller has run so that you can see which pollers are running most frequently.
|
||||
|
||||
* Poller name - name of currently selected poller.
|
||||
* Type - type of poller (Active/Paused/Timed).
|
||||
* On thread - thread on which the poller is running.
|
||||
* Run count - how many times poller was run.
|
||||
* Period - poller period in microseconds. If period equals 0 then it is not displayed.
|
||||
* Status - whether poller is currently Busy (red color) or Idle (blue color).
|
||||
|
||||
\n
|
||||
Poller pop-up window can be displayed by pressing ENTER on a selected data row and displays above information.
|
||||
Pop-up can be closed by pressing ESC key.
|
||||
![Pollers Tab](img/spdk_top_page2_pollers.png)
|
||||
|
||||
# Cores Tab
|
||||
The cores tab provides insights into how the application is using the CPU cores assigned to it. The information displayed for each core shows:
|
||||
The cores tab provides insights into how the application is using the CPU cores assigned to it.
|
||||
It has a line item for each CPU core assigned to the application which shows the number of threads and poller
|
||||
running on the CPU core. The tab also indicates how busy/idle the each CPU core was in the last 1 second.
|
||||
The busy column displays how many microseconds the CPU core was doing actual work in the last 1 second.
|
||||
The idle column displays how many microseconds the CPU core was idle in the last 1 second,
|
||||
including the time when the CPU core ran pollers but did not find any work.
|
||||
|
||||
* Core - core number.
|
||||
* Thread count - number of threads currently running on core.
|
||||
* Poller count - total number of pollers running on core.
|
||||
* Idle/Busy - how many microseconds core was idle (including time when core ran pollers but did not find any work) or doing actual work.
|
||||
![Cores Tab](img/spdk_top_page3_cores.png)
|
||||
|
||||
\n
|
||||
Pressing ENTER key makes a pop-up window appear, showing above information, along with a list of threads running on selected core. Cores details window allows to select a thread and display thread details pop-up on top of it. To close both pop-ups use ESC key.
|
||||
# Refresh Rate
|
||||
You can control how often the spdk_top application refreshes the data displayed by hitting the 'r' key on your keyboard and specifying a value between 0 and 255 seconds.
|
||||
|
||||
# Sorting
|
||||
You can sort the data displayed by hitting the 's' key on your keyboard and selecting a column to sort by in the sub menu that is displayed.
|
||||
|
||||
# Filtering
|
||||
You can filter out any column by hitting the 'c' key on your keyboard and unselecting the column in the menu that is displayed.
|
||||
|
2
dpdk
2
dpdk
@ -1 +1 @@
|
||||
Subproject commit 4f93dbc0c0ab3804abaa20123030ad7fccf78709
|
||||
Subproject commit 707692e67d0c6c685c42f5dd48f8f112bde6b381
|
@ -45,10 +45,6 @@ DPDK_KMODS = true
|
||||
endif
|
||||
DPDK_OPTS += -Denable_kmods=$(DPDK_KMODS)
|
||||
|
||||
ifeq ($(CONFIG_DEBUG),y)
|
||||
DPDK_OPTS += --buildtype=debug
|
||||
endif
|
||||
|
||||
# the drivers we use
|
||||
DPDK_DRIVERS = bus bus/pci bus/vdev mempool/ring
|
||||
|
||||
@ -73,16 +69,16 @@ endif
|
||||
DPDK_OPTS += -Dmachine=$(TARGET_ARCHITECTURE)
|
||||
|
||||
ifneq ($(CONFIG_CROSS_PREFIX),)
|
||||
ifeq ($(findstring mingw,$(CONFIG_CROSS_PREFIX)),mingw)
|
||||
DPDK_OPTS += --cross-file $(SPDK_ROOT_DIR)/dpdk/config/x86/cross-mingw
|
||||
else
|
||||
$(error Automatic DPDK cross build is not supported. Please compile DPDK manually \
|
||||
with e.g. `meson build --cross-file config/arm/arm64_armv8_linux_gcc`)
|
||||
endif
|
||||
endif
|
||||
|
||||
DPDK_CFLAGS += -fPIC
|
||||
|
||||
ifeq ($(CONFIG_DEBUG),y)
|
||||
DPDK_CFLAGS += -O0 -g
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_WERROR),y)
|
||||
DPDK_CFLAGS += -Werror
|
||||
else
|
||||
@ -121,7 +117,7 @@ DPDK_ALL_DRIVER_DIRS = $(shell find $(SPDK_ROOT_DIR)/dpdk/drivers -mindepth 1 -t
|
||||
DPDK_ALL_DRIVERS = $(DPDK_ALL_DRIVER_DIRS:$(SPDK_ROOT_DIR)/dpdk/drivers/%=%)
|
||||
DPDK_DISABLED_DRVERS = $(filter-out $(DPDK_DRIVERS),$(DPDK_ALL_DRIVERS))
|
||||
|
||||
ifneq ($(OS),FreeBSD)
|
||||
ifeq ($(OS),Linux)
|
||||
SED_INPLACE_FLAG = "-i"
|
||||
MESON_PREFIX = $(SPDK_ROOT_DIR)/dpdk/build
|
||||
else
|
||||
@ -137,7 +133,7 @@ ifeq ($(MAKE_PID),)
|
||||
MAKE_PID := $(shell echo $$PPID)
|
||||
endif
|
||||
|
||||
MAKE_NUMJOBS := $(shell ps T | sed -nE 's/[[:space:]]*$(MAKE_PID)[[:space:]].* (-j|--jobs=)( *[0-9]+).*/\1\2/p')
|
||||
MAKE_NUMJOBS := $(shell ps T | sed -nE 's/\s*$(MAKE_PID)\s.* (-j|--jobs=)( *[0-9]+).*/\1\2/p')
|
||||
|
||||
all: $(SPDK_ROOT_DIR)/dpdk/build-tmp
|
||||
$(Q)# DPDK doesn't handle nested make calls, so unset MAKEFLAGS
|
||||
|
@ -45,15 +45,12 @@
|
||||
#define ALIGN_4K 0x1000
|
||||
|
||||
static uint64_t g_tsc_rate;
|
||||
static uint64_t g_tsc_us_rate;
|
||||
static uint64_t g_tsc_end;
|
||||
static int g_rc;
|
||||
static int g_xfer_size_bytes = 4096;
|
||||
static int g_queue_depth = 32;
|
||||
static int g_ops_per_batch = 0;
|
||||
static int g_threads_per_core = 1;
|
||||
static int g_time_in_sec = 5;
|
||||
static uint32_t g_crc32c_seed = 0;
|
||||
static uint32_t g_crc32c_chained_count = 1;
|
||||
static int g_fail_percent_goal = 0;
|
||||
static uint8_t g_fill_pattern = 255;
|
||||
static bool g_verify = false;
|
||||
@ -67,15 +64,8 @@ uint64_t g_capabilites;
|
||||
struct worker_thread;
|
||||
static void accel_done(void *ref, int status);
|
||||
|
||||
struct display_info {
|
||||
int core;
|
||||
int thread;
|
||||
};
|
||||
|
||||
struct ap_task {
|
||||
void *src;
|
||||
struct iovec *iovs;
|
||||
uint32_t iov_cnt;
|
||||
void *dst;
|
||||
void *dst2;
|
||||
struct worker_thread *worker;
|
||||
@ -84,14 +74,6 @@ struct ap_task {
|
||||
TAILQ_ENTRY(ap_task) link;
|
||||
};
|
||||
|
||||
struct accel_batch {
|
||||
int status;
|
||||
int cmd_count;
|
||||
struct spdk_accel_batch *batch;
|
||||
struct worker_thread *worker;
|
||||
TAILQ_ENTRY(accel_batch) link;
|
||||
};
|
||||
|
||||
struct worker_thread {
|
||||
struct spdk_io_channel *ch;
|
||||
uint64_t xfer_completed;
|
||||
@ -106,11 +88,6 @@ struct worker_thread {
|
||||
struct spdk_poller *is_draining_poller;
|
||||
struct spdk_poller *stop_poller;
|
||||
void *task_base;
|
||||
struct accel_batch *batch_base;
|
||||
struct display_info display;
|
||||
TAILQ_HEAD(, accel_batch) in_prep_batches;
|
||||
TAILQ_HEAD(, accel_batch) in_use_batches;
|
||||
TAILQ_HEAD(, accel_batch) to_submit_batches;
|
||||
};
|
||||
|
||||
static void
|
||||
@ -122,7 +99,6 @@ dump_user_config(struct spdk_app_opts *opts)
|
||||
printf("Workload Type: %s\n", g_workload_type);
|
||||
if (g_workload_selection == ACCEL_CRC32C) {
|
||||
printf("CRC-32C seed: %u\n", g_crc32c_seed);
|
||||
printf("vector size: %u\n", g_crc32c_chained_count);
|
||||
} else if (g_workload_selection == ACCEL_FILL) {
|
||||
printf("Fill pattern: 0x%x\n", g_fill_pattern);
|
||||
} else if ((g_workload_selection == ACCEL_COMPARE) && g_fail_percent_goal > 0) {
|
||||
@ -130,13 +106,7 @@ dump_user_config(struct spdk_app_opts *opts)
|
||||
}
|
||||
printf("Transfer size: %u bytes\n", g_xfer_size_bytes);
|
||||
printf("Queue depth: %u\n", g_queue_depth);
|
||||
printf("# threads/core: %u\n", g_threads_per_core);
|
||||
printf("Run time: %u seconds\n", g_time_in_sec);
|
||||
if (g_ops_per_batch > 0) {
|
||||
printf("Batching: %u operations\n", g_ops_per_batch);
|
||||
} else {
|
||||
printf("Batching: Disabled\n");
|
||||
}
|
||||
printf("Verify: %s\n\n", g_verify ? "Yes" : "No");
|
||||
}
|
||||
|
||||
@ -145,9 +115,7 @@ usage(void)
|
||||
{
|
||||
printf("accel_perf options:\n");
|
||||
printf("\t[-h help message]\n");
|
||||
printf("\t[-q queue depth per core]\n");
|
||||
printf("\t[-C for crc32c workload, use this value to configre the io vector size to test (default 1)\n");
|
||||
printf("\t[-T number of threads per core\n");
|
||||
printf("\t[-q queue depth]\n");
|
||||
printf("\t[-n number of channels]\n");
|
||||
printf("\t[-o transfer size in bytes]\n");
|
||||
printf("\t[-t time in seconds]\n");
|
||||
@ -156,25 +124,15 @@ usage(void)
|
||||
printf("\t[-P for compare workload, percentage of operations that should miscompare (percent, default 0)\n");
|
||||
printf("\t[-f for fill workload, use this BYTE value (default 255)\n");
|
||||
printf("\t[-y verify result if this switch is on]\n");
|
||||
printf("\t[-b batch this number of operations at a time (default 0 = disabled)]\n");
|
||||
}
|
||||
|
||||
static int
|
||||
parse_args(int argc, char *argv)
|
||||
{
|
||||
switch (argc) {
|
||||
case 'b':
|
||||
g_ops_per_batch = spdk_strtol(optarg, 10);
|
||||
break;
|
||||
case 'C':
|
||||
g_crc32c_chained_count = spdk_strtol(optarg, 10);
|
||||
break;
|
||||
case 'f':
|
||||
g_fill_pattern = (uint8_t)spdk_strtol(optarg, 10);
|
||||
break;
|
||||
case 'T':
|
||||
g_threads_per_core = spdk_strtol(optarg, 10);
|
||||
break;
|
||||
case 'o':
|
||||
g_xfer_size_bytes = spdk_strtol(optarg, 10);
|
||||
break;
|
||||
@ -211,24 +169,20 @@ parse_args(int argc, char *argv)
|
||||
usage();
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dump_result(void);
|
||||
static void
|
||||
unregister_worker(void *arg1)
|
||||
{
|
||||
struct worker_thread *worker = arg1;
|
||||
|
||||
free(worker->task_base);
|
||||
free(worker->batch_base);
|
||||
spdk_put_io_channel(worker->ch);
|
||||
pthread_mutex_lock(&g_workers_lock);
|
||||
assert(g_num_workers >= 1);
|
||||
if (--g_num_workers == 0) {
|
||||
pthread_mutex_unlock(&g_workers_lock);
|
||||
g_rc = dump_result();
|
||||
spdk_app_stop(0);
|
||||
}
|
||||
pthread_mutex_unlock(&g_workers_lock);
|
||||
@ -238,7 +192,6 @@ static int
|
||||
_get_task_data_bufs(struct ap_task *task)
|
||||
{
|
||||
uint32_t align = 0;
|
||||
uint32_t i = 0;
|
||||
|
||||
/* For dualcast, the DSA HW requires 4K alignment on destination addresses but
|
||||
* we do this for all engines to keep it simple.
|
||||
@ -247,38 +200,12 @@ _get_task_data_bufs(struct ap_task *task)
|
||||
align = ALIGN_4K;
|
||||
}
|
||||
|
||||
if (g_workload_selection == ACCEL_CRC32C) {
|
||||
assert(g_crc32c_chained_count > 0);
|
||||
task->iov_cnt = g_crc32c_chained_count;
|
||||
task->iovs = calloc(task->iov_cnt, sizeof(struct iovec));
|
||||
if (!task->iovs) {
|
||||
fprintf(stderr, "cannot allocated task->iovs fot task=%p\n", task);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
for (i = 0; i < task->iov_cnt; i++) {
|
||||
task->iovs[i].iov_base = spdk_dma_zmalloc(g_xfer_size_bytes, 0, NULL);
|
||||
if (task->iovs[i].iov_base == NULL) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
memset(task->iovs[i].iov_base, DATA_PATTERN, g_xfer_size_bytes);
|
||||
task->iovs[i].iov_len = g_xfer_size_bytes;
|
||||
}
|
||||
|
||||
} else {
|
||||
task->src = spdk_dma_zmalloc(g_xfer_size_bytes, 0, NULL);
|
||||
if (task->src == NULL) {
|
||||
fprintf(stderr, "Unable to alloc src buffer\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* For fill, set the entire src buffer so we can check if verify is enabled. */
|
||||
if (g_workload_selection == ACCEL_FILL) {
|
||||
memset(task->src, g_fill_pattern, g_xfer_size_bytes);
|
||||
} else {
|
||||
memset(task->src, DATA_PATTERN, g_xfer_size_bytes);
|
||||
}
|
||||
task->src = spdk_dma_zmalloc(g_xfer_size_bytes, 0, NULL);
|
||||
if (task->src == NULL) {
|
||||
fprintf(stderr, "Unable to alloc src buffer\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
memset(task->src, DATA_PATTERN, g_xfer_size_bytes);
|
||||
|
||||
task->dst = spdk_dma_zmalloc(g_xfer_size_bytes, align, NULL);
|
||||
if (task->dst == NULL) {
|
||||
@ -293,6 +220,11 @@ _get_task_data_bufs(struct ap_task *task)
|
||||
memset(task->dst, ~DATA_PATTERN, g_xfer_size_bytes);
|
||||
}
|
||||
|
||||
/* For fill, set the entire src buffer so we can check if verify is enabled. */
|
||||
if (g_workload_selection == ACCEL_FILL) {
|
||||
memset(task->src, g_fill_pattern, g_xfer_size_bytes);
|
||||
}
|
||||
|
||||
if (g_workload_selection == ACCEL_DUALCAST) {
|
||||
task->dst2 = spdk_dma_zmalloc(g_xfer_size_bytes, align, NULL);
|
||||
if (task->dst2 == NULL) {
|
||||
@ -323,7 +255,8 @@ _get_task(struct worker_thread *worker)
|
||||
return task;
|
||||
}
|
||||
|
||||
/* Submit one operation using the same ap task that just completed. */
|
||||
static void accel_done(void *ref, int status);
|
||||
|
||||
static void
|
||||
_submit_single(struct worker_thread *worker, struct ap_task *task)
|
||||
{
|
||||
@ -343,9 +276,9 @@ _submit_single(struct worker_thread *worker, struct ap_task *task)
|
||||
g_xfer_size_bytes, accel_done, task);
|
||||
break;
|
||||
case ACCEL_CRC32C:
|
||||
rc = spdk_accel_submit_crc32cv(worker->ch, (uint32_t *)task->dst,
|
||||
task->iovs, task->iov_cnt, g_crc32c_seed,
|
||||
accel_done, task);
|
||||
rc = spdk_accel_submit_crc32c(worker->ch, (uint32_t *)task->dst,
|
||||
task->src, g_crc32c_seed,
|
||||
g_xfer_size_bytes, accel_done, task);
|
||||
break;
|
||||
case ACCEL_COMPARE:
|
||||
random_num = rand() % 100;
|
||||
@ -375,15 +308,10 @@ _submit_single(struct worker_thread *worker, struct ap_task *task)
|
||||
}
|
||||
|
||||
static int
|
||||
_batch_prep_cmd(struct worker_thread *worker, struct ap_task *task,
|
||||
struct accel_batch *worker_batch)
|
||||
_batch_prep_cmd(struct worker_thread *worker, struct ap_task *task, struct spdk_accel_batch *batch)
|
||||
{
|
||||
struct spdk_accel_batch *batch = worker_batch->batch;
|
||||
int rc = 0;
|
||||
|
||||
worker_batch->cmd_count++;
|
||||
assert(worker_batch->cmd_count <= g_ops_per_batch);
|
||||
|
||||
switch (g_workload_selection) {
|
||||
case ACCEL_COPY:
|
||||
rc = spdk_accel_batch_prep_copy(worker->ch, batch, task->dst,
|
||||
@ -403,8 +331,8 @@ _batch_prep_cmd(struct worker_thread *worker, struct ap_task *task,
|
||||
g_xfer_size_bytes, accel_done, task);
|
||||
break;
|
||||
case ACCEL_CRC32C:
|
||||
rc = spdk_accel_batch_prep_crc32cv(worker->ch, batch, (uint32_t *)task->dst,
|
||||
task->iovs, task->iov_cnt, g_crc32c_seed, accel_done, task);
|
||||
rc = spdk_accel_batch_prep_crc32c(worker->ch, batch, (uint32_t *)task->dst,
|
||||
task->src, g_crc32c_seed, g_xfer_size_bytes, accel_done, task);
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
@ -415,162 +343,23 @@ _batch_prep_cmd(struct worker_thread *worker, struct ap_task *task,
|
||||
}
|
||||
|
||||
static void
|
||||
_free_task_buffers(struct ap_task *task)
|
||||
_free_task(struct ap_task *task)
|
||||
{
|
||||
uint32_t i;
|
||||
|
||||
if (g_workload_selection == ACCEL_CRC32C) {
|
||||
if (task->iovs) {
|
||||
for (i = 0; i < task->iov_cnt; i++) {
|
||||
if (task->iovs[i].iov_base) {
|
||||
spdk_dma_free(task->iovs[i].iov_base);
|
||||
}
|
||||
}
|
||||
free(task->iovs);
|
||||
}
|
||||
} else {
|
||||
spdk_dma_free(task->src);
|
||||
}
|
||||
|
||||
spdk_dma_free(task->src);
|
||||
spdk_dma_free(task->dst);
|
||||
if (g_workload_selection == ACCEL_DUALCAST) {
|
||||
spdk_dma_free(task->dst2);
|
||||
}
|
||||
}
|
||||
|
||||
static void _batch_done(void *cb_arg);
|
||||
static void
|
||||
_build_batch(struct worker_thread *worker, struct ap_task *task)
|
||||
{
|
||||
struct accel_batch *worker_batch = NULL;
|
||||
int rc;
|
||||
|
||||
assert(!TAILQ_EMPTY(&worker->in_prep_batches));
|
||||
|
||||
worker_batch = TAILQ_FIRST(&worker->in_prep_batches);
|
||||
|
||||
/* If an accel batch hasn't been created yet do so now. */
|
||||
if (worker_batch->batch == NULL) {
|
||||
worker_batch->batch = spdk_accel_batch_create(worker->ch);
|
||||
if (worker_batch->batch == NULL) {
|
||||
fprintf(stderr, "error unable to create new batch\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Prep the command re-using the last completed command's task */
|
||||
rc = _batch_prep_cmd(worker, task, worker_batch);
|
||||
if (rc) {
|
||||
fprintf(stderr, "error preping command for batch\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* If this batch is full move it to the to_submit list so it gets
|
||||
* submitted as batches complete.
|
||||
*/
|
||||
if (worker_batch->cmd_count == g_ops_per_batch) {
|
||||
TAILQ_REMOVE(&worker->in_prep_batches, worker_batch, link);
|
||||
TAILQ_INSERT_TAIL(&worker->to_submit_batches, worker_batch, link);
|
||||
}
|
||||
|
||||
return;
|
||||
error:
|
||||
spdk_accel_batch_cancel(worker->ch, worker_batch->batch);
|
||||
|
||||
}
|
||||
|
||||
static void batch_done(void *cb_arg, int status);
|
||||
static void
|
||||
_drain_batch(struct worker_thread *worker)
|
||||
{
|
||||
struct accel_batch *worker_batch, *tmp;
|
||||
int rc;
|
||||
|
||||
/* submit any batches that were being built up. */
|
||||
TAILQ_FOREACH_SAFE(worker_batch, &worker->in_prep_batches, link, tmp) {
|
||||
if (worker_batch->cmd_count == 0) {
|
||||
continue;
|
||||
}
|
||||
worker->current_queue_depth += worker_batch->cmd_count + 1;
|
||||
|
||||
TAILQ_REMOVE(&worker->in_prep_batches, worker_batch, link);
|
||||
TAILQ_INSERT_TAIL(&worker->in_use_batches, worker_batch, link);
|
||||
rc = spdk_accel_batch_submit(worker->ch, worker_batch->batch, batch_done, worker_batch);
|
||||
if (rc == 0) {
|
||||
worker_batch->cmd_count = 0;
|
||||
} else {
|
||||
fprintf(stderr, "error sending final batch\n");
|
||||
worker->current_queue_depth -= worker_batch->cmd_count + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
_batch_done(void *cb_arg)
|
||||
{
|
||||
struct accel_batch *worker_batch = (struct accel_batch *)cb_arg;
|
||||
struct worker_thread *worker = worker_batch->worker;
|
||||
int rc;
|
||||
|
||||
assert(TAILQ_EMPTY(&worker->in_use_batches) == 0);
|
||||
|
||||
if (worker_batch->status) {
|
||||
SPDK_ERRLOG("error %d\n", worker_batch->status);
|
||||
}
|
||||
|
||||
worker->current_queue_depth--;
|
||||
TAILQ_REMOVE(&worker->in_use_batches, worker_batch, link);
|
||||
TAILQ_INSERT_TAIL(&worker->in_prep_batches, worker_batch, link);
|
||||
worker_batch->batch = NULL;
|
||||
worker_batch->cmd_count = 0;
|
||||
|
||||
if (!worker->is_draining) {
|
||||
worker_batch = TAILQ_FIRST(&worker->to_submit_batches);
|
||||
if (worker_batch != NULL) {
|
||||
|
||||
assert(worker_batch->cmd_count == g_ops_per_batch);
|
||||
|
||||
/* Add one for the batch command itself. */
|
||||
worker->current_queue_depth += g_ops_per_batch + 1;
|
||||
TAILQ_REMOVE(&worker->to_submit_batches, worker_batch, link);
|
||||
TAILQ_INSERT_TAIL(&worker->in_use_batches, worker_batch, link);
|
||||
|
||||
rc = spdk_accel_batch_submit(worker->ch, worker_batch->batch, batch_done, worker_batch);
|
||||
if (rc) {
|
||||
fprintf(stderr, "error ending batch\n");
|
||||
worker->current_queue_depth -= g_ops_per_batch + 1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
_drain_batch(worker);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
batch_done(void *cb_arg, int status)
|
||||
{
|
||||
struct accel_batch *worker_batch = (struct accel_batch *)cb_arg;
|
||||
struct ap_task *task = (struct ap_task *)cb_arg;
|
||||
struct worker_thread *worker = task->worker;
|
||||
|
||||
assert(worker_batch->worker);
|
||||
|
||||
worker_batch->status = status;
|
||||
spdk_thread_send_msg(worker_batch->worker->thread, _batch_done, worker_batch);
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
_update_crc32c_iov(struct iovec *iov, int iovcnt, uint32_t crc32c)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < iovcnt; i++) {
|
||||
assert(iov[i].iov_base != NULL);
|
||||
assert(iov[i].iov_len != 0);
|
||||
crc32c = spdk_crc32c_update(iov[i].iov_base, iov[i].iov_len, crc32c);
|
||||
|
||||
}
|
||||
return crc32c;
|
||||
worker->current_queue_depth--;
|
||||
TAILQ_INSERT_TAIL(&worker->tasks_pool, task, link);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -586,7 +375,8 @@ _accel_done(void *arg1)
|
||||
if (g_verify && task->status == 0) {
|
||||
switch (g_workload_selection) {
|
||||
case ACCEL_CRC32C:
|
||||
sw_crc32c = _update_crc32c_iov(task->iovs, task->iov_cnt, ~g_crc32c_seed);
|
||||
/* calculate sw CRC-32C and compare to sw aceel result. */
|
||||
sw_crc32c = spdk_crc32c_update(task->src, g_xfer_size_bytes, ~g_crc32c_seed);
|
||||
if (*(uint32_t *)task->dst != sw_crc32c) {
|
||||
SPDK_NOTICELOG("CRC-32C miscompare\n");
|
||||
worker->xfer_failed++;
|
||||
@ -626,7 +416,7 @@ _accel_done(void *arg1)
|
||||
assert(task->status != 0);
|
||||
worker->injected_miscompares++;
|
||||
} else if (task->status) {
|
||||
/* Expected to pass but the accel engine reported an error (ex: COMPARE operation). */
|
||||
/* Expected to pass but API reported error. */
|
||||
worker->xfer_failed++;
|
||||
}
|
||||
|
||||
@ -634,16 +424,8 @@ _accel_done(void *arg1)
|
||||
worker->current_queue_depth--;
|
||||
|
||||
if (!worker->is_draining) {
|
||||
if (g_ops_per_batch == 0) {
|
||||
_submit_single(worker, task);
|
||||
worker->current_queue_depth++;
|
||||
} else {
|
||||
_build_batch(worker, task);
|
||||
}
|
||||
} else if (g_ops_per_batch > 0) {
|
||||
_drain_batch(worker);
|
||||
} else {
|
||||
TAILQ_INSERT_TAIL(&worker->tasks_pool, task, link);
|
||||
_submit_single(worker, task);
|
||||
worker->current_queue_depth++;
|
||||
}
|
||||
}
|
||||
|
||||
@ -656,8 +438,8 @@ dump_result(void)
|
||||
uint64_t total_xfer_per_sec, total_bw_in_MiBps;
|
||||
struct worker_thread *worker = g_workers;
|
||||
|
||||
printf("\nCore,Thread Transfers Bandwidth Failed Miscompares\n");
|
||||
printf("------------------------------------------------------------------------\n");
|
||||
printf("\nCore Transfers Bandwidth Failed Miscompares\n");
|
||||
printf("-----------------------------------------------------------------\n");
|
||||
while (worker != NULL) {
|
||||
|
||||
uint64_t xfer_per_sec = worker->xfer_completed / g_time_in_sec;
|
||||
@ -669,8 +451,8 @@ dump_result(void)
|
||||
total_miscompared += worker->injected_miscompares;
|
||||
|
||||
if (xfer_per_sec) {
|
||||
printf("%u,%u%17" PRIu64 "/s%9" PRIu64 " MiB/s%7" PRIu64 " %11" PRIu64 "\n",
|
||||
worker->display.core, worker->display.thread, xfer_per_sec,
|
||||
printf("%10d%12" PRIu64 "/s%8" PRIu64 " MiB/s%11" PRIu64 " %11" PRIu64 "\n",
|
||||
worker->core, xfer_per_sec,
|
||||
bw_in_MiBps, worker->xfer_failed, worker->injected_miscompares);
|
||||
}
|
||||
|
||||
@ -681,34 +463,26 @@ dump_result(void)
|
||||
total_bw_in_MiBps = (total_completed * g_xfer_size_bytes) /
|
||||
(g_time_in_sec * 1024 * 1024);
|
||||
|
||||
printf("=========================================================================\n");
|
||||
printf("Total:%15" PRIu64 "/s%9" PRIu64 " MiB/s%6" PRIu64 " %11" PRIu64"\n\n",
|
||||
printf("==================================================================\n");
|
||||
printf("Total:%16" PRIu64 "/s%8" PRIu64 " MiB/s%11" PRIu64 " %11" PRIu64"\n\n",
|
||||
total_xfer_per_sec, total_bw_in_MiBps, total_failed, total_miscompared);
|
||||
|
||||
return total_failed ? 1 : 0;
|
||||
}
|
||||
|
||||
static inline void
|
||||
_free_task_buffers_in_pool(struct worker_thread *worker)
|
||||
{
|
||||
struct ap_task *task;
|
||||
|
||||
assert(worker);
|
||||
while ((task = TAILQ_FIRST(&worker->tasks_pool))) {
|
||||
TAILQ_REMOVE(&worker->tasks_pool, task, link);
|
||||
_free_task_buffers(task);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
_check_draining(void *arg)
|
||||
{
|
||||
struct worker_thread *worker = arg;
|
||||
struct ap_task *task;
|
||||
|
||||
assert(worker);
|
||||
|
||||
if (worker->current_queue_depth == 0) {
|
||||
_free_task_buffers_in_pool(worker);
|
||||
while ((task = TAILQ_FIRST(&worker->tasks_pool))) {
|
||||
TAILQ_REMOVE(&worker->tasks_pool, task, link);
|
||||
_free_task(task);
|
||||
}
|
||||
spdk_poller_unregister(&worker->is_draining_poller);
|
||||
unregister_worker(worker);
|
||||
}
|
||||
@ -732,83 +506,36 @@ _worker_stop(void *arg)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
_init_thread_done(void *ctx)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
_init_thread(void *arg1)
|
||||
{
|
||||
struct worker_thread *worker;
|
||||
struct ap_task *task;
|
||||
int i, rc, num_batches;
|
||||
int max_per_batch;
|
||||
int i, rc, max_per_batch, batch_count, num_tasks;
|
||||
int remaining = g_queue_depth;
|
||||
int num_tasks = g_queue_depth;
|
||||
struct accel_batch *tmp;
|
||||
struct accel_batch *worker_batch = NULL;
|
||||
struct display_info *display = arg1;
|
||||
struct spdk_accel_batch *batch, *new_batch;
|
||||
|
||||
worker = calloc(1, sizeof(*worker));
|
||||
if (worker == NULL) {
|
||||
fprintf(stderr, "Unable to allocate worker\n");
|
||||
free(display);
|
||||
return;
|
||||
}
|
||||
|
||||
worker->display.core = display->core;
|
||||
worker->display.thread = display->thread;
|
||||
free(display);
|
||||
worker->core = spdk_env_get_current_core();
|
||||
worker->thread = spdk_get_thread();
|
||||
pthread_mutex_lock(&g_workers_lock);
|
||||
g_num_workers++;
|
||||
worker->next = g_workers;
|
||||
g_workers = worker;
|
||||
pthread_mutex_unlock(&g_workers_lock);
|
||||
worker->ch = spdk_accel_engine_get_io_channel();
|
||||
|
||||
max_per_batch = spdk_accel_batch_get_max(worker->ch);
|
||||
assert(max_per_batch > 0);
|
||||
num_tasks = g_queue_depth + spdk_divide_round_up(g_queue_depth, max_per_batch);
|
||||
|
||||
TAILQ_INIT(&worker->tasks_pool);
|
||||
|
||||
if (g_ops_per_batch > 0) {
|
||||
|
||||
max_per_batch = spdk_accel_batch_get_max(worker->ch);
|
||||
assert(max_per_batch > 0);
|
||||
|
||||
if (g_ops_per_batch > max_per_batch) {
|
||||
fprintf(stderr, "Reducing requested batch amount to max supported of %d\n", max_per_batch);
|
||||
g_ops_per_batch = max_per_batch;
|
||||
}
|
||||
|
||||
if (g_ops_per_batch > g_queue_depth) {
|
||||
fprintf(stderr, "Batch amount > queue depth, resetting to %d\n", g_queue_depth);
|
||||
g_ops_per_batch = g_queue_depth;
|
||||
}
|
||||
|
||||
TAILQ_INIT(&worker->in_prep_batches);
|
||||
TAILQ_INIT(&worker->to_submit_batches);
|
||||
TAILQ_INIT(&worker->in_use_batches);
|
||||
|
||||
/* A worker_batch will live on one of 3 lists:
|
||||
* IN_PREP: as individual IOs complete new ones are built on on a
|
||||
* worker_batch on this list until it reaches g_ops_per_batch.
|
||||
* TO_SUBMIT: as batches are built up on IO completion they are moved
|
||||
* to this list once they are full. This list is used in
|
||||
* batch completion to start new batches.
|
||||
* IN_USE: the worker_batch is outstanding and will be moved to in prep
|
||||
* list when the batch is completed.
|
||||
*
|
||||
* So we need enough to cover Q depth loading and then one to replace
|
||||
* each one of those and for when everything is outstanding there needs
|
||||
* to be one extra batch to build up while the last batch is completing
|
||||
* IO but before it's completed the batch command.
|
||||
*/
|
||||
num_batches = (g_queue_depth / g_ops_per_batch * 2) + 1;
|
||||
worker->batch_base = calloc(num_batches, sizeof(struct accel_batch));
|
||||
worker_batch = worker->batch_base;
|
||||
for (i = 0; i < num_batches; i++) {
|
||||
worker_batch->worker = worker;
|
||||
TAILQ_INSERT_TAIL(&worker->in_prep_batches, worker_batch, link);
|
||||
worker_batch++;
|
||||
}
|
||||
}
|
||||
|
||||
worker->task_base = calloc(num_tasks, sizeof(struct ap_task));
|
||||
if (worker->task_base == NULL) {
|
||||
fprintf(stderr, "Could not allocate task base.\n");
|
||||
@ -829,53 +556,66 @@ _init_thread(void *arg1)
|
||||
worker->stop_poller = SPDK_POLLER_REGISTER(_worker_stop, worker,
|
||||
g_time_in_sec * 1000000ULL);
|
||||
|
||||
/* If batching is enabled load up to the full Q depth before
|
||||
* processing any completions, then ping pong between two batches,
|
||||
* one processing and one being built up for when the other completes.
|
||||
*/
|
||||
if (g_ops_per_batch > 0) {
|
||||
do {
|
||||
worker_batch = TAILQ_FIRST(&worker->in_prep_batches);
|
||||
if (worker_batch == NULL) {
|
||||
goto error;
|
||||
}
|
||||
g_workers = worker;
|
||||
pthread_mutex_lock(&g_workers_lock);
|
||||
g_num_workers++;
|
||||
pthread_mutex_unlock(&g_workers_lock);
|
||||
|
||||
worker_batch->batch = spdk_accel_batch_create(worker->ch);
|
||||
if (worker_batch->batch == NULL) {
|
||||
raise(SIGINT);
|
||||
/* Batching is only possible if there is at least 2 operations. */
|
||||
if (g_queue_depth > 1) {
|
||||
|
||||
/* Outter loop sets up each batch command, inner loop populates the
|
||||
* batch descriptors.
|
||||
*/
|
||||
do {
|
||||
new_batch = spdk_accel_batch_create(worker->ch);
|
||||
if (new_batch == NULL) {
|
||||
break;
|
||||
}
|
||||
|
||||
for (i = 0; i < g_ops_per_batch; i++) {
|
||||
batch = new_batch;
|
||||
batch_count = 0;
|
||||
|
||||
do {
|
||||
task = _get_task(worker);
|
||||
if (task == NULL) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
rc = _batch_prep_cmd(worker, task, worker_batch);
|
||||
rc = _batch_prep_cmd(worker, task, batch);
|
||||
if (rc) {
|
||||
fprintf(stderr, "error preping command\n");
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
remaining--;
|
||||
batch_count++;
|
||||
} while (batch_count < max_per_batch && remaining > 0);
|
||||
|
||||
/* for the batch operation itself. */
|
||||
task->worker->current_queue_depth++;
|
||||
TAILQ_REMOVE(&worker->in_prep_batches, worker_batch, link);
|
||||
TAILQ_INSERT_TAIL(&worker->in_use_batches, worker_batch, link);
|
||||
|
||||
rc = spdk_accel_batch_submit(worker->ch, worker_batch->batch, batch_done, worker_batch);
|
||||
if (rc) {
|
||||
fprintf(stderr, "error ending batch\n");
|
||||
/* Now send the batch command. */
|
||||
task = _get_task(worker);
|
||||
if (task == NULL) {
|
||||
goto error;
|
||||
}
|
||||
assert(remaining >= g_ops_per_batch);
|
||||
remaining -= g_ops_per_batch;
|
||||
} while (remaining > 0);
|
||||
|
||||
rc = spdk_accel_batch_submit(worker->ch, batch, batch_done, task);
|
||||
if (rc) {
|
||||
fprintf(stderr, "error ending batch %d\n", rc);
|
||||
goto error;
|
||||
}
|
||||
/* We can't build a batch unless it has 2 descriptors (per spec). */
|
||||
} while (remaining > 1);
|
||||
|
||||
/* If there are no more left, we're done. */
|
||||
if (remaining == 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Submit as singles when no batching is enabled or we ran out of batches. */
|
||||
/* For engines that don't support batch or for the odd event that
|
||||
* a batch ends with only one descriptor left.
|
||||
*/
|
||||
for (i = 0; i < remaining; i++) {
|
||||
|
||||
task = _get_task(worker);
|
||||
if (task == NULL) {
|
||||
goto error;
|
||||
@ -885,15 +625,10 @@ _init_thread(void *arg1)
|
||||
}
|
||||
return;
|
||||
error:
|
||||
if (worker_batch && worker_batch->batch) {
|
||||
TAILQ_FOREACH_SAFE(worker_batch, &worker->in_use_batches, link, tmp) {
|
||||
spdk_accel_batch_cancel(worker->ch, worker_batch->batch);
|
||||
TAILQ_REMOVE(&worker->in_use_batches, worker_batch, link);
|
||||
}
|
||||
while ((task = TAILQ_FIRST(&worker->tasks_pool))) {
|
||||
TAILQ_REMOVE(&worker->tasks_pool, task, link);
|
||||
_free_task(task);
|
||||
}
|
||||
|
||||
_free_task_buffers_in_pool(worker);
|
||||
free(worker->batch_base);
|
||||
free(worker->task_base);
|
||||
free(worker);
|
||||
spdk_app_stop(-1);
|
||||
@ -915,12 +650,6 @@ static void
|
||||
accel_perf_start(void *arg1)
|
||||
{
|
||||
struct spdk_io_channel *accel_ch;
|
||||
struct spdk_cpuset tmp_cpumask = {};
|
||||
char thread_name[32];
|
||||
uint32_t i;
|
||||
int j;
|
||||
struct spdk_thread *thread;
|
||||
struct display_info *display;
|
||||
|
||||
accel_ch = spdk_accel_engine_get_io_channel();
|
||||
g_capabilites = spdk_accel_get_capabilities(accel_ch);
|
||||
@ -932,29 +661,13 @@ accel_perf_start(void *arg1)
|
||||
}
|
||||
|
||||
g_tsc_rate = spdk_get_ticks_hz();
|
||||
g_tsc_us_rate = g_tsc_rate / (1000 * 1000);
|
||||
g_tsc_end = spdk_get_ticks() + g_time_in_sec * g_tsc_rate;
|
||||
|
||||
printf("Running for %d seconds...\n", g_time_in_sec);
|
||||
fflush(stdout);
|
||||
|
||||
/* Create worker threads for each core that was specified. */
|
||||
SPDK_ENV_FOREACH_CORE(i) {
|
||||
for (j = 0; j < g_threads_per_core; j++) {
|
||||
snprintf(thread_name, sizeof(thread_name), "ap_worker_%u_%u", i, j);
|
||||
spdk_cpuset_zero(&tmp_cpumask);
|
||||
spdk_cpuset_set_cpu(&tmp_cpumask, i, true);
|
||||
thread = spdk_thread_create(thread_name, &tmp_cpumask);
|
||||
display = calloc(1, sizeof(*display));
|
||||
if (display == NULL) {
|
||||
fprintf(stderr, "Unable to allocate memory\n");
|
||||
spdk_app_stop(-1);
|
||||
return;
|
||||
}
|
||||
display->core = i;
|
||||
display->thread = j;
|
||||
spdk_thread_send_msg(thread, _init_thread, display);
|
||||
}
|
||||
}
|
||||
spdk_for_each_thread(_init_thread, NULL, _init_thread_done);
|
||||
}
|
||||
|
||||
int
|
||||
@ -962,13 +675,14 @@ main(int argc, char **argv)
|
||||
{
|
||||
struct spdk_app_opts opts = {};
|
||||
struct worker_thread *worker, *tmp;
|
||||
int rc = 0;
|
||||
|
||||
pthread_mutex_init(&g_workers_lock, NULL);
|
||||
spdk_app_opts_init(&opts, sizeof(opts));
|
||||
opts.reactor_mask = "0x1";
|
||||
if (spdk_app_parse_args(argc, argv, &opts, "C:o:q:t:yw:P:f:b:T:", NULL, parse_args,
|
||||
if (spdk_app_parse_args(argc, argv, &opts, "o:q:t:yw:P:f:", NULL, parse_args,
|
||||
usage) != SPDK_APP_PARSE_ARGS_SUCCESS) {
|
||||
g_rc = -1;
|
||||
rc = -1;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
@ -978,28 +692,16 @@ main(int argc, char **argv)
|
||||
(g_workload_selection != ACCEL_COMPARE) &&
|
||||
(g_workload_selection != ACCEL_DUALCAST)) {
|
||||
usage();
|
||||
g_rc = -1;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (g_ops_per_batch > 0 && (g_queue_depth % g_ops_per_batch > 0)) {
|
||||
fprintf(stdout, "batch size must be a multiple of queue depth\n");
|
||||
usage();
|
||||
g_rc = -1;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (g_workload_selection == ACCEL_CRC32C &&
|
||||
g_crc32c_chained_count == 0) {
|
||||
usage();
|
||||
g_rc = -1;
|
||||
rc = -1;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
dump_user_config(&opts);
|
||||
g_rc = spdk_app_start(&opts, accel_perf_start, NULL);
|
||||
if (g_rc) {
|
||||
rc = spdk_app_start(&opts, accel_perf_start, NULL);
|
||||
if (rc) {
|
||||
SPDK_ERRLOG("ERROR starting application\n");
|
||||
} else {
|
||||
dump_result();
|
||||
}
|
||||
|
||||
pthread_mutex_destroy(&g_workers_lock);
|
||||
@ -1012,5 +714,5 @@ main(int argc, char **argv)
|
||||
}
|
||||
cleanup:
|
||||
spdk_app_fini();
|
||||
return g_rc;
|
||||
return rc;
|
||||
}
|
||||
|
@ -59,7 +59,7 @@ struct spdk_fio_options {
|
||||
char *conf;
|
||||
char *json_conf;
|
||||
unsigned mem_mb;
|
||||
int mem_single_seg;
|
||||
bool mem_single_seg;
|
||||
};
|
||||
|
||||
struct spdk_fio_request {
|
||||
@ -789,7 +789,6 @@ static struct fio_option options[] = {
|
||||
.type = FIO_OPT_BOOL,
|
||||
.off1 = offsetof(struct spdk_fio_options, mem_single_seg),
|
||||
.help = "If set to 1, SPDK will use just a single hugetlbfs file",
|
||||
.def = "0",
|
||||
.category = FIO_OPT_C_ENGINE,
|
||||
.group = FIO_OPT_G_INVALID,
|
||||
},
|
||||
|
@ -38,7 +38,7 @@
|
||||
#include "spdk/event.h"
|
||||
#include "spdk/log.h"
|
||||
#include "spdk/string.h"
|
||||
#include "spdk/bdev_zone.h"
|
||||
#include "spdk/bdev_module.h"
|
||||
|
||||
static char *g_bdev_name = "Malloc0";
|
||||
|
||||
@ -191,50 +191,6 @@ hello_bdev_event_cb(enum spdk_bdev_event_type type, struct spdk_bdev *bdev,
|
||||
SPDK_NOTICELOG("Unsupported bdev event: type %d\n", type);
|
||||
}
|
||||
|
||||
static void
|
||||
reset_zone_complete(struct spdk_bdev_io *bdev_io, bool success, void *cb_arg)
|
||||
{
|
||||
struct hello_context_t *hello_context = cb_arg;
|
||||
|
||||
/* Complete the I/O */
|
||||
spdk_bdev_free_io(bdev_io);
|
||||
|
||||
if (!success) {
|
||||
SPDK_ERRLOG("bdev io reset zone error: %d\n", EIO);
|
||||
spdk_put_io_channel(hello_context->bdev_io_channel);
|
||||
spdk_bdev_close(hello_context->bdev_desc);
|
||||
spdk_app_stop(-1);
|
||||
return;
|
||||
}
|
||||
|
||||
hello_write(hello_context);
|
||||
}
|
||||
|
||||
static void
|
||||
hello_reset_zone(void *arg)
|
||||
{
|
||||
struct hello_context_t *hello_context = arg;
|
||||
int rc = 0;
|
||||
|
||||
rc = spdk_bdev_zone_management(hello_context->bdev_desc, hello_context->bdev_io_channel,
|
||||
0, SPDK_BDEV_ZONE_RESET, reset_zone_complete, hello_context);
|
||||
|
||||
if (rc == -ENOMEM) {
|
||||
SPDK_NOTICELOG("Queueing io\n");
|
||||
/* In case we cannot perform I/O now, queue I/O */
|
||||
hello_context->bdev_io_wait.bdev = hello_context->bdev;
|
||||
hello_context->bdev_io_wait.cb_fn = hello_reset_zone;
|
||||
hello_context->bdev_io_wait.cb_arg = hello_context;
|
||||
spdk_bdev_queue_io_wait(hello_context->bdev, hello_context->bdev_io_channel,
|
||||
&hello_context->bdev_io_wait);
|
||||
} else if (rc) {
|
||||
SPDK_ERRLOG("%s error while resetting zone: %d\n", spdk_strerror(-rc), rc);
|
||||
spdk_put_io_channel(hello_context->bdev_io_channel);
|
||||
spdk_bdev_close(hello_context->bdev_desc);
|
||||
spdk_app_stop(-1);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Our initial event that kicks off everything from main().
|
||||
*/
|
||||
@ -294,12 +250,6 @@ hello_start(void *arg1)
|
||||
}
|
||||
snprintf(hello_context->buff, blk_size, "%s", "Hello World!\n");
|
||||
|
||||
if (spdk_bdev_is_zoned(hello_context->bdev)) {
|
||||
hello_reset_zone(hello_context);
|
||||
/* If bdev is zoned, the callback, reset_zone_complete, will call hello_write() */
|
||||
return;
|
||||
}
|
||||
|
||||
hello_write(hello_context);
|
||||
}
|
||||
|
||||
|
@ -1,15 +0,0 @@
|
||||
from rpc.client import print_json
|
||||
|
||||
|
||||
def reactor_set_interrupt_mode(args):
|
||||
params = {'lcore': args.lcore, 'disable_interrupt': args.disable_interrupt}
|
||||
return args.client.call('reactor_set_interrupt_mode', params)
|
||||
|
||||
|
||||
def spdk_rpc_plugin_initialize(subparsers):
|
||||
p = subparsers.add_parser('reactor_set_interrupt_mode',
|
||||
help="""Set reactor to interrupt or back to poll mode.""")
|
||||
p.add_argument('lcore', type=int, help='lcore of the reactor')
|
||||
p.add_argument('-d', '--disable-interrupt', dest='disable_interrupt', action='store_true',
|
||||
help='Set reactor back to poll mode')
|
||||
p.set_defaults(func=reactor_set_interrupt_mode)
|
@ -35,70 +35,6 @@
|
||||
#include "spdk/conf.h"
|
||||
#include "spdk/event.h"
|
||||
#include "spdk/vhost.h"
|
||||
#include "spdk/json.h"
|
||||
#include "spdk/jsonrpc.h"
|
||||
#include "spdk/rpc.h"
|
||||
#include "spdk/env.h"
|
||||
|
||||
#include "spdk_internal/event.h"
|
||||
|
||||
struct rpc_reactor_set_interrupt_mode {
|
||||
int32_t lcore;
|
||||
bool disable_interrupt;
|
||||
};
|
||||
|
||||
static const struct spdk_json_object_decoder rpc_reactor_set_interrupt_mode_decoders[] = {
|
||||
{"lcore", offsetof(struct rpc_reactor_set_interrupt_mode, lcore), spdk_json_decode_int32},
|
||||
{"disable_interrupt", offsetof(struct rpc_reactor_set_interrupt_mode, disable_interrupt), spdk_json_decode_bool},
|
||||
};
|
||||
|
||||
static void
|
||||
rpc_reactor_set_interrupt_mode_cb(void *cb_arg)
|
||||
{
|
||||
struct spdk_jsonrpc_request *request = cb_arg;
|
||||
|
||||
SPDK_NOTICELOG("complete reactor switch\n");
|
||||
|
||||
spdk_jsonrpc_send_bool_response(request, true);
|
||||
}
|
||||
|
||||
static void
|
||||
rpc_reactor_set_interrupt_mode(struct spdk_jsonrpc_request *request,
|
||||
const struct spdk_json_val *params)
|
||||
{
|
||||
struct rpc_reactor_set_interrupt_mode req = {};
|
||||
int rc;
|
||||
|
||||
if (spdk_json_decode_object(params, rpc_reactor_set_interrupt_mode_decoders,
|
||||
SPDK_COUNTOF(rpc_reactor_set_interrupt_mode_decoders),
|
||||
&req)) {
|
||||
SPDK_ERRLOG("spdk_json_decode_object failed\n");
|
||||
spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
|
||||
"spdk_json_decode_object failed");
|
||||
return;
|
||||
}
|
||||
|
||||
SPDK_NOTICELOG("RPC Start to %s interrupt mode on reactor %d.\n",
|
||||
req.disable_interrupt ? "disable" : "enable", req.lcore);
|
||||
if (req.lcore >= (int64_t)spdk_env_get_first_core() &&
|
||||
req.lcore <= (int64_t)spdk_env_get_last_core()) {
|
||||
rc = spdk_reactor_set_interrupt_mode(req.lcore, !req.disable_interrupt,
|
||||
rpc_reactor_set_interrupt_mode_cb, request);
|
||||
if (rc) {
|
||||
goto err;
|
||||
}
|
||||
} else {
|
||||
goto err;
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
err:
|
||||
spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS,
|
||||
"Invalid parameters");
|
||||
}
|
||||
/* private */ SPDK_RPC_REGISTER("reactor_set_interrupt_mode", rpc_reactor_set_interrupt_mode,
|
||||
SPDK_RPC_RUNTIME)
|
||||
|
||||
static void
|
||||
interrupt_tgt_usage(void)
|
||||
|
@ -35,7 +35,7 @@ SPDK_ROOT_DIR := $(abspath $(CURDIR)/../..)
|
||||
include $(SPDK_ROOT_DIR)/mk/spdk.common.mk
|
||||
|
||||
DIRS-y += hello_world identify perf reconnect nvme_manage arbitration \
|
||||
hotplug cmb_copy abort pmr_persistence
|
||||
hotplug cmb_copy abort
|
||||
|
||||
DIRS-$(CONFIG_FIO_PLUGIN) += fio_plugin
|
||||
|
||||
|
@ -642,7 +642,7 @@ parse_args(int argc, char **argv)
|
||||
long int val;
|
||||
int rc;
|
||||
|
||||
while ((op = getopt(argc, argv, "a:c:i:o:q:r:s:t:w:GM:T:")) != -1) {
|
||||
while ((op = getopt(argc, argv, "a:c:i:o:q:r:s:t:w:M:")) != -1) {
|
||||
switch (op) {
|
||||
case 'a':
|
||||
case 'i':
|
||||
|
@ -139,17 +139,6 @@ then you can reset all zones before fio start running its jobs by using the engi
|
||||
|
||||
--initial_zone_reset=1
|
||||
|
||||
## Zone Append
|
||||
|
||||
When running FIO against a Zoned Namespace you need to specify --iodepth=1 to avoid
|
||||
"Zone Invalid Write: The write to a zone was not at the write pointer." I/O errors.
|
||||
However, if your controller supports Zone Append, you can use the engine option:
|
||||
|
||||
--zone_append=1
|
||||
|
||||
To send zone append commands instead of write commands to the controller.
|
||||
When using zone append, you will be able to specify a --iodepth greater than 1.
|
||||
|
||||
## Shared Memory Increase
|
||||
|
||||
If your device has a lot of zones, fio can give you errors such as:
|
||||
|
@ -9,6 +9,7 @@ ramp_time=0
|
||||
runtime=2
|
||||
iodepth=128
|
||||
rw=randrw
|
||||
bs=4k
|
||||
|
||||
[test]
|
||||
numjobs=1
|
||||
|
@ -93,8 +93,6 @@ struct spdk_fio_options {
|
||||
char *digest_enable;
|
||||
int enable_vmd;
|
||||
int initial_zone_reset;
|
||||
int zone_append;
|
||||
int print_qid_mappings;
|
||||
};
|
||||
|
||||
struct spdk_fio_request {
|
||||
@ -132,7 +130,6 @@ struct spdk_fio_qpair {
|
||||
struct spdk_nvme_qpair *qpair;
|
||||
struct spdk_nvme_ns *ns;
|
||||
uint32_t io_flags;
|
||||
bool zone_append_enabled;
|
||||
bool nvme_pi_enabled;
|
||||
/* True for DIF and false for DIX, and this is valid only if nvme_pi_enabled is true. */
|
||||
bool extended_lba;
|
||||
@ -290,35 +287,13 @@ pcu(struct spdk_nvme_qpair *qpair, int *completed)
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline uint32_t
|
||||
_nvme_get_host_buffer_sector_size(struct spdk_nvme_ns *ns, uint32_t io_flags)
|
||||
{
|
||||
bool md_excluded_from_xfer = false;
|
||||
uint32_t md_size;
|
||||
uint32_t ns_flags;
|
||||
|
||||
ns_flags = spdk_nvme_ns_get_flags(ns);
|
||||
md_size = spdk_nvme_ns_get_md_size(ns);
|
||||
|
||||
/* For extended LBA format, if the metadata size is 8 bytes and PRACT is
|
||||
* enabled(controller inserts/strips PI), we should reduce metadata size
|
||||
* from block size.
|
||||
*/
|
||||
md_excluded_from_xfer = ((io_flags & SPDK_NVME_IO_FLAGS_PRACT) &&
|
||||
(ns_flags & SPDK_NVME_NS_EXTENDED_LBA_SUPPORTED) &&
|
||||
(ns_flags & SPDK_NVME_NS_DPS_PI_SUPPORTED) &&
|
||||
(md_size == 8));
|
||||
|
||||
return md_excluded_from_xfer ? spdk_nvme_ns_get_sector_size(ns) :
|
||||
spdk_nvme_ns_get_extended_sector_size(ns);
|
||||
}
|
||||
|
||||
static 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)
|
||||
{
|
||||
struct thread_data *td = cb_ctx;
|
||||
struct spdk_fio_thread *fio_thread = td->io_ops_data;
|
||||
struct spdk_nvme_io_qpair_opts qpopts;
|
||||
struct spdk_fio_ctrlr *fio_ctrlr;
|
||||
struct spdk_fio_qpair *fio_qpair;
|
||||
struct spdk_nvme_ns *ns;
|
||||
@ -327,7 +302,6 @@ attach_cb(void *cb_ctx, const struct spdk_nvme_transport_id *trid,
|
||||
uint32_t ns_id;
|
||||
char *p;
|
||||
long int tmp;
|
||||
uint32_t block_size;
|
||||
struct spdk_fio_options *fio_options = td->eo;
|
||||
|
||||
p = strstr(f->file_name, "ns=");
|
||||
@ -397,7 +371,20 @@ attach_cb(void *cb_ctx, const struct spdk_nvme_transport_id *trid,
|
||||
return;
|
||||
}
|
||||
|
||||
f->engine_data = fio_qpair;
|
||||
spdk_nvme_ctrlr_get_default_io_qpair_opts(fio_ctrlr->ctrlr, &qpopts, sizeof(qpopts));
|
||||
qpopts.delay_cmd_submit = true;
|
||||
if (fio_options->enable_wrr) {
|
||||
qpopts.qprio = fio_options->wrr_priority;
|
||||
}
|
||||
|
||||
fio_qpair->qpair = spdk_nvme_ctrlr_alloc_io_qpair(fio_ctrlr->ctrlr, &qpopts, sizeof(qpopts));
|
||||
if (!fio_qpair->qpair) {
|
||||
SPDK_ERRLOG("Cannot allocate nvme io_qpair any more\n");
|
||||
g_error = true;
|
||||
free(fio_qpair);
|
||||
return;
|
||||
}
|
||||
|
||||
fio_qpair->ns = ns;
|
||||
fio_qpair->f = f;
|
||||
fio_qpair->fio_ctrlr = fio_ctrlr;
|
||||
@ -413,57 +400,14 @@ attach_cb(void *cb_ctx, const struct spdk_nvme_transport_id *trid,
|
||||
fio_qpair->extended_lba ? "extended lba" : "separate metadata");
|
||||
}
|
||||
|
||||
block_size = _nvme_get_host_buffer_sector_size(ns, fio_qpair->io_flags);
|
||||
if (td->o.bs[DDIR_READ] % block_size != 0 || td->o.bs[DDIR_WRITE] % block_size != 0) {
|
||||
if (spdk_nvme_ns_supports_extended_lba(ns)) {
|
||||
SPDK_ERRLOG("--bs has to be a multiple of (LBA data size + Metadata size)\n");
|
||||
} else {
|
||||
SPDK_ERRLOG("--bs has to be a multiple of LBA data size\n");
|
||||
}
|
||||
if (spdk_nvme_ns_supports_extended_lba(ns) &&
|
||||
(td->o.bs[DDIR_READ] % spdk_nvme_ns_get_extended_sector_size(ns) != 0 ||
|
||||
td->o.bs[DDIR_WRITE] % spdk_nvme_ns_get_extended_sector_size(ns) != 0)) {
|
||||
SPDK_ERRLOG("--bs has to be equal to LBA data size + Metadata size\n");
|
||||
g_error = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (fio_options->zone_append && spdk_nvme_ns_get_csi(ns) == SPDK_NVME_CSI_ZNS) {
|
||||
if (spdk_nvme_ctrlr_get_flags(ctrlr) & SPDK_NVME_CTRLR_ZONE_APPEND_SUPPORTED) {
|
||||
fprintf(stdout, "Using zone appends instead of writes on: '%s'\n",
|
||||
fio_qpair->f->file_name);
|
||||
fio_qpair->zone_append_enabled = true;
|
||||
} else {
|
||||
SPDK_WARNLOG("Falling back to writes on: '%s' - ns lacks zone append cmd\n",
|
||||
fio_qpair->f->file_name);
|
||||
}
|
||||
}
|
||||
|
||||
if (fio_options->initial_zone_reset == 1 && spdk_nvme_ns_get_csi(ns) == SPDK_NVME_CSI_ZNS) {
|
||||
#if FIO_HAS_ZBD
|
||||
struct spdk_nvme_qpair *tmp_qpair;
|
||||
int completed = 0, err;
|
||||
|
||||
/* qpair has not been allocated yet (it gets allocated in spdk_fio_open()).
|
||||
* Create a temporary qpair in order to perform the initial zone reset.
|
||||
*/
|
||||
assert(!fio_qpair->qpair);
|
||||
|
||||
tmp_qpair = spdk_nvme_ctrlr_alloc_io_qpair(ctrlr, NULL, 0);
|
||||
if (!tmp_qpair) {
|
||||
SPDK_ERRLOG("Cannot allocate a temporary qpair\n");
|
||||
g_error = true;
|
||||
return;
|
||||
}
|
||||
|
||||
err = spdk_nvme_zns_reset_zone(ns, tmp_qpair, 0x0, true, pcu_cb, &completed);
|
||||
if (err || pcu(tmp_qpair, &completed) || completed < 0) {
|
||||
log_err("spdk/nvme: warn: initial_zone_reset: err: %d, cpl: %d\n",
|
||||
err, completed);
|
||||
}
|
||||
|
||||
spdk_nvme_ctrlr_free_io_qpair(tmp_qpair);
|
||||
#else
|
||||
log_err("spdk/nvme: ZBD/ZNS is not supported\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
f->real_file_size = spdk_nvme_ns_get_size(fio_qpair->ns);
|
||||
if (f->real_file_size <= 0) {
|
||||
g_error = true;
|
||||
@ -653,35 +597,39 @@ static int spdk_fio_setup(struct thread_data *td)
|
||||
g_td_count++;
|
||||
pthread_mutex_unlock(&g_mutex);
|
||||
|
||||
if (fio_options->initial_zone_reset == 1) {
|
||||
#if FIO_HAS_ZBD
|
||||
struct spdk_fio_qpair *fio_qpair;
|
||||
|
||||
TAILQ_FOREACH(fio_qpair, &fio_thread->fio_qpair, link) {
|
||||
const struct spdk_nvme_zns_ns_data *zns_data;
|
||||
int completed = 0, err;
|
||||
|
||||
if (!fio_qpair->ns) {
|
||||
continue;
|
||||
}
|
||||
zns_data = spdk_nvme_zns_ns_get_data(fio_qpair->ns);
|
||||
if (!zns_data) {
|
||||
continue;
|
||||
}
|
||||
|
||||
err = spdk_nvme_zns_reset_zone(fio_qpair->ns, fio_qpair->qpair, 0x0, true,
|
||||
pcu_cb, &completed);
|
||||
if (err || pcu(fio_qpair->qpair, &completed) || completed < 0) {
|
||||
log_err("spdk/nvme: warn: initial_zone_reset: err: %d, cpl: %d\n",
|
||||
err, completed);
|
||||
}
|
||||
}
|
||||
#else
|
||||
log_err("spdk/nvme: ZBD/ZNS is not supported\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int spdk_fio_open(struct thread_data *td, struct fio_file *f)
|
||||
{
|
||||
struct spdk_fio_qpair *fio_qpair = f->engine_data;
|
||||
struct spdk_fio_ctrlr *fio_ctrlr = fio_qpair->fio_ctrlr;
|
||||
struct spdk_fio_options *fio_options = td->eo;
|
||||
struct spdk_nvme_io_qpair_opts qpopts;
|
||||
|
||||
spdk_nvme_ctrlr_get_default_io_qpair_opts(fio_ctrlr->ctrlr, &qpopts, sizeof(qpopts));
|
||||
qpopts.delay_cmd_submit = true;
|
||||
if (fio_options->enable_wrr) {
|
||||
qpopts.qprio = fio_options->wrr_priority;
|
||||
}
|
||||
|
||||
fio_qpair->qpair = spdk_nvme_ctrlr_alloc_io_qpair(fio_ctrlr->ctrlr, &qpopts, sizeof(qpopts));
|
||||
if (!fio_qpair->qpair) {
|
||||
SPDK_ERRLOG("Cannot allocate nvme io_qpair any more\n");
|
||||
g_error = true;
|
||||
free(fio_qpair);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (fio_options->print_qid_mappings == 1) {
|
||||
log_info("job %s: %s qid %d\n", td->o.name, f->file_name,
|
||||
spdk_nvme_qpair_get_id(fio_qpair->qpair));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -740,12 +688,6 @@ static void spdk_fio_io_u_free(struct thread_data *td, struct io_u *io_u)
|
||||
}
|
||||
}
|
||||
|
||||
static inline uint64_t
|
||||
fio_offset_to_zslba(unsigned long long offset, struct spdk_nvme_ns *ns)
|
||||
{
|
||||
return (offset / spdk_nvme_zns_ns_get_zone_size(ns)) * spdk_nvme_zns_ns_get_zone_size_sectors(ns);
|
||||
}
|
||||
|
||||
static int
|
||||
fio_extended_lba_setup_pi(struct spdk_fio_qpair *fio_qpair, struct io_u *io_u)
|
||||
{
|
||||
@ -915,10 +857,6 @@ static void spdk_fio_completion_cb(void *ctx, const struct spdk_nvme_cpl *cpl)
|
||||
}
|
||||
}
|
||||
|
||||
if (spdk_nvme_cpl_is_error(cpl)) {
|
||||
fio_req->io->error = EIO;
|
||||
}
|
||||
|
||||
assert(fio_thread->iocq_count < fio_thread->iocq_size);
|
||||
fio_thread->iocq[fio_thread->iocq_count++] = fio_req->io;
|
||||
}
|
||||
@ -1003,7 +941,16 @@ spdk_fio_queue(struct thread_data *td, struct io_u *io_u)
|
||||
}
|
||||
fio_req->fio_qpair = fio_qpair;
|
||||
|
||||
block_size = _nvme_get_host_buffer_sector_size(ns, fio_qpair->io_flags);
|
||||
block_size = spdk_nvme_ns_get_extended_sector_size(ns);
|
||||
if ((fio_qpair->io_flags & g_spdk_pract_flag) && (spdk_nvme_ns_get_md_size(ns) == 8)) {
|
||||
/* If metadata size = 8 bytes, PI is stripped (read) or inserted (write), and
|
||||
* so reduce metadata size from block size. (If metadata size > 8 bytes, PI
|
||||
* is passed (read) or replaced (write). So block size is not necessary to
|
||||
* change.)
|
||||
*/
|
||||
block_size = spdk_nvme_ns_get_sector_size(ns);
|
||||
}
|
||||
|
||||
lba = io_u->offset / block_size;
|
||||
lba_count = io_u->xfer_buflen / block_size;
|
||||
|
||||
@ -1035,31 +982,15 @@ spdk_fio_queue(struct thread_data *td, struct io_u *io_u)
|
||||
break;
|
||||
case DDIR_WRITE:
|
||||
if (!g_spdk_enable_sgl) {
|
||||
if (!fio_qpair->zone_append_enabled) {
|
||||
rc = spdk_nvme_ns_cmd_write_with_md(ns, fio_qpair->qpair, io_u->buf, md_buf, lba,
|
||||
lba_count,
|
||||
spdk_fio_completion_cb, fio_req,
|
||||
fio_qpair->io_flags, dif_ctx->apptag_mask, dif_ctx->app_tag);
|
||||
} else {
|
||||
uint64_t zslba = fio_offset_to_zslba(io_u->offset, fio_qpair->ns);
|
||||
rc = spdk_nvme_zns_zone_append_with_md(ns, fio_qpair->qpair, io_u->buf, md_buf, zslba,
|
||||
lba_count,
|
||||
spdk_fio_completion_cb, fio_req,
|
||||
fio_qpair->io_flags, dif_ctx->apptag_mask, dif_ctx->app_tag);
|
||||
}
|
||||
rc = spdk_nvme_ns_cmd_write_with_md(ns, fio_qpair->qpair, io_u->buf, md_buf, lba,
|
||||
lba_count,
|
||||
spdk_fio_completion_cb, fio_req,
|
||||
fio_qpair->io_flags, dif_ctx->apptag_mask, dif_ctx->app_tag);
|
||||
} else {
|
||||
if (!fio_qpair->zone_append_enabled) {
|
||||
rc = spdk_nvme_ns_cmd_writev_with_md(ns, fio_qpair->qpair, lba,
|
||||
lba_count, spdk_fio_completion_cb, fio_req, fio_qpair->io_flags,
|
||||
spdk_nvme_io_reset_sgl, spdk_nvme_io_next_sge, md_buf,
|
||||
dif_ctx->apptag_mask, dif_ctx->app_tag);
|
||||
} else {
|
||||
uint64_t zslba = fio_offset_to_zslba(io_u->offset, fio_qpair->ns);
|
||||
rc = spdk_nvme_zns_zone_appendv_with_md(ns, fio_qpair->qpair, zslba,
|
||||
lba_count, spdk_fio_completion_cb, fio_req, fio_qpair->io_flags,
|
||||
spdk_nvme_io_reset_sgl, spdk_nvme_io_next_sge, md_buf,
|
||||
dif_ctx->apptag_mask, dif_ctx->app_tag);
|
||||
}
|
||||
rc = spdk_nvme_ns_cmd_writev_with_md(ns, fio_qpair->qpair, lba,
|
||||
lba_count, spdk_fio_completion_cb, fio_req, fio_qpair->io_flags,
|
||||
spdk_nvme_io_reset_sgl, spdk_nvme_io_next_sge, md_buf,
|
||||
dif_ctx->apptag_mask, dif_ctx->app_tag);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@ -1157,13 +1088,11 @@ spdk_fio_get_zoned_model(struct thread_data *td, struct fio_file *f, enum zbd_zo
|
||||
struct spdk_fio_qpair *fio_qpair = NULL;
|
||||
const struct spdk_nvme_zns_ns_data *zns_data = NULL;
|
||||
|
||||
*model = ZBD_IGNORE;
|
||||
|
||||
if (f->filetype != FIO_TYPE_FILE && \
|
||||
f->filetype != FIO_TYPE_BLOCK && \
|
||||
f->filetype != FIO_TYPE_CHAR) {
|
||||
log_info("spdk/nvme: ignoring filetype: %d\n", f->filetype);
|
||||
return 0;
|
||||
log_info("spdk/nvme: unsupported filetype: %d\n", f->filetype);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
fio_qpair = get_fio_qpair(fio_thread, f);
|
||||
@ -1227,7 +1156,6 @@ spdk_fio_report_zones(struct thread_data *td, struct fio_file *f, uint64_t offse
|
||||
struct spdk_fio_qpair *fio_qpair = NULL;
|
||||
const struct spdk_nvme_zns_ns_data *zns = NULL;
|
||||
struct spdk_nvme_zns_zone_report *report;
|
||||
struct spdk_nvme_qpair *tmp_qpair;
|
||||
uint32_t report_nzones = 0, report_nzones_max, report_nbytes, mdts_nbytes;
|
||||
uint64_t zsze_nbytes, ns_nzones, lba_nbytes;
|
||||
int completed = 0, err;
|
||||
@ -1243,17 +1171,6 @@ spdk_fio_report_zones(struct thread_data *td, struct fio_file *f, uint64_t offse
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* qpair has not been allocated yet (it gets allocated in spdk_fio_open()).
|
||||
* Create a temporary qpair in order to perform report zones.
|
||||
*/
|
||||
assert(!fio_qpair->qpair);
|
||||
|
||||
tmp_qpair = spdk_nvme_ctrlr_alloc_io_qpair(fio_qpair->fio_ctrlr->ctrlr, NULL, 0);
|
||||
if (!tmp_qpair) {
|
||||
log_err("spdk/nvme: cannot allocate a temporary qpair\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/** Retrieve device parameters */
|
||||
mdts_nbytes = spdk_nvme_ns_get_max_io_xfer_size(fio_qpair->ns);
|
||||
lba_nbytes = spdk_nvme_ns_get_sector_size(fio_qpair->ns);
|
||||
@ -1264,17 +1181,16 @@ spdk_fio_report_zones(struct thread_data *td, struct fio_file *f, uint64_t offse
|
||||
report_nzones_max = (mdts_nbytes - sizeof(*report)) / sizeof(report->descs[0]);
|
||||
report_nzones_max = spdk_min(spdk_min(report_nzones_max, nr_zones), ns_nzones);
|
||||
report_nbytes = sizeof(report->descs[0]) * report_nzones_max + sizeof(*report);
|
||||
report = calloc(1, report_nbytes);
|
||||
report = spdk_dma_zmalloc(report_nbytes, NVME_IO_ALIGN, NULL);
|
||||
if (!report) {
|
||||
log_err("spdk/nvme: failed report_zones(): ENOMEM\n");
|
||||
err = -ENOMEM;
|
||||
goto exit;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
err = spdk_nvme_zns_report_zones(fio_qpair->ns, tmp_qpair, report, report_nbytes,
|
||||
err = spdk_nvme_zns_report_zones(fio_qpair->ns, fio_qpair->qpair, report, report_nbytes,
|
||||
offset / lba_nbytes, SPDK_NVME_ZRA_LIST_ALL, true, pcu_cb,
|
||||
&completed);
|
||||
if (err || pcu(tmp_qpair, &completed) || completed < 0) {
|
||||
if (err || pcu(fio_qpair->qpair, &completed) || completed < 0) {
|
||||
log_err("spdk/nvme: report_zones(): err: %d, cpl: %d\n", err, completed);
|
||||
err = err ? err : -EIO;
|
||||
goto exit;
|
||||
@ -1332,8 +1248,7 @@ spdk_fio_report_zones(struct thread_data *td, struct fio_file *f, uint64_t offse
|
||||
}
|
||||
|
||||
exit:
|
||||
spdk_nvme_ctrlr_free_io_qpair(tmp_qpair);
|
||||
free(report);
|
||||
spdk_dma_free(report);
|
||||
|
||||
return err ? err : (int)report_nzones;
|
||||
}
|
||||
@ -1628,26 +1543,6 @@ static struct fio_option options[] = {
|
||||
.category = FIO_OPT_C_ENGINE,
|
||||
.group = FIO_OPT_G_INVALID,
|
||||
},
|
||||
{
|
||||
.name = "zone_append",
|
||||
.lname = "Use zone append instead of write",
|
||||
.type = FIO_OPT_INT,
|
||||
.off1 = offsetof(struct spdk_fio_options, zone_append),
|
||||
.def = "0",
|
||||
.help = "Use zone append instead of write (zone_append=1 or zone_append=0)",
|
||||
.category = FIO_OPT_C_ENGINE,
|
||||
.group = FIO_OPT_G_INVALID,
|
||||
},
|
||||
{
|
||||
.name = "print_qid_mappings",
|
||||
.lname = "Print job-to-qid mappings",
|
||||
.type = FIO_OPT_INT,
|
||||
.off1 = offsetof(struct spdk_fio_options, print_qid_mappings),
|
||||
.def = "0",
|
||||
.help = "Print job-to-qid mappings (0=disable, 1=enable)",
|
||||
.category = FIO_OPT_C_ENGINE,
|
||||
.group = FIO_OPT_G_INVALID,
|
||||
},
|
||||
{
|
||||
.name = NULL,
|
||||
},
|
||||
|
@ -50,7 +50,6 @@
|
||||
#define MAX_DISCOVERY_LOG_ENTRIES ((uint64_t)1000)
|
||||
|
||||
#define NUM_CHUNK_INFO_ENTRIES 8
|
||||
#define MAX_OCSSD_PU 128
|
||||
#define MAX_ZONE_DESC_ENTRIES 8
|
||||
|
||||
static int outstanding_commands;
|
||||
@ -86,9 +85,9 @@ static uint64_t g_discovery_page_numrec;
|
||||
|
||||
static struct spdk_ocssd_geometry_data geometry_data;
|
||||
|
||||
static struct spdk_ocssd_chunk_information_entry *g_ocssd_chunk_info_page;
|
||||
static struct spdk_ocssd_chunk_information_entry g_ocssd_chunk_info_page[NUM_CHUNK_INFO_ENTRIES ];
|
||||
|
||||
static int64_t g_zone_report_limit = 8;
|
||||
static bool g_zone_report_full = false;
|
||||
|
||||
static bool g_hex_dump = false;
|
||||
|
||||
@ -109,8 +108,6 @@ static int g_controllers_found = 0;
|
||||
|
||||
static bool g_vmd = false;
|
||||
|
||||
static bool g_ocssd_verbose = false;
|
||||
|
||||
static void
|
||||
hex_dump(const void *data, size_t size)
|
||||
{
|
||||
@ -228,10 +225,7 @@ get_features(struct spdk_nvme_ctrlr *ctrlr)
|
||||
SPDK_OCSSD_FEAT_MEDIA_FEEDBACK,
|
||||
};
|
||||
|
||||
/* Submit only one GET FEATURES at a time. There is a known issue #1799
|
||||
* with Google Cloud Platform NVMe SSDs that do not handle overlapped
|
||||
* GET FEATURES commands correctly.
|
||||
*/
|
||||
/* Submit several GET FEATURES commands and wait for them to complete */
|
||||
outstanding_commands = 0;
|
||||
for (i = 0; i < SPDK_COUNTOF(features_to_get); i++) {
|
||||
if (!spdk_nvme_ctrlr_is_ocssd_supported(ctrlr) &&
|
||||
@ -243,12 +237,11 @@ get_features(struct spdk_nvme_ctrlr *ctrlr)
|
||||
} else {
|
||||
printf("get_feature(0x%02X) failed to submit command\n", features_to_get[i]);
|
||||
}
|
||||
|
||||
while (outstanding_commands) {
|
||||
spdk_nvme_ctrlr_process_admin_completions(ctrlr);
|
||||
}
|
||||
}
|
||||
|
||||
while (outstanding_commands) {
|
||||
spdk_nvme_ctrlr_process_admin_completions(ctrlr);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
@ -549,34 +542,15 @@ get_ocssd_chunk_info_log_page(struct spdk_nvme_ns *ns)
|
||||
{
|
||||
struct spdk_nvme_ctrlr *ctrlr = spdk_nvme_ns_get_ctrlr(ns);
|
||||
int nsid = spdk_nvme_ns_get_id(ns);
|
||||
uint32_t num_entry = geometry_data.num_grp * geometry_data.num_pu * geometry_data.num_chk;
|
||||
uint32_t xfer_size = spdk_nvme_ns_get_max_io_xfer_size(ns);
|
||||
uint32_t buf_size = 0;
|
||||
uint64_t buf_offset = 0;
|
||||
outstanding_commands = 0;
|
||||
|
||||
assert(num_entry != 0);
|
||||
if (!g_ocssd_verbose) {
|
||||
num_entry = spdk_min(num_entry, NUM_CHUNK_INFO_ENTRIES);
|
||||
}
|
||||
|
||||
g_ocssd_chunk_info_page = calloc(num_entry, sizeof(struct spdk_ocssd_chunk_information_entry));
|
||||
assert(g_ocssd_chunk_info_page != NULL);
|
||||
|
||||
buf_size = num_entry * sizeof(struct spdk_ocssd_chunk_information_entry);
|
||||
while (buf_size > 0) {
|
||||
xfer_size = spdk_min(buf_size, xfer_size);
|
||||
if (spdk_nvme_ctrlr_cmd_get_log_page(ctrlr, SPDK_OCSSD_LOG_CHUNK_INFO,
|
||||
nsid, (void *) g_ocssd_chunk_info_page + buf_offset,
|
||||
xfer_size, buf_offset, get_log_page_completion, NULL) == 0) {
|
||||
outstanding_commands++;
|
||||
} else {
|
||||
printf("get_ocssd_chunk_info_log_page() failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
buf_size -= xfer_size;
|
||||
buf_offset += xfer_size;
|
||||
if (spdk_nvme_ctrlr_cmd_get_log_page(ctrlr, SPDK_OCSSD_LOG_CHUNK_INFO,
|
||||
nsid, &g_ocssd_chunk_info_page, sizeof(g_ocssd_chunk_info_page), 0,
|
||||
get_log_page_completion, NULL) == 0) {
|
||||
outstanding_commands++;
|
||||
} else {
|
||||
printf("get_ocssd_chunk_info_log_page() failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (outstanding_commands) {
|
||||
@ -674,16 +648,6 @@ print_ascii_string(const void *buf, size_t size)
|
||||
}
|
||||
}
|
||||
|
||||
/* Underline a "line" with the given marker, e.g. print_uline("=", printf(...)); */
|
||||
static void
|
||||
print_uline(char marker, int line_len)
|
||||
{
|
||||
for (int i = 1; i < line_len; ++i) {
|
||||
putchar(marker);
|
||||
}
|
||||
putchar('\n');
|
||||
}
|
||||
|
||||
static void
|
||||
print_ocssd_chunk_info(struct spdk_ocssd_chunk_information_entry *chk_info, int chk_num)
|
||||
{
|
||||
@ -713,45 +677,6 @@ print_ocssd_chunk_info(struct spdk_ocssd_chunk_information_entry *chk_info, int
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
print_ocssd_chunk_info_verbose(struct spdk_ocssd_chunk_information_entry *chk_info)
|
||||
{
|
||||
uint32_t pu, chk, i;
|
||||
uint32_t cnt_free, cnt_closed, cnt_open, cnt_offline;
|
||||
uint32_t max_pu = spdk_min(MAX_OCSSD_PU, (geometry_data.num_grp * geometry_data.num_pu));
|
||||
char cs_str[MAX_OCSSD_PU + 1], cs;
|
||||
|
||||
assert(chk_info != NULL);
|
||||
printf("OCSSD Chunk Info Verbose\n");
|
||||
printf("======================\n");
|
||||
|
||||
printf("%4s %-*s %3s %3s %3s %3s\n", "band", max_pu, "chunk state", "fr", "cl", "op", "of");
|
||||
for (chk = 0; chk < geometry_data.num_chk; chk++) {
|
||||
cnt_free = cnt_closed = cnt_open = cnt_offline = 0;
|
||||
for (pu = 0; pu < max_pu; pu++) {
|
||||
i = (pu * geometry_data.num_chk) + chk;
|
||||
if (chk_info[i].cs.free) {
|
||||
cnt_free++;
|
||||
cs = 'f';
|
||||
} else if (chk_info[i].cs.closed) {
|
||||
cnt_closed++;
|
||||
cs = 'c';
|
||||
} else if (chk_info[i].cs.open) {
|
||||
cnt_open++;
|
||||
cs = 'o';
|
||||
} else if (chk_info[i].cs.offline) {
|
||||
cnt_offline++;
|
||||
cs = 'l';
|
||||
} else {
|
||||
cs = '.';
|
||||
}
|
||||
cs_str[pu] = cs;
|
||||
}
|
||||
cs_str[pu] = 0;
|
||||
printf("%4d %s %3d %3d %3d %3d\n", chk, cs_str, cnt_free, cnt_closed, cnt_open, cnt_offline);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
print_ocssd_geometry(struct spdk_ocssd_geometry_data *geometry_data)
|
||||
{
|
||||
@ -802,7 +727,7 @@ get_and_print_zns_zone_report(struct spdk_nvme_ns *ns, struct spdk_nvme_qpair *q
|
||||
{
|
||||
struct spdk_nvme_zns_zone_report *report_buf;
|
||||
size_t report_bufsize;
|
||||
uint64_t zone_size_lba = spdk_nvme_zns_ns_get_zone_size_sectors(ns);
|
||||
uint64_t zone_size_lba = spdk_nvme_zns_ns_get_zone_size(ns) / spdk_nvme_ns_get_sector_size(ns);
|
||||
uint64_t total_zones = spdk_nvme_zns_ns_get_num_zones(ns);
|
||||
uint64_t max_zones_per_buf, zones_to_print, i;
|
||||
uint64_t handled_zones = 0;
|
||||
@ -811,16 +736,21 @@ get_and_print_zns_zone_report(struct spdk_nvme_ns *ns, struct spdk_nvme_qpair *q
|
||||
outstanding_commands = 0;
|
||||
|
||||
report_bufsize = spdk_nvme_ns_get_max_io_xfer_size(ns);
|
||||
report_buf = calloc(1, report_bufsize);
|
||||
report_buf = malloc(report_bufsize);
|
||||
if (!report_buf) {
|
||||
printf("Zone report allocation failed!\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
zones_to_print = g_zone_report_limit ? spdk_min(total_zones, (uint64_t)g_zone_report_limit) : \
|
||||
total_zones;
|
||||
|
||||
print_uline('=', printf("NVMe ZNS Zone Report (first %zu of %zu)\n", zones_to_print, total_zones));
|
||||
if (g_zone_report_full) {
|
||||
zones_to_print = total_zones;
|
||||
printf("NVMe ZNS Zone Report\n");
|
||||
printf("====================\n");
|
||||
} else {
|
||||
zones_to_print = spdk_min(total_zones, MAX_ZONE_DESC_ENTRIES);
|
||||
printf("NVMe ZNS Zone Report Glance\n");
|
||||
printf("===========================\n");
|
||||
}
|
||||
|
||||
while (handled_zones < zones_to_print) {
|
||||
memset(report_buf, 0, report_bufsize);
|
||||
@ -896,24 +826,6 @@ print_zns_ns_data(const struct spdk_nvme_zns_ns_data *nsdata_zns)
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static const char *
|
||||
csi_name(enum spdk_nvme_csi csi)
|
||||
{
|
||||
switch (csi) {
|
||||
case SPDK_NVME_CSI_NVM:
|
||||
return "NVM";
|
||||
case SPDK_NVME_CSI_KV:
|
||||
return "KV";
|
||||
case SPDK_NVME_CSI_ZNS:
|
||||
return "ZNS";
|
||||
default:
|
||||
if (csi >= 0x30 && csi <= 0x3f) {
|
||||
return "Vendor specific";
|
||||
}
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
print_namespace(struct spdk_nvme_ctrlr *ctrlr, struct spdk_nvme_ns *ns)
|
||||
{
|
||||
@ -925,7 +837,6 @@ print_namespace(struct spdk_nvme_ctrlr *ctrlr, struct spdk_nvme_ns *ns)
|
||||
uint32_t flags;
|
||||
char uuid_str[SPDK_UUID_STRING_LEN];
|
||||
uint32_t blocksize;
|
||||
enum spdk_nvme_dealloc_logical_block_read_value dlfeat_read_value;
|
||||
|
||||
cdata = spdk_nvme_ctrlr_get_data(ctrlr);
|
||||
nsdata = spdk_nvme_ns_get_data(ns);
|
||||
@ -942,16 +853,13 @@ print_namespace(struct spdk_nvme_ctrlr *ctrlr, struct spdk_nvme_ns *ns)
|
||||
/* This function is only called for active namespaces. */
|
||||
assert(spdk_nvme_ns_is_active(ns));
|
||||
|
||||
printf("Command Set Identifier: %s (%02Xh)\n",
|
||||
csi_name(spdk_nvme_ns_get_csi(ns)), spdk_nvme_ns_get_csi(ns));
|
||||
printf("Deallocate: %s\n",
|
||||
(flags & SPDK_NVME_NS_DEALLOCATE_SUPPORTED) ? "Supported" : "Not Supported");
|
||||
printf("Deallocated/Unwritten Error: %s\n",
|
||||
nsdata->nsfeat.dealloc_or_unwritten_error ? "Supported" : "Not Supported");
|
||||
dlfeat_read_value = spdk_nvme_ns_get_dealloc_logical_block_read_value(ns);
|
||||
printf("Deallocated Read Value: %s\n",
|
||||
dlfeat_read_value == SPDK_NVME_DEALLOC_READ_00 ? "All 0x00" :
|
||||
dlfeat_read_value == SPDK_NVME_DEALLOC_READ_FF ? "All 0xFF" :
|
||||
nsdata->dlfeat.bits.read_value == SPDK_NVME_DEALLOC_READ_00 ? "All 0x00" :
|
||||
nsdata->dlfeat.bits.read_value == SPDK_NVME_DEALLOC_READ_FF ? "All 0xFF" :
|
||||
"Unknown");
|
||||
printf("Deallocate in Write Zeroes: %s\n",
|
||||
nsdata->dlfeat.bits.write_zero_deallocate ? "Supported" : "Not Supported");
|
||||
@ -1048,11 +956,7 @@ print_namespace(struct spdk_nvme_ctrlr *ctrlr, struct spdk_nvme_ns *ns)
|
||||
get_ocssd_geometry(ns, &geometry_data);
|
||||
print_ocssd_geometry(&geometry_data);
|
||||
get_ocssd_chunk_info_log_page(ns);
|
||||
if (g_ocssd_verbose) {
|
||||
print_ocssd_chunk_info_verbose(g_ocssd_chunk_info_page);
|
||||
} else {
|
||||
print_ocssd_chunk_info(g_ocssd_chunk_info_page, NUM_CHUNK_INFO_ENTRIES);
|
||||
}
|
||||
print_ocssd_chunk_info(g_ocssd_chunk_info_page, NUM_CHUNK_INFO_ENTRIES);
|
||||
} else if (spdk_nvme_ns_get_csi(ns) == SPDK_NVME_CSI_ZNS) {
|
||||
struct spdk_nvme_qpair *qpair = spdk_nvme_ctrlr_alloc_io_qpair(ctrlr, NULL, 0);
|
||||
if (qpair == NULL) {
|
||||
@ -1170,7 +1074,6 @@ print_controller(struct spdk_nvme_ctrlr *ctrlr, const struct spdk_nvme_transport
|
||||
union spdk_nvme_cap_register cap;
|
||||
union spdk_nvme_vs_register vs;
|
||||
union spdk_nvme_cmbsz_register cmbsz;
|
||||
union spdk_nvme_pmrcap_register pmrcap;
|
||||
uint8_t str[512];
|
||||
uint32_t i, j;
|
||||
struct spdk_nvme_error_information_entry *error_entry;
|
||||
@ -1178,14 +1081,11 @@ print_controller(struct spdk_nvme_ctrlr *ctrlr, const struct spdk_nvme_transport
|
||||
struct spdk_pci_device *pci_dev;
|
||||
struct spdk_pci_id pci_id;
|
||||
uint32_t nsid;
|
||||
uint64_t pmrsz;
|
||||
struct spdk_nvme_ana_group_descriptor *desc;
|
||||
|
||||
cap = spdk_nvme_ctrlr_get_regs_cap(ctrlr);
|
||||
vs = spdk_nvme_ctrlr_get_regs_vs(ctrlr);
|
||||
cmbsz = spdk_nvme_ctrlr_get_regs_cmbsz(ctrlr);
|
||||
pmrcap = spdk_nvme_ctrlr_get_regs_pmrcap(ctrlr);
|
||||
pmrsz = spdk_nvme_ctrlr_get_pmrsz(ctrlr);
|
||||
|
||||
if (!spdk_nvme_ctrlr_is_discovery(ctrlr)) {
|
||||
/*
|
||||
@ -1298,9 +1198,6 @@ print_controller(struct spdk_nvme_ctrlr *ctrlr, const struct spdk_nvme_transport
|
||||
(uint64_t)1 << (12 + cap.bits.mpsmin));
|
||||
printf("Memory Page Size Maximum: %" PRIu64 " bytes\n",
|
||||
(uint64_t)1 << (12 + cap.bits.mpsmax));
|
||||
printf("Persistent Memory Region: %s\n",
|
||||
cap.bits.pmrs ? "Supported" : "Not Supported");
|
||||
|
||||
printf("Optional Asynchronous Events Supported\n");
|
||||
printf(" Namespace Attribute Notices: %s\n",
|
||||
cdata->oaes.ns_attribute_notices ? "Supported" : "Not Supported");
|
||||
@ -1336,20 +1233,6 @@ print_controller(struct spdk_nvme_ctrlr *ctrlr, const struct spdk_nvme_transport
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
printf("Persistent Memory Region Support\n");
|
||||
printf("================================\n");
|
||||
if (cap.bits.pmrs != 0) {
|
||||
printf("Supported: Yes\n");
|
||||
printf("Total Size: %" PRIu64 " bytes\n", pmrsz);
|
||||
printf("Read data and metadata in PMR %s\n",
|
||||
pmrcap.bits.rds ? "Supported" : "Not Supported");
|
||||
printf("Write data and metadata in PMR: %s\n",
|
||||
pmrcap.bits.wds ? "Supported" : "Not Supported");
|
||||
} else {
|
||||
printf("Supported: No\n");
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
printf("Admin Command Set Attributes\n");
|
||||
printf("============================\n");
|
||||
printf("Security Send/Receive: %s\n",
|
||||
@ -2050,7 +1933,8 @@ usage(const char *program_name)
|
||||
printf(" -d DPDK huge memory size in MB\n");
|
||||
printf(" -g use single file descriptor for DPDK memory segments\n");
|
||||
printf(" -x print hex dump of raw data\n");
|
||||
printf(" -z For NVMe Zoned Namespaces, dump the full zone report (-z) or the first N entries (-z N)\n");
|
||||
printf(" -z For NVMe Zoned Namespaces, dump the full zone report\n");
|
||||
printf(" -v verbose (enable warnings)\n");
|
||||
printf(" -V enumerate VMD\n");
|
||||
printf(" -H show this usage\n");
|
||||
}
|
||||
@ -2064,7 +1948,7 @@ parse_args(int argc, char **argv)
|
||||
spdk_nvme_trid_populate_transport(&g_trid, SPDK_NVME_TRANSPORT_PCIE);
|
||||
snprintf(g_trid.subnqn, sizeof(g_trid.subnqn), "%s", SPDK_NVMF_DISCOVERY_NQN);
|
||||
|
||||
while ((op = getopt(argc, argv, "d:gi:op:r:xz::HL:V")) != -1) {
|
||||
while ((op = getopt(argc, argv, "d:gi:p:r:xzHL:V")) != -1) {
|
||||
switch (op) {
|
||||
case 'd':
|
||||
g_dpdk_mem = spdk_strtol(optarg, 10);
|
||||
@ -2083,9 +1967,6 @@ parse_args(int argc, char **argv)
|
||||
return g_shm_id;
|
||||
}
|
||||
break;
|
||||
case 'o':
|
||||
g_ocssd_verbose = true;
|
||||
break;
|
||||
case 'p':
|
||||
g_main_core = spdk_strtol(optarg, 10);
|
||||
if (g_main_core < 0) {
|
||||
@ -2100,7 +1981,6 @@ parse_args(int argc, char **argv)
|
||||
return 1;
|
||||
}
|
||||
|
||||
assert(optarg != NULL);
|
||||
hostnqn = strcasestr(optarg, "hostnqn:");
|
||||
if (hostnqn) {
|
||||
size_t len;
|
||||
@ -2121,18 +2001,7 @@ parse_args(int argc, char **argv)
|
||||
g_hex_dump = true;
|
||||
break;
|
||||
case 'z':
|
||||
if (optarg == NULL && argv[optind] != NULL && argv[optind][0] != '-') {
|
||||
g_zone_report_limit = spdk_strtol(argv[optind], 10);
|
||||
++optind;
|
||||
} else if (optarg) {
|
||||
g_zone_report_limit = spdk_strtol(optarg, 10);
|
||||
} else {
|
||||
g_zone_report_limit = 0;
|
||||
}
|
||||
if (g_zone_report_limit < 0) {
|
||||
fprintf(stderr, "Invalid Zone Report limit\n");
|
||||
return g_zone_report_limit;
|
||||
}
|
||||
g_zone_report_full = true;
|
||||
break;
|
||||
case 'L':
|
||||
rc = spdk_log_set_flag(optarg);
|
||||
|
@ -136,11 +136,6 @@ struct ns_worker_stats {
|
||||
uint64_t total_tsc;
|
||||
uint64_t min_tsc;
|
||||
uint64_t max_tsc;
|
||||
uint64_t last_tsc;
|
||||
uint64_t busy_tsc;
|
||||
uint64_t idle_tsc;
|
||||
uint64_t last_busy_tsc;
|
||||
uint64_t last_idle_tsc;
|
||||
};
|
||||
|
||||
struct ns_worker_ctx {
|
||||
@ -208,14 +203,13 @@ struct ns_fn_table {
|
||||
int (*submit_io)(struct perf_task *task, struct ns_worker_ctx *ns_ctx,
|
||||
struct ns_entry *entry, uint64_t offset_in_ios);
|
||||
|
||||
int64_t (*check_io)(struct ns_worker_ctx *ns_ctx);
|
||||
void (*check_io)(struct ns_worker_ctx *ns_ctx);
|
||||
|
||||
void (*verify_io)(struct perf_task *task, struct ns_entry *entry);
|
||||
|
||||
int (*init_ns_worker_ctx)(struct ns_worker_ctx *ns_ctx);
|
||||
|
||||
void (*cleanup_ns_worker_ctx)(struct ns_worker_ctx *ns_ctx);
|
||||
void (*dump_transport_stats)(uint32_t lcore, struct ns_worker_ctx *ns_ctx);
|
||||
};
|
||||
|
||||
static uint32_t g_io_unit_size = (UINT32_MAX & (~0x03));
|
||||
@ -237,8 +231,6 @@ static pthread_barrier_t g_worker_sync_barrier;
|
||||
|
||||
static uint64_t g_tsc_rate;
|
||||
|
||||
static bool g_monitor_perf_cores = false;
|
||||
|
||||
static uint32_t g_io_align = 0x200;
|
||||
static bool g_io_align_specified;
|
||||
static uint32_t g_io_size_bytes;
|
||||
@ -252,11 +244,14 @@ static int g_queue_depth;
|
||||
static int g_nr_io_queues_per_ns = 1;
|
||||
static int g_nr_unused_io_queues;
|
||||
static int g_time_in_sec;
|
||||
static uint64_t g_elapsed_time_in_usec;
|
||||
static int g_warmup_time_in_sec;
|
||||
static uint32_t g_max_completions;
|
||||
static int g_dpdk_mem;
|
||||
static bool g_dpdk_mem_single_seg = false;
|
||||
static int g_shm_id = -1;
|
||||
static uint32_t g_disable_sq_cmb;
|
||||
static bool g_use_uring;
|
||||
static bool g_no_pci;
|
||||
static bool g_warn;
|
||||
static bool g_header_digest;
|
||||
static bool g_data_digest;
|
||||
@ -290,11 +285,11 @@ static uint32_t g_quiet_count = 1;
|
||||
__count++; \
|
||||
}
|
||||
|
||||
static bool g_dump_transport_stats;
|
||||
static pthread_mutex_t g_stats_mutex;
|
||||
static const char *g_core_mask;
|
||||
|
||||
#define MAX_ALLOWED_PCI_DEVICE_NUM 128
|
||||
static struct spdk_pci_addr g_allowed_pci_addr[MAX_ALLOWED_PCI_DEVICE_NUM];
|
||||
static uint32_t g_allowed_pci_addr_num;
|
||||
|
||||
struct trid_entry {
|
||||
struct spdk_nvme_transport_id trid;
|
||||
@ -335,7 +330,6 @@ perf_set_sock_zcopy(const char *impl_name, bool enable)
|
||||
}
|
||||
|
||||
sock_opts.enable_zerocopy_send = enable;
|
||||
sock_opts.enable_zerocopy_send_client = enable;
|
||||
|
||||
if (spdk_sock_impl_set_opts(impl_name, &sock_opts, opts_size)) {
|
||||
fprintf(stderr, "Failed to %s zcopy send for sock impl %s: error %d (%s)\n",
|
||||
@ -453,10 +447,10 @@ uring_submit_io(struct perf_task *task, struct ns_worker_ctx *ns_ctx,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int64_t
|
||||
static void
|
||||
uring_check_io(struct ns_worker_ctx *ns_ctx)
|
||||
{
|
||||
int i, to_complete, to_submit, count = 0, ret = 0;
|
||||
int i, count, to_complete, to_submit, ret = 0;
|
||||
struct perf_task *task;
|
||||
|
||||
to_submit = ns_ctx->u.uring.io_pending;
|
||||
@ -466,7 +460,7 @@ uring_check_io(struct ns_worker_ctx *ns_ctx)
|
||||
* It will automatically call spdk_io_uring_enter appropriately. */
|
||||
ret = io_uring_submit(&ns_ctx->u.uring.ring);
|
||||
if (ret < 0) {
|
||||
return -1;
|
||||
return;
|
||||
}
|
||||
ns_ctx->u.uring.io_pending = 0;
|
||||
ns_ctx->u.uring.io_inflight += to_submit;
|
||||
@ -487,7 +481,6 @@ uring_check_io(struct ns_worker_ctx *ns_ctx)
|
||||
task_complete(task);
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
static void
|
||||
@ -587,7 +580,7 @@ aio_submit_io(struct perf_task *task, struct ns_worker_ctx *ns_ctx,
|
||||
}
|
||||
}
|
||||
|
||||
static int64_t
|
||||
static void
|
||||
aio_check_io(struct ns_worker_ctx *ns_ctx)
|
||||
{
|
||||
int count, i;
|
||||
@ -605,7 +598,6 @@ aio_check_io(struct ns_worker_ctx *ns_ctx)
|
||||
for (i = 0; i < count; i++) {
|
||||
task_complete(ns_ctx->u.aio.events[i].data);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
static void
|
||||
@ -892,18 +884,16 @@ perf_disconnect_cb(struct spdk_nvme_qpair *qpair, void *ctx)
|
||||
|
||||
}
|
||||
|
||||
static int64_t
|
||||
static void
|
||||
nvme_check_io(struct ns_worker_ctx *ns_ctx)
|
||||
{
|
||||
int64_t rc;
|
||||
|
||||
rc = spdk_nvme_poll_group_process_completions(ns_ctx->u.nvme.group, g_max_completions,
|
||||
perf_disconnect_cb);
|
||||
rc = spdk_nvme_poll_group_process_completions(ns_ctx->u.nvme.group, 0, perf_disconnect_cb);
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "NVMe io qpair process completion error\n");
|
||||
exit(1);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void
|
||||
@ -960,7 +950,7 @@ nvme_init_ns_worker_ctx(struct ns_worker_ctx *ns_ctx)
|
||||
opts.delay_cmd_submit = true;
|
||||
opts.create_only = true;
|
||||
|
||||
ns_ctx->u.nvme.group = spdk_nvme_poll_group_create(NULL, NULL);
|
||||
ns_ctx->u.nvme.group = spdk_nvme_poll_group_create(NULL);
|
||||
if (ns_ctx->u.nvme.group == NULL) {
|
||||
goto poll_group_failed;
|
||||
}
|
||||
@ -1017,84 +1007,6 @@ nvme_cleanup_ns_worker_ctx(struct ns_worker_ctx *ns_ctx)
|
||||
free(ns_ctx->u.nvme.qpair);
|
||||
}
|
||||
|
||||
static void
|
||||
nvme_dump_rdma_statistics(struct spdk_nvme_transport_poll_group_stat *stat)
|
||||
{
|
||||
struct spdk_nvme_rdma_device_stat *device_stats;
|
||||
uint32_t i;
|
||||
|
||||
printf("RDMA transport:\n");
|
||||
for (i = 0; i < stat->rdma.num_devices; i++) {
|
||||
device_stats = &stat->rdma.device_stats[i];
|
||||
printf("\tdev name: %s\n", device_stats->name);
|
||||
printf("\tpolls: %"PRIu64"\n", device_stats->polls);
|
||||
printf("\tidle_polls: %"PRIu64"\n", device_stats->idle_polls);
|
||||
printf("\tcompletions: %"PRIu64"\n", device_stats->completions);
|
||||
printf("\tqueued_requests: %"PRIu64"\n", device_stats->queued_requests);
|
||||
printf("\ttotal_send_wrs: %"PRIu64"\n", device_stats->total_send_wrs);
|
||||
printf("\tsend_doorbell_updates: %"PRIu64"\n", device_stats->send_doorbell_updates);
|
||||
printf("\ttotal_recv_wrs: %"PRIu64"\n", device_stats->total_recv_wrs);
|
||||
printf("\trecv_doorbell_updates: %"PRIu64"\n", device_stats->recv_doorbell_updates);
|
||||
printf("\t---------------------------------\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
nvme_dump_pcie_statistics(struct spdk_nvme_transport_poll_group_stat *stat)
|
||||
{
|
||||
struct spdk_nvme_pcie_stat *pcie_stat;
|
||||
|
||||
pcie_stat = &stat->pcie;
|
||||
|
||||
printf("PCIE transport:\n");
|
||||
printf("\tpolls: %"PRIu64"\n", pcie_stat->polls);
|
||||
printf("\tidle_polls: %"PRIu64"\n", pcie_stat->idle_polls);
|
||||
printf("\tcompletions: %"PRIu64"\n", pcie_stat->completions);
|
||||
printf("\tcq_doorbell_updates: %"PRIu64"\n", pcie_stat->cq_doorbell_updates);
|
||||
printf("\tsubmitted_requests: %"PRIu64"\n", pcie_stat->submitted_requests);
|
||||
printf("\tsq_doobell_updates: %"PRIu64"\n", pcie_stat->sq_doobell_updates);
|
||||
printf("\tqueued_requests: %"PRIu64"\n", pcie_stat->queued_requests);
|
||||
}
|
||||
|
||||
static void
|
||||
nvme_dump_transport_stats(uint32_t lcore, struct ns_worker_ctx *ns_ctx)
|
||||
{
|
||||
struct spdk_nvme_poll_group *group;
|
||||
struct spdk_nvme_poll_group_stat *stat = NULL;
|
||||
uint32_t i;
|
||||
int rc;
|
||||
|
||||
group = ns_ctx->u.nvme.group;
|
||||
if (group == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
rc = spdk_nvme_poll_group_get_stats(group, &stat);
|
||||
if (rc) {
|
||||
fprintf(stderr, "Can't get transport stats, error %d\n", rc);
|
||||
return;
|
||||
}
|
||||
|
||||
printf("\n====================\n");
|
||||
printf("lcore %u, ns %s statistics:\n", lcore, ns_ctx->entry->name);
|
||||
|
||||
for (i = 0; i < stat->num_transports; i++) {
|
||||
switch (stat->transport_stat[i]->trtype) {
|
||||
case SPDK_NVME_TRANSPORT_RDMA:
|
||||
nvme_dump_rdma_statistics(stat->transport_stat[i]);
|
||||
break;
|
||||
case SPDK_NVME_TRANSPORT_PCIE:
|
||||
nvme_dump_pcie_statistics(stat->transport_stat[i]);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Unknown transport statistics %d %s\n", stat->transport_stat[i]->trtype,
|
||||
spdk_nvme_transport_id_trtype_str(stat->transport_stat[i]->trtype));
|
||||
}
|
||||
}
|
||||
|
||||
spdk_nvme_poll_group_free_stats(group, stat);
|
||||
}
|
||||
|
||||
static const struct ns_fn_table nvme_fn_table = {
|
||||
.setup_payload = nvme_setup_payload,
|
||||
.submit_io = nvme_submit_io,
|
||||
@ -1102,7 +1014,6 @@ static const struct ns_fn_table nvme_fn_table = {
|
||||
.verify_io = nvme_verify_io,
|
||||
.init_ns_worker_ctx = nvme_init_ns_worker_ctx,
|
||||
.cleanup_ns_worker_ctx = nvme_cleanup_ns_worker_ctx,
|
||||
.dump_transport_stats = nvme_dump_transport_stats
|
||||
};
|
||||
|
||||
static int
|
||||
@ -1373,10 +1284,6 @@ submit_single_io(struct perf_task *task)
|
||||
|
||||
if (spdk_unlikely(rc != 0)) {
|
||||
RATELIMIT_LOG("starting I/O failed\n");
|
||||
spdk_dma_free(task->iovs[0].iov_base);
|
||||
free(task->iovs);
|
||||
spdk_dma_free(task->md_iov.iov_base);
|
||||
free(task);
|
||||
} else {
|
||||
ns_ctx->current_queue_depth++;
|
||||
}
|
||||
@ -1497,11 +1404,6 @@ print_periodic_performance(bool warmup)
|
||||
double mb_this_second;
|
||||
struct worker_thread *worker;
|
||||
struct ns_worker_ctx *ns_ctx;
|
||||
uint64_t busy_tsc;
|
||||
uint64_t idle_tsc;
|
||||
uint64_t core_busy_tsc = 0;
|
||||
uint64_t core_idle_tsc = 0;
|
||||
double core_busy_perc = 0;
|
||||
|
||||
if (!isatty(STDOUT_FILENO)) {
|
||||
/* Don't print periodic stats if output is not going
|
||||
@ -1509,60 +1411,29 @@ print_periodic_performance(bool warmup)
|
||||
*/
|
||||
return;
|
||||
}
|
||||
|
||||
io_this_second = 0;
|
||||
TAILQ_FOREACH(worker, &g_workers, link) {
|
||||
busy_tsc = 0;
|
||||
idle_tsc = 0;
|
||||
TAILQ_FOREACH(ns_ctx, &worker->ns_ctx, link) {
|
||||
io_this_second += ns_ctx->stats.io_completed - ns_ctx->stats.last_io_completed;
|
||||
ns_ctx->stats.last_io_completed = ns_ctx->stats.io_completed;
|
||||
|
||||
if (g_monitor_perf_cores) {
|
||||
busy_tsc += ns_ctx->stats.busy_tsc - ns_ctx->stats.last_busy_tsc;
|
||||
idle_tsc += ns_ctx->stats.idle_tsc - ns_ctx->stats.last_idle_tsc;
|
||||
ns_ctx->stats.last_busy_tsc = ns_ctx->stats.busy_tsc;
|
||||
ns_ctx->stats.last_idle_tsc = ns_ctx->stats.idle_tsc;
|
||||
}
|
||||
}
|
||||
if (g_monitor_perf_cores) {
|
||||
core_busy_tsc += busy_tsc;
|
||||
core_idle_tsc += idle_tsc;
|
||||
core_busy_perc += (double)core_busy_tsc / (core_idle_tsc + core_busy_tsc) * 100;
|
||||
}
|
||||
}
|
||||
|
||||
mb_this_second = (double)io_this_second * g_io_size_bytes / (1024 * 1024);
|
||||
|
||||
printf("%s%9ju IOPS, %8.2f MiB/s", warmup ? "[warmup] " : "", io_this_second, mb_this_second);
|
||||
if (g_monitor_perf_cores) {
|
||||
printf("%3d Core(s): %6.2f%% Busy", g_num_workers, core_busy_perc);
|
||||
}
|
||||
printf("\r");
|
||||
printf("%s%9ju IOPS, %8.2f MiB/s\r", warmup ? "[warmup] " : "", io_this_second, mb_this_second);
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
static void
|
||||
perf_dump_transport_statistics(struct worker_thread *worker)
|
||||
{
|
||||
struct ns_worker_ctx *ns_ctx;
|
||||
|
||||
TAILQ_FOREACH(ns_ctx, &worker->ns_ctx, link) {
|
||||
if (ns_ctx->entry->fn_table->dump_transport_stats) {
|
||||
ns_ctx->entry->fn_table->dump_transport_stats(worker->lcore, ns_ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
work_fn(void *arg)
|
||||
{
|
||||
uint64_t tsc_start, tsc_end, tsc_current, tsc_next_print;
|
||||
uint64_t tsc_end, tsc_current, tsc_next_print;
|
||||
struct worker_thread *worker = (struct worker_thread *) arg;
|
||||
struct ns_worker_ctx *ns_ctx = NULL;
|
||||
uint32_t unfinished_ns_ctx;
|
||||
bool warmup = false;
|
||||
int rc;
|
||||
int64_t check_rc;
|
||||
uint64_t check_now;
|
||||
|
||||
/* Allocate queue pairs for each namespace. */
|
||||
TAILQ_FOREACH(ns_ctx, &worker->ns_ctx, link) {
|
||||
@ -1580,8 +1451,7 @@ work_fn(void *arg)
|
||||
return 1;
|
||||
}
|
||||
|
||||
tsc_start = spdk_get_ticks();
|
||||
tsc_current = tsc_start;
|
||||
tsc_current = spdk_get_ticks();
|
||||
tsc_next_print = tsc_current + g_tsc_rate;
|
||||
|
||||
if (g_warmup_time_in_sec) {
|
||||
@ -1603,15 +1473,7 @@ work_fn(void *arg)
|
||||
* to replace each I/O that is completed.
|
||||
*/
|
||||
TAILQ_FOREACH(ns_ctx, &worker->ns_ctx, link) {
|
||||
check_now = spdk_get_ticks();
|
||||
check_rc = ns_ctx->entry->fn_table->check_io(ns_ctx);
|
||||
|
||||
if (check_rc > 0) {
|
||||
ns_ctx->stats.busy_tsc += check_now - ns_ctx->stats.last_tsc;
|
||||
} else {
|
||||
ns_ctx->stats.idle_tsc += check_now - ns_ctx->stats.last_tsc;
|
||||
}
|
||||
ns_ctx->stats.last_tsc = check_now;
|
||||
ns_ctx->entry->fn_table->check_io(ns_ctx);
|
||||
}
|
||||
|
||||
tsc_current = spdk_get_ticks();
|
||||
@ -1643,20 +1505,6 @@ work_fn(void *arg)
|
||||
}
|
||||
}
|
||||
|
||||
/* Capture the actual elapsed time when we break out of the main loop. This will account
|
||||
* for cases where we exit prematurely due to a signal. We only need to capture it on
|
||||
* one core, so use the main core.
|
||||
*/
|
||||
if (worker->lcore == g_main_core) {
|
||||
g_elapsed_time_in_usec = (tsc_current - tsc_start) * SPDK_SEC_TO_USEC / g_tsc_rate;
|
||||
}
|
||||
|
||||
if (g_dump_transport_stats) {
|
||||
pthread_mutex_lock(&g_stats_mutex);
|
||||
perf_dump_transport_statistics(worker);
|
||||
pthread_mutex_unlock(&g_stats_mutex);
|
||||
}
|
||||
|
||||
/* drain the io of each ns_ctx in round robin to make the fairness */
|
||||
do {
|
||||
unfinished_ns_ctx = 0;
|
||||
@ -1668,17 +1516,15 @@ work_fn(void *arg)
|
||||
|
||||
if (ns_ctx->current_queue_depth > 0) {
|
||||
ns_ctx->entry->fn_table->check_io(ns_ctx);
|
||||
if (ns_ctx->current_queue_depth > 0) {
|
||||
if (ns_ctx->current_queue_depth == 0) {
|
||||
cleanup_ns_worker_ctx(ns_ctx);
|
||||
} else {
|
||||
unfinished_ns_ctx++;
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (unfinished_ns_ctx > 0);
|
||||
|
||||
TAILQ_FOREACH(ns_ctx, &worker->ns_ctx, link) {
|
||||
cleanup_ns_worker_ctx(ns_ctx);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1689,28 +1535,28 @@ static void usage(char *program_name)
|
||||
printf(" [Kernel device(s)]...");
|
||||
#endif
|
||||
printf("\n");
|
||||
printf("\t[-b, --allowed-pci-addr <addr> allowed local PCIe device address]\n");
|
||||
printf("\t[-b allowed local PCIe device address]\n");
|
||||
printf("\t Example: -b 0000:d8:00.0 -b 0000:d9:00.0\n");
|
||||
printf("\t[-q, --io-depth <val> io depth]\n");
|
||||
printf("\t[-o, --io-size <val> io size in bytes]\n");
|
||||
printf("\t[-O, --io-unit-size io unit size in bytes (4-byte aligned) for SPDK driver. default: same as io size]\n");
|
||||
printf("\t[-P, --num-qpairs <val> number of io queues per namespace. default: 1]\n");
|
||||
printf("\t[-U, --num-unused-qpairs <val> number of unused io queues per controller. default: 0]\n");
|
||||
printf("\t[-w, --io-pattern <pattern> io pattern type, must be one of\n");
|
||||
printf("\t[-q io depth]\n");
|
||||
printf("\t[-o io size in bytes]\n");
|
||||
printf("\t[-O io unit size in bytes (4-byte aligned) for SPDK driver. default: same as io size]\n");
|
||||
printf("\t[-P number of io queues per namespace. default: 1]\n");
|
||||
printf("\t[-U number of unused io queues per controller. default: 0]\n");
|
||||
printf("\t[-w io pattern type, must be one of\n");
|
||||
printf("\t\t(read, write, randread, randwrite, rw, randrw)]\n");
|
||||
printf("\t[-M, --rwmixread <0-100> rwmixread (100 for reads, 0 for writes)]\n");
|
||||
printf("\t[-L, --enable-sw-latency-tracking enable latency tracking via sw, default: disabled]\n");
|
||||
printf("\t[-M rwmixread (100 for reads, 0 for writes)]\n");
|
||||
printf("\t[-L enable latency tracking via sw, default: disabled]\n");
|
||||
printf("\t\t-L for latency summary, -LL for detailed histogram\n");
|
||||
printf("\t[-l, --enable-ssd-latency-tracking enable latency tracking via ssd (if supported), default: disabled]\n");
|
||||
printf("\t[-t, --time <sec> time in seconds]\n");
|
||||
printf("\t[-a, --warmup-time <sec> warmup time in seconds]\n");
|
||||
printf("\t[-c, --core-mask <mask> core mask for I/O submission/completion.]\n");
|
||||
printf("\t[-l enable latency tracking via ssd (if supported), default: disabled]\n");
|
||||
printf("\t[-t time in seconds]\n");
|
||||
printf("\t[-a warmup time in seconds]\n");
|
||||
printf("\t[-c core mask for I/O submission/completion.]\n");
|
||||
printf("\t\t(default: 1)\n");
|
||||
printf("\t[-D, --disable-sq-cmb disable submission queue in controller memory buffer, default: enabled]\n");
|
||||
printf("\t[-H, --enable-tcp-hdgst enable header digest for TCP transport, default: disabled]\n");
|
||||
printf("\t[-I, --enable-tcp-ddgst enable data digest for TCP transport, default: disabled]\n");
|
||||
printf("\t[-N, --no-shst-notification no shutdown notification process for controllers, default: disabled]\n");
|
||||
printf("\t[-r, --transport <fmt> Transport ID for local PCIe NVMe or NVMeoF]\n");
|
||||
printf("\t[-D disable submission queue in controller memory buffer, default: enabled]\n");
|
||||
printf("\t[-H enable header digest for TCP transport, default: disabled]\n");
|
||||
printf("\t[-I enable data digest for TCP transport, default: disabled]\n");
|
||||
printf("\t[-N no shutdown notification process for controllers, default: disabled]\n");
|
||||
printf("\t[-r Transport ID for local PCIe NVMe or NVMeoF]\n");
|
||||
printf("\t Format: 'key:value [key:value] ...'\n");
|
||||
printf("\t Keys:\n");
|
||||
printf("\t trtype Transport type (e.g. PCIe, RDMA)\n");
|
||||
@ -1718,42 +1564,37 @@ static void usage(char *program_name)
|
||||
printf("\t traddr Transport address (e.g. 0000:04:00.0 for PCIe or 192.168.100.8 for RDMA)\n");
|
||||
printf("\t trsvcid Transport service identifier (e.g. 4420)\n");
|
||||
printf("\t subnqn Subsystem NQN (default: %s)\n", SPDK_NVMF_DISCOVERY_NQN);
|
||||
printf("\t ns NVMe namespace ID (all active namespaces are used by default)\n");
|
||||
printf("\t hostnqn Host NQN\n");
|
||||
printf("\t Example: -r 'trtype:PCIe traddr:0000:04:00.0' for PCIe or\n");
|
||||
printf("\t -r 'trtype:RDMA adrfam:IPv4 traddr:192.168.100.8 trsvcid:4420' for NVMeoF\n");
|
||||
printf("\t Note: can be specified multiple times to test multiple disks/targets.\n");
|
||||
printf("\t[-e, --metadata <fmt> metadata configuration]\n");
|
||||
printf("\t[-e metadata configuration]\n");
|
||||
printf("\t Keys:\n");
|
||||
printf("\t PRACT Protection Information Action bit (PRACT=1 or PRACT=0)\n");
|
||||
printf("\t PRCHK Control of Protection Information Checking (PRCHK=GUARD|REFTAG|APPTAG)\n");
|
||||
printf("\t Example: -e 'PRACT=0,PRCHK=GUARD|REFTAG|APPTAG'\n");
|
||||
printf("\t -e 'PRACT=1,PRCHK=GUARD'\n");
|
||||
printf("\t[-k, --keepalive <ms> keep alive timeout period in millisecond]\n");
|
||||
printf("\t[-s, --hugemem-size <MB> DPDK huge memory size in MB.]\n");
|
||||
printf("\t[-g, --mem-single-seg use single file descriptor for DPDK memory segments]\n");
|
||||
printf("\t[-C, --max-completion-per-poll <val> max completions per poll]\n");
|
||||
printf("\t[-k keep alive timeout period in millisecond]\n");
|
||||
printf("\t[-s DPDK huge memory size in MB.]\n");
|
||||
printf("\t[-g use single file descriptor for DPDK memory segments]\n");
|
||||
printf("\t[-C max completions per poll]\n");
|
||||
printf("\t\t(default: 0 - unlimited)\n");
|
||||
printf("\t[-i, --shmem-grp-id <id> shared memory group ID]\n");
|
||||
printf("\t[-Q, --skip-errors log I/O errors every N times (default: 1)\n");
|
||||
printf("\t[-i shared memory group ID]\n");
|
||||
printf("\t[-Q log I/O errors every N times (default: 1)\n");
|
||||
printf("\t");
|
||||
spdk_log_usage(stdout, "-T");
|
||||
printf("\t[-V, --enable-vmd enable VMD enumeration]\n");
|
||||
printf("\t[-z, --disable-zcopy <impl> disable zero copy send for the given sock implementation. Default for posix impl]\n");
|
||||
printf("\t[-Z, --enable-zcopy <impl> enable zero copy send for the given sock implementation]\n");
|
||||
printf("\t[-A, --buffer-alignment IO buffer alignment. Must be power of 2 and not less than cache line (%u)]\n",
|
||||
printf("\t[-V enable VMD enumeration]\n");
|
||||
printf("\t[-z disable zero copy send for the given sock implementation. Default for posix impl]\n");
|
||||
printf("\t[-Z enable zero copy send for the given sock implementation]\n");
|
||||
printf("\t[-A IO buffer alignment. Must be power of 2 and not less than cache line (%u)]\n",
|
||||
SPDK_CACHE_LINE_SIZE);
|
||||
printf("\t[-S, --default-sock-impl <impl> set the default sock impl, e.g. \"posix\"]\n");
|
||||
printf("\t[-m, --cpu-usage display real-time overall cpu usage on used cores]\n");
|
||||
printf("\t[-S set the default sock impl, e.g. \"posix\"]\n");
|
||||
#ifdef SPDK_CONFIG_URING
|
||||
printf("\t[-R, --enable-uring enable using liburing to drive kernel devices (Default: libaio)]\n");
|
||||
printf("\t[-R enable using liburing to drive kernel devices (Default: libaio)]\n");
|
||||
#endif
|
||||
#ifdef DEBUG
|
||||
printf("\t[-G, --enable-debug enable debug logging]\n");
|
||||
printf("\t[-G enable debug logging]\n");
|
||||
#else
|
||||
printf("\t[-G, --enable-debug enable debug logging (flag disabled, must reconfigure with --enable-debug)\n");
|
||||
printf("\t[--transport-stats dump transport statistics]\n");
|
||||
printf("\t[--iova-mode <mode> specify DPDK IOVA mode: va|pa]\n");
|
||||
printf("\t[-G enable debug logging (flag disabled, must reconfigure with --enable-debug)\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -1827,7 +1668,7 @@ print_performance(void)
|
||||
TAILQ_FOREACH(worker, &g_workers, link) {
|
||||
TAILQ_FOREACH(ns_ctx, &worker->ns_ctx, link) {
|
||||
if (ns_ctx->stats.io_completed != 0) {
|
||||
io_per_second = (double)ns_ctx->stats.io_completed * 1000 * 1000 / g_elapsed_time_in_usec;
|
||||
io_per_second = (double)ns_ctx->stats.io_completed / g_time_in_sec;
|
||||
mb_per_second = io_per_second * g_io_size_bytes / (1024 * 1024);
|
||||
average_latency = ((double)ns_ctx->stats.total_tsc / ns_ctx->stats.io_completed) * 1000 * 1000 /
|
||||
g_tsc_rate;
|
||||
@ -2061,23 +1902,23 @@ add_trid(const char *trid_str)
|
||||
}
|
||||
|
||||
static int
|
||||
add_allowed_pci_device(const char *bdf_str, struct spdk_env_opts *env_opts)
|
||||
add_allowed_pci_device(const char *bdf_str)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (env_opts->num_pci_addr >= MAX_ALLOWED_PCI_DEVICE_NUM) {
|
||||
if (g_allowed_pci_addr_num >= MAX_ALLOWED_PCI_DEVICE_NUM) {
|
||||
fprintf(stderr, "Currently we only support allowed PCI device num=%d\n",
|
||||
MAX_ALLOWED_PCI_DEVICE_NUM);
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = spdk_pci_addr_parse(&env_opts->pci_allowed[env_opts->num_pci_addr], bdf_str);
|
||||
rc = spdk_pci_addr_parse(&g_allowed_pci_addr[g_allowed_pci_addr_num], bdf_str);
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "Failed to parse the given bdf_str=%s\n", bdf_str);
|
||||
return -1;
|
||||
}
|
||||
|
||||
env_opts->num_pci_addr++;
|
||||
g_allowed_pci_addr_num++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -2171,155 +2012,77 @@ parse_metadata(const char *metacfg_str)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define PERF_GETOPT_SHORT "a:b:c:e:gi:lmo:q:r:k:s:t:w:z:A:C:DGHILM:NO:P:Q:RS:T:U:VZ:"
|
||||
|
||||
static const struct option g_perf_cmdline_opts[] = {
|
||||
#define PERF_WARMUP_TIME 'a'
|
||||
{"warmup-time", required_argument, NULL, PERF_WARMUP_TIME},
|
||||
#define PERF_ALLOWED_PCI_ADDR 'b'
|
||||
{"allowed-pci-addr", required_argument, NULL, PERF_ALLOWED_PCI_ADDR},
|
||||
#define PERF_CORE_MASK 'c'
|
||||
{"core-mask", required_argument, NULL, PERF_CORE_MASK},
|
||||
#define PERF_METADATA 'e'
|
||||
{"metadata", required_argument, NULL, PERF_METADATA},
|
||||
#define PERF_MEM_SINGL_SEG 'g'
|
||||
{"mem-single-seg", no_argument, NULL, PERF_MEM_SINGL_SEG},
|
||||
#define PERF_SHMEM_GROUP_ID 'i'
|
||||
{"shmem-grp-id", required_argument, NULL, PERF_SHMEM_GROUP_ID},
|
||||
#define PERF_ENABLE_SSD_LATENCY_TRACING 'l'
|
||||
{"enable-ssd-latency-tracking", no_argument, NULL, PERF_ENABLE_SSD_LATENCY_TRACING},
|
||||
#define PERF_CPU_USAGE 'm'
|
||||
{"cpu-usage", no_argument, NULL, PERF_CPU_USAGE},
|
||||
#define PERF_IO_SIZE 'o'
|
||||
{"io-size", required_argument, NULL, PERF_IO_SIZE},
|
||||
#define PERF_IO_DEPTH 'q'
|
||||
{"io-depth", required_argument, NULL, PERF_IO_DEPTH},
|
||||
#define PERF_TRANSPORT 'r'
|
||||
{"transport", required_argument, NULL, PERF_TRANSPORT},
|
||||
#define PERF_KEEPALIVE 'k'
|
||||
{"keepalive", required_argument, NULL, PERF_KEEPALIVE},
|
||||
#define PERF_HUGEMEM_SIZE 's'
|
||||
{"hugemem-size", required_argument, NULL, PERF_HUGEMEM_SIZE},
|
||||
#define PERF_TIME 't'
|
||||
{"time", required_argument, NULL, PERF_TIME},
|
||||
#define PERF_IO_PATTERN 'w'
|
||||
{"io-pattern", required_argument, NULL, PERF_IO_PATTERN},
|
||||
#define PERF_DISABLE_ZCOPY 'z'
|
||||
{"disable-zcopy", required_argument, NULL, PERF_DISABLE_ZCOPY},
|
||||
#define PERF_BUFFER_ALIGNMENT 'A'
|
||||
{"buffer-alignment", required_argument, NULL, PERF_BUFFER_ALIGNMENT},
|
||||
#define PERF_MAX_COMPLETIONS_PER_POLL 'C'
|
||||
{"max-completion-per-poll", required_argument, NULL, PERF_MAX_COMPLETIONS_PER_POLL},
|
||||
#define PERF_DISABLE_SQ_CMB 'D'
|
||||
{"disable-sq-cmb", no_argument, NULL, PERF_DISABLE_SQ_CMB},
|
||||
#define PERF_ENABLE_DEBUG 'G'
|
||||
{"enable-debug", no_argument, NULL, PERF_ENABLE_DEBUG},
|
||||
#define PERF_ENABLE_TCP_HDGST 'H'
|
||||
{"enable-tcp-hdgst", no_argument, NULL, PERF_ENABLE_TCP_HDGST},
|
||||
#define PERF_ENABLE_TCP_DDGST 'I'
|
||||
{"enable-tcp-ddgst", no_argument, NULL, PERF_ENABLE_TCP_DDGST},
|
||||
#define PERF_ENABLE_SW_LATENCY_TRACING 'L'
|
||||
{"enable-sw-latency-tracking", no_argument, NULL, PERF_ENABLE_SW_LATENCY_TRACING},
|
||||
#define PERF_RW_MIXREAD 'M'
|
||||
{"rwmixread", required_argument, NULL, PERF_RW_MIXREAD},
|
||||
#define PERF_NO_SHST_NOTIFICATION 'N'
|
||||
{"no-shst-notification", no_argument, NULL, PERF_NO_SHST_NOTIFICATION},
|
||||
#define PERF_IO_UNIT_SIZE 'O'
|
||||
{"io-unit-size", required_argument, NULL, PERF_IO_UNIT_SIZE},
|
||||
#define PERF_IO_QUEUES_PER_NS 'P'
|
||||
{"num-qpairs", required_argument, NULL, PERF_IO_QUEUES_PER_NS},
|
||||
#define PERF_SKIP_ERRRORS 'Q'
|
||||
{"skip-errors", required_argument, NULL, PERF_SKIP_ERRRORS},
|
||||
#define PERF_ENABLE_URING 'R'
|
||||
{"enable-uring", no_argument, NULL, PERF_ENABLE_URING},
|
||||
#define PERF_DEFAULT_SOCK_IMPL 'S'
|
||||
{"default-sock-impl", required_argument, NULL, PERF_DEFAULT_SOCK_IMPL},
|
||||
#define PERF_LOG_FLAG 'T'
|
||||
{"logflag", required_argument, NULL, PERF_LOG_FLAG},
|
||||
#define PERF_NUM_UNUSED_IO_QPAIRS 'U'
|
||||
{"num-unused-qpairs", required_argument, NULL, PERF_NUM_UNUSED_IO_QPAIRS},
|
||||
#define PERF_ENABLE_VMD 'V'
|
||||
{"enable-vmd", no_argument, NULL, PERF_ENABLE_VMD},
|
||||
#define PERF_ENABLE_ZCOPY 'Z'
|
||||
{"enable-zcopy", required_argument, NULL, PERF_ENABLE_ZCOPY},
|
||||
#define PERF_TRANSPORT_STATISTICS 257
|
||||
{"transport-stats", no_argument, NULL, PERF_TRANSPORT_STATISTICS},
|
||||
#define PERF_IOVA_MODE 258
|
||||
{"iova-mode", required_argument, NULL, PERF_IOVA_MODE},
|
||||
/* Should be the last element */
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
static int
|
||||
parse_args(int argc, char **argv, struct spdk_env_opts *env_opts)
|
||||
parse_args(int argc, char **argv)
|
||||
{
|
||||
int op, long_idx;
|
||||
int op;
|
||||
long int val;
|
||||
int rc;
|
||||
|
||||
while ((op = getopt_long(argc, argv, PERF_GETOPT_SHORT, g_perf_cmdline_opts, &long_idx)) != -1) {
|
||||
while ((op = getopt(argc, argv,
|
||||
"a:b:c:e:gi:lo:q:r:k:s:t:w:z:A:C:DGHILM:NO:P:Q:RS:T:U:VZ:")) != -1) {
|
||||
switch (op) {
|
||||
case PERF_WARMUP_TIME:
|
||||
case PERF_BUFFER_ALIGNMENT:
|
||||
case PERF_SHMEM_GROUP_ID:
|
||||
case PERF_MAX_COMPLETIONS_PER_POLL:
|
||||
case PERF_IO_QUEUES_PER_NS:
|
||||
case PERF_IO_SIZE:
|
||||
case PERF_IO_UNIT_SIZE:
|
||||
case PERF_IO_DEPTH:
|
||||
case PERF_KEEPALIVE:
|
||||
case PERF_HUGEMEM_SIZE:
|
||||
case PERF_TIME:
|
||||
case PERF_RW_MIXREAD:
|
||||
case PERF_NUM_UNUSED_IO_QPAIRS:
|
||||
case PERF_SKIP_ERRRORS:
|
||||
case 'a':
|
||||
case 'A':
|
||||
case 'i':
|
||||
case 'C':
|
||||
case 'P':
|
||||
case 'o':
|
||||
case 'O':
|
||||
case 'q':
|
||||
case 'k':
|
||||
case 's':
|
||||
case 't':
|
||||
case 'M':
|
||||
case 'Q':
|
||||
case 'U':
|
||||
val = spdk_strtol(optarg, 10);
|
||||
if (val < 0) {
|
||||
fprintf(stderr, "Converting a string to integer failed\n");
|
||||
return val;
|
||||
}
|
||||
switch (op) {
|
||||
case PERF_WARMUP_TIME:
|
||||
case 'a':
|
||||
g_warmup_time_in_sec = val;
|
||||
break;
|
||||
case PERF_SHMEM_GROUP_ID:
|
||||
env_opts->shm_id = val;
|
||||
case 'i':
|
||||
g_shm_id = val;
|
||||
break;
|
||||
case PERF_MAX_COMPLETIONS_PER_POLL:
|
||||
case 'C':
|
||||
g_max_completions = val;
|
||||
break;
|
||||
case PERF_IO_QUEUES_PER_NS:
|
||||
case 'P':
|
||||
g_nr_io_queues_per_ns = val;
|
||||
break;
|
||||
case PERF_IO_SIZE:
|
||||
case 'o':
|
||||
g_io_size_bytes = val;
|
||||
break;
|
||||
case PERF_IO_UNIT_SIZE:
|
||||
case 'O':
|
||||
g_io_unit_size = val;
|
||||
break;
|
||||
case PERF_IO_DEPTH:
|
||||
case 'q':
|
||||
g_queue_depth = val;
|
||||
break;
|
||||
case PERF_KEEPALIVE:
|
||||
case 'k':
|
||||
g_keep_alive_timeout_in_ms = val;
|
||||
break;
|
||||
case PERF_HUGEMEM_SIZE:
|
||||
env_opts->mem_size = val;
|
||||
case 's':
|
||||
g_dpdk_mem = val;
|
||||
break;
|
||||
case PERF_TIME:
|
||||
case 't':
|
||||
g_time_in_sec = val;
|
||||
break;
|
||||
case PERF_RW_MIXREAD:
|
||||
case 'M':
|
||||
g_rw_percentage = val;
|
||||
g_mix_specified = true;
|
||||
break;
|
||||
case PERF_SKIP_ERRRORS:
|
||||
case 'Q':
|
||||
g_quiet_count = val;
|
||||
break;
|
||||
case PERF_NUM_UNUSED_IO_QPAIRS:
|
||||
case 'U':
|
||||
g_nr_unused_io_queues = val;
|
||||
break;
|
||||
case PERF_BUFFER_ALIGNMENT:
|
||||
case 'A':
|
||||
g_io_align = val;
|
||||
if (!spdk_u32_is_pow2(g_io_align) || g_io_align < SPDK_CACHE_LINE_SIZE) {
|
||||
fprintf(stderr, "Wrong alignment %u. Must be power of 2 and not less than cache lize (%u)\n",
|
||||
@ -2331,43 +2094,40 @@ parse_args(int argc, char **argv, struct spdk_env_opts *env_opts)
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case PERF_ALLOWED_PCI_ADDR:
|
||||
if (add_allowed_pci_device(optarg, env_opts)) {
|
||||
case 'b':
|
||||
if (add_allowed_pci_device(optarg)) {
|
||||
usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
case PERF_CORE_MASK:
|
||||
env_opts->core_mask = optarg;
|
||||
case 'c':
|
||||
g_core_mask = optarg;
|
||||
break;
|
||||
case PERF_METADATA:
|
||||
case 'e':
|
||||
if (parse_metadata(optarg)) {
|
||||
usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
case PERF_MEM_SINGL_SEG:
|
||||
env_opts->hugepage_single_segments = true;
|
||||
case 'g':
|
||||
g_dpdk_mem_single_seg = true;
|
||||
break;
|
||||
case PERF_ENABLE_SSD_LATENCY_TRACING:
|
||||
case 'l':
|
||||
g_latency_ssd_tracking_enable = true;
|
||||
break;
|
||||
case PERF_CPU_USAGE:
|
||||
g_monitor_perf_cores = true;
|
||||
break;
|
||||
case PERF_TRANSPORT:
|
||||
case 'r':
|
||||
if (add_trid(optarg)) {
|
||||
usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
case PERF_IO_PATTERN:
|
||||
case 'w':
|
||||
g_workload_type = optarg;
|
||||
break;
|
||||
case PERF_DISABLE_SQ_CMB:
|
||||
case 'D':
|
||||
g_disable_sq_cmb = 1;
|
||||
break;
|
||||
case PERF_ENABLE_DEBUG:
|
||||
case 'G':
|
||||
#ifndef DEBUG
|
||||
fprintf(stderr, "%s must be configured with --enable-debug for -G flag\n",
|
||||
argv[0]);
|
||||
@ -2378,19 +2138,19 @@ parse_args(int argc, char **argv, struct spdk_env_opts *env_opts)
|
||||
spdk_log_set_print_level(SPDK_LOG_DEBUG);
|
||||
break;
|
||||
#endif
|
||||
case PERF_ENABLE_TCP_HDGST:
|
||||
case 'H':
|
||||
g_header_digest = 1;
|
||||
break;
|
||||
case PERF_ENABLE_TCP_DDGST:
|
||||
case 'I':
|
||||
g_data_digest = 1;
|
||||
break;
|
||||
case PERF_ENABLE_SW_LATENCY_TRACING:
|
||||
case 'L':
|
||||
g_latency_sw_tracking_level++;
|
||||
break;
|
||||
case PERF_NO_SHST_NOTIFICATION:
|
||||
case 'N':
|
||||
g_no_shn_notification = true;
|
||||
break;
|
||||
case PERF_ENABLE_URING:
|
||||
case 'R':
|
||||
#ifndef SPDK_CONFIG_URING
|
||||
fprintf(stderr, "%s must be rebuilt with CONFIG_URING=y for -R flag.\n",
|
||||
argv[0]);
|
||||
@ -2399,7 +2159,7 @@ parse_args(int argc, char **argv, struct spdk_env_opts *env_opts)
|
||||
#endif
|
||||
g_use_uring = true;
|
||||
break;
|
||||
case PERF_LOG_FLAG:
|
||||
case 'T':
|
||||
rc = spdk_log_set_flag(optarg);
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "unknown flag\n");
|
||||
@ -2410,28 +2170,22 @@ parse_args(int argc, char **argv, struct spdk_env_opts *env_opts)
|
||||
spdk_log_set_print_level(SPDK_LOG_DEBUG);
|
||||
#endif
|
||||
break;
|
||||
case PERF_ENABLE_VMD:
|
||||
case 'V':
|
||||
g_vmd = true;
|
||||
break;
|
||||
case PERF_DISABLE_ZCOPY:
|
||||
case 'z':
|
||||
perf_set_sock_zcopy(optarg, false);
|
||||
break;
|
||||
case PERF_ENABLE_ZCOPY:
|
||||
case 'Z':
|
||||
perf_set_sock_zcopy(optarg, true);
|
||||
break;
|
||||
case PERF_DEFAULT_SOCK_IMPL:
|
||||
case 'S':
|
||||
rc = spdk_sock_set_default_impl(optarg);
|
||||
if (rc) {
|
||||
fprintf(stderr, "Failed to set sock impl %s, err %d (%s)\n", optarg, errno, strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
case PERF_TRANSPORT_STATISTICS:
|
||||
g_dump_transport_stats = true;
|
||||
break;
|
||||
case PERF_IOVA_MODE:
|
||||
env_opts->iova_mode = optarg;
|
||||
break;
|
||||
default:
|
||||
usage(argv[0]);
|
||||
return 1;
|
||||
@ -2444,12 +2198,12 @@ parse_args(int argc, char **argv, struct spdk_env_opts *env_opts)
|
||||
}
|
||||
|
||||
if (!g_queue_depth) {
|
||||
fprintf(stderr, "missing -q (--io-depth) operand\n");
|
||||
fprintf(stderr, "missing -q (queue size) operand\n");
|
||||
usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
if (!g_io_size_bytes) {
|
||||
fprintf(stderr, "missing -o (--io-size) operand\n");
|
||||
fprintf(stderr, "missing -o (block size) operand\n");
|
||||
usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
@ -2458,17 +2212,17 @@ parse_args(int argc, char **argv, struct spdk_env_opts *env_opts)
|
||||
return 1;
|
||||
}
|
||||
if (!g_workload_type) {
|
||||
fprintf(stderr, "missing -w (--io-pattern) operand\n");
|
||||
fprintf(stderr, "missing -w (io pattern type) operand\n");
|
||||
usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
if (!g_time_in_sec) {
|
||||
fprintf(stderr, "missing -t (--time) operand\n");
|
||||
fprintf(stderr, "missing -t (test time in seconds) operand\n");
|
||||
usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
if (!g_quiet_count) {
|
||||
fprintf(stderr, "-Q (--skip-errors) value must be greater than 0\n");
|
||||
fprintf(stderr, "-Q value must be greater than 0\n");
|
||||
usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
@ -2481,19 +2235,19 @@ parse_args(int argc, char **argv, struct spdk_env_opts *env_opts)
|
||||
if (strcmp(g_workload_type, "read") == 0 || strcmp(g_workload_type, "write") == 0) {
|
||||
g_rw_percentage = strcmp(g_workload_type, "read") == 0 ? 100 : 0;
|
||||
if (g_mix_specified) {
|
||||
fprintf(stderr, "Ignoring -M (--rwmixread) option... Please use -M option"
|
||||
fprintf(stderr, "Ignoring -M option... Please use -M option"
|
||||
" only when using rw or randrw.\n");
|
||||
}
|
||||
} else if (strcmp(g_workload_type, "rw") == 0) {
|
||||
if (g_rw_percentage < 0 || g_rw_percentage > 100) {
|
||||
fprintf(stderr,
|
||||
"-M (--rwmixread) must be specified to value from 0 to 100 "
|
||||
"-M must be specified to value from 0 to 100 "
|
||||
"for rw or randrw.\n");
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr,
|
||||
"-o (--io-pattern) io pattern type must be one of\n"
|
||||
"io pattern type must be one of\n"
|
||||
"(read, write, randread, randwrite, rw, randrw)\n");
|
||||
return 1;
|
||||
}
|
||||
@ -2504,11 +2258,11 @@ parse_args(int argc, char **argv, struct spdk_env_opts *env_opts)
|
||||
} else {
|
||||
struct trid_entry *trid_entry, *trid_entry_tmp;
|
||||
|
||||
env_opts->no_pci = true;
|
||||
g_no_pci = true;
|
||||
/* check whether there is local PCIe type */
|
||||
TAILQ_FOREACH_SAFE(trid_entry, &g_trid_list, tailq, trid_entry_tmp) {
|
||||
if (trid_entry->trid.trtype == SPDK_NVME_TRANSPORT_PCIE) {
|
||||
env_opts->no_pci = false;
|
||||
g_no_pci = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -2792,19 +2546,29 @@ int main(int argc, char **argv)
|
||||
struct spdk_env_opts opts;
|
||||
pthread_t thread_id = 0;
|
||||
|
||||
spdk_env_opts_init(&opts);
|
||||
opts.name = "perf";
|
||||
opts.pci_allowed = g_allowed_pci_addr;
|
||||
rc = parse_args(argc, argv, &opts);
|
||||
rc = parse_args(argc, argv);
|
||||
if (rc != 0) {
|
||||
return rc;
|
||||
}
|
||||
/* Transport statistics are printed from each thread.
|
||||
* To avoid mess in terminal, init and use mutex */
|
||||
rc = pthread_mutex_init(&g_stats_mutex, NULL);
|
||||
if (rc != 0) {
|
||||
fprintf(stderr, "Failed to init mutex\n");
|
||||
goto cleanup;
|
||||
|
||||
spdk_env_opts_init(&opts);
|
||||
opts.name = "perf";
|
||||
opts.shm_id = g_shm_id;
|
||||
if (g_core_mask) {
|
||||
opts.core_mask = g_core_mask;
|
||||
}
|
||||
|
||||
if (g_dpdk_mem) {
|
||||
opts.mem_size = g_dpdk_mem;
|
||||
}
|
||||
opts.hugepage_single_segments = g_dpdk_mem_single_seg;
|
||||
if (g_no_pci) {
|
||||
opts.no_pci = g_no_pci;
|
||||
}
|
||||
|
||||
if (g_allowed_pci_addr_num) {
|
||||
opts.pci_allowed = g_allowed_pci_addr;
|
||||
opts.num_pci_addr = g_allowed_pci_addr_num;
|
||||
}
|
||||
if (spdk_env_init(&opts) < 0) {
|
||||
fprintf(stderr, "Unable to initialize SPDK env\n");
|
||||
@ -2900,8 +2664,6 @@ cleanup:
|
||||
unregister_controllers();
|
||||
unregister_workers();
|
||||
|
||||
pthread_mutex_destroy(&g_stats_mutex);
|
||||
|
||||
if (rc != 0) {
|
||||
fprintf(stderr, "%s: errors occured\n", argv[0]);
|
||||
}
|
||||
|
@ -1,38 +0,0 @@
|
||||
#
|
||||
# BSD LICENSE
|
||||
#
|
||||
# Copyright (c) Samsung Electronics Co., Ltd.
|
||||
# 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 Samsung Electronics Co., Ltd. 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)/../../..)
|
||||
|
||||
APP = pmr_persistence
|
||||
|
||||
include $(SPDK_ROOT_DIR)/mk/nvme.libtest.mk
|
@ -1,419 +0,0 @@
|
||||
/*-
|
||||
* BSD LICENSE
|
||||
*
|
||||
* Copyright (c) Samsung Electronics Co., Ltd.
|
||||
* 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 Samsung Electronics Co., Ltd., 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/env.h"
|
||||
#include "spdk/nvme.h"
|
||||
#include "spdk/string.h"
|
||||
|
||||
struct nvme_io {
|
||||
struct spdk_nvme_ctrlr *ctrlr;
|
||||
struct spdk_nvme_transport_id trid;
|
||||
struct spdk_nvme_ns *ns;
|
||||
unsigned nsid;
|
||||
unsigned rlba;
|
||||
unsigned nlbas;
|
||||
unsigned wlba;
|
||||
uint32_t lba_size;
|
||||
unsigned done;
|
||||
};
|
||||
|
||||
struct config {
|
||||
struct nvme_io pmr_dev;
|
||||
size_t copy_size;
|
||||
};
|
||||
|
||||
static struct config g_config;
|
||||
|
||||
/* Namespaces index from 1. Return 0 to invoke an error */
|
||||
static unsigned
|
||||
get_nsid(const struct spdk_nvme_transport_id *trid)
|
||||
{
|
||||
if (!strcmp(trid->traddr, g_config.pmr_dev.trid.traddr)) {
|
||||
return g_config.pmr_dev.nsid;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
check_io(void *arg, const struct spdk_nvme_cpl *completion)
|
||||
{
|
||||
g_config.pmr_dev.done = 1;
|
||||
}
|
||||
|
||||
static int
|
||||
pmr_persistence(void)
|
||||
{
|
||||
int rc = 0;
|
||||
void *pmr_buf, *buf;
|
||||
size_t sz;
|
||||
struct spdk_nvme_qpair *qpair;
|
||||
|
||||
/* Allocate Queue Pair for the Controller with PMR */
|
||||
qpair = spdk_nvme_ctrlr_alloc_io_qpair(g_config.pmr_dev.ctrlr, NULL, 0);
|
||||
if (qpair == NULL) {
|
||||
printf("ERROR: spdk_nvme_ctrlr_alloc_io_qpair() failed\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Enable the PMR */
|
||||
rc = spdk_nvme_ctrlr_enable_pmr(g_config.pmr_dev.ctrlr);
|
||||
if (rc) {
|
||||
printf("ERROR: Enabling PMR failed\n");
|
||||
printf("Are you sure %s has a valid PMR?\n",
|
||||
g_config.pmr_dev.trid.traddr);
|
||||
goto free_qpair;
|
||||
}
|
||||
|
||||
/* Allocate buffer from PMR */
|
||||
pmr_buf = spdk_nvme_ctrlr_map_pmr(g_config.pmr_dev.ctrlr, &sz);
|
||||
if (pmr_buf == NULL || sz < g_config.copy_size) {
|
||||
printf("ERROR: PMR buffer allocation failed\n");
|
||||
rc = -ENOMEM;
|
||||
goto disable_pmr;
|
||||
}
|
||||
|
||||
/* Clear the done flag */
|
||||
g_config.pmr_dev.done = 0;
|
||||
|
||||
/* Do the write to the PMR IO buffer, reading from rlba */
|
||||
rc = spdk_nvme_ns_cmd_read(g_config.pmr_dev.ns, qpair, pmr_buf,
|
||||
g_config.pmr_dev.rlba, g_config.pmr_dev.nlbas,
|
||||
check_io, NULL, 0);
|
||||
if (rc != 0) {
|
||||
fprintf(stderr, "Read I/O to PMR failed\n");
|
||||
rc = -EIO;
|
||||
goto unmap_pmr;
|
||||
}
|
||||
while (!g_config.pmr_dev.done) {
|
||||
spdk_nvme_qpair_process_completions(qpair, 0);
|
||||
}
|
||||
|
||||
/* Clear the done flag */
|
||||
g_config.pmr_dev.done = 0;
|
||||
|
||||
pmr_buf = NULL;
|
||||
|
||||
/* Free PMR buffer */
|
||||
rc = spdk_nvme_ctrlr_unmap_pmr(g_config.pmr_dev.ctrlr);
|
||||
if (rc) {
|
||||
printf("ERROR: Unmapping PMR failed\n");
|
||||
goto disable_pmr;
|
||||
}
|
||||
|
||||
/* Disable the PMR */
|
||||
rc = spdk_nvme_ctrlr_disable_pmr(g_config.pmr_dev.ctrlr);
|
||||
if (rc) {
|
||||
printf("ERROR: Disabling PMR failed\n");
|
||||
goto free_qpair;
|
||||
}
|
||||
|
||||
/* Free the queue */
|
||||
spdk_nvme_ctrlr_free_io_qpair(qpair);
|
||||
|
||||
rc = spdk_nvme_ctrlr_reset(g_config.pmr_dev.ctrlr);
|
||||
if (rc) {
|
||||
printf("ERROR: Resetting Controller failed\n");
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Allocate Queue Pair for the Controller with PMR */
|
||||
qpair = spdk_nvme_ctrlr_alloc_io_qpair(g_config.pmr_dev.ctrlr, NULL, 0);
|
||||
if (qpair == NULL) {
|
||||
printf("ERROR: spdk_nvme_ctrlr_alloc_io_qpair() failed\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Enable the PMR */
|
||||
rc = spdk_nvme_ctrlr_enable_pmr(g_config.pmr_dev.ctrlr);
|
||||
if (rc) {
|
||||
printf("ERROR: Enabling PMR failed\n");
|
||||
goto free_qpair;
|
||||
}
|
||||
|
||||
/* Allocate buffer from PMR */
|
||||
pmr_buf = spdk_nvme_ctrlr_map_pmr(g_config.pmr_dev.ctrlr, &sz);
|
||||
if (pmr_buf == NULL || sz < g_config.copy_size) {
|
||||
printf("ERROR: PMR buffer allocation failed\n");
|
||||
rc = -ENOMEM;
|
||||
goto disable_pmr;
|
||||
}
|
||||
|
||||
/* Do the read from the PMR IO buffer, write to wlba */
|
||||
rc = spdk_nvme_ns_cmd_write(g_config.pmr_dev.ns, qpair, pmr_buf,
|
||||
g_config.pmr_dev.wlba, g_config.pmr_dev.nlbas,
|
||||
check_io, NULL, 0);
|
||||
if (rc != 0) {
|
||||
fprintf(stderr, "Read I/O from PMR failed\n");
|
||||
rc = -EIO;
|
||||
goto unmap_pmr;
|
||||
}
|
||||
while (!g_config.pmr_dev.done) {
|
||||
spdk_nvme_qpair_process_completions(qpair, 0);
|
||||
}
|
||||
|
||||
/* Clear the done flag */
|
||||
g_config.pmr_dev.done = 0;
|
||||
|
||||
buf = spdk_zmalloc(g_config.copy_size, 0x1000, NULL, SPDK_ENV_SOCKET_ID_ANY, SPDK_MALLOC_DMA);
|
||||
if (buf == NULL) {
|
||||
printf("ERROR: Buffer allocation failed\n");
|
||||
rc = -ENOMEM;
|
||||
goto unmap_pmr;
|
||||
}
|
||||
|
||||
/* Do the read from wlba to a buffer */
|
||||
rc = spdk_nvme_ns_cmd_read(g_config.pmr_dev.ns, qpair, buf,
|
||||
g_config.pmr_dev.wlba, g_config.pmr_dev.nlbas,
|
||||
check_io, NULL, 0);
|
||||
if (rc != 0) {
|
||||
fprintf(stderr, "Read I/O from WLBA failed\n");
|
||||
rc = -EIO;
|
||||
goto free_buf;
|
||||
}
|
||||
while (!g_config.pmr_dev.done) {
|
||||
spdk_nvme_qpair_process_completions(qpair, 0);
|
||||
}
|
||||
|
||||
/* Clear the done flag */
|
||||
g_config.pmr_dev.done = 0;
|
||||
|
||||
/* Compare the data in the read buffer to the PMR buffer */
|
||||
if (memcmp(buf, pmr_buf, g_config.copy_size)) {
|
||||
printf("PMR Data Not Persistent, after Controller Reset\n");
|
||||
rc = -EIO;
|
||||
} else {
|
||||
printf("PMR Data is Persistent across Controller Reset\n");
|
||||
}
|
||||
|
||||
free_buf:
|
||||
spdk_free(buf);
|
||||
|
||||
unmap_pmr:
|
||||
/* Free PMR buffer */
|
||||
spdk_nvme_ctrlr_unmap_pmr(g_config.pmr_dev.ctrlr);
|
||||
|
||||
disable_pmr:
|
||||
/* Disable the PMR */
|
||||
spdk_nvme_ctrlr_disable_pmr(g_config.pmr_dev.ctrlr);
|
||||
|
||||
free_qpair:
|
||||
/* Free the queue */
|
||||
spdk_nvme_ctrlr_free_io_qpair(qpair);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static bool
|
||||
probe_cb(void *cb_ctx, const struct spdk_nvme_transport_id *trid,
|
||||
struct spdk_nvme_ctrlr_opts *opts)
|
||||
{
|
||||
/* We will only attach to the Controller specified by the user */
|
||||
if (spdk_nvme_transport_id_compare(trid, &g_config.pmr_dev.trid)) {
|
||||
printf("%s - not probed %s!\n", __func__, trid->traddr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
printf("%s - probed %s!\n", __func__, trid->traddr);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static 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)
|
||||
{
|
||||
struct spdk_nvme_ns *ns;
|
||||
|
||||
ns = spdk_nvme_ctrlr_get_ns(ctrlr, get_nsid(trid));
|
||||
if (ns == NULL) {
|
||||
fprintf(stderr, "Could not locate namespace %d on controller %s.\n",
|
||||
get_nsid(trid), trid->traddr);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
g_config.pmr_dev.ctrlr = ctrlr;
|
||||
g_config.pmr_dev.ns = ns;
|
||||
g_config.pmr_dev.lba_size = spdk_nvme_ns_get_sector_size(ns);
|
||||
|
||||
printf("%s - attached %s!\n", __func__, trid->traddr);
|
||||
}
|
||||
|
||||
static void
|
||||
usage(char *program_name)
|
||||
{
|
||||
printf("%s options (all mandatory)", program_name);
|
||||
printf("\n");
|
||||
printf("\t[-p PCIe address of the NVMe Device with PMR support]\n");
|
||||
printf("\t[-n Namespace ID]\n");
|
||||
printf("\t[-r Read LBA]\n");
|
||||
printf("\t[-l Number of LBAs to read]\n");
|
||||
printf("\t[-w Write LBA]\n");
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static int
|
||||
parse_args(int argc, char **argv)
|
||||
{
|
||||
int op;
|
||||
unsigned num_args = 0;
|
||||
long int val;
|
||||
|
||||
while ((op = getopt(argc, argv, "p:n:r:l:w:")) != -1) {
|
||||
switch (op) {
|
||||
case 'p':
|
||||
snprintf(&g_config.pmr_dev.trid.traddr[0], SPDK_NVMF_TRADDR_MAX_LEN + 1,
|
||||
"%s", optarg);
|
||||
|
||||
g_config.pmr_dev.trid.trtype = SPDK_NVME_TRANSPORT_PCIE;
|
||||
|
||||
spdk_nvme_transport_id_populate_trstring(&g_config.pmr_dev.trid,
|
||||
spdk_nvme_transport_id_trtype_str(g_config.pmr_dev.trid.trtype));
|
||||
|
||||
num_args++;
|
||||
break;
|
||||
case 'n':
|
||||
case 'r':
|
||||
case 'l':
|
||||
case 'w':
|
||||
val = spdk_strtol(optarg, 10);
|
||||
if (val < 0) {
|
||||
fprintf(stderr, "Converting a string to integer failed\n");
|
||||
return val;
|
||||
}
|
||||
switch (op) {
|
||||
case 'n':
|
||||
g_config.pmr_dev.nsid = (unsigned)val;
|
||||
num_args++;
|
||||
break;
|
||||
case 'r':
|
||||
g_config.pmr_dev.rlba = (unsigned)val;
|
||||
num_args++;
|
||||
break;
|
||||
case 'l':
|
||||
g_config.pmr_dev.nlbas = (unsigned)val;
|
||||
num_args++;
|
||||
break;
|
||||
case 'w':
|
||||
g_config.pmr_dev.wlba = (unsigned)val;
|
||||
num_args++;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (num_args != 5) {
|
||||
usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
cleanup(void)
|
||||
{
|
||||
struct spdk_nvme_detach_ctx *detach_ctx = NULL;
|
||||
|
||||
spdk_nvme_detach_async(g_config.pmr_dev.ctrlr, &detach_ctx);
|
||||
|
||||
while (detach_ctx && spdk_nvme_detach_poll_async(detach_ctx) == -EAGAIN) {
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int rc = 0;
|
||||
struct spdk_env_opts opts;
|
||||
|
||||
/*
|
||||
* Parse the input arguments. For now we use the following
|
||||
* format list:
|
||||
*
|
||||
* -p <pci id> -n <namespace> -r <Read LBA> -l <number of LBAs> -w <Write LBA>
|
||||
*
|
||||
*/
|
||||
rc = parse_args(argc, argv);
|
||||
if (rc) {
|
||||
fprintf(stderr, "Error in parse_args(): %d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* SPDK relies on an abstraction around the local environment
|
||||
* named env that handles memory allocation and PCI device operations.
|
||||
* This library must be initialized first.
|
||||
*
|
||||
*/
|
||||
spdk_env_opts_init(&opts);
|
||||
opts.name = "pmr_persistence";
|
||||
opts.shm_id = 0;
|
||||
if (spdk_env_init(&opts) < 0) {
|
||||
fprintf(stderr, "Unable to initialize SPDK env\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* PMRs only apply to PCIe attached NVMe controllers so we
|
||||
* only probe the PCIe bus. This is the default when we pass
|
||||
* in NULL for the first argument.
|
||||
*/
|
||||
|
||||
rc = spdk_nvme_probe(NULL, NULL, probe_cb, attach_cb, NULL);
|
||||
if (rc) {
|
||||
fprintf(stderr, "Error in spdk_nvme_probe(): %d\n", rc);
|
||||
cleanup();
|
||||
return rc;
|
||||
}
|
||||
|
||||
g_config.copy_size = g_config.pmr_dev.nlbas * g_config.pmr_dev.lba_size;
|
||||
|
||||
/*
|
||||
* Call the pmr_persistence() function which performs the data copy
|
||||
* to PMR region, resets the Controller and verifies the data persistence
|
||||
* or returns an error code if it fails.
|
||||
*/
|
||||
rc = pmr_persistence();
|
||||
if (rc) {
|
||||
fprintf(stderr, "Error in pmr_persistence(): %d\n", rc);
|
||||
}
|
||||
|
||||
cleanup();
|
||||
|
||||
return rc;
|
||||
}
|
@ -183,8 +183,6 @@ parse_args(int argc, char **argv, struct spdk_env_opts *opts)
|
||||
opts->no_pci = true;
|
||||
break;
|
||||
case 'h':
|
||||
usage(argv[0]);
|
||||
exit(EXIT_SUCCESS);
|
||||
default:
|
||||
usage(argv[0]);
|
||||
return 1;
|
||||
@ -711,13 +709,7 @@ static void
|
||||
nvmf_subsystem_init_done(int rc, void *cb_arg)
|
||||
{
|
||||
fprintf(stdout, "bdev subsystem init successfully\n");
|
||||
|
||||
rc = spdk_rpc_initialize(g_rpc_addr);
|
||||
if (rc) {
|
||||
spdk_app_stop(rc);
|
||||
return;
|
||||
}
|
||||
|
||||
spdk_rpc_initialize(g_rpc_addr);
|
||||
spdk_rpc_set_state(SPDK_RPC_RUNTIME);
|
||||
|
||||
g_target_state = NVMF_INIT_TARGET;
|
||||
|
@ -315,27 +315,6 @@ int spdk_accel_batch_prep_crc32c(struct spdk_io_channel *ch, struct spdk_accel_b
|
||||
uint32_t *dst, void *src, uint32_t seed, uint64_t nbytes,
|
||||
spdk_accel_completion_cb cb_fn, void *cb_arg);
|
||||
|
||||
/**
|
||||
* Synchronous call to prepare a chained crc32c request into a previously initialized batch
|
||||
* created with spdk_accel_batch_create(). The callback will be called when the crc32c
|
||||
* completes after the batch has been submitted by an asynchronous call to
|
||||
* spdk_accel_batch_submit().
|
||||
*
|
||||
* \param ch I/O channel associated with this call.
|
||||
* \param batch Handle provided when the batch was started with spdk_accel_batch_create().
|
||||
* \param dst Destination to write the CRC-32C to.
|
||||
* \param iovs The io vector array which stores the src data and len.
|
||||
* \param iovcnt The size of the iov.
|
||||
* \param seed Four byte seed value.
|
||||
* \param cb_fn Called when this operation completes.
|
||||
* \param cb_arg Callback argument.
|
||||
*
|
||||
* \return 0 on success, negative errno on failure.
|
||||
*/
|
||||
int spdk_accel_batch_prep_crc32cv(struct spdk_io_channel *ch, struct spdk_accel_batch *batch,
|
||||
uint32_t *dst, struct iovec *iovs, uint32_t iovcnt, uint32_t seed,
|
||||
spdk_accel_completion_cb cb_fn, void *cb_arg);
|
||||
|
||||
/**
|
||||
* Submit a CRC-32C calculation request.
|
||||
*
|
||||
@ -354,24 +333,6 @@ int spdk_accel_batch_prep_crc32cv(struct spdk_io_channel *ch, struct spdk_accel_
|
||||
int spdk_accel_submit_crc32c(struct spdk_io_channel *ch, uint32_t *dst, void *src, uint32_t seed,
|
||||
uint64_t nbytes, spdk_accel_completion_cb cb_fn, void *cb_arg);
|
||||
|
||||
/**
|
||||
* Submit a chained CRC-32C calculation request.
|
||||
*
|
||||
* This operation will calculate the 4 byte CRC32-C for the given data.
|
||||
*
|
||||
* \param ch I/O channel associated with this call.
|
||||
* \param dst Destination to write the CRC-32C to.
|
||||
* \param iovs The io vector array which stores the src data and len.
|
||||
* \param iovcnt The size of the iov.
|
||||
* \param seed Four byte seed value.
|
||||
* \param cb_fn Called when this CRC-32C operation completes.
|
||||
* \param cb_arg Callback argument.
|
||||
*
|
||||
* \return 0 on success, negative errno on failure.
|
||||
*/
|
||||
int spdk_accel_submit_crc32cv(struct spdk_io_channel *ch, uint32_t *dst, struct iovec *iovs,
|
||||
uint32_t iovcnt, uint32_t seed, spdk_accel_completion_cb cb_fn, void *cb_arg);
|
||||
|
||||
struct spdk_json_write_ctx;
|
||||
|
||||
/**
|
||||
|
@ -90,7 +90,7 @@ typedef void (*spdk_bdev_remove_cb_t)(void *remove_ctx);
|
||||
/**
|
||||
* Block device event callback.
|
||||
*
|
||||
* \param type Event type.
|
||||
* \param event Event details.
|
||||
* \param bdev Block device that triggered event.
|
||||
* \param event_ctx Context for the block device event.
|
||||
*/
|
||||
@ -332,6 +332,23 @@ struct spdk_bdev *spdk_bdev_first_leaf(void);
|
||||
*/
|
||||
struct spdk_bdev *spdk_bdev_next_leaf(struct spdk_bdev *prev);
|
||||
|
||||
/**
|
||||
* Open a block device for I/O operations (deprecated, please use spdk_bdev_open_ext).
|
||||
*
|
||||
* \param bdev Block device to open.
|
||||
* \param write true is read/write access requested, false if read-only
|
||||
* \param remove_cb notification callback to be called when the bdev gets
|
||||
* hotremoved. This will always be called on the same thread that
|
||||
* spdk_bdev_open() was called on. It can be NULL, in which case the upper
|
||||
* layer won't be notified about the bdev hotremoval. The descriptor will
|
||||
* have to be manually closed to make the bdev unregister proceed.
|
||||
* \param remove_ctx param for remove_cb.
|
||||
* \param desc output parameter for the descriptor when operation is successful
|
||||
* \return 0 if operation is successful, suitable errno value otherwise
|
||||
*/
|
||||
int spdk_bdev_open(struct spdk_bdev *bdev, bool write, spdk_bdev_remove_cb_t remove_cb,
|
||||
void *remove_ctx, struct spdk_bdev_desc **desc);
|
||||
|
||||
/**
|
||||
* Open a block device for I/O operations.
|
||||
*
|
||||
@ -339,7 +356,7 @@ struct spdk_bdev *spdk_bdev_next_leaf(struct spdk_bdev *prev);
|
||||
* \param write true is read/write access requested, false if read-only
|
||||
* \param event_cb notification callback to be called when the bdev triggers
|
||||
* asynchronous event such as bdev removal. This will always be called on the
|
||||
* same thread that spdk_bdev_open_ext() was called on. In case of removal event
|
||||
* same thread that spdk_bdev_open() was called on. In case of removal event
|
||||
* the descriptor will have to be manually closed to make the bdev unregister
|
||||
* proceed.
|
||||
* \param event_ctx param for event_cb.
|
||||
@ -352,7 +369,7 @@ int spdk_bdev_open_ext(const char *bdev_name, bool write, spdk_bdev_event_cb_t e
|
||||
/**
|
||||
* Close a previously opened block device.
|
||||
*
|
||||
* Must be called on the same thread that the spdk_bdev_open_ext()
|
||||
* Must be called on the same thread that the spdk_bdev_open()
|
||||
* was performed on.
|
||||
*
|
||||
* \param desc Block device descriptor to close.
|
||||
|
@ -85,6 +85,13 @@ struct spdk_bdev_module {
|
||||
*/
|
||||
void (*module_fini)(void);
|
||||
|
||||
/**
|
||||
* Function called to return a text string representing the
|
||||
* module's configuration options for inclusion in a configuration file.
|
||||
* (Deprecated and shall not be called by bdev layer)
|
||||
*/
|
||||
void (*config_text)(FILE *fp);
|
||||
|
||||
/**
|
||||
* Function called to return a text string representing the module-level
|
||||
* JSON RPCs required to regenerate the current configuration. This will
|
||||
@ -361,21 +368,11 @@ struct spdk_bdev {
|
||||
*/
|
||||
uint64_t zone_size;
|
||||
|
||||
/**
|
||||
* Maximum zone append data transfer size (in blocks).
|
||||
*/
|
||||
uint32_t max_zone_append_size;
|
||||
|
||||
/**
|
||||
* Maximum number of open zones.
|
||||
*/
|
||||
uint32_t max_open_zones;
|
||||
|
||||
/**
|
||||
* Maximum number of active zones.
|
||||
*/
|
||||
uint32_t max_active_zones;
|
||||
|
||||
/**
|
||||
* Optimal number of open zones.
|
||||
*/
|
||||
@ -737,6 +734,25 @@ void spdk_bdev_unregister(struct spdk_bdev *bdev, spdk_bdev_unregister_cb cb_fn,
|
||||
*/
|
||||
void spdk_bdev_destruct_done(struct spdk_bdev *bdev, int bdeverrno);
|
||||
|
||||
/**
|
||||
* Register a virtual bdev.
|
||||
*
|
||||
* This function is deprecated. Users should call spdk_bdev_register instead.
|
||||
* The bdev layer currently makes no use of the base_bdevs array, so switching
|
||||
* to spdk_bdev_register results in no loss of functionality.
|
||||
*
|
||||
* \param vbdev Virtual bdev to register.
|
||||
* \param base_bdevs Array of bdevs upon which this vbdev is based.
|
||||
* \param base_bdev_count Number of bdevs in base_bdevs.
|
||||
*
|
||||
* \return 0 on success
|
||||
* \return -EINVAL if the bdev name is NULL.
|
||||
* \return -EEXIST if the bdev already exists.
|
||||
* \return -ENOMEM if allocation of the base_bdevs array or the base bdevs vbdevs array fails.
|
||||
*/
|
||||
int spdk_vbdev_register(struct spdk_bdev *vbdev, struct spdk_bdev **base_bdevs,
|
||||
int base_bdev_count);
|
||||
|
||||
/**
|
||||
* Indicate to the bdev layer that the module is done examining a bdev.
|
||||
*
|
||||
@ -1096,6 +1112,35 @@ int spdk_bdev_part_free(struct spdk_bdev_part *part);
|
||||
void spdk_bdev_part_base_hotremove(struct spdk_bdev_part_base *part_base,
|
||||
struct bdev_part_tailq *tailq);
|
||||
|
||||
/**
|
||||
* Construct a new spdk_bdev_part_base on top of the provided bdev
|
||||
* (deprecated. please use spdk_bdev_part_base_construct_ext).
|
||||
*
|
||||
* \param bdev The spdk_bdev upon which this base will be built.
|
||||
* \param remove_cb Function to be called upon hotremove of the bdev.
|
||||
* \param module The module to which this bdev base belongs.
|
||||
* \param fn_table Function table for communicating with the bdev backend.
|
||||
* \param tailq The head of the list of all spdk_bdev_part structures registered to this base's module.
|
||||
* \param free_fn User provided function to free base related context upon bdev removal or shutdown.
|
||||
* \param ctx Module specific context for this bdev part base.
|
||||
* \param channel_size Channel size in bytes.
|
||||
* \param ch_create_cb Called after a new channel is allocated.
|
||||
* \param ch_destroy_cb Called upon channel deletion.
|
||||
*
|
||||
* \return The part object on top of the bdev if operation is successful, or
|
||||
* NULL otherwise.
|
||||
*/
|
||||
struct spdk_bdev_part_base *spdk_bdev_part_base_construct(struct spdk_bdev *bdev,
|
||||
spdk_bdev_remove_cb_t remove_cb,
|
||||
struct spdk_bdev_module *module,
|
||||
struct spdk_bdev_fn_table *fn_table,
|
||||
struct bdev_part_tailq *tailq,
|
||||
spdk_bdev_part_base_free_fn free_fn,
|
||||
void *ctx,
|
||||
uint32_t channel_size,
|
||||
spdk_io_channel_create_cb ch_create_cb,
|
||||
spdk_io_channel_destroy_cb ch_destroy_cb);
|
||||
|
||||
/**
|
||||
* Construct a new spdk_bdev_part_base on top of the provided bdev.
|
||||
*
|
||||
|
@ -53,20 +53,16 @@ enum spdk_bdev_zone_action {
|
||||
SPDK_BDEV_ZONE_CLOSE,
|
||||
SPDK_BDEV_ZONE_FINISH,
|
||||
SPDK_BDEV_ZONE_OPEN,
|
||||
SPDK_BDEV_ZONE_RESET,
|
||||
SPDK_BDEV_ZONE_OFFLINE,
|
||||
SPDK_BDEV_ZONE_RESET
|
||||
};
|
||||
|
||||
enum spdk_bdev_zone_state {
|
||||
SPDK_BDEV_ZONE_STATE_EMPTY = 0x0,
|
||||
SPDK_BDEV_ZONE_STATE_IMP_OPEN = 0x1,
|
||||
/* OPEN is an alias for IMP_OPEN. OPEN is kept for backwards compatibility. */
|
||||
SPDK_BDEV_ZONE_STATE_OPEN = SPDK_BDEV_ZONE_STATE_IMP_OPEN,
|
||||
SPDK_BDEV_ZONE_STATE_FULL = 0x2,
|
||||
SPDK_BDEV_ZONE_STATE_CLOSED = 0x3,
|
||||
SPDK_BDEV_ZONE_STATE_READ_ONLY = 0x4,
|
||||
SPDK_BDEV_ZONE_STATE_OFFLINE = 0x5,
|
||||
SPDK_BDEV_ZONE_STATE_EXP_OPEN = 0x6,
|
||||
SPDK_BDEV_ZONE_STATE_EMPTY,
|
||||
SPDK_BDEV_ZONE_STATE_OPEN,
|
||||
SPDK_BDEV_ZONE_STATE_FULL,
|
||||
SPDK_BDEV_ZONE_STATE_CLOSED,
|
||||
SPDK_BDEV_ZONE_STATE_READ_ONLY,
|
||||
SPDK_BDEV_ZONE_STATE_OFFLINE
|
||||
};
|
||||
|
||||
struct spdk_bdev_zone_info {
|
||||
@ -84,30 +80,9 @@ struct spdk_bdev_zone_info {
|
||||
*/
|
||||
uint64_t spdk_bdev_get_zone_size(const struct spdk_bdev *bdev);
|
||||
|
||||
/**
|
||||
* Get the number of zones for the given device.
|
||||
*
|
||||
* \param bdev Block device to query.
|
||||
* \return The number of zones.
|
||||
*/
|
||||
uint64_t spdk_bdev_get_num_zones(const struct spdk_bdev *bdev);
|
||||
|
||||
/**
|
||||
* Get device maximum zone append data transfer size in logical blocks.
|
||||
*
|
||||
* If this value is 0, there is no limit.
|
||||
*
|
||||
* \param bdev Block device to query.
|
||||
* \return Maximum zone append data transfer size for this zoned device in logical blocks.
|
||||
*/
|
||||
uint32_t spdk_bdev_get_max_zone_append_size(const struct spdk_bdev *bdev);
|
||||
|
||||
/**
|
||||
* Get device maximum number of open zones.
|
||||
*
|
||||
* An open zone is defined as a zone being in zone state
|
||||
* SPDK_BDEV_ZONE_STATE_IMP_OPEN or SPDK_BDEV_ZONE_STATE_EXP_OPEN.
|
||||
*
|
||||
* If this value is 0, there is no limit.
|
||||
*
|
||||
* \param bdev Block device to query.
|
||||
@ -115,20 +90,6 @@ uint32_t spdk_bdev_get_max_zone_append_size(const struct spdk_bdev *bdev);
|
||||
*/
|
||||
uint32_t spdk_bdev_get_max_open_zones(const struct spdk_bdev *bdev);
|
||||
|
||||
/**
|
||||
* Get device maximum number of active zones.
|
||||
*
|
||||
* An active zone is defined as a zone being in zone state
|
||||
* SPDK_BDEV_ZONE_STATE_IMP_OPEN, SPDK_BDEV_ZONE_STATE_EXP_OPEN or
|
||||
* SPDK_BDEV_ZONE_STATE_CLOSED.
|
||||
*
|
||||
* If this value is 0, there is no limit.
|
||||
*
|
||||
* \param bdev Block device to query.
|
||||
* \return Maximum number of active zones for this zoned device.
|
||||
*/
|
||||
uint32_t spdk_bdev_get_max_active_zones(const struct spdk_bdev *bdev);
|
||||
|
||||
/**
|
||||
* Get device optimal number of open zones.
|
||||
*
|
||||
@ -168,7 +129,7 @@ int spdk_bdev_get_zone_info(struct spdk_bdev_desc *desc, struct spdk_io_channel
|
||||
* \param desc Block device descriptor.
|
||||
* \param ch I/O channel. Obtained by calling spdk_bdev_get_io_channel().
|
||||
* \param zone_id First logical block of a zone.
|
||||
* \param action Action to perform on a zone (open, close, reset, finish, offline).
|
||||
* \param action Action to perform on a zone (open, close, reset, finish).
|
||||
* \param cb Called when the request is complete.
|
||||
* \param cb_arg Argument passed to cb.
|
||||
*
|
||||
|
@ -127,7 +127,7 @@ typedef void (*spdk_blob_op_with_id_complete)(void *cb_arg, spdk_blob_id blobid,
|
||||
* Blob operation completion callback with handle.
|
||||
*
|
||||
* \param cb_arg Callback argument.
|
||||
* \param blb Handle to a blob.
|
||||
* \param bs Handle to a blob.
|
||||
* \param bserrno 0 if it completed successfully, or negative errno if it failed.
|
||||
*/
|
||||
typedef void (*spdk_blob_op_with_handle_complete)(void *cb_arg, struct spdk_blob *blb, int bserrno);
|
||||
|
@ -49,6 +49,27 @@ struct spdk_bs_dev;
|
||||
struct spdk_bdev;
|
||||
struct spdk_bdev_module;
|
||||
|
||||
/**
|
||||
* Create a blobstore block device from a bdev (deprecated, please use spdk_bdev_create_bs_dev_ext).
|
||||
*
|
||||
* \param bdev Bdev to use.
|
||||
* \param remove_cb Called when the block device is removed.
|
||||
* \param remove_ctx Argument passed to function remove_cb.
|
||||
*
|
||||
* \return a pointer to the blobstore block device on success or NULL otherwise.
|
||||
*/
|
||||
struct spdk_bs_dev *spdk_bdev_create_bs_dev(struct spdk_bdev *bdev, spdk_bdev_remove_cb_t remove_cb,
|
||||
void *remove_ctx);
|
||||
|
||||
/**
|
||||
* Create a blobstore block device from the descriptor of a bdev (deprecated, please use spdk_bdev_create_bs_dev_ext).
|
||||
*
|
||||
* \param desc Descriptor of a bdev. spdk_bdev_open_ext() is recommended to get the desc.
|
||||
*
|
||||
* \return a pointer to the blobstore block device on success or NULL otherwise.
|
||||
*/
|
||||
struct spdk_bs_dev *spdk_bdev_create_bs_dev_from_desc(struct spdk_bdev_desc *desc);
|
||||
|
||||
/**
|
||||
* Create a blobstore block device from a bdev.
|
||||
*
|
||||
|
@ -112,10 +112,10 @@ typedef void (*fs_request_fn)(void *arg);
|
||||
* This function will be invoked any time when the filesystem wants to pass a
|
||||
* message to the main dispatch thread.
|
||||
*
|
||||
* \param fn A pointer to the request function.
|
||||
* \param fs_request_fn A pointer to the request function.
|
||||
* \param arg Argument to the request function.
|
||||
*/
|
||||
typedef void (*fs_send_request_fn)(fs_request_fn fn, void *arg);
|
||||
typedef void (*fs_send_request_fn)(fs_request_fn, void *arg);
|
||||
|
||||
/**
|
||||
* Initialize a spdk_blobfs_opts structure to the default option values.
|
||||
|
@ -76,15 +76,24 @@ struct spdk_env_opts {
|
||||
const char *core_mask;
|
||||
int shm_id;
|
||||
int mem_channel;
|
||||
int main_core;
|
||||
union {
|
||||
int main_core;
|
||||
int master_core __attribute__((deprecated));
|
||||
};
|
||||
int mem_size;
|
||||
bool no_pci;
|
||||
bool hugepage_single_segments;
|
||||
bool unlink_hugepage;
|
||||
size_t num_pci_addr;
|
||||
const char *hugedir;
|
||||
struct spdk_pci_addr *pci_blocked;
|
||||
struct spdk_pci_addr *pci_allowed;
|
||||
union {
|
||||
struct spdk_pci_addr *pci_blocked;
|
||||
struct spdk_pci_addr *pci_blacklist __attribute__((deprecated));
|
||||
};
|
||||
union {
|
||||
struct spdk_pci_addr *pci_allowed;
|
||||
struct spdk_pci_addr *pci_whitelist __attribute__((deprecated));
|
||||
};
|
||||
const char *iova_mode;
|
||||
uint64_t base_virtaddr;
|
||||
|
||||
@ -999,22 +1008,6 @@ void spdk_pci_device_detach(struct spdk_pci_device *device);
|
||||
int spdk_pci_device_attach(struct spdk_pci_driver *driver, spdk_pci_enum_cb enum_cb,
|
||||
void *enum_ctx, struct spdk_pci_addr *pci_address);
|
||||
|
||||
/**
|
||||
* Allow the specified PCI device to be probed by the calling process.
|
||||
*
|
||||
* When using spdk_pci_enumerate(), only devices with allowed PCI addresses will
|
||||
* be probed. By default, this is all PCI addresses, but the pci_allowed
|
||||
* and pci_blocked environment options can override this behavior.
|
||||
* This API enables the caller to allow a new PCI address that may have previously
|
||||
* been blocked.
|
||||
*
|
||||
* \param pci_addr PCI address to allow
|
||||
* \return 0 if successful
|
||||
* \return -ENOMEM if environment-specific data structures cannot be allocated
|
||||
* \return -EINVAL if specified PCI address is not valid
|
||||
*/
|
||||
int spdk_pci_device_allow(struct spdk_pci_addr *pci_addr);
|
||||
|
||||
/**
|
||||
* Read \c len bytes from the PCI configuration space.
|
||||
*
|
||||
@ -1329,60 +1322,10 @@ int spdk_mem_reserve(void *vaddr, size_t len);
|
||||
* \param vaddr Virtual address to get
|
||||
* \param offset Virtual address's map offset to the file descriptor
|
||||
*
|
||||
* \return negative errno on failure, otherwise return the file descriptor
|
||||
* \ return negative errno on failure, otherwise return the file descriptor
|
||||
*/
|
||||
int spdk_mem_get_fd_and_offset(void *vaddr, uint64_t *offset);
|
||||
|
||||
enum spdk_pci_event_type {
|
||||
SPDK_UEVENT_ADD = 0,
|
||||
SPDK_UEVENT_REMOVE = 1,
|
||||
};
|
||||
|
||||
struct spdk_pci_event {
|
||||
enum spdk_pci_event_type action;
|
||||
struct spdk_pci_addr traddr;
|
||||
};
|
||||
|
||||
typedef void (*spdk_pci_error_handler)(siginfo_t *info, void *ctx);
|
||||
|
||||
/**
|
||||
* Begin listening for PCI bus events. This is used to detect hot-insert and
|
||||
* hot-remove events. Once the system is listening, events may be retrieved
|
||||
* by calling spdk_pci_get_event() periodically.
|
||||
*
|
||||
* \return negative errno on failure, otherwise, return a file descriptor
|
||||
* that may be later passed to spdk_pci_get_event().
|
||||
*/
|
||||
int spdk_pci_event_listen(void);
|
||||
|
||||
/**
|
||||
* Get the next PCI bus event.
|
||||
*
|
||||
* \param fd A file descriptor returned by spdk_pci_event_listen()
|
||||
* \param event An event on the PCI bus
|
||||
*
|
||||
* \return Negative errno on failure. 0 for no event. A positive number
|
||||
* when an event has been returned
|
||||
*/
|
||||
int spdk_pci_get_event(int fd, struct spdk_pci_event *event);
|
||||
|
||||
/**
|
||||
* Register a signal handler to handle bus errors on the PCI bus
|
||||
*
|
||||
* \param sighandler Signal bus handler of the PCI bus
|
||||
* \param ctx The arg pass to the registered signal bus handler.
|
||||
*
|
||||
* \return negative errno on failure, otherwise it means successful
|
||||
*/
|
||||
int spdk_pci_register_error_handler(spdk_pci_error_handler sighandler, void *ctx);
|
||||
|
||||
/**
|
||||
* Register a signal handler to handle bus errors on the PCI bus
|
||||
*
|
||||
* \param sighandler Signal bus handler of the PCI bus
|
||||
*/
|
||||
void spdk_pci_unregister_error_handler(spdk_pci_error_handler sighandler);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -89,6 +89,7 @@ typedef void (*spdk_sighandler_t)(int signal);
|
||||
*/
|
||||
struct spdk_app_opts {
|
||||
const char *name;
|
||||
const char *config_file; /* deprecated */
|
||||
const char *json_config_file;
|
||||
bool json_config_ignore_errors;
|
||||
const char *rpc_addr; /* Can be UNIX domain socket path or IP address + TCP port */
|
||||
@ -101,7 +102,10 @@ struct spdk_app_opts {
|
||||
|
||||
bool enable_coredump;
|
||||
int mem_channel;
|
||||
int main_core;
|
||||
union {
|
||||
int main_core;
|
||||
int master_core __attribute__((deprecated));
|
||||
};
|
||||
int mem_size;
|
||||
bool no_pci;
|
||||
bool hugepage_single_segments;
|
||||
@ -109,10 +113,25 @@ struct spdk_app_opts {
|
||||
const char *hugedir;
|
||||
enum spdk_log_level print_level;
|
||||
size_t num_pci_addr;
|
||||
struct spdk_pci_addr *pci_blocked;
|
||||
struct spdk_pci_addr *pci_allowed;
|
||||
union {
|
||||
struct spdk_pci_addr *pci_blocked;
|
||||
struct spdk_pci_addr *pci_blacklist __attribute__((deprecated));
|
||||
};
|
||||
union {
|
||||
struct spdk_pci_addr *pci_allowed;
|
||||
struct spdk_pci_addr *pci_whitelist __attribute__((deprecated));
|
||||
};
|
||||
const char *iova_mode;
|
||||
|
||||
/* DEPRECATED. No longer has any effect.
|
||||
*
|
||||
* The maximum latency allowed when passing an event
|
||||
* from one core to another. A value of 0
|
||||
* means all cores continually poll. This is
|
||||
* specified in microseconds.
|
||||
*/
|
||||
uint64_t max_delay_us;
|
||||
|
||||
/* Wait for the associated RPC before initializing subsystems
|
||||
* when this flag is enabled.
|
||||
*/
|
||||
|
@ -64,7 +64,7 @@ struct idxd_batch;
|
||||
/**
|
||||
* Signature for configuring a channel
|
||||
*
|
||||
* \param chan IDXD channel to be configured
|
||||
* \param chan IDXD channel to be configured.
|
||||
* \return 0 on success, negative errno on failure.
|
||||
*/
|
||||
int spdk_idxd_configure_chan(struct spdk_idxd_io_channel *chan);
|
||||
@ -73,9 +73,10 @@ int spdk_idxd_configure_chan(struct spdk_idxd_io_channel *chan);
|
||||
* Reconfigures this channel based on how many current channels there are.
|
||||
*
|
||||
* \param chan IDXD channel to be set.
|
||||
* \param num_channels total number of channels in use.
|
||||
* \return 0 on success, negative errno on failure.
|
||||
*/
|
||||
int spdk_idxd_reconfigure_chan(struct spdk_idxd_io_channel *chan);
|
||||
int spdk_idxd_reconfigure_chan(struct spdk_idxd_io_channel *chan, uint32_t num_channels);
|
||||
|
||||
/**
|
||||
* Signature for callback function invoked when a request is completed.
|
||||
@ -391,9 +392,8 @@ int spdk_idxd_submit_crc32c(struct spdk_idxd_io_channel *chan, uint32_t *dst, vo
|
||||
* Check for completed requests on an IDXD channel.
|
||||
*
|
||||
* \param chan IDXD channel to check for completions.
|
||||
* \return number of operations completed.
|
||||
*/
|
||||
int spdk_idxd_process_events(struct spdk_idxd_io_channel *chan);
|
||||
void spdk_idxd_process_events(struct spdk_idxd_io_channel *chan);
|
||||
|
||||
/**
|
||||
* Returns an IDXD channel for a given IDXD device.
|
||||
@ -408,17 +408,8 @@ struct spdk_idxd_io_channel *spdk_idxd_get_channel(struct spdk_idxd_device *idxd
|
||||
* Free an IDXD channel.
|
||||
*
|
||||
* \param chan IDXD channel to free.
|
||||
* \return true if the underlying device needs a rebalance
|
||||
*/
|
||||
bool spdk_idxd_put_channel(struct spdk_idxd_io_channel *chan);
|
||||
|
||||
/**
|
||||
* Determine if the idxd device needs rebalancing.
|
||||
*
|
||||
* \param idxd IDXD device.
|
||||
* \return true if rebalance is needed, false if not.
|
||||
*/
|
||||
bool spdk_idxd_device_needs_rebalance(struct spdk_idxd_device *idxd);
|
||||
void spdk_idxd_put_channel(struct spdk_idxd_io_channel *chan);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@ -82,7 +82,7 @@ struct spdk_jsonrpc_client_response {
|
||||
*
|
||||
* \param request JSON-RPC request to handle.
|
||||
* \param method Function to handle the request.
|
||||
* \param params Parameters passed to the function 'method'.
|
||||
* \param param Parameters passed to the function 'method'.
|
||||
*/
|
||||
typedef void (*spdk_jsonrpc_handle_request_fn)(
|
||||
struct spdk_jsonrpc_request *request,
|
||||
|
@ -2,7 +2,7 @@
|
||||
* BSD LICENSE
|
||||
*
|
||||
* Copyright (c) Intel Corporation. All rights reserved.
|
||||
* Copyright (c) 2019-2021 Mellanox Technologies LTD. All rights reserved.
|
||||
* Copyright (c) 2019, 2020 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
|
||||
@ -258,36 +258,6 @@ struct spdk_nvme_ctrlr_opts {
|
||||
uint64_t fabrics_connect_timeout_us;
|
||||
};
|
||||
|
||||
/**
|
||||
* NVMe acceleration operation callback.
|
||||
*
|
||||
* \param cb_arg The user provided arg which is passed to the corresponding accelerated function call
|
||||
* defined in struct spdk_nvme_accel_fn_table.
|
||||
* \param status 0 if it completed successfully, or negative errno if it failed.
|
||||
*/
|
||||
typedef void (*spdk_nvme_accel_completion_cb)(void *cb_arg, int status);
|
||||
|
||||
/**
|
||||
* Function table for the NVMe acccelerator device.
|
||||
*
|
||||
* This table provides a set of APIs to allow user to leverage
|
||||
* accelerator functions.
|
||||
*/
|
||||
struct spdk_nvme_accel_fn_table {
|
||||
/**
|
||||
* The size of spdk_nvme_accel_fun_table according to the caller of
|
||||
* this library is used for ABI compatibility. The library uses this
|
||||
* field to know how many fields in this structure are valid.
|
||||
* And the library will populate any remaining fields with default values.
|
||||
* Newly added fields should be put at the end of the struct.
|
||||
*/
|
||||
size_t table_size;
|
||||
|
||||
/** The accelerated crc32c function. */
|
||||
void (*submit_accel_crc32c)(void *ctx, uint32_t *dst, struct iovec *iov,
|
||||
uint32_t iov_cnt, uint32_t seed, spdk_nvme_accel_completion_cb cb_fn, void *cb_arg);
|
||||
};
|
||||
|
||||
/**
|
||||
* Indicate whether a ctrlr handle is associated with a Discovery controller.
|
||||
*
|
||||
@ -450,44 +420,6 @@ struct spdk_nvme_host_id {
|
||||
char hostsvcid[SPDK_NVMF_TRSVCID_MAX_LEN + 1];
|
||||
};
|
||||
|
||||
struct spdk_nvme_rdma_device_stat {
|
||||
const char *name;
|
||||
uint64_t polls;
|
||||
uint64_t idle_polls;
|
||||
uint64_t completions;
|
||||
uint64_t queued_requests;
|
||||
uint64_t total_send_wrs;
|
||||
uint64_t send_doorbell_updates;
|
||||
uint64_t total_recv_wrs;
|
||||
uint64_t recv_doorbell_updates;
|
||||
};
|
||||
|
||||
struct spdk_nvme_pcie_stat {
|
||||
uint64_t polls;
|
||||
uint64_t idle_polls;
|
||||
uint64_t completions;
|
||||
uint64_t cq_doorbell_updates;
|
||||
uint64_t submitted_requests;
|
||||
uint64_t queued_requests;
|
||||
uint64_t sq_doobell_updates;
|
||||
};
|
||||
|
||||
struct spdk_nvme_transport_poll_group_stat {
|
||||
spdk_nvme_transport_type_t trtype;
|
||||
union {
|
||||
struct {
|
||||
uint32_t num_devices;
|
||||
struct spdk_nvme_rdma_device_stat *device_stats;
|
||||
} rdma;
|
||||
struct spdk_nvme_pcie_stat pcie;
|
||||
};
|
||||
};
|
||||
|
||||
struct spdk_nvme_poll_group_stat {
|
||||
uint32_t num_transports;
|
||||
struct spdk_nvme_transport_poll_group_stat **transport_stat;
|
||||
};
|
||||
|
||||
/*
|
||||
* Controller support flags
|
||||
*
|
||||
@ -725,24 +657,6 @@ typedef void (*spdk_nvme_attach_cb)(void *cb_ctx, const struct spdk_nvme_transpo
|
||||
*/
|
||||
typedef void (*spdk_nvme_remove_cb)(void *cb_ctx, struct spdk_nvme_ctrlr *ctrlr);
|
||||
|
||||
typedef bool (*spdk_nvme_pcie_hotplug_filter_cb)(const struct spdk_pci_addr *addr);
|
||||
|
||||
/**
|
||||
* Register the associated function to allow filtering of hot-inserted PCIe SSDs.
|
||||
*
|
||||
* If an application is using spdk_nvme_probe() to detect hot-inserted SSDs,
|
||||
* this function may be used to register a function to filter those SSDs.
|
||||
* If the filter function returns true, the nvme library will notify the SPDK
|
||||
* env layer to allow probing of the device.
|
||||
*
|
||||
* Registering a filter function is optional. If none is registered, the nvme
|
||||
* library will allow probing of all hot-inserted SSDs.
|
||||
*
|
||||
* \param filter_cb Filter function callback routine
|
||||
*/
|
||||
void
|
||||
spdk_nvme_pcie_set_hotplug_filter(spdk_nvme_pcie_hotplug_filter_cb filter_cb);
|
||||
|
||||
/**
|
||||
* Enumerate the bus indicated by the transport ID and attach the userspace NVMe
|
||||
* driver to each device found if desired.
|
||||
@ -1085,24 +999,6 @@ union spdk_nvme_vs_register spdk_nvme_ctrlr_get_regs_vs(struct spdk_nvme_ctrlr *
|
||||
*/
|
||||
union spdk_nvme_cmbsz_register spdk_nvme_ctrlr_get_regs_cmbsz(struct spdk_nvme_ctrlr *ctrlr);
|
||||
|
||||
/**
|
||||
* Get the NVMe controller PMRCAP (Persistent Memory Region Capabilities) register.
|
||||
*
|
||||
* \param ctrlr Opaque handle to NVMe controller.
|
||||
*
|
||||
* \return the NVMe controller PMRCAP (Persistent Memory Region Capabilities) register.
|
||||
*/
|
||||
union spdk_nvme_pmrcap_register spdk_nvme_ctrlr_get_regs_pmrcap(struct spdk_nvme_ctrlr *ctrlr);
|
||||
|
||||
/**
|
||||
* Get the NVMe controller PMR size.
|
||||
*
|
||||
* \param ctrlr Opaque handle to NVMe controller.
|
||||
*
|
||||
* \return the NVMe controller PMR size or 0 if PMR is not supported.
|
||||
*/
|
||||
uint64_t spdk_nvme_ctrlr_get_pmrsz(struct spdk_nvme_ctrlr *ctrlr);
|
||||
|
||||
/**
|
||||
* Get the number of namespaces for the given NVMe controller.
|
||||
*
|
||||
@ -1215,21 +1111,21 @@ bool spdk_nvme_ctrlr_is_feature_supported(struct spdk_nvme_ctrlr *ctrlr, uint8_t
|
||||
/**
|
||||
* Signature for callback function invoked when a command is completed.
|
||||
*
|
||||
* \param ctx Callback context provided when the command was submitted.
|
||||
* \param cpl Completion queue entry that contains the completion status.
|
||||
* \param spdk_nvme_cpl Completion queue entry that coontains the completion status.
|
||||
*/
|
||||
typedef void (*spdk_nvme_cmd_cb)(void *ctx, const struct spdk_nvme_cpl *cpl);
|
||||
typedef void (*spdk_nvme_cmd_cb)(void *, const struct spdk_nvme_cpl *);
|
||||
|
||||
/**
|
||||
* Signature for callback function invoked when an asynchronous error request
|
||||
* command is completed.
|
||||
*
|
||||
* \param ctrlr Opaque handle to NVMe controller.
|
||||
* \param aer_cb_arg Context specified by spdk_nvme_register_aer_callback().
|
||||
* \param cpl Completion queue entry that contains the completion status
|
||||
* \param spdk_nvme_cpl Completion queue entry that contains the completion status
|
||||
* of the asynchronous event request that was completed.
|
||||
*/
|
||||
typedef void (*spdk_nvme_aer_cb)(void *aer_cb_arg,
|
||||
const struct spdk_nvme_cpl *cpl);
|
||||
const struct spdk_nvme_cpl *);
|
||||
|
||||
/**
|
||||
* Register callback function invoked when an AER command is completed for the
|
||||
@ -1487,8 +1383,7 @@ spdk_nvme_qp_failure_reason spdk_nvme_ctrlr_get_admin_qp_failure_reason(
|
||||
*
|
||||
* \param qpair I/O queue pair to free.
|
||||
*
|
||||
* \return 0 on success, -1 on failure. On failure, the caller should reset
|
||||
* the controller and try to free the io qpair again after the reset.
|
||||
* \return 0 on success, -1 on failure.
|
||||
*/
|
||||
int spdk_nvme_ctrlr_free_io_qpair(struct spdk_nvme_qpair *qpair);
|
||||
|
||||
@ -2236,56 +2131,6 @@ void *spdk_nvme_ctrlr_map_cmb(struct spdk_nvme_ctrlr *ctrlr, size_t *size);
|
||||
*/
|
||||
void spdk_nvme_ctrlr_unmap_cmb(struct spdk_nvme_ctrlr *ctrlr);
|
||||
|
||||
/**
|
||||
* Enable the Persistent Memory Region
|
||||
*
|
||||
* \param ctrlr Controller that contains the Persistent Memory Region
|
||||
*
|
||||
* \return 0 on success. Negated errno on the following error conditions:
|
||||
* -ENOTSUP: PMR is not supported by the Controller.
|
||||
* -EIO: Registers access failure.
|
||||
* -EINVAL: PMR Time Units Invalid or PMR is already enabled.
|
||||
* -ETIMEDOUT: Timed out to Enable PMR.
|
||||
* -ENOSYS: Transport does not support Enable PMR function.
|
||||
*/
|
||||
int spdk_nvme_ctrlr_enable_pmr(struct spdk_nvme_ctrlr *ctrlr);
|
||||
|
||||
/**
|
||||
* Disable the Persistent Memory Region
|
||||
*
|
||||
* \param ctrlr Controller that contains the Persistent Memory Region
|
||||
*
|
||||
* \return 0 on success. Negated errno on the following error conditions:
|
||||
* -ENOTSUP: PMR is not supported by the Controller.
|
||||
* -EIO: Registers access failure.
|
||||
* -EINVAL: PMR Time Units Invalid or PMR is already disabled.
|
||||
* -ETIMEDOUT: Timed out to Disable PMR.
|
||||
* -ENOSYS: Transport does not support Disable PMR function.
|
||||
*/
|
||||
int spdk_nvme_ctrlr_disable_pmr(struct spdk_nvme_ctrlr *ctrlr);
|
||||
|
||||
/**
|
||||
* Map the Persistent Memory Region so that it's data is
|
||||
* visible from the CPU.
|
||||
*
|
||||
* \param ctrlr Controller that contains the Persistent Memory Region
|
||||
* \param size Size of the region that was mapped.
|
||||
*
|
||||
* \return Pointer to Persistent Memory Region, or NULL on failure.
|
||||
*/
|
||||
void *spdk_nvme_ctrlr_map_pmr(struct spdk_nvme_ctrlr *ctrlr, size_t *size);
|
||||
|
||||
/**
|
||||
* Free the Persistent Memory Region.
|
||||
*
|
||||
* \param ctrlr Controller from which to unmap the Persistent Memory Region.
|
||||
*
|
||||
* \return 0 on success, negative errno on failure.
|
||||
* -ENXIO: Either PMR is not supported by the Controller or the PMR is already unmapped.
|
||||
* -ENOSYS: Transport does not support Unmap PMR function.
|
||||
*/
|
||||
int spdk_nvme_ctrlr_unmap_pmr(struct spdk_nvme_ctrlr *ctrlr);
|
||||
|
||||
/**
|
||||
* Get the transport ID for a given NVMe controller.
|
||||
*
|
||||
@ -2336,22 +2181,10 @@ typedef void (*spdk_nvme_disconnected_qpair_cb)(struct spdk_nvme_qpair *qpair,
|
||||
* Create a new poll group.
|
||||
*
|
||||
* \param ctx A user supplied context that can be retrieved later with spdk_nvme_poll_group_get_ctx
|
||||
* \param table The call back table defined by users which contains the accelerated functions
|
||||
* which can be used to accelerate some operations such as crc32c.
|
||||
*
|
||||
* \return Pointer to the new poll group, or NULL on error.
|
||||
*/
|
||||
struct spdk_nvme_poll_group *spdk_nvme_poll_group_create(void *ctx,
|
||||
struct spdk_nvme_accel_fn_table *table);
|
||||
|
||||
/**
|
||||
* Get a optimal poll group.
|
||||
*
|
||||
* \param qpair The qpair to get the optimal poll group.
|
||||
*
|
||||
* \return Pointer to the optimal poll group, or NULL if not found.
|
||||
*/
|
||||
struct spdk_nvme_poll_group *spdk_nvme_qpair_get_optimal_poll_group(struct spdk_nvme_qpair *qpair);
|
||||
struct spdk_nvme_poll_group *spdk_nvme_poll_group_create(void *ctx);
|
||||
|
||||
/**
|
||||
* Add an spdk_nvme_qpair to a poll group. qpairs may only be added to
|
||||
@ -2412,28 +2245,6 @@ int64_t spdk_nvme_poll_group_process_completions(struct spdk_nvme_poll_group *gr
|
||||
*/
|
||||
void *spdk_nvme_poll_group_get_ctx(struct spdk_nvme_poll_group *group);
|
||||
|
||||
/**
|
||||
* Retrieves transport statistics for the given poll group.
|
||||
*
|
||||
* Note: the structure returned by this function should later be freed with
|
||||
* @b spdk_nvme_poll_group_free_stats function
|
||||
*
|
||||
* \param group Pointer to NVME poll group
|
||||
* \param stats Double pointer to statistics to be filled by this function
|
||||
* \return 0 on success or negated errno on failure
|
||||
*/
|
||||
int spdk_nvme_poll_group_get_stats(struct spdk_nvme_poll_group *group,
|
||||
struct spdk_nvme_poll_group_stat **stats);
|
||||
|
||||
/**
|
||||
* Frees poll group statistics retrieved using @b spdk_nvme_poll_group_get_stats function
|
||||
*
|
||||
* @param group Pointer to a poll group
|
||||
* @param stat Pointer to statistics to be released
|
||||
*/
|
||||
void spdk_nvme_poll_group_free_stats(struct spdk_nvme_poll_group *group,
|
||||
struct spdk_nvme_poll_group_stat *stat);
|
||||
|
||||
/**
|
||||
* Get the identify namespace data as defined by the NVMe specification.
|
||||
*
|
||||
@ -2644,16 +2455,16 @@ enum spdk_nvme_csi spdk_nvme_ns_get_csi(const struct spdk_nvme_ns *ns);
|
||||
* \brief Namespace command support flags.
|
||||
*/
|
||||
enum spdk_nvme_ns_flags {
|
||||
SPDK_NVME_NS_DEALLOCATE_SUPPORTED = 1 << 0, /**< The deallocate command is supported */
|
||||
SPDK_NVME_NS_FLUSH_SUPPORTED = 1 << 1, /**< The flush command is supported */
|
||||
SPDK_NVME_NS_RESERVATION_SUPPORTED = 1 << 2, /**< The reservation command is supported */
|
||||
SPDK_NVME_NS_WRITE_ZEROES_SUPPORTED = 1 << 3, /**< The write zeroes command is supported */
|
||||
SPDK_NVME_NS_DPS_PI_SUPPORTED = 1 << 4, /**< The end-to-end data protection is supported */
|
||||
SPDK_NVME_NS_EXTENDED_LBA_SUPPORTED = 1 << 5, /**< The extended lba format is supported,
|
||||
SPDK_NVME_NS_DEALLOCATE_SUPPORTED = 0x1, /**< The deallocate command is supported */
|
||||
SPDK_NVME_NS_FLUSH_SUPPORTED = 0x2, /**< The flush command is supported */
|
||||
SPDK_NVME_NS_RESERVATION_SUPPORTED = 0x4, /**< The reservation command is supported */
|
||||
SPDK_NVME_NS_WRITE_ZEROES_SUPPORTED = 0x8, /**< The write zeroes command is supported */
|
||||
SPDK_NVME_NS_DPS_PI_SUPPORTED = 0x10, /**< The end-to-end data protection is supported */
|
||||
SPDK_NVME_NS_EXTENDED_LBA_SUPPORTED = 0x20, /**< The extended lba format is supported,
|
||||
metadata is transferred as a contiguous
|
||||
part of the logical block that it is associated with */
|
||||
SPDK_NVME_NS_WRITE_UNCORRECTABLE_SUPPORTED = 1 << 6, /**< The write uncorrectable command is supported */
|
||||
SPDK_NVME_NS_COMPARE_SUPPORTED = 1 << 7, /**< The compare command is supported */
|
||||
SPDK_NVME_NS_WRITE_UNCORRECTABLE_SUPPORTED = 0x40, /**< The write uncorrectable command is supported */
|
||||
SPDK_NVME_NS_COMPARE_SUPPORTED = 0x80, /**< The compare command is supported */
|
||||
};
|
||||
|
||||
/**
|
||||
@ -3397,14 +3208,6 @@ void spdk_nvme_qpair_print_command(struct spdk_nvme_qpair *qpair,
|
||||
void spdk_nvme_qpair_print_completion(struct spdk_nvme_qpair *qpair,
|
||||
struct spdk_nvme_cpl *cpl);
|
||||
|
||||
/**
|
||||
* \brief Gets the NVMe qpair ID for the specified qpair.
|
||||
*
|
||||
* \param qpair Pointer to the NVMe queue pair.
|
||||
* \returns ID for the specified qpair.
|
||||
*/
|
||||
uint16_t spdk_nvme_qpair_get_id(struct spdk_nvme_qpair *qpair);
|
||||
|
||||
/**
|
||||
* \brief Prints (SPDK_NOTICELOG) the contents of an NVMe submission queue entry (command).
|
||||
*
|
||||
@ -3531,21 +3334,6 @@ int spdk_nvme_map_prps(void *prv, struct spdk_nvme_cmd *cmd, struct iovec *iovs,
|
||||
uint32_t len, size_t mps,
|
||||
void *(*gpa_to_vva)(void *prv, uint64_t addr, uint64_t len));
|
||||
|
||||
/**
|
||||
* Map NVMe command data buffers sent from Virtual Machine to virtual addresses
|
||||
*
|
||||
*\param prv Opaque handle to gpa_to_vva callback
|
||||
*\param cmd NVMe command
|
||||
*\param iovs IO vectors used to point the data buffers in NVMe command
|
||||
*\param max_iovcnt Maximum IO vectors that can be used
|
||||
*\param len Total buffer length for the NVMe command
|
||||
*\param mps Memory page size
|
||||
*\param gpa_to_vva Callback to map memory from Guest Physical address to Virtual address
|
||||
*/
|
||||
int spdk_nvme_map_cmd(void *prv, struct spdk_nvme_cmd *cmd, struct iovec *iovs, uint32_t max_iovcnt,
|
||||
uint32_t len, size_t mps,
|
||||
void *(*gpa_to_vva)(void *prv, uint64_t addr, uint64_t len));
|
||||
|
||||
/**
|
||||
* Opaque handle for a transport poll group. Used by the transport function table.
|
||||
*/
|
||||
@ -3596,14 +3384,6 @@ struct spdk_nvme_transport_ops {
|
||||
|
||||
int (*ctrlr_unmap_cmb)(struct spdk_nvme_ctrlr *ctrlr);
|
||||
|
||||
int (*ctrlr_enable_pmr)(struct spdk_nvme_ctrlr *ctrlr);
|
||||
|
||||
int (*ctrlr_disable_pmr)(struct spdk_nvme_ctrlr *ctrlr);
|
||||
|
||||
void *(*ctrlr_map_pmr)(struct spdk_nvme_ctrlr *ctrlr, size_t *size);
|
||||
|
||||
int (*ctrlr_unmap_pmr)(struct spdk_nvme_ctrlr *ctrlr);
|
||||
|
||||
struct spdk_nvme_qpair *(*ctrlr_create_io_qpair)(struct spdk_nvme_ctrlr *ctrlr, uint16_t qid,
|
||||
const struct spdk_nvme_io_qpair_opts *opts);
|
||||
|
||||
@ -3628,8 +3408,6 @@ struct spdk_nvme_transport_ops {
|
||||
void (*admin_qpair_abort_aers)(struct spdk_nvme_qpair *qpair);
|
||||
|
||||
struct spdk_nvme_transport_poll_group *(*poll_group_create)(void);
|
||||
struct spdk_nvme_transport_poll_group *(*qpair_get_optimal_poll_group)(
|
||||
struct spdk_nvme_qpair *qpair);
|
||||
|
||||
int (*poll_group_add)(struct spdk_nvme_transport_poll_group *tgroup, struct spdk_nvme_qpair *qpair);
|
||||
|
||||
@ -3644,12 +3422,6 @@ struct spdk_nvme_transport_ops {
|
||||
uint32_t completions_per_qpair, spdk_nvme_disconnected_qpair_cb disconnected_qpair_cb);
|
||||
|
||||
int (*poll_group_destroy)(struct spdk_nvme_transport_poll_group *tgroup);
|
||||
|
||||
int (*poll_group_get_stats)(struct spdk_nvme_transport_poll_group *tgroup,
|
||||
struct spdk_nvme_transport_poll_group_stat **stats);
|
||||
|
||||
void (*poll_group_free_stats)(struct spdk_nvme_transport_poll_group *tgroup,
|
||||
struct spdk_nvme_transport_poll_group_stat *stats);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -109,10 +109,7 @@ union spdk_nvme_cap_register {
|
||||
/** memory page size maximum */
|
||||
uint32_t mpsmax : 4;
|
||||
|
||||
/** persistent memory region supported */
|
||||
uint32_t pmrs : 1;
|
||||
|
||||
uint32_t reserved3 : 7;
|
||||
uint32_t reserved3 : 8;
|
||||
} bits;
|
||||
};
|
||||
SPDK_STATIC_ASSERT(sizeof(union spdk_nvme_cap_register) == 8, "Incorrect size");
|
||||
@ -302,139 +299,6 @@ union spdk_nvme_cmbsts_register {
|
||||
};
|
||||
SPDK_STATIC_ASSERT(sizeof(union spdk_nvme_cmbsts_register) == 4, "Incorrect size");
|
||||
|
||||
union spdk_nvme_pmrcap_register {
|
||||
uint32_t raw;
|
||||
struct {
|
||||
uint32_t reserved1 : 3;
|
||||
|
||||
/** read data support */
|
||||
uint32_t rds : 1;
|
||||
|
||||
/** write data support */
|
||||
uint32_t wds : 1;
|
||||
|
||||
/** base indicator register */
|
||||
uint32_t bir : 3;
|
||||
|
||||
/**
|
||||
* persistent memory region time units
|
||||
* 00b: 500 milliseconds
|
||||
* 01b: minutes
|
||||
*/
|
||||
uint32_t pmrtu : 2;
|
||||
|
||||
/** persistent memory region write barrier mechanisms */
|
||||
uint32_t pmrwbm : 4;
|
||||
|
||||
uint32_t reserved2 : 2;
|
||||
|
||||
/** persistent memory region timeout */
|
||||
uint32_t pmrto : 8;
|
||||
|
||||
/** controller memory space supported */
|
||||
uint32_t cmss : 1;
|
||||
|
||||
uint32_t reserved3 : 7;
|
||||
} bits;
|
||||
};
|
||||
SPDK_STATIC_ASSERT(sizeof(union spdk_nvme_pmrcap_register) == 4, "Incorrect size");
|
||||
|
||||
union spdk_nvme_pmrctl_register {
|
||||
uint32_t raw;
|
||||
struct {
|
||||
/** enable */
|
||||
uint32_t en : 1;
|
||||
|
||||
uint32_t reserved : 31;
|
||||
} bits;
|
||||
};
|
||||
SPDK_STATIC_ASSERT(sizeof(union spdk_nvme_pmrctl_register) == 4, "Incorrect size");
|
||||
|
||||
union spdk_nvme_pmrsts_register {
|
||||
uint32_t raw;
|
||||
struct {
|
||||
/** err */
|
||||
uint32_t err : 8;
|
||||
|
||||
/** not ready */
|
||||
uint32_t nrdy : 1;
|
||||
|
||||
/**
|
||||
* health status
|
||||
* 000b: Normal Operation
|
||||
* 001b: Restore Error
|
||||
* 010b: Read Only
|
||||
* 011b: Unreliable
|
||||
*/
|
||||
uint32_t hsts : 3;
|
||||
|
||||
/** controller base address invalid */
|
||||
uint32_t cbai : 1;
|
||||
|
||||
uint32_t reserved : 19;
|
||||
} bits;
|
||||
};
|
||||
SPDK_STATIC_ASSERT(sizeof(union spdk_nvme_pmrsts_register) == 4, "Incorrect size");
|
||||
|
||||
union spdk_nvme_pmrebs_register {
|
||||
uint32_t raw;
|
||||
struct {
|
||||
/**
|
||||
* pmr elasicity buffer size units
|
||||
* 0h: Bytes
|
||||
* 1h: 1 KiB
|
||||
* 2h: 1 MiB
|
||||
* 3h: 1 GiB
|
||||
*/
|
||||
uint32_t pmrszu : 4;
|
||||
|
||||
/** read bypass behavior */
|
||||
uint32_t rbb : 1;
|
||||
|
||||
uint32_t reserved : 3;
|
||||
|
||||
/** pmr elasticity buffer size base */
|
||||
uint32_t pmrwbz : 24;
|
||||
} bits;
|
||||
};
|
||||
SPDK_STATIC_ASSERT(sizeof(union spdk_nvme_pmrebs_register) == 4, "Incorrect size");
|
||||
|
||||
union spdk_nvme_pmrswtp_register {
|
||||
uint32_t raw;
|
||||
struct {
|
||||
/**
|
||||
* pmr sustained write throughput units
|
||||
* 0h: Bytes per second
|
||||
* 1h: 1 KiB / s
|
||||
* 2h: 1 MiB / s
|
||||
* 3h: 1 GiB / s
|
||||
*/
|
||||
uint32_t pmrswtu : 4;
|
||||
|
||||
uint32_t reserved : 4;
|
||||
|
||||
/** pmr sustained write throughput */
|
||||
uint32_t pmrswtv : 24;
|
||||
} bits;
|
||||
};
|
||||
SPDK_STATIC_ASSERT(sizeof(union spdk_nvme_pmrswtp_register) == 4, "Incorrect size");
|
||||
|
||||
union spdk_nvme_pmrmscl_register {
|
||||
uint32_t raw;
|
||||
struct {
|
||||
uint32_t reserved1 : 1;
|
||||
|
||||
/** controller memory space enable */
|
||||
uint32_t cmse : 1;
|
||||
|
||||
uint32_t reserved2 : 10;
|
||||
|
||||
/** controller base address */
|
||||
uint32_t cba : 20;
|
||||
} bits;
|
||||
};
|
||||
SPDK_STATIC_ASSERT(sizeof(union spdk_nvme_pmrmscl_register) == 4, "Incorrect size");
|
||||
|
||||
/** Boot partition information */
|
||||
union spdk_nvme_bpinfo_register {
|
||||
uint32_t raw;
|
||||
@ -523,29 +387,7 @@ struct spdk_nvme_registers {
|
||||
/** controller memory buffer status */
|
||||
union spdk_nvme_cmbsts_register cmbsts;
|
||||
|
||||
uint32_t reserved2[0x369];
|
||||
|
||||
/** persistent memory region capabilities */
|
||||
union spdk_nvme_pmrcap_register pmrcap;
|
||||
|
||||
/** persistent memory region control */
|
||||
union spdk_nvme_pmrctl_register pmrctl;
|
||||
|
||||
/** persistent memory region status */
|
||||
union spdk_nvme_pmrsts_register pmrsts;
|
||||
|
||||
/** persistent memory region elasticity buffer size */
|
||||
union spdk_nvme_pmrebs_register pmrebs;
|
||||
|
||||
/** persistent memory region sustained write throughput */
|
||||
union spdk_nvme_pmrswtp_register pmrswtp;
|
||||
|
||||
/** persistent memory region memory space control lower */
|
||||
union spdk_nvme_pmrmscl_register pmrmscl;
|
||||
|
||||
uint32_t pmrmscu; /* persistent memory region memory space control upper */
|
||||
|
||||
uint32_t reserved3[0x79];
|
||||
uint32_t reserved3[0x3e9];
|
||||
|
||||
struct {
|
||||
uint32_t sq_tdbl; /* submission queue tail doorbell */
|
||||
@ -581,20 +423,6 @@ SPDK_STATIC_ASSERT(0x50 == offsetof(struct spdk_nvme_registers, cmbmsc),
|
||||
"Incorrect register offset");
|
||||
SPDK_STATIC_ASSERT(0x58 == offsetof(struct spdk_nvme_registers, cmbsts),
|
||||
"Incorrect register offset");
|
||||
SPDK_STATIC_ASSERT(0xE00 == offsetof(struct spdk_nvme_registers, pmrcap),
|
||||
"Incorrect register offset");
|
||||
SPDK_STATIC_ASSERT(0xE04 == offsetof(struct spdk_nvme_registers, pmrctl),
|
||||
"Incorrect register offset");
|
||||
SPDK_STATIC_ASSERT(0xE08 == offsetof(struct spdk_nvme_registers, pmrsts),
|
||||
"Incorrect register offset");
|
||||
SPDK_STATIC_ASSERT(0xE0C == offsetof(struct spdk_nvme_registers, pmrebs),
|
||||
"Incorrect register offset");
|
||||
SPDK_STATIC_ASSERT(0xE10 == offsetof(struct spdk_nvme_registers, pmrswtp),
|
||||
"Incorrect register offset");
|
||||
SPDK_STATIC_ASSERT(0xE14 == offsetof(struct spdk_nvme_registers, pmrmscl),
|
||||
"Incorrect register offset");
|
||||
SPDK_STATIC_ASSERT(0xE18 == offsetof(struct spdk_nvme_registers, pmrmscu),
|
||||
"Incorrect register offset");
|
||||
|
||||
enum spdk_nvme_sgl_descriptor_type {
|
||||
SPDK_NVME_SGL_TYPE_DATA_BLOCK = 0x0,
|
||||
@ -884,14 +712,13 @@ union spdk_nvme_feat_async_event_configuration {
|
||||
uint32_t raw;
|
||||
struct {
|
||||
union spdk_nvme_critical_warning_state crit_warn;
|
||||
uint8_t ns_attr_notice : 1;
|
||||
uint8_t fw_activation_notice : 1;
|
||||
uint8_t telemetry_log_notice : 1;
|
||||
uint8_t ana_change_notice : 1;
|
||||
uint8_t reserved1 : 4;
|
||||
uint16_t reserved2 : 15;
|
||||
uint32_t ns_attr_notice : 1;
|
||||
uint32_t fw_activation_notice : 1;
|
||||
uint32_t telemetry_log_notice : 1;
|
||||
uint32_t ana_change_notice : 1;
|
||||
uint32_t reserved : 19;
|
||||
/** Discovery log change (refer to the NVMe over Fabrics specification) */
|
||||
uint16_t discovery_log_change_notice : 1;
|
||||
uint32_t discovery_log_change_notice : 1;
|
||||
} bits;
|
||||
};
|
||||
SPDK_STATIC_ASSERT(sizeof(union spdk_nvme_feat_async_event_configuration) == 4, "Incorrect size");
|
||||
@ -1283,7 +1110,7 @@ struct spdk_nvme_status {
|
||||
uint16_t p : 1; /* phase tag */
|
||||
uint16_t sc : 8; /* status code */
|
||||
uint16_t sct : 3; /* status code type */
|
||||
uint16_t crd : 2; /* command retry delay */
|
||||
uint16_t rsvd2 : 2;
|
||||
uint16_t m : 1; /* more */
|
||||
uint16_t dnr : 1; /* do not retry */
|
||||
};
|
||||
@ -1587,12 +1414,6 @@ spdk_nvme_bytes_to_numd(uint32_t len)
|
||||
return (len >> 2) - 1;
|
||||
}
|
||||
|
||||
struct __attribute__((packed)) spdk_nvme_host_behavior {
|
||||
uint8_t acre;
|
||||
uint8_t reserved[511];
|
||||
};
|
||||
SPDK_STATIC_ASSERT(sizeof(struct spdk_nvme_host_behavior) == 512, "Incorrect size");
|
||||
|
||||
enum spdk_nvme_feat {
|
||||
/* 0x00 - reserved */
|
||||
|
||||
@ -1630,14 +1451,6 @@ enum spdk_nvme_feat {
|
||||
/** cdw11 layout defined by \ref spdk_nvme_feat_non_operational_power_state_config */
|
||||
SPDK_NVME_FEAT_NON_OPERATIONAL_POWER_STATE_CONFIG = 0x11,
|
||||
|
||||
SPDK_NVME_FEAT_READ_RECOVERY_LEVEL_CONFIG = 0x12,
|
||||
SPDK_NVME_FEAT_PREDICTABLE_LATENCY_MODE_CONFIG = 0x13,
|
||||
SPDK_NVME_FEAT_PREDICTABLE_LATENCY_MODE_WINDOW = 0x14,
|
||||
SPDK_NVME_FEAT_LBA_STATUS_INFORMATION_ATTRIBUTES = 0x15,
|
||||
/** data buffer layout defined by \ref spdk_nvme_host_behavior */
|
||||
SPDK_NVME_FEAT_HOST_BEHAVIOR_SUPPORT = 0x16,
|
||||
SPDK_NVME_FEAT_SANITIZE_CONFIG = 0x17,
|
||||
SPDK_NVME_FEAT_ENDURANCE_GROUP_EVENT = 0x18,
|
||||
/* 0x12-0x77 - reserved */
|
||||
|
||||
/* 0x78-0x7F - NVMe-MI features */
|
||||
@ -1908,10 +1721,7 @@ struct __attribute__((packed)) __attribute__((aligned)) spdk_nvme_ctrlr_data {
|
||||
/** FRU globally unique identifier */
|
||||
uint8_t fguid[16];
|
||||
|
||||
/** Command Retry Delay Time 1, 2 and 3 */
|
||||
uint16_t crdt[3];
|
||||
|
||||
uint8_t reserved_122[122];
|
||||
uint8_t reserved_128[128];
|
||||
|
||||
/* bytes 256-511: admin command set attributes */
|
||||
|
||||
@ -2312,10 +2122,7 @@ struct spdk_nvme_ns_data {
|
||||
/** Non-zero NGUID and EUI64 for namespace are never reused */
|
||||
uint8_t guid_never_reused : 1;
|
||||
|
||||
/** Optimal Performance field */
|
||||
uint8_t optperf : 1;
|
||||
|
||||
uint8_t reserved1 : 3;
|
||||
uint8_t reserved1 : 4;
|
||||
} nsfeat;
|
||||
|
||||
/** number of lba formats */
|
||||
@ -2464,22 +2271,7 @@ struct spdk_nvme_ns_data {
|
||||
/** NVM capacity */
|
||||
uint64_t nvmcap[2];
|
||||
|
||||
/** Namespace Preferred Write Granularity */
|
||||
uint16_t npwg;
|
||||
|
||||
/** Namespace Preferred Write Alignment */
|
||||
uint16_t npwa;
|
||||
|
||||
/** Namespace Preferred Deallocate Granularity */
|
||||
uint16_t npdg;
|
||||
|
||||
/** Namespace Preferred Deallocate Alignment */
|
||||
uint16_t npda;
|
||||
|
||||
/** Namespace Optimal Write Size */
|
||||
uint16_t nows;
|
||||
|
||||
uint8_t reserved64[18];
|
||||
uint8_t reserved64[28];
|
||||
|
||||
/** ANA group identifier */
|
||||
uint32_t anagrpid;
|
||||
|
@ -60,18 +60,6 @@ extern "C" {
|
||||
*/
|
||||
const struct spdk_nvme_zns_ns_data *spdk_nvme_zns_ns_get_data(struct spdk_nvme_ns *ns);
|
||||
|
||||
/**
|
||||
* Get the zone size, in number of sectors, of the given namespace.
|
||||
*
|
||||
* This function is thread safe and can be called at any point while the controller
|
||||
* is attached to the SPDK NVMe driver.
|
||||
*
|
||||
* \param ns Namespace to query.
|
||||
*
|
||||
* \return the zone size of the given namespace in number of sectors.
|
||||
*/
|
||||
uint64_t spdk_nvme_zns_ns_get_zone_size_sectors(struct spdk_nvme_ns *ns);
|
||||
|
||||
/**
|
||||
* Get the zone size, in bytes, of the given namespace.
|
||||
*
|
||||
@ -96,40 +84,6 @@ uint64_t spdk_nvme_zns_ns_get_zone_size(struct spdk_nvme_ns *ns);
|
||||
*/
|
||||
uint64_t spdk_nvme_zns_ns_get_num_zones(struct spdk_nvme_ns *ns);
|
||||
|
||||
/**
|
||||
* Get the maximum number of open zones for the given namespace.
|
||||
*
|
||||
* An open zone is a zone in any of the zone states:
|
||||
* EXPLICIT OPEN or IMPLICIT OPEN.
|
||||
*
|
||||
* If this value is 0, there is no limit.
|
||||
*
|
||||
* This function is thread safe and can be called at any point while the controller
|
||||
* is attached to the SPDK NVMe driver.
|
||||
*
|
||||
* \param ns Namespace to query.
|
||||
*
|
||||
* \return the maximum number of open zones.
|
||||
*/
|
||||
uint32_t spdk_nvme_zns_ns_get_max_open_zones(struct spdk_nvme_ns *ns);
|
||||
|
||||
/**
|
||||
* Get the maximum number of active zones for the given namespace.
|
||||
*
|
||||
* An active zone is a zone in any of the zone states:
|
||||
* EXPLICIT OPEN, IMPLICIT OPEN or CLOSED.
|
||||
*
|
||||
* If this value is 0, there is no limit.
|
||||
*
|
||||
* This function is thread safe and can be called at any point while the controller
|
||||
* is attached to the SPDK NVMe driver.
|
||||
*
|
||||
* \param ns Namespace to query.
|
||||
*
|
||||
* \return the maximum number of active zones.
|
||||
*/
|
||||
uint32_t spdk_nvme_zns_ns_get_max_active_zones(struct spdk_nvme_ns *ns);
|
||||
|
||||
/**
|
||||
* Get the Zoned Namespace Command Set Specific Identify Controller data
|
||||
* as defined by the NVMe Zoned Namespace Command Set Specification.
|
||||
@ -160,11 +114,11 @@ uint32_t spdk_nvme_zns_ctrlr_get_max_zone_append_size(const struct spdk_nvme_ctr
|
||||
* The user must ensure that only one thread submits I/O on a given qpair at any
|
||||
* given time.
|
||||
*
|
||||
* \param ns NVMe namespace to submit the zone append I/O.
|
||||
* \param ns NVMe namespace to submit the write I/O.
|
||||
* \param qpair I/O queue pair to submit the request.
|
||||
* \param buffer Virtual address pointer to the data payload buffer.
|
||||
* \param zslba Zone Start LBA of the zone that we are appending to.
|
||||
* \param lba_count Length (in sectors) for the zone append operation.
|
||||
* \param lba_count Length (in sectors) for the write operation.
|
||||
* \param cb_fn Callback function to invoke when the I/O is completed.
|
||||
* \param cb_arg Argument to pass to the callback function.
|
||||
* \param io_flags Set flags, defined by the SPDK_NVME_IO_FLAGS_* entries in
|
||||
@ -187,13 +141,13 @@ int spdk_nvme_zns_zone_append(struct spdk_nvme_ns *ns, struct spdk_nvme_qpair *q
|
||||
* The user must ensure that only one thread submits I/O on a given qpair at any
|
||||
* given time.
|
||||
*
|
||||
* \param ns NVMe namespace to submit the zone append I/O.
|
||||
* \param ns NVMe namespace to submit the write I/O.
|
||||
* \param qpair I/O queue pair to submit the request.
|
||||
* \param buffer Virtual address pointer to the data payload buffer.
|
||||
* \param metadata Virtual address pointer to the metadata payload, the length
|
||||
* of metadata is specified by spdk_nvme_ns_get_md_size().
|
||||
* \param zslba Zone Start LBA of the zone that we are appending to.
|
||||
* \param lba_count Length (in sectors) for the zone append operation.
|
||||
* \param lba_count Length (in sectors) for the write operation.
|
||||
* \param cb_fn Callback function to invoke when the I/O is completed.
|
||||
* \param cb_arg Argument to pass to the callback function.
|
||||
* \param io_flags Set flags, defined by the SPDK_NVME_IO_FLAGS_* entries in
|
||||
@ -211,69 +165,6 @@ int spdk_nvme_zns_zone_append_with_md(struct spdk_nvme_ns *ns, struct spdk_nvme_
|
||||
uint32_t lba_count, spdk_nvme_cmd_cb cb_fn, void *cb_arg,
|
||||
uint32_t io_flags, uint16_t apptag_mask, uint16_t apptag);
|
||||
|
||||
/**
|
||||
* Submit a zone append I/O to the specified NVMe namespace.
|
||||
*
|
||||
* The command is submitted to a qpair allocated by spdk_nvme_ctrlr_alloc_io_qpair().
|
||||
* The user must ensure that only one thread submits I/O on a given qpair at any
|
||||
* given time.
|
||||
*
|
||||
* \param ns NVMe namespace to submit the zone append I/O.
|
||||
* \param qpair I/O queue pair to submit the request.
|
||||
* \param zslba Zone Start LBA of the zone that we are appending to.
|
||||
* \param lba_count Length (in sectors) for the zone append operation.
|
||||
* \param cb_fn Callback function to invoke when the I/O is completed.
|
||||
* \param cb_arg Argument to pass to the callback function.
|
||||
* \param io_flags Set flags, defined in nvme_spec.h, for this I/O.
|
||||
* \param reset_sgl_fn Callback function to reset scattered payload.
|
||||
* \param next_sge_fn Callback function to iterate each scattered payload memory
|
||||
* segment.
|
||||
*
|
||||
* \return 0 if successfully submitted, negated errnos on the following error conditions:
|
||||
* -EINVAL: The request is malformed.
|
||||
* -ENOMEM: The request cannot be allocated.
|
||||
* -ENXIO: The qpair is failed at the transport level.
|
||||
*/
|
||||
int spdk_nvme_zns_zone_appendv(struct spdk_nvme_ns *ns, struct spdk_nvme_qpair *qpair,
|
||||
uint64_t zslba, uint32_t lba_count,
|
||||
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);
|
||||
|
||||
/**
|
||||
* Submit a zone append I/O to the specified NVMe namespace.
|
||||
*
|
||||
* The command is submitted to a qpair allocated by spdk_nvme_ctrlr_alloc_io_qpair().
|
||||
* The user must ensure that only one thread submits I/O on a given qpair at any
|
||||
* given time.
|
||||
*
|
||||
* \param ns NVMe namespace to submit the zone append I/O.
|
||||
* \param qpair I/O queue pair to submit the request.
|
||||
* \param zslba Zone Start LBA of the zone that we are appending to.
|
||||
* \param lba_count Length (in sectors) for the zone append operation.
|
||||
* \param cb_fn Callback function to invoke when the I/O is completed.
|
||||
* \param cb_arg Argument to pass to the callback function.
|
||||
* \param io_flags Set flags, defined in nvme_spec.h, for this I/O.
|
||||
* \param reset_sgl_fn Callback function to reset scattered payload.
|
||||
* \param next_sge_fn Callback function to iterate each scattered payload memory
|
||||
* segment.
|
||||
* \param metadata Virtual address pointer to the metadata payload, the length
|
||||
* of metadata is specified by spdk_nvme_ns_get_md_size().
|
||||
* \param apptag_mask Application tag mask.
|
||||
* \param apptag Application tag to use end-to-end protection information.
|
||||
*
|
||||
* \return 0 if successfully submitted, negated errnos on the following error conditions:
|
||||
* -EINVAL: The request is malformed.
|
||||
* -ENOMEM: The request cannot be allocated.
|
||||
* -ENXIO: The qpair is failed at the transport level.
|
||||
*/
|
||||
int spdk_nvme_zns_zone_appendv_with_md(struct spdk_nvme_ns *ns, struct spdk_nvme_qpair *qpair,
|
||||
uint64_t zslba, uint32_t lba_count,
|
||||
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, void *metadata,
|
||||
uint16_t apptag_mask, uint16_t apptag);
|
||||
|
||||
/**
|
||||
* Submit a Close Zone operation to the specified NVMe namespace.
|
||||
*
|
||||
|
@ -2,7 +2,7 @@
|
||||
* BSD LICENSE
|
||||
*
|
||||
* Copyright (c) Intel Corporation. All rights reserved.
|
||||
* Copyright (c) 2018-2021 Mellanox Technologies LTD. All rights reserved.
|
||||
* Copyright (c) 2018-2019 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
|
||||
@ -76,7 +76,6 @@ struct spdk_nvmf_transport_opts {
|
||||
uint16_t max_queue_depth;
|
||||
uint16_t max_qpairs_per_ctrlr;
|
||||
uint32_t in_capsule_data_size;
|
||||
/* used to calculate mdts */
|
||||
uint32_t max_io_size;
|
||||
uint32_t io_unit_size;
|
||||
uint32_t max_aq_depth;
|
||||
@ -125,30 +124,17 @@ struct spdk_nvmf_poll_group_stat {
|
||||
uint64_t pending_bdev_io;
|
||||
};
|
||||
|
||||
/* Deprecated.
|
||||
* Please use the flow with spdk_nvmf_poll_group_dump_stat,
|
||||
* which hides statistics structures within the transport.
|
||||
*/
|
||||
struct spdk_nvmf_rdma_device_stat {
|
||||
const char *name;
|
||||
uint64_t polls;
|
||||
uint64_t idle_polls;
|
||||
uint64_t completions;
|
||||
uint64_t requests;
|
||||
uint64_t request_latency;
|
||||
uint64_t pending_free_request;
|
||||
uint64_t pending_rdma_read;
|
||||
uint64_t pending_rdma_write;
|
||||
uint64_t total_send_wrs;
|
||||
uint64_t send_doorbell_updates;
|
||||
uint64_t total_recv_wrs;
|
||||
uint64_t recv_doorbell_updates;
|
||||
};
|
||||
|
||||
/* Deprecated.
|
||||
* Please use the flow with spdk_nvmf_poll_group_dump_stat,
|
||||
* which hides statistics structures within the transport.
|
||||
*/
|
||||
struct spdk_nvmf_transport_poll_group_stat {
|
||||
spdk_nvme_transport_type_t trtype;
|
||||
union {
|
||||
@ -242,6 +228,21 @@ struct spdk_nvmf_tgt *spdk_nvmf_get_next_tgt(struct spdk_nvmf_tgt *prev);
|
||||
*/
|
||||
void spdk_nvmf_tgt_write_config_json(struct spdk_json_write_ctx *w, struct spdk_nvmf_tgt *tgt);
|
||||
|
||||
/**
|
||||
* Begin accepting new connections at the address provided (deprecated, please use spdk_nvmf_tgt_listen_ext).
|
||||
*
|
||||
* The connections will be matched with a subsystem, which may or may not allow
|
||||
* the connection based on a subsystem-specific list of allowed hosts. See
|
||||
* spdk_nvmf_subsystem_add_host() and spdk_nvmf_subsystem_add_listener()
|
||||
*
|
||||
* \param tgt The target associated with this listen address.
|
||||
* \param trid The address to listen at.
|
||||
*
|
||||
* \return 0 on success or a negated errno on failure.
|
||||
*/
|
||||
int spdk_nvmf_tgt_listen(struct spdk_nvmf_tgt *tgt,
|
||||
struct spdk_nvme_transport_id *trid);
|
||||
|
||||
/**
|
||||
* Begin accepting new connections at the address provided.
|
||||
*
|
||||
@ -261,7 +262,7 @@ int spdk_nvmf_tgt_listen_ext(struct spdk_nvmf_tgt *tgt, const struct spdk_nvme_t
|
||||
/**
|
||||
* Stop accepting new connections at the provided address.
|
||||
*
|
||||
* This is a counterpart to spdk_nvmf_tgt_listen_ext().
|
||||
* This is a counterpart to spdk_nvmf_tgt_listen().
|
||||
*
|
||||
* \param tgt The target associated with the listen address.
|
||||
* \param trid The address to stop listening at.
|
||||
@ -314,7 +315,7 @@ int spdk_nvmf_poll_group_add(struct spdk_nvmf_poll_group *group,
|
||||
struct spdk_nvmf_qpair *qpair);
|
||||
|
||||
/**
|
||||
* Get current poll group statistics. (deprecated)
|
||||
* Get current poll group statistics.
|
||||
*
|
||||
* \param tgt The NVMf target.
|
||||
* \param stat Pointer to allocated statistics structure to fill with values.
|
||||
@ -408,7 +409,7 @@ void spdk_nvmf_subsystem_destroy(struct spdk_nvmf_subsystem *subsystem);
|
||||
/**
|
||||
* Function to be called once the subsystem has changed state.
|
||||
*
|
||||
* \param subsystem NVMe-oF subsystem that has changed state.
|
||||
* \param subsytem NVMe-oF subsystem that has changed state.
|
||||
* \param cb_arg Argument passed to callback function.
|
||||
* \param status 0 if it completed successfully, or negative errno if it failed.
|
||||
*/
|
||||
@ -617,7 +618,7 @@ const char *spdk_nvmf_host_get_nqn(const struct spdk_nvmf_host *host);
|
||||
/**
|
||||
* Accept new connections on the address provided.
|
||||
*
|
||||
* This does not start the listener. Use spdk_nvmf_tgt_listen_ext() for that.
|
||||
* This does not start the listener. Use spdk_nvmf_tgt_listen() for that.
|
||||
*
|
||||
* May only be performed on subsystems in the PAUSED or INACTIVE states.
|
||||
* No namespaces are required to be paused.
|
||||
@ -771,6 +772,23 @@ struct spdk_nvmf_ns_opts {
|
||||
*/
|
||||
void spdk_nvmf_ns_opts_get_defaults(struct spdk_nvmf_ns_opts *opts, size_t opts_size);
|
||||
|
||||
/**
|
||||
* Add a namespace to a subsytem (deprecated, please use spdk_nvmf_subsystem_add_ns_ext).
|
||||
*
|
||||
* May only be performed on subsystems in the PAUSED or INACTIVE states.
|
||||
*
|
||||
* \param subsystem Subsystem to add namespace to.
|
||||
* \param bdev Block device to add as a namespace.
|
||||
* \param opts Namespace options, or NULL to use defaults.
|
||||
* \param opts_size sizeof(*opts)
|
||||
* \param ptpl_file Persist through power loss file path.
|
||||
*
|
||||
* \return newly added NSID on success, or 0 on failure.
|
||||
*/
|
||||
uint32_t spdk_nvmf_subsystem_add_ns(struct spdk_nvmf_subsystem *subsystem, struct spdk_bdev *bdev,
|
||||
const struct spdk_nvmf_ns_opts *opts, size_t opts_size,
|
||||
const char *ptpl_file);
|
||||
|
||||
/**
|
||||
* Add a namespace to a subsystems in the PAUSED or INACTIVE states.
|
||||
*
|
||||
@ -1088,7 +1106,7 @@ spdk_nvmf_transport_stop_listen(struct spdk_nvmf_transport *transport,
|
||||
/**
|
||||
* Stop accepting new connections at the provided address.
|
||||
*
|
||||
* This is a counterpart to spdk_nvmf_tgt_listen_ext(). It differs
|
||||
* This is a counterpart to spdk_nvmf_tgt_listen(). It differs
|
||||
* from spdk_nvmf_transport_stop_listen() in that it also destroys all
|
||||
* qpairs that are connected to the specified listener. Because
|
||||
* this function disconnects the qpairs, it has to be asynchronous.
|
||||
@ -1105,11 +1123,8 @@ int spdk_nvmf_transport_stop_listen_async(struct spdk_nvmf_transport *transport,
|
||||
spdk_nvmf_tgt_subsystem_listen_done_fn cb_fn,
|
||||
void *cb_arg);
|
||||
|
||||
|
||||
/**
|
||||
* \brief Get current transport poll group statistics. (deprecated)
|
||||
*
|
||||
* Please use the flow with spdk_nvmf_poll_group_dump_stat.
|
||||
* \brief Get current transport poll group statistics.
|
||||
*
|
||||
* This function allocates memory for statistics and returns it
|
||||
* in \p stat parameter. Caller must free this memory with
|
||||
@ -1132,9 +1147,7 @@ spdk_nvmf_transport_poll_group_get_stat(struct spdk_nvmf_tgt *tgt,
|
||||
struct spdk_nvmf_transport_poll_group_stat **stat);
|
||||
|
||||
/**
|
||||
* Free statistics memory previously allocated with spdk_nvmf_transport_poll_group_get_stat(). (deprecated)
|
||||
*
|
||||
* Please use the flow with spdk_nvmf_poll_group_dump_stat.
|
||||
* Free statistics memory previously allocated with spdk_nvmf_transport_poll_group_get_stat().
|
||||
*
|
||||
* \param transport The NVMf transport.
|
||||
* \param stat Pointer to transport poll group statistics structure.
|
||||
@ -1143,15 +1156,6 @@ void
|
||||
spdk_nvmf_transport_poll_group_free_stat(struct spdk_nvmf_transport *transport,
|
||||
struct spdk_nvmf_transport_poll_group_stat *stat);
|
||||
|
||||
/**
|
||||
* Dump poll group statistics into JSON.
|
||||
*
|
||||
* \param group The group which statistics should be dumped.
|
||||
* \param w The JSON write context to which statistics should be dumped.
|
||||
*/
|
||||
void spdk_nvmf_poll_group_dump_stat(struct spdk_nvmf_poll_group *group,
|
||||
struct spdk_json_write_ctx *w);
|
||||
|
||||
/**
|
||||
* \brief Set the global hooks for the RDMA transport, if necessary.
|
||||
*
|
||||
|
@ -48,7 +48,7 @@
|
||||
#define SPDK_NVMF_MAX_SGL_ENTRIES 16
|
||||
|
||||
/* The maximum number of buffers per request */
|
||||
#define NVMF_REQ_MAX_BUFFERS (SPDK_NVMF_MAX_SGL_ENTRIES * 2 + 1)
|
||||
#define NVMF_REQ_MAX_BUFFERS (SPDK_NVMF_MAX_SGL_ENTRIES * 2)
|
||||
|
||||
/* AIO backend requires block size aligned data buffers,
|
||||
* extra 4KiB aligned data buffer should work for most devices.
|
||||
@ -368,23 +368,15 @@ struct spdk_nvmf_transport_ops {
|
||||
struct spdk_nvmf_request *req);
|
||||
|
||||
/*
|
||||
* Get transport poll group statistics. (deprecated)
|
||||
* Please use the flow with spdk_nvmf_poll_group_dump_stat.
|
||||
* Get transport poll group statistics
|
||||
*/
|
||||
int (*poll_group_get_stat)(struct spdk_nvmf_tgt *tgt,
|
||||
struct spdk_nvmf_transport_poll_group_stat **stat);
|
||||
|
||||
/*
|
||||
* Free transport poll group statistics previously allocated with poll_group_get_stat(). (deprecated)
|
||||
* Please use the flow with spdk_nvmf_poll_group_dump_stat.
|
||||
* Free transport poll group statistics previously allocated with poll_group_get_stat()
|
||||
*/
|
||||
void (*poll_group_free_stat)(struct spdk_nvmf_transport_poll_group_stat *stat);
|
||||
|
||||
/*
|
||||
* Dump transport poll group statistics into JSON.
|
||||
*/
|
||||
void (*poll_group_dump_stat)(struct spdk_nvmf_transport_poll_group *group,
|
||||
struct spdk_json_write_ctx *w);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -102,6 +102,8 @@ void spdk_opal_dev_destruct(struct spdk_opal_dev *dev);
|
||||
|
||||
struct spdk_opal_d0_features_info *spdk_opal_get_d0_features_info(struct spdk_opal_dev *dev);
|
||||
|
||||
__attribute__((__deprecated__)) bool spdk_opal_supported(struct spdk_opal_dev *dev);
|
||||
|
||||
int spdk_opal_cmd_take_ownership(struct spdk_opal_dev *dev, char *new_passwd);
|
||||
|
||||
/**
|
||||
|
@ -52,6 +52,7 @@ extern "C" {
|
||||
#define SPDK_PCI_VID_VIRTIO 0x1af4
|
||||
#define SPDK_PCI_VID_CNEXLABS 0x1d1d
|
||||
#define SPDK_PCI_VID_VMWARE 0x15ad
|
||||
#define SPDK_PCI_VID_REDHAT 0x1b36
|
||||
|
||||
#define SPDK_PCI_CLASS_ANY_ID 0xffffff
|
||||
/**
|
||||
|
@ -105,6 +105,8 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "spdk/util.h"
|
||||
|
||||
/*
|
||||
* Singly-linked Tail queue declarations.
|
||||
*/
|
||||
|
@ -80,13 +80,6 @@ struct spdk_sock_request {
|
||||
|
||||
#define SPDK_SOCK_REQUEST_IOV(req, i) ((struct iovec *)(((uint8_t *)req + sizeof(struct spdk_sock_request)) + (sizeof(struct iovec) * i)))
|
||||
|
||||
enum spdk_placement_mode {
|
||||
PLACEMENT_NONE,
|
||||
PLACEMENT_NAPI,
|
||||
PLACEMENT_CPU,
|
||||
PLACEMENT_MARK,
|
||||
};
|
||||
|
||||
/**
|
||||
* SPDK socket implementation options.
|
||||
*
|
||||
@ -111,7 +104,6 @@ struct spdk_sock_impl_opts {
|
||||
bool enable_recv_pipe;
|
||||
|
||||
/**
|
||||
* **Deprecated, please use enable_zerocopy_send_server or enable_zerocopy_send_client instead**
|
||||
* Enable or disable use of zero copy flow on send. Used by posix socket module.
|
||||
*/
|
||||
bool enable_zerocopy_send;
|
||||
@ -123,19 +115,9 @@ struct spdk_sock_impl_opts {
|
||||
|
||||
/**
|
||||
* Enable or disable placement_id. Used by posix and uring socket modules.
|
||||
* Valid values in the enum spdk_placement_mode.
|
||||
*/
|
||||
uint32_t enable_placement_id;
|
||||
bool enable_placement_id;
|
||||
|
||||
/**
|
||||
* Enable or disable use of zero copy flow on send for server sockets. Used by posix socket module.
|
||||
*/
|
||||
bool enable_zerocopy_send_server;
|
||||
|
||||
/**
|
||||
* Enable or disable use of zero copy flow on send for client sockets. Used by posix socket module.
|
||||
*/
|
||||
bool enable_zerocopy_send_client;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -131,7 +131,7 @@ typedef int (*spdk_poller_fn)(void *ctx);
|
||||
* \param thread_ctx Context for the thread.
|
||||
* \param fn Callback function for a poller.
|
||||
* \param arg Argument passed to callback.
|
||||
* \param period_microseconds Polling period in microseconds.
|
||||
* \param period Polling period in microseconds.
|
||||
*
|
||||
* \return a pointer to the poller on success, or NULL on failure.
|
||||
*/
|
||||
@ -148,30 +148,6 @@ typedef struct spdk_poller *(*spdk_start_poller)(void *thread_ctx,
|
||||
*/
|
||||
typedef void (*spdk_stop_poller)(struct spdk_poller *poller, void *thread_ctx);
|
||||
|
||||
/**
|
||||
* Callback function to set poller into interrupt mode or back to poll mode.
|
||||
*
|
||||
* \param poller Poller to set interrupt or poll mode.
|
||||
* \param cb_arg Argument passed to the callback function.
|
||||
* \param interrupt_mode Set interrupt mode for true, or poll mode for false
|
||||
*/
|
||||
typedef void (*spdk_poller_set_interrupt_mode_cb)(struct spdk_poller *poller, void *cb_arg,
|
||||
bool interrupt_mode);
|
||||
|
||||
/**
|
||||
* Mark that the poller is capable of entering interrupt mode.
|
||||
*
|
||||
* When registering the poller set interrupt callback, the callback will get
|
||||
* executed immediately if its spdk_thread is in the interrupt mode.
|
||||
*
|
||||
* \param poller The poller to register callback function.
|
||||
* \param cb_fn Callback function called when the poller must transition into or out of interrupt mode
|
||||
* \param cb_arg Argument passed to the callback function.
|
||||
*/
|
||||
void spdk_poller_register_interrupt(struct spdk_poller *poller,
|
||||
spdk_poller_set_interrupt_mode_cb cb_fn,
|
||||
void *cb_arg);
|
||||
|
||||
/**
|
||||
* I/O channel creation callback.
|
||||
*
|
||||
@ -537,16 +513,6 @@ int spdk_thread_send_critical_msg(struct spdk_thread *thread, spdk_msg_fn fn);
|
||||
*/
|
||||
void spdk_for_each_thread(spdk_msg_fn fn, void *ctx, spdk_msg_fn cpl);
|
||||
|
||||
/**
|
||||
* Set current spdk_thread into interrupt mode or back to poll mode.
|
||||
*
|
||||
* Only valid when thread interrupt facility is enabled by
|
||||
* spdk_interrupt_mode_enable().
|
||||
*
|
||||
* \param enable_interrupt Set interrupt mode for true, or poll mode for false
|
||||
*/
|
||||
void spdk_thread_set_interrupt_mode(bool enable_interrupt);
|
||||
|
||||
/**
|
||||
* Register a poller on the current thread.
|
||||
*
|
||||
@ -755,15 +721,6 @@ struct spdk_io_channel *spdk_io_channel_iter_get_channel(struct spdk_io_channel_
|
||||
*/
|
||||
void *spdk_io_channel_iter_get_ctx(struct spdk_io_channel_iter *i);
|
||||
|
||||
/**
|
||||
* Get the io_device for the specified I/O channel.
|
||||
*
|
||||
* \param ch I/O channel.
|
||||
*
|
||||
* \return a pointer to the io_device for the I/O channel
|
||||
*/
|
||||
void *spdk_io_channel_get_io_device(struct spdk_io_channel *ch);
|
||||
|
||||
/**
|
||||
* Helper function to iterate all channels for spdk_for_each_channel().
|
||||
*
|
||||
|
@ -46,7 +46,7 @@
|
||||
/**
|
||||
* Minor version number (month of original release).
|
||||
*/
|
||||
#define SPDK_VERSION_MINOR 4
|
||||
#define SPDK_VERSION_MINOR 1
|
||||
|
||||
/**
|
||||
* Patch level.
|
||||
@ -54,7 +54,7 @@
|
||||
* Patch level is incremented on maintenance branch releases and reset to 0 for each
|
||||
* new major.minor release.
|
||||
*/
|
||||
#define SPDK_VERSION_PATCH 1
|
||||
#define SPDK_VERSION_PATCH 2
|
||||
|
||||
/**
|
||||
* Version string suffix.
|
||||
|
@ -80,26 +80,14 @@ struct spdk_accel_task {
|
||||
struct spdk_accel_batch *batch;
|
||||
spdk_accel_completion_cb cb_fn;
|
||||
void *cb_arg;
|
||||
union {
|
||||
struct {
|
||||
struct iovec *iovs; /* iovs passed by the caller */
|
||||
uint32_t iovcnt; /* iovcnt passed by the caller */
|
||||
} v;
|
||||
void *src;
|
||||
};
|
||||
void *src;
|
||||
union {
|
||||
void *dst;
|
||||
void *src2;
|
||||
};
|
||||
union {
|
||||
struct {
|
||||
spdk_accel_completion_cb cb_fn;
|
||||
void *cb_arg;
|
||||
} chained;
|
||||
void *dst2;
|
||||
uint32_t seed;
|
||||
uint64_t fill_pattern;
|
||||
};
|
||||
void *dst2;
|
||||
uint32_t seed;
|
||||
uint64_t fill_pattern;
|
||||
enum accel_opcode op_code;
|
||||
uint64_t nbytes;
|
||||
TAILQ_ENTRY(spdk_accel_task) link;
|
||||
|
@ -214,7 +214,7 @@ void spdk_app_json_config_load(const char *json_config_file, const char *rpc_add
|
||||
*/
|
||||
void spdk_subsystem_config_json(struct spdk_json_write_ctx *w, struct spdk_subsystem *subsystem);
|
||||
|
||||
int spdk_rpc_initialize(const char *listen_addr);
|
||||
void spdk_rpc_initialize(const char *listen_addr);
|
||||
void spdk_rpc_finish(void);
|
||||
|
||||
struct spdk_governor_capabilities {
|
||||
@ -236,7 +236,7 @@ struct spdk_governor {
|
||||
/* freqs - the buffer array to save the frequencies; num - the number of frequencies to get; return - the number of available frequencies */
|
||||
uint32_t (*get_core_freqs)(uint32_t lcore_id, uint32_t *freqs, uint32_t num);
|
||||
|
||||
/* return - current frequency on success, 0 on failure */
|
||||
/* return - current frequency */
|
||||
uint32_t (*get_core_curr_freq)(uint32_t lcore_id);
|
||||
|
||||
/**
|
||||
|
@ -102,7 +102,6 @@ struct nvme_tcp_pdu {
|
||||
bool has_hdgst;
|
||||
bool ddgst_enable;
|
||||
uint32_t header_digest_crc32;
|
||||
uint32_t data_digest_crc32;
|
||||
uint8_t data_digest[SPDK_NVME_TCP_DIGEST_LEN];
|
||||
|
||||
uint8_t ch_valid_bytes;
|
||||
@ -619,12 +618,12 @@ nvme_tcp_pdu_calc_psh_len(struct nvme_tcp_pdu *pdu, bool hdgst_enable)
|
||||
if (g_nvme_tcp_hdgst[pdu->hdr.common.pdu_type] && hdgst_enable) {
|
||||
pdu->has_hdgst = true;
|
||||
psh_len += SPDK_NVME_TCP_DIGEST_LEN;
|
||||
}
|
||||
if (pdu->hdr.common.plen > psh_len) {
|
||||
pdo = pdu->hdr.common.pdo;
|
||||
padding_len = pdo - psh_len;
|
||||
if (padding_len > 0) {
|
||||
psh_len = pdo;
|
||||
if (pdu->hdr.common.plen > psh_len) {
|
||||
pdo = pdu->hdr.common.pdo;
|
||||
padding_len = pdo - psh_len;
|
||||
if (padding_len > 0) {
|
||||
psh_len = pdo;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -41,18 +41,6 @@
|
||||
/* Contains hooks definition */
|
||||
#include "spdk/nvme.h"
|
||||
|
||||
struct spdk_rdma_wr_stats {
|
||||
/* Total number of submitted requests */
|
||||
uint64_t num_submitted_wrs;
|
||||
/* Total number of doorbell updates */
|
||||
uint64_t doorbell_updates;
|
||||
};
|
||||
|
||||
struct spdk_rdma_qp_stats {
|
||||
struct spdk_rdma_wr_stats send;
|
||||
struct spdk_rdma_wr_stats recv;
|
||||
};
|
||||
|
||||
struct spdk_rdma_qp_init_attr {
|
||||
void *qp_context;
|
||||
struct ibv_cq *send_cq;
|
||||
@ -60,7 +48,6 @@ struct spdk_rdma_qp_init_attr {
|
||||
struct ibv_srq *srq;
|
||||
struct ibv_qp_cap cap;
|
||||
struct ibv_pd *pd;
|
||||
struct spdk_rdma_qp_stats *stats;
|
||||
};
|
||||
|
||||
struct spdk_rdma_send_wr_list {
|
||||
@ -68,18 +55,10 @@ struct spdk_rdma_send_wr_list {
|
||||
struct ibv_send_wr *last;
|
||||
};
|
||||
|
||||
struct spdk_rdma_recv_wr_list {
|
||||
struct ibv_recv_wr *first;
|
||||
struct ibv_recv_wr *last;
|
||||
};
|
||||
|
||||
struct spdk_rdma_qp {
|
||||
struct ibv_qp *qp;
|
||||
struct rdma_cm_id *cm_id;
|
||||
struct spdk_rdma_send_wr_list send_wrs;
|
||||
struct spdk_rdma_recv_wr_list recv_wrs;
|
||||
struct spdk_rdma_qp_stats *stats;
|
||||
bool shared_stats;
|
||||
};
|
||||
|
||||
struct spdk_rdma_mem_map;
|
||||
@ -98,58 +77,10 @@ struct spdk_rdma_memory_translation {
|
||||
union spdk_rdma_mr mr_or_key;
|
||||
uint8_t translation_type;
|
||||
};
|
||||
struct spdk_rdma_srq_init_attr {
|
||||
struct ibv_pd *pd;
|
||||
struct spdk_rdma_wr_stats *stats;
|
||||
struct ibv_srq_init_attr srq_init_attr;
|
||||
};
|
||||
|
||||
struct spdk_rdma_srq {
|
||||
struct ibv_srq *srq;
|
||||
struct spdk_rdma_recv_wr_list recv_wrs;
|
||||
struct spdk_rdma_wr_stats *stats;
|
||||
bool shared_stats;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create RDMA SRQ
|
||||
*
|
||||
* \param init_attr Pointer to SRQ init attr
|
||||
* \return pointer to srq on success or NULL on failure. errno is updated in failure case.
|
||||
*/
|
||||
struct spdk_rdma_srq *spdk_rdma_srq_create(struct spdk_rdma_srq_init_attr *init_attr);
|
||||
|
||||
/**
|
||||
* Destroy RDMA SRQ
|
||||
*
|
||||
* \param rdma_srq Pointer to SRQ
|
||||
* \return 0 on succes, errno on failure
|
||||
*/
|
||||
int spdk_rdma_srq_destroy(struct spdk_rdma_srq *rdma_srq);
|
||||
|
||||
/**
|
||||
* Append the given recv wr structure to the SRQ's outstanding recv list.
|
||||
* This function accepts either a single Work Request or the first WR in a linked list.
|
||||
*
|
||||
* \param rdma_srq Pointer to SRQ
|
||||
* \param first pointer to the first Work Request
|
||||
* \return true if there were no outstanding WRs before, false otherwise
|
||||
*/
|
||||
bool spdk_rdma_srq_queue_recv_wrs(struct spdk_rdma_srq *rdma_srq, struct ibv_recv_wr *first);
|
||||
|
||||
/**
|
||||
* Submit all queued receive Work Request
|
||||
*
|
||||
* \param rdma_srq Pointer to SRQ
|
||||
* \param bad_wr Stores a pointer to the first failed WR if this function return nonzero value
|
||||
* \return 0 on succes, errno on failure
|
||||
*/
|
||||
int spdk_rdma_srq_flush_recv_wrs(struct spdk_rdma_srq *rdma_srq, struct ibv_recv_wr **bad_wr);
|
||||
|
||||
/**
|
||||
* Create RDMA provider specific qpair
|
||||
*
|
||||
* \param cm_id Pointer to RDMA_CM cm_id
|
||||
* \param cm_id Pointer to RDMACM cm_id
|
||||
* \param qp_attr Pointer to qpair init attributes
|
||||
* \return Pointer to a newly created qpair on success or NULL on failure
|
||||
*/
|
||||
@ -158,8 +89,7 @@ struct spdk_rdma_qp *spdk_rdma_qp_create(struct rdma_cm_id *cm_id,
|
||||
|
||||
/**
|
||||
* Accept a connection request. Called by the passive side (NVMEoF target)
|
||||
*
|
||||
* \param spdk_rdma_qp Pointer to SPDK RDMA qpair
|
||||
* \param spdk_rdma_qp Pointer to a qpair
|
||||
* \param conn_param Optional information needed to establish the connection
|
||||
* \return 0 on success, errno on failure
|
||||
*/
|
||||
@ -168,24 +98,21 @@ int spdk_rdma_qp_accept(struct spdk_rdma_qp *spdk_rdma_qp, struct rdma_conn_para
|
||||
/**
|
||||
* Complete the connection process, must be called by the active
|
||||
* side (NVMEoF initiator) upon receipt RDMA_CM_EVENT_CONNECT_RESPONSE
|
||||
*
|
||||
* \param spdk_rdma_qp Pointer to SPDK RDMA qpair
|
||||
* \param spdk_rdma_qp Pointer to a qpair
|
||||
* \return 0 on success, errno on failure
|
||||
*/
|
||||
int spdk_rdma_qp_complete_connect(struct spdk_rdma_qp *spdk_rdma_qp);
|
||||
|
||||
/**
|
||||
* Destroy RDMA provider specific qpair
|
||||
*
|
||||
* \param spdk_rdma_qp Pointer to SPDK RDMA qpair to be destroyed
|
||||
* \param spdk_rdma_qp Pointer to qpair to be destroyed
|
||||
*/
|
||||
void spdk_rdma_qp_destroy(struct spdk_rdma_qp *spdk_rdma_qp);
|
||||
|
||||
/**
|
||||
* Disconnect a connection and transition associated qpair to error state.
|
||||
* Disconnect a connection and transition assoiciated qpair to error state.
|
||||
* Generates RDMA_CM_EVENT_DISCONNECTED on both connection sides
|
||||
*
|
||||
* \param spdk_rdma_qp Pointer to qpair to be disconnected
|
||||
* \param spdk_rdma_qp Pointer to qpair to be destroyed
|
||||
*/
|
||||
int spdk_rdma_qp_disconnect(struct spdk_rdma_qp *spdk_rdma_qp);
|
||||
|
||||
@ -200,32 +127,13 @@ int spdk_rdma_qp_disconnect(struct spdk_rdma_qp *spdk_rdma_qp);
|
||||
bool spdk_rdma_qp_queue_send_wrs(struct spdk_rdma_qp *spdk_rdma_qp, struct ibv_send_wr *first);
|
||||
|
||||
/**
|
||||
* Submit all queued send Work Request
|
||||
*
|
||||
* Submit all queued Work Request
|
||||
* \param spdk_rdma_qp Pointer to SPDK RDMA qpair
|
||||
* \param bad_wr Stores a pointer to the first failed WR if this function return nonzero value
|
||||
* \return 0 on succes, errno on failure
|
||||
*/
|
||||
int spdk_rdma_qp_flush_send_wrs(struct spdk_rdma_qp *spdk_rdma_qp, struct ibv_send_wr **bad_wr);
|
||||
|
||||
/**
|
||||
* Append the given recv wr structure to the qpair's outstanding recv list.
|
||||
* This function accepts either a single Work Request or the first WR in a linked list.
|
||||
*
|
||||
* \param spdk_rdma_qp Pointer to SPDK RDMA qpair
|
||||
* \param first Pointer to the first Work Request
|
||||
* \return true if there were no outstanding WRs before, false otherwise
|
||||
*/
|
||||
bool spdk_rdma_qp_queue_recv_wrs(struct spdk_rdma_qp *spdk_rdma_qp, struct ibv_recv_wr *first);
|
||||
|
||||
/**
|
||||
* Submit all queued recv Work Request
|
||||
* \param spdk_rdma_qp Pointer to SPDK RDMA qpair
|
||||
* \param bad_wr Stores a pointer to the first failed WR if this function return nonzero value
|
||||
* \return 0 on succes, errno on failure
|
||||
*/
|
||||
int spdk_rdma_qp_flush_recv_wrs(struct spdk_rdma_qp *spdk_rdma_qp, struct ibv_recv_wr **bad_wr);
|
||||
|
||||
/**
|
||||
* Create a memory map which is used to register Memory Regions and perform address -> memory
|
||||
* key translations
|
||||
|
@ -66,6 +66,7 @@ struct spdk_sock {
|
||||
int cb_cnt;
|
||||
spdk_sock_cb cb_fn;
|
||||
void *cb_arg;
|
||||
int placement_id;
|
||||
struct {
|
||||
uint8_t closed : 1;
|
||||
uint8_t reserved : 7;
|
||||
@ -79,14 +80,14 @@ struct spdk_sock_group {
|
||||
|
||||
struct spdk_sock_group_impl {
|
||||
struct spdk_net_impl *net_impl;
|
||||
struct spdk_sock_group *group;
|
||||
TAILQ_HEAD(, spdk_sock) socks;
|
||||
STAILQ_ENTRY(spdk_sock_group_impl) link;
|
||||
};
|
||||
|
||||
struct spdk_sock_map {
|
||||
STAILQ_HEAD(, spdk_sock_placement_id_entry) entries;
|
||||
pthread_mutex_t mtx;
|
||||
/* List of removed sockets. refreshed each time we poll the sock group. */
|
||||
int num_removed_socks;
|
||||
/* Unfortunately, we can't just keep a tailq of the sockets in case they are freed
|
||||
* or added to another poll group later.
|
||||
*/
|
||||
uintptr_t removed_socks[MAX_EVENTS_PER_POLL];
|
||||
};
|
||||
|
||||
struct spdk_net_impl {
|
||||
@ -114,7 +115,7 @@ struct spdk_net_impl {
|
||||
bool (*is_ipv4)(struct spdk_sock *sock);
|
||||
bool (*is_connected)(struct spdk_sock *sock);
|
||||
|
||||
struct spdk_sock_group_impl *(*group_impl_get_optimal)(struct spdk_sock *sock);
|
||||
int (*get_placement_id)(struct spdk_sock *sock, int *placement_id);
|
||||
struct spdk_sock_group_impl *(*group_impl_create)(void);
|
||||
int (*group_impl_add_sock)(struct spdk_sock_group_impl *group, struct spdk_sock *sock);
|
||||
int (*group_impl_remove_sock)(struct spdk_sock_group_impl *group, struct spdk_sock *sock);
|
||||
@ -276,65 +277,6 @@ end:
|
||||
return iovcnt;
|
||||
}
|
||||
|
||||
static inline void
|
||||
spdk_sock_get_placement_id(int fd, enum spdk_placement_mode mode, int *placement_id)
|
||||
{
|
||||
*placement_id = -1;
|
||||
|
||||
switch (mode) {
|
||||
case PLACEMENT_NONE:
|
||||
break;
|
||||
case PLACEMENT_MARK:
|
||||
case PLACEMENT_NAPI: {
|
||||
#if defined(SO_INCOMING_NAPI_ID)
|
||||
socklen_t len = sizeof(int);
|
||||
|
||||
getsockopt(fd, SOL_SOCKET, SO_INCOMING_NAPI_ID, placement_id, &len);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case PLACEMENT_CPU: {
|
||||
#if defined(SO_INCOMING_CPU)
|
||||
socklen_t len = sizeof(int);
|
||||
|
||||
getsockopt(fd, SOL_SOCKET, SO_INCOMING_CPU, placement_id, &len);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert a group into the placement map.
|
||||
* If the group is already in the map, take a reference.
|
||||
*/
|
||||
int spdk_sock_map_insert(struct spdk_sock_map *map, int placement_id,
|
||||
struct spdk_sock_group_impl *group_impl);
|
||||
|
||||
/**
|
||||
* Release a reference for the given placement_id. If the reference count goes to 0, the
|
||||
* entry will no longer be associated with a group.
|
||||
*/
|
||||
void spdk_sock_map_release(struct spdk_sock_map *map, int placement_id);
|
||||
|
||||
/**
|
||||
* Look up the group for the given placement_id.
|
||||
*/
|
||||
int spdk_sock_map_lookup(struct spdk_sock_map *map, int placement_id,
|
||||
struct spdk_sock_group_impl **group_impl);
|
||||
|
||||
/**
|
||||
* Find a placement id with no associated group
|
||||
*/
|
||||
int spdk_sock_map_find_free(struct spdk_sock_map *map);
|
||||
|
||||
/**
|
||||
* Clean up all memory associated with the given map
|
||||
*/
|
||||
void spdk_sock_map_cleanup(struct spdk_sock_map *map);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -74,9 +74,7 @@ struct spdk_poller {
|
||||
spdk_poller_fn fn;
|
||||
void *arg;
|
||||
struct spdk_thread *thread;
|
||||
int interruptfd;
|
||||
spdk_poller_set_interrupt_mode_cb set_intr_cb_fn;
|
||||
void *set_intr_cb_arg;
|
||||
int timerfd;
|
||||
|
||||
char name[SPDK_MAX_POLLER_NAME_LEN + 1];
|
||||
};
|
||||
@ -130,8 +128,7 @@ struct spdk_thread {
|
||||
struct spdk_cpuset cpumask;
|
||||
uint64_t exit_timeout_tsc;
|
||||
|
||||
/* Indicates whether this spdk_thread currently runs in interrupt. */
|
||||
bool in_interrupt;
|
||||
bool interrupt_mode;
|
||||
struct spdk_fd_group *fgrp;
|
||||
|
||||
/* User context allocated at the end */
|
||||
|
@ -483,16 +483,4 @@ int virtio_user_dev_init(struct virtio_dev *vdev, const char *name, const char *
|
||||
int virtio_pci_dev_init(struct virtio_dev *vdev, const char *name,
|
||||
struct virtio_pci_ctx *pci_ctx);
|
||||
|
||||
/**
|
||||
* Process the uevent which is accepted from the kernel and the
|
||||
* uevent descript the physical device hot add or remove action.
|
||||
*
|
||||
* \param fd the file descriptor of the kobject netlink socket
|
||||
* \param device_id virtio device ID used to represent virtio-blk or other device.
|
||||
* \return the name of the virtio device on success, NULL means it
|
||||
* is not a suitable uevent.
|
||||
*/
|
||||
const char *
|
||||
virtio_pci_dev_event_process(int fd, uint16_t device_id);
|
||||
|
||||
#endif /* SPDK_VIRTIO_H */
|
||||
|
@ -34,7 +34,7 @@
|
||||
SPDK_ROOT_DIR := $(abspath $(CURDIR)/../..)
|
||||
include $(SPDK_ROOT_DIR)/mk/spdk.common.mk
|
||||
|
||||
SO_VER := 5
|
||||
SO_VER := 4
|
||||
SO_MINOR := 0
|
||||
SO_SUFFIX := $(SO_VER).$(SO_MINOR)
|
||||
|
||||
|
@ -36,7 +36,6 @@
|
||||
#include "spdk_internal/accel_engine.h"
|
||||
|
||||
#include "spdk/env.h"
|
||||
#include "spdk/likely.h"
|
||||
#include "spdk/log.h"
|
||||
#include "spdk/thread.h"
|
||||
#include "spdk/json.h"
|
||||
@ -73,7 +72,6 @@ static void _sw_accel_copy(void *dst, void *src, uint64_t nbytes);
|
||||
static int _sw_accel_compare(void *src1, void *src2, uint64_t nbytes);
|
||||
static void _sw_accel_fill(void *dst, uint8_t fill, uint64_t nbytes);
|
||||
static void _sw_accel_crc32c(uint32_t *dst, void *src, uint32_t seed, uint64_t nbytes);
|
||||
static void _sw_accel_crc32cv(uint32_t *dst, struct iovec *iov, uint32_t iovcnt, uint32_t seed);
|
||||
|
||||
/* Registration of hw modules (currently supports only 1 at a time) */
|
||||
void
|
||||
@ -113,19 +111,13 @@ void
|
||||
spdk_accel_task_complete(struct spdk_accel_task *accel_task, int status)
|
||||
{
|
||||
struct accel_io_channel *accel_ch = accel_task->accel_ch;
|
||||
struct spdk_accel_batch *batch = accel_task->batch;
|
||||
spdk_accel_completion_cb cb_fn = accel_task->cb_fn;
|
||||
void *cb_arg = accel_task->cb_arg;
|
||||
struct spdk_accel_batch *batch;
|
||||
|
||||
/* We should put the accel_task into the list firstly in order to avoid
|
||||
* the accel task list is exhausted when there is recursive call to
|
||||
* allocate accel_task in user's call back function (cb_fn)
|
||||
*/
|
||||
TAILQ_INSERT_TAIL(&accel_ch->task_pool, accel_task, link);
|
||||
accel_task->cb_fn(accel_task->cb_arg, status);
|
||||
|
||||
cb_fn(cb_arg, status);
|
||||
/* If this task is part of a batch, check for completion of the batch. */
|
||||
if (batch) {
|
||||
if (accel_task->batch) {
|
||||
batch = accel_task->batch;
|
||||
assert(batch->count > 0);
|
||||
batch->count--;
|
||||
if (batch->count == 0) {
|
||||
@ -137,6 +129,8 @@ spdk_accel_task_complete(struct spdk_accel_task *accel_task, int status)
|
||||
TAILQ_INSERT_TAIL(&accel_ch->batch_pool, batch, link);
|
||||
}
|
||||
}
|
||||
|
||||
TAILQ_INSERT_TAIL(&accel_ch->task_pool, accel_task, link);
|
||||
}
|
||||
|
||||
/* Accel framework public API for discovering current engine capabilities. */
|
||||
@ -314,7 +308,6 @@ spdk_accel_submit_crc32c(struct spdk_io_channel *ch, uint32_t *dst, void *src, u
|
||||
|
||||
accel_task->dst = (void *)dst;
|
||||
accel_task->src = src;
|
||||
accel_task->v.iovcnt = 0;
|
||||
accel_task->seed = seed;
|
||||
accel_task->nbytes = nbytes;
|
||||
accel_task->op_code = ACCEL_OPCODE_CRC32C;
|
||||
@ -328,78 +321,6 @@ spdk_accel_submit_crc32c(struct spdk_io_channel *ch, uint32_t *dst, void *src, u
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
crc32cv_done(void *cb_arg, int status)
|
||||
{
|
||||
struct spdk_accel_task *accel_task = cb_arg;
|
||||
struct spdk_io_channel *ch = spdk_io_channel_from_ctx(accel_task->accel_ch);
|
||||
|
||||
assert(accel_task->chained.cb_fn != NULL);
|
||||
assert(accel_task->chained.cb_arg != NULL);
|
||||
|
||||
if (spdk_likely(!status)) {
|
||||
status = spdk_accel_submit_crc32cv(ch, accel_task->dst, ++accel_task->v.iovs,
|
||||
accel_task->v.iovcnt - 1, ~(*((uint32_t *)accel_task->dst)),
|
||||
accel_task->chained.cb_fn, accel_task->chained.cb_arg);
|
||||
if (spdk_likely(!status)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
accel_task->chained.cb_fn(accel_task->chained.cb_arg, status);
|
||||
}
|
||||
|
||||
/* Accel framework public API for chained CRC-32C function */
|
||||
int
|
||||
spdk_accel_submit_crc32cv(struct spdk_io_channel *ch, uint32_t *dst, struct iovec *iov,
|
||||
uint32_t iov_cnt, uint32_t seed, spdk_accel_completion_cb cb_fn, void *cb_arg)
|
||||
{
|
||||
struct accel_io_channel *accel_ch;
|
||||
struct spdk_accel_task *accel_task;
|
||||
|
||||
if (iov == NULL) {
|
||||
SPDK_ERRLOG("iov should not be NULL");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!iov_cnt) {
|
||||
SPDK_ERRLOG("iovcnt should not be zero value\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (iov_cnt == 1) {
|
||||
return spdk_accel_submit_crc32c(ch, dst, iov[0].iov_base, seed, iov[0].iov_len, cb_fn, cb_arg);
|
||||
}
|
||||
|
||||
accel_ch = spdk_io_channel_get_ctx(ch);
|
||||
accel_task = _get_task(accel_ch, NULL, cb_fn, cb_arg);
|
||||
if (accel_task == NULL) {
|
||||
SPDK_ERRLOG("no memory\n");
|
||||
assert(0);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
accel_task->v.iovs = iov;
|
||||
accel_task->v.iovcnt = iov_cnt;
|
||||
accel_task->dst = (void *)dst;
|
||||
accel_task->op_code = ACCEL_OPCODE_CRC32C;
|
||||
|
||||
if (_is_supported(accel_ch->engine, ACCEL_CRC32C)) {
|
||||
accel_task->cb_fn = crc32cv_done;
|
||||
accel_task->cb_arg = accel_task;
|
||||
accel_task->chained.cb_fn = cb_fn;
|
||||
accel_task->chained.cb_arg = cb_arg;
|
||||
|
||||
accel_task->nbytes = iov[0].iov_len;
|
||||
|
||||
return accel_ch->engine->submit_tasks(accel_ch->engine_ch, accel_task);
|
||||
} else {
|
||||
_sw_accel_crc32cv(dst, iov, iov_cnt, seed);
|
||||
spdk_accel_task_complete(accel_task, 0);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Accel framework public API for getting max operations for a batch. */
|
||||
uint32_t
|
||||
spdk_accel_batch_get_max(struct spdk_io_channel *ch)
|
||||
@ -538,7 +459,6 @@ spdk_accel_batch_prep_crc32c(struct spdk_io_channel *ch, struct spdk_accel_batch
|
||||
|
||||
accel_task->dst = dst;
|
||||
accel_task->src = src;
|
||||
accel_task->v.iovcnt = 0;
|
||||
accel_task->seed = seed;
|
||||
accel_task->nbytes = nbytes;
|
||||
accel_task->op_code = ACCEL_OPCODE_CRC32C;
|
||||
@ -552,82 +472,6 @@ spdk_accel_batch_prep_crc32c(struct spdk_io_channel *ch, struct spdk_accel_batch
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
batched_crc32cv_done(void *cb_arg, int status)
|
||||
{
|
||||
struct spdk_accel_task *accel_task = cb_arg;
|
||||
struct spdk_io_channel *ch = spdk_io_channel_from_ctx(accel_task->accel_ch);
|
||||
struct spdk_accel_batch *batch;
|
||||
|
||||
batch = accel_task->batch;
|
||||
assert(batch != NULL);
|
||||
assert(accel_task->chained.cb_fn != NULL);
|
||||
assert(accel_task->chained.cb_arg != NULL);
|
||||
|
||||
if (spdk_likely(!status)) {
|
||||
status = spdk_accel_batch_prep_crc32cv(ch, batch, accel_task->dst,
|
||||
++accel_task->v.iovs, accel_task->v.iovcnt - 1, ~(*((uint32_t *)accel_task->dst)),
|
||||
accel_task->chained.cb_fn, accel_task->chained.cb_arg);
|
||||
if (spdk_likely(!status)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
accel_task->chained.cb_fn(accel_task->chained.cb_arg, status);
|
||||
}
|
||||
|
||||
int
|
||||
spdk_accel_batch_prep_crc32cv(struct spdk_io_channel *ch, struct spdk_accel_batch *batch,
|
||||
uint32_t *dst, struct iovec *iovs, uint32_t iov_cnt, uint32_t seed,
|
||||
spdk_accel_completion_cb cb_fn, void *cb_arg)
|
||||
{
|
||||
struct accel_io_channel *accel_ch;
|
||||
struct spdk_accel_task *accel_task;
|
||||
|
||||
if (iovs == NULL) {
|
||||
SPDK_ERRLOG("iovs should not be NULL\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (iov_cnt == 0) {
|
||||
SPDK_ERRLOG("iovcnt should not be zero value\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (iov_cnt == 1) {
|
||||
return spdk_accel_batch_prep_crc32c(ch, batch, dst, iovs[0].iov_base, seed, iovs[0].iov_len, cb_fn,
|
||||
cb_arg);
|
||||
}
|
||||
|
||||
accel_ch = spdk_io_channel_get_ctx(ch);
|
||||
accel_task = _get_task(accel_ch, batch, cb_fn, cb_arg);
|
||||
if (accel_task == NULL) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
accel_task->v.iovs = iovs;
|
||||
accel_task->v.iovcnt = iov_cnt;
|
||||
accel_task->dst = dst;
|
||||
accel_task->seed = seed;
|
||||
accel_task->op_code = ACCEL_OPCODE_CRC32C;
|
||||
|
||||
if (_is_supported(accel_ch->engine, ACCEL_CRC32C)) {
|
||||
accel_task->cb_arg = accel_task;
|
||||
accel_task->cb_fn = batched_crc32cv_done;
|
||||
accel_task->cb_arg = accel_task;
|
||||
accel_task->chained.cb_fn = cb_fn;
|
||||
accel_task->chained.cb_arg = cb_arg;
|
||||
|
||||
accel_task->nbytes = iovs[0].iov_len;
|
||||
|
||||
TAILQ_INSERT_TAIL(&batch->hw_tasks, accel_task, link);
|
||||
} else {
|
||||
TAILQ_INSERT_TAIL(&batch->sw_tasks, accel_task, link);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Accel framework public API for batch_create function. */
|
||||
struct spdk_accel_batch *
|
||||
spdk_accel_batch_create(struct spdk_io_channel *ch)
|
||||
@ -707,12 +551,8 @@ spdk_accel_batch_submit(struct spdk_io_channel *ch, struct spdk_accel_batch *bat
|
||||
batch->status |= rc;
|
||||
break;
|
||||
case ACCEL_OPCODE_CRC32C:
|
||||
if (accel_task->v.iovcnt == 0) {
|
||||
_sw_accel_crc32c(accel_task->dst, accel_task->src, accel_task->seed,
|
||||
accel_task->nbytes);
|
||||
} else {
|
||||
_sw_accel_crc32cv(accel_task->dst, accel_task->v.iovs, accel_task->v.iovcnt, accel_task->seed);
|
||||
}
|
||||
_sw_accel_crc32c(accel_task->dst, accel_task->src, accel_task->seed,
|
||||
accel_task->nbytes);
|
||||
spdk_accel_task_complete(accel_task, 0);
|
||||
break;
|
||||
case ACCEL_OPCODE_DUALCAST:
|
||||
@ -964,20 +804,6 @@ _sw_accel_crc32c(uint32_t *dst, void *src, uint32_t seed, uint64_t nbytes)
|
||||
*dst = spdk_crc32c_update(src, nbytes, ~seed);
|
||||
}
|
||||
|
||||
static void
|
||||
_sw_accel_crc32cv(uint32_t *dst, struct iovec *iov, uint32_t iovcnt, uint32_t seed)
|
||||
{
|
||||
uint32_t i, crc32c = ~seed;
|
||||
|
||||
for (i = 0; i < iovcnt; i++) {
|
||||
assert(iov[i].iov_base != NULL);
|
||||
assert(iov[i].iov_len != 0);
|
||||
crc32c = spdk_crc32c_update(iov[i].iov_base, iov[i].iov_len, crc32c);
|
||||
}
|
||||
|
||||
*dst = crc32c;
|
||||
}
|
||||
|
||||
static struct spdk_io_channel *sw_accel_get_io_channel(void);
|
||||
|
||||
static uint32_t
|
||||
|
@ -14,7 +14,6 @@
|
||||
spdk_accel_batch_prep_compare;
|
||||
spdk_accel_batch_prep_fill;
|
||||
spdk_accel_batch_prep_crc32c;
|
||||
spdk_accel_batch_prep_crc32cv;
|
||||
spdk_accel_batch_submit;
|
||||
spdk_accel_batch_cancel;
|
||||
spdk_accel_submit_copy;
|
||||
@ -22,7 +21,6 @@
|
||||
spdk_accel_submit_compare;
|
||||
spdk_accel_submit_fill;
|
||||
spdk_accel_submit_crc32c;
|
||||
spdk_accel_submit_crc32cv;
|
||||
spdk_accel_write_config_json;
|
||||
|
||||
# functions needed by modules
|
||||
|
@ -34,7 +34,7 @@
|
||||
SPDK_ROOT_DIR := $(abspath $(CURDIR)/../..)
|
||||
include $(SPDK_ROOT_DIR)/mk/spdk.common.mk
|
||||
|
||||
SO_VER := 6
|
||||
SO_VER := 5
|
||||
SO_MINOR := 0
|
||||
|
||||
ifeq ($(CONFIG_VTUNE),y)
|
||||
|
@ -304,7 +304,11 @@ struct spdk_bdev_desc {
|
||||
struct spdk_bdev *bdev;
|
||||
struct spdk_thread *thread;
|
||||
struct {
|
||||
spdk_bdev_event_cb_t event_fn;
|
||||
bool open_with_ext;
|
||||
union {
|
||||
spdk_bdev_remove_cb_t remove_fn;
|
||||
spdk_bdev_event_cb_t event_fn;
|
||||
};
|
||||
void *ctx;
|
||||
} callback;
|
||||
bool closed;
|
||||
@ -1471,6 +1475,7 @@ bdev_mgr_unregister_cb(void *io_device)
|
||||
g_fini_cb_arg = NULL;
|
||||
g_bdev_mgr.init_complete = false;
|
||||
g_bdev_mgr.module_init_complete = false;
|
||||
pthread_mutex_destroy(&g_bdev_mgr.mutex);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -3459,7 +3464,7 @@ spdk_bdev_notify_blockcnt_change(struct spdk_bdev *bdev, uint64_t size)
|
||||
bdev->blockcnt = size;
|
||||
TAILQ_FOREACH(desc, &bdev->internal.open_descs, link) {
|
||||
pthread_mutex_lock(&desc->mutex);
|
||||
if (!desc->closed) {
|
||||
if (desc->callback.open_with_ext && !desc->closed) {
|
||||
desc->refs++;
|
||||
spdk_thread_send_msg(desc->thread, _resize_notify, desc);
|
||||
}
|
||||
@ -5306,8 +5311,6 @@ spdk_bdev_io_complete_nvme_status(struct spdk_bdev_io *bdev_io, uint32_t cdw0, i
|
||||
{
|
||||
if (sct == SPDK_NVME_SCT_GENERIC && sc == SPDK_NVME_SC_SUCCESS) {
|
||||
bdev_io->internal.status = SPDK_BDEV_IO_STATUS_SUCCESS;
|
||||
} else if (sct == SPDK_NVME_SCT_GENERIC && sc == SPDK_NVME_SC_ABORTED_BY_REQUEST) {
|
||||
bdev_io->internal.status = SPDK_BDEV_IO_STATUS_ABORTED;
|
||||
} else {
|
||||
bdev_io->internal.status = SPDK_BDEV_IO_STATUS_NVME_ERROR;
|
||||
}
|
||||
@ -5495,9 +5498,6 @@ bdev_destroy_cb(void *io_device)
|
||||
cb_fn = bdev->internal.unregister_cb;
|
||||
cb_arg = bdev->internal.unregister_ctx;
|
||||
|
||||
pthread_mutex_destroy(&bdev->internal.mutex);
|
||||
free(bdev->internal.qos);
|
||||
|
||||
rc = bdev->fn_table->destruct(bdev->ctxt);
|
||||
if (rc < 0) {
|
||||
SPDK_ERRLOG("destruct failed\n");
|
||||
@ -5507,6 +5507,17 @@ bdev_destroy_cb(void *io_device)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
bdev_fini(struct spdk_bdev *bdev)
|
||||
{
|
||||
pthread_mutex_destroy(&bdev->internal.mutex);
|
||||
|
||||
free(bdev->internal.qos);
|
||||
|
||||
spdk_io_device_unregister(__bdev_to_io_dev(bdev), bdev_destroy_cb);
|
||||
}
|
||||
|
||||
static void
|
||||
bdev_start_finished(void *arg)
|
||||
{
|
||||
@ -5539,6 +5550,13 @@ spdk_bdev_register(struct spdk_bdev *bdev)
|
||||
return rc;
|
||||
}
|
||||
|
||||
int
|
||||
spdk_vbdev_register(struct spdk_bdev *vbdev, struct spdk_bdev **base_bdevs, int base_bdev_count)
|
||||
{
|
||||
SPDK_ERRLOG("This function is deprecated. Use spdk_bdev_register() instead.\n");
|
||||
return spdk_bdev_register(vbdev);
|
||||
}
|
||||
|
||||
void
|
||||
spdk_bdev_destruct_done(struct spdk_bdev *bdev, int bdeverrno)
|
||||
{
|
||||
@ -5557,7 +5575,11 @@ _remove_notify(void *arg)
|
||||
|
||||
if (!desc->closed) {
|
||||
pthread_mutex_unlock(&desc->mutex);
|
||||
desc->callback.event_fn(SPDK_BDEV_EVENT_REMOVE, desc->bdev, desc->callback.ctx);
|
||||
if (desc->callback.open_with_ext) {
|
||||
desc->callback.event_fn(SPDK_BDEV_EVENT_REMOVE, desc->bdev, desc->callback.ctx);
|
||||
} else {
|
||||
desc->callback.remove_fn(desc->callback.ctx);
|
||||
}
|
||||
return;
|
||||
} else if (0 == desc->refs) {
|
||||
/* This descriptor was closed after this remove_notify message was sent.
|
||||
@ -5623,7 +5645,9 @@ spdk_bdev_unregister(struct spdk_bdev *bdev, spdk_bdev_unregister_cb cb_fn, void
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&g_bdev_mgr.mutex);
|
||||
pthread_mutex_lock(&bdev->internal.mutex);
|
||||
if (bdev->internal.status == SPDK_BDEV_STATUS_REMOVING) {
|
||||
pthread_mutex_unlock(&bdev->internal.mutex);
|
||||
pthread_mutex_unlock(&g_bdev_mgr.mutex);
|
||||
if (cb_fn) {
|
||||
cb_fn(cb_arg, -EBUSY);
|
||||
@ -5631,7 +5655,6 @@ spdk_bdev_unregister(struct spdk_bdev *bdev, spdk_bdev_unregister_cb cb_fn, void
|
||||
return;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&bdev->internal.mutex);
|
||||
bdev->internal.status = SPDK_BDEV_STATUS_REMOVING;
|
||||
bdev->internal.unregister_cb = cb_fn;
|
||||
bdev->internal.unregister_ctx = cb_arg;
|
||||
@ -5642,10 +5665,16 @@ spdk_bdev_unregister(struct spdk_bdev *bdev, spdk_bdev_unregister_cb cb_fn, void
|
||||
pthread_mutex_unlock(&g_bdev_mgr.mutex);
|
||||
|
||||
if (rc == 0) {
|
||||
spdk_io_device_unregister(__bdev_to_io_dev(bdev), bdev_destroy_cb);
|
||||
bdev_fini(bdev);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
bdev_dummy_event_cb(void *remove_ctx)
|
||||
{
|
||||
SPDK_DEBUGLOG(bdev, "Bdev remove event received with no remove callback specified");
|
||||
}
|
||||
|
||||
static int
|
||||
bdev_start_qos(struct spdk_bdev *bdev)
|
||||
{
|
||||
@ -5713,6 +5742,46 @@ bdev_open(struct spdk_bdev *bdev, bool write, struct spdk_bdev_desc *desc)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
spdk_bdev_open(struct spdk_bdev *bdev, bool write, spdk_bdev_remove_cb_t remove_cb,
|
||||
void *remove_ctx, struct spdk_bdev_desc **_desc)
|
||||
{
|
||||
struct spdk_bdev_desc *desc;
|
||||
int rc;
|
||||
|
||||
desc = calloc(1, sizeof(*desc));
|
||||
if (desc == NULL) {
|
||||
SPDK_ERRLOG("Failed to allocate memory for bdev descriptor\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (remove_cb == NULL) {
|
||||
remove_cb = bdev_dummy_event_cb;
|
||||
}
|
||||
|
||||
TAILQ_INIT(&desc->pending_media_events);
|
||||
TAILQ_INIT(&desc->free_media_events);
|
||||
|
||||
desc->callback.open_with_ext = false;
|
||||
desc->callback.remove_fn = remove_cb;
|
||||
desc->callback.ctx = remove_ctx;
|
||||
pthread_mutex_init(&desc->mutex, NULL);
|
||||
|
||||
pthread_mutex_lock(&g_bdev_mgr.mutex);
|
||||
|
||||
rc = bdev_open(bdev, write, desc);
|
||||
if (rc != 0) {
|
||||
bdev_desc_free(desc);
|
||||
desc = NULL;
|
||||
}
|
||||
|
||||
*_desc = desc;
|
||||
|
||||
pthread_mutex_unlock(&g_bdev_mgr.mutex);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int
|
||||
spdk_bdev_open_ext(const char *bdev_name, bool write, spdk_bdev_event_cb_t event_cb,
|
||||
void *event_ctx, struct spdk_bdev_desc **_desc)
|
||||
@ -5747,6 +5816,7 @@ spdk_bdev_open_ext(const char *bdev_name, bool write, spdk_bdev_event_cb_t event
|
||||
TAILQ_INIT(&desc->pending_media_events);
|
||||
TAILQ_INIT(&desc->free_media_events);
|
||||
|
||||
desc->callback.open_with_ext = true;
|
||||
desc->callback.event_fn = event_cb;
|
||||
desc->callback.ctx = event_ctx;
|
||||
pthread_mutex_init(&desc->mutex, NULL);
|
||||
@ -5827,7 +5897,7 @@ spdk_bdev_close(struct spdk_bdev_desc *desc)
|
||||
pthread_mutex_unlock(&bdev->internal.mutex);
|
||||
|
||||
if (rc == 0) {
|
||||
spdk_io_device_unregister(__bdev_to_io_dev(bdev), bdev_destroy_cb);
|
||||
bdev_fini(bdev);
|
||||
}
|
||||
} else {
|
||||
pthread_mutex_unlock(&bdev->internal.mutex);
|
||||
|
@ -44,30 +44,12 @@ spdk_bdev_get_zone_size(const struct spdk_bdev *bdev)
|
||||
return bdev->zone_size;
|
||||
}
|
||||
|
||||
uint64_t
|
||||
spdk_bdev_get_num_zones(const struct spdk_bdev *bdev)
|
||||
{
|
||||
return bdev->zone_size ? bdev->blockcnt / bdev->zone_size : 0;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
spdk_bdev_get_max_zone_append_size(const struct spdk_bdev *bdev)
|
||||
{
|
||||
return bdev->max_zone_append_size;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
spdk_bdev_get_max_open_zones(const struct spdk_bdev *bdev)
|
||||
{
|
||||
return bdev->max_open_zones;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
spdk_bdev_get_max_active_zones(const struct spdk_bdev *bdev)
|
||||
{
|
||||
return bdev->max_active_zones;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
spdk_bdev_get_optimal_open_zones(const struct spdk_bdev *bdev)
|
||||
{
|
||||
|
@ -491,6 +491,27 @@ spdk_bdev_part_base_construct_ext(const char *bdev_name,
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct spdk_bdev_part_base *
|
||||
spdk_bdev_part_base_construct(struct spdk_bdev *bdev,
|
||||
spdk_bdev_remove_cb_t remove_cb, struct spdk_bdev_module *module,
|
||||
struct spdk_bdev_fn_table *fn_table, struct bdev_part_tailq *tailq,
|
||||
spdk_bdev_part_base_free_fn free_fn, void *ctx,
|
||||
uint32_t channel_size, spdk_io_channel_create_cb ch_create_cb,
|
||||
spdk_io_channel_destroy_cb ch_destroy_cb)
|
||||
{
|
||||
struct spdk_bdev_part_base *base = NULL;
|
||||
int rc;
|
||||
|
||||
rc = spdk_bdev_part_base_construct_ext(spdk_bdev_get_name(bdev), remove_cb, module,
|
||||
fn_table, tailq, free_fn, ctx,
|
||||
channel_size, ch_create_cb, ch_destroy_cb, &base);
|
||||
if (rc == 0) {
|
||||
return base;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
spdk_bdev_part_construct(struct spdk_bdev_part *part, struct spdk_bdev_part_base *base,
|
||||
char *name, uint64_t offset_blocks, uint64_t num_blocks,
|
||||
|
@ -99,6 +99,7 @@
|
||||
spdk_bdev_register;
|
||||
spdk_bdev_unregister;
|
||||
spdk_bdev_destruct_done;
|
||||
spdk_vbdev_register;
|
||||
spdk_bdev_module_examine_done;
|
||||
spdk_bdev_module_init_done;
|
||||
spdk_bdev_module_finish_done;
|
||||
@ -131,6 +132,7 @@
|
||||
spdk_bdev_part_base_free;
|
||||
spdk_bdev_part_free;
|
||||
spdk_bdev_part_base_hotremove;
|
||||
spdk_bdev_part_base_construct;
|
||||
spdk_bdev_part_base_construct_ext;
|
||||
spdk_bdev_part_construct;
|
||||
spdk_bdev_part_submit_request;
|
||||
@ -143,10 +145,7 @@
|
||||
|
||||
# Public functions in bdev_zone.h
|
||||
spdk_bdev_get_zone_size;
|
||||
spdk_bdev_get_num_zones;
|
||||
spdk_bdev_get_max_zone_append_size;
|
||||
spdk_bdev_get_max_open_zones;
|
||||
spdk_bdev_get_max_active_zones;
|
||||
spdk_bdev_get_optimal_open_zones;
|
||||
spdk_bdev_get_zone_info;
|
||||
spdk_bdev_zone_management;
|
||||
|
@ -34,7 +34,7 @@
|
||||
SPDK_ROOT_DIR := $(abspath $(CURDIR)/../..)
|
||||
include $(SPDK_ROOT_DIR)/mk/spdk.common.mk
|
||||
|
||||
SO_VER := 5
|
||||
SO_VER := 4
|
||||
SO_MINOR := 0
|
||||
|
||||
C_SRCS = blobstore.c request.c zeroes.c blob_bs_dev.c
|
||||
|
@ -63,8 +63,8 @@ static int blob_get_xattr_value(struct spdk_blob *blob, const char *name,
|
||||
const void **value, size_t *value_len, bool internal);
|
||||
static int blob_remove_xattr(struct spdk_blob *blob, const char *name, bool internal);
|
||||
|
||||
static void blob_write_extent_page(struct spdk_blob *blob, uint32_t extent, uint64_t cluster_num,
|
||||
spdk_blob_op_complete cb_fn, void *cb_arg);
|
||||
static void blob_insert_extent(struct spdk_blob *blob, uint32_t extent, uint64_t cluster_num,
|
||||
spdk_blob_op_complete cb_fn, void *cb_arg);
|
||||
|
||||
static void
|
||||
blob_verify_md_op(struct spdk_blob *blob)
|
||||
@ -1642,7 +1642,6 @@ blob_persist_complete(spdk_bs_sequence_t *seq, struct spdk_blob_persist_ctx *ctx
|
||||
free(ctx);
|
||||
|
||||
if (next_persist != NULL) {
|
||||
blob->state = SPDK_BLOB_STATE_DIRTY;
|
||||
blob_persist_check_dirty(next_persist);
|
||||
}
|
||||
}
|
||||
@ -1766,7 +1765,7 @@ blob_persist_clear_clusters(spdk_bs_sequence_t *seq, struct spdk_blob_persist_ct
|
||||
spdk_bs_batch_t *batch;
|
||||
size_t i;
|
||||
uint64_t lba;
|
||||
uint32_t lba_count;
|
||||
uint64_t lba_count;
|
||||
|
||||
/* Clusters don't move around in blobs. The list shrinks or grows
|
||||
* at the end, but no changes ever occur in the middle of the list.
|
||||
@ -1779,9 +1778,10 @@ blob_persist_clear_clusters(spdk_bs_sequence_t *seq, struct spdk_blob_persist_ct
|
||||
lba_count = 0;
|
||||
for (i = blob->active.num_clusters; i < blob->active.cluster_array_size; i++) {
|
||||
uint64_t next_lba = blob->active.clusters[i];
|
||||
uint32_t next_lba_count = bs_cluster_to_lba(bs, 1);
|
||||
uint64_t next_lba_count = bs_cluster_to_lba(bs, 1);
|
||||
|
||||
if (next_lba > 0 && (lba + lba_count) == next_lba) {
|
||||
if (next_lba > 0 && (lba + lba_count) == next_lba &&
|
||||
(lba_count + next_lba_count <= UINT32_MAX)) {
|
||||
/* This cluster is contiguous with the previous one. */
|
||||
lba_count += next_lba_count;
|
||||
continue;
|
||||
@ -2132,31 +2132,36 @@ blob_persist_write_extent_pages(spdk_bs_sequence_t *seq, void *cb_arg, int bserr
|
||||
return;
|
||||
}
|
||||
|
||||
/* Only write out Extent Pages when blob was resized. */
|
||||
for (i = ctx->next_extent_page; i < blob->active.extent_pages_array_size; i++) {
|
||||
/* Only write out changed extent pages */
|
||||
for (i = ctx->next_extent_page; i < blob->active.num_extent_pages; i++) {
|
||||
extent_page_id = blob->active.extent_pages[i];
|
||||
if (extent_page_id == 0) {
|
||||
/* No Extent Page to persist */
|
||||
assert(spdk_blob_is_thin_provisioned(blob));
|
||||
continue;
|
||||
}
|
||||
assert(spdk_bit_array_get(blob->bs->used_md_pages, extent_page_id));
|
||||
ctx->next_extent_page = i + 1;
|
||||
rc = blob_serialize_add_page(ctx->blob, &ctx->extent_page, &page_count, &ctx->extent_page);
|
||||
if (rc < 0) {
|
||||
blob_persist_complete(seq, ctx, rc);
|
||||
/* Writing out new extent page for the first time. Either active extent pages is larger
|
||||
* than clean extent pages or there was no extent page assigned due to thin provisioning. */
|
||||
if (i >= blob->clean.extent_pages_array_size || blob->clean.extent_pages[i] == 0) {
|
||||
blob->state = SPDK_BLOB_STATE_DIRTY;
|
||||
assert(spdk_bit_array_get(blob->bs->used_md_pages, extent_page_id));
|
||||
ctx->next_extent_page = i + 1;
|
||||
rc = blob_serialize_add_page(ctx->blob, &ctx->extent_page, &page_count, &ctx->extent_page);
|
||||
if (rc < 0) {
|
||||
blob_persist_complete(seq, ctx, rc);
|
||||
return;
|
||||
}
|
||||
|
||||
blob_serialize_extent_page(blob, i * SPDK_EXTENTS_PER_EP, ctx->extent_page);
|
||||
|
||||
ctx->extent_page->crc = blob_md_page_calc_crc(ctx->extent_page);
|
||||
|
||||
bs_sequence_write_dev(seq, ctx->extent_page, bs_md_page_to_lba(blob->bs, extent_page_id),
|
||||
bs_byte_to_lba(blob->bs, SPDK_BS_PAGE_SIZE),
|
||||
blob_persist_write_extent_pages, ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
blob->state = SPDK_BLOB_STATE_DIRTY;
|
||||
blob_serialize_extent_page(blob, i * SPDK_EXTENTS_PER_EP, ctx->extent_page);
|
||||
|
||||
ctx->extent_page->crc = blob_md_page_calc_crc(ctx->extent_page);
|
||||
|
||||
bs_sequence_write_dev(seq, ctx->extent_page, bs_md_page_to_lba(blob->bs, extent_page_id),
|
||||
bs_byte_to_lba(blob->bs, SPDK_BS_PAGE_SIZE),
|
||||
blob_persist_write_extent_pages, ctx);
|
||||
return;
|
||||
assert(blob->clean.extent_pages[i] != 0);
|
||||
}
|
||||
|
||||
blob_persist_generate_new_md(ctx);
|
||||
@ -2178,20 +2183,6 @@ blob_persist_start(struct spdk_blob_persist_ctx *ctx)
|
||||
|
||||
}
|
||||
|
||||
if (blob->clean.num_clusters < blob->active.num_clusters) {
|
||||
/* Blob was resized up */
|
||||
assert(blob->clean.num_extent_pages <= blob->active.num_extent_pages);
|
||||
ctx->next_extent_page = spdk_max(1, blob->clean.num_extent_pages) - 1;
|
||||
} else if (blob->active.num_clusters < blob->active.cluster_array_size) {
|
||||
/* Blob was resized down */
|
||||
assert(blob->clean.num_extent_pages >= blob->active.num_extent_pages);
|
||||
ctx->next_extent_page = spdk_max(1, blob->active.num_extent_pages) - 1;
|
||||
} else {
|
||||
/* No change in size occured */
|
||||
blob_persist_generate_new_md(ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
blob_persist_write_extent_pages(seq, ctx, 0);
|
||||
}
|
||||
|
||||
@ -2278,6 +2269,7 @@ blob_persist(spdk_bs_sequence_t *seq, struct spdk_blob *blob,
|
||||
ctx->seq = seq;
|
||||
ctx->cb_fn = cb_fn;
|
||||
ctx->cb_arg = cb_arg;
|
||||
ctx->next_extent_page = 0;
|
||||
|
||||
/* Multiple blob persists can affect one another, via blob->state or
|
||||
* blob mutable data changes. To prevent it, queue up the persists. */
|
||||
@ -4349,6 +4341,12 @@ bs_load_super_cpl(spdk_bs_sequence_t *seq, void *cb_arg, int bserrno)
|
||||
}
|
||||
ctx->bs->md_start = ctx->super->md_start;
|
||||
ctx->bs->md_len = ctx->super->md_len;
|
||||
rc = spdk_bit_array_resize(&ctx->bs->open_blobids, ctx->bs->md_len);
|
||||
if (rc < 0) {
|
||||
bs_load_ctx_fail(ctx, -ENOMEM);
|
||||
return;
|
||||
}
|
||||
|
||||
ctx->bs->total_data_clusters = ctx->bs->total_clusters - spdk_divide_round_up(
|
||||
ctx->bs->md_start + ctx->bs->md_len, ctx->bs->pages_per_cluster);
|
||||
ctx->bs->super_blob = ctx->super->super_blob;
|
||||
@ -4920,7 +4918,7 @@ spdk_bs_init(struct spdk_bs_dev *dev, struct spdk_bs_opts *o,
|
||||
|
||||
lba = num_md_lba;
|
||||
while (lba < ctx->bs->dev->blockcnt) {
|
||||
lba_count = spdk_min(UINT32_MAX, ctx->bs->dev->blockcnt - lba);
|
||||
lba_count = spdk_min(UINT32_MAX - 127, ctx->bs->dev->blockcnt - lba);
|
||||
switch (opts.clear_method) {
|
||||
case BS_CLEAR_WITH_UNMAP:
|
||||
/* Trim data clusters */
|
||||
@ -6355,7 +6353,6 @@ struct delete_snapshot_ctx {
|
||||
spdk_blob_op_with_handle_complete cb_fn;
|
||||
void *cb_arg;
|
||||
int bserrno;
|
||||
uint32_t next_extent_page;
|
||||
};
|
||||
|
||||
static void
|
||||
@ -6525,62 +6522,6 @@ delete_snapshot_sync_clone_cpl(void *cb_arg, int bserrno)
|
||||
spdk_blob_sync_md(ctx->snapshot, delete_snapshot_sync_snapshot_cpl, ctx);
|
||||
}
|
||||
|
||||
static void
|
||||
delete_snapshot_update_extent_pages_cpl(struct delete_snapshot_ctx *ctx)
|
||||
{
|
||||
/* Delete old backing bs_dev from clone (related to snapshot that will be removed) */
|
||||
ctx->clone->back_bs_dev->destroy(ctx->clone->back_bs_dev);
|
||||
|
||||
/* Set/remove snapshot xattr and switch parent ID and backing bs_dev on clone... */
|
||||
if (ctx->parent_snapshot_entry != NULL) {
|
||||
/* ...to parent snapshot */
|
||||
ctx->clone->parent_id = ctx->parent_snapshot_entry->id;
|
||||
ctx->clone->back_bs_dev = ctx->snapshot->back_bs_dev;
|
||||
blob_set_xattr(ctx->clone, BLOB_SNAPSHOT, &ctx->parent_snapshot_entry->id,
|
||||
sizeof(spdk_blob_id),
|
||||
true);
|
||||
} else {
|
||||
/* ...to blobid invalid and zeroes dev */
|
||||
ctx->clone->parent_id = SPDK_BLOBID_INVALID;
|
||||
ctx->clone->back_bs_dev = bs_create_zeroes_dev();
|
||||
blob_remove_xattr(ctx->clone, BLOB_SNAPSHOT, true);
|
||||
}
|
||||
|
||||
spdk_blob_sync_md(ctx->clone, delete_snapshot_sync_clone_cpl, ctx);
|
||||
}
|
||||
|
||||
static void
|
||||
delete_snapshot_update_extent_pages(void *cb_arg, int bserrno)
|
||||
{
|
||||
struct delete_snapshot_ctx *ctx = cb_arg;
|
||||
uint32_t *extent_page;
|
||||
uint64_t i;
|
||||
|
||||
for (i = ctx->next_extent_page; i < ctx->snapshot->active.num_extent_pages &&
|
||||
i < ctx->clone->active.num_extent_pages; i++) {
|
||||
if (ctx->snapshot->active.extent_pages[i] == 0) {
|
||||
/* No extent page to use from snapshot */
|
||||
continue;
|
||||
}
|
||||
|
||||
extent_page = &ctx->clone->active.extent_pages[i];
|
||||
if (*extent_page == 0) {
|
||||
/* Copy extent page from snapshot when clone did not have a matching one */
|
||||
*extent_page = ctx->snapshot->active.extent_pages[i];
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Clone and snapshot both contain partialy filled matching extent pages.
|
||||
* Update the clone extent page in place with cluster map containing the mix of both. */
|
||||
ctx->next_extent_page = i + 1;
|
||||
|
||||
blob_write_extent_page(ctx->clone, *extent_page, i * SPDK_EXTENTS_PER_EP,
|
||||
delete_snapshot_update_extent_pages, ctx);
|
||||
return;
|
||||
}
|
||||
delete_snapshot_update_extent_pages_cpl(ctx);
|
||||
}
|
||||
|
||||
static void
|
||||
delete_snapshot_sync_snapshot_xattr_cpl(void *cb_arg, int bserrno)
|
||||
{
|
||||
@ -6604,8 +6545,32 @@ delete_snapshot_sync_snapshot_xattr_cpl(void *cb_arg, int bserrno)
|
||||
ctx->clone->active.clusters[i] = ctx->snapshot->active.clusters[i];
|
||||
}
|
||||
}
|
||||
ctx->next_extent_page = 0;
|
||||
delete_snapshot_update_extent_pages(ctx, 0);
|
||||
for (i = 0; i < ctx->snapshot->active.num_extent_pages &&
|
||||
i < ctx->clone->active.num_extent_pages; i++) {
|
||||
if (ctx->clone->active.extent_pages[i] == 0) {
|
||||
ctx->clone->active.extent_pages[i] = ctx->snapshot->active.extent_pages[i];
|
||||
}
|
||||
}
|
||||
|
||||
/* Delete old backing bs_dev from clone (related to snapshot that will be removed) */
|
||||
ctx->clone->back_bs_dev->destroy(ctx->clone->back_bs_dev);
|
||||
|
||||
/* Set/remove snapshot xattr and switch parent ID and backing bs_dev on clone... */
|
||||
if (ctx->parent_snapshot_entry != NULL) {
|
||||
/* ...to parent snapshot */
|
||||
ctx->clone->parent_id = ctx->parent_snapshot_entry->id;
|
||||
ctx->clone->back_bs_dev = ctx->snapshot->back_bs_dev;
|
||||
blob_set_xattr(ctx->clone, BLOB_SNAPSHOT, &ctx->parent_snapshot_entry->id,
|
||||
sizeof(spdk_blob_id),
|
||||
true);
|
||||
} else {
|
||||
/* ...to blobid invalid and zeroes dev */
|
||||
ctx->clone->parent_id = SPDK_BLOBID_INVALID;
|
||||
ctx->clone->back_bs_dev = bs_create_zeroes_dev();
|
||||
blob_remove_xattr(ctx->clone, BLOB_SNAPSHOT, true);
|
||||
}
|
||||
|
||||
spdk_blob_sync_md(ctx->clone, delete_snapshot_sync_clone_cpl, ctx);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -7075,18 +7040,6 @@ blob_insert_cluster_msg_cb(void *arg, int bserrno)
|
||||
spdk_thread_send_msg(ctx->thread, blob_insert_cluster_msg_cpl, ctx);
|
||||
}
|
||||
|
||||
static void
|
||||
blob_insert_new_ep_cb(void *arg, int bserrno)
|
||||
{
|
||||
struct spdk_blob_insert_cluster_ctx *ctx = arg;
|
||||
uint32_t *extent_page;
|
||||
|
||||
extent_page = bs_cluster_to_extent_page(ctx->blob, ctx->cluster_num);
|
||||
*extent_page = ctx->extent_page;
|
||||
ctx->blob->state = SPDK_BLOB_STATE_DIRTY;
|
||||
blob_sync_md(ctx->blob, blob_insert_cluster_msg_cb, ctx);
|
||||
}
|
||||
|
||||
static void
|
||||
blob_persist_extent_page_cpl(spdk_bs_sequence_t *seq, void *cb_arg, int bserrno)
|
||||
{
|
||||
@ -7097,8 +7050,8 @@ blob_persist_extent_page_cpl(spdk_bs_sequence_t *seq, void *cb_arg, int bserrno)
|
||||
}
|
||||
|
||||
static void
|
||||
blob_write_extent_page(struct spdk_blob *blob, uint32_t extent, uint64_t cluster_num,
|
||||
spdk_blob_op_complete cb_fn, void *cb_arg)
|
||||
blob_insert_extent(struct spdk_blob *blob, uint32_t extent, uint64_t cluster_num,
|
||||
spdk_blob_op_complete cb_fn, void *cb_arg)
|
||||
{
|
||||
spdk_bs_sequence_t *seq;
|
||||
struct spdk_bs_cpl cpl;
|
||||
@ -7154,11 +7107,13 @@ blob_insert_cluster_msg(void *arg)
|
||||
extent_page = bs_cluster_to_extent_page(ctx->blob, ctx->cluster_num);
|
||||
if (*extent_page == 0) {
|
||||
/* Extent page requires allocation.
|
||||
* It was already claimed in the used_md_pages map and placed in ctx. */
|
||||
* It was already claimed in the used_md_pages map and placed in ctx.
|
||||
* Blob persist will take care of writing out new extent page on disk. */
|
||||
assert(ctx->extent_page != 0);
|
||||
assert(spdk_bit_array_get(ctx->blob->bs->used_md_pages, ctx->extent_page) == true);
|
||||
blob_write_extent_page(ctx->blob, ctx->extent_page, ctx->cluster_num,
|
||||
blob_insert_new_ep_cb, ctx);
|
||||
*extent_page = ctx->extent_page;
|
||||
ctx->blob->state = SPDK_BLOB_STATE_DIRTY;
|
||||
blob_sync_md(ctx->blob, blob_insert_cluster_msg_cb, ctx);
|
||||
} else {
|
||||
/* It is possible for original thread to allocate extent page for
|
||||
* different cluster in the same extent page. In such case proceed with
|
||||
@ -7170,8 +7125,8 @@ blob_insert_cluster_msg(void *arg)
|
||||
}
|
||||
/* Extent page already allocated.
|
||||
* Every cluster allocation, requires just an update of single extent page. */
|
||||
blob_write_extent_page(ctx->blob, *extent_page, ctx->cluster_num,
|
||||
blob_insert_cluster_msg_cb, ctx);
|
||||
blob_insert_extent(ctx->blob, *extent_page, ctx->cluster_num,
|
||||
blob_insert_cluster_msg_cb, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -34,7 +34,7 @@
|
||||
SPDK_ROOT_DIR := $(abspath $(CURDIR)/../..)
|
||||
include $(SPDK_ROOT_DIR)/mk/spdk.common.mk
|
||||
|
||||
SO_VER := 4
|
||||
SO_VER := 3
|
||||
SO_MINOR := 0
|
||||
|
||||
C_SRCS = blobfs.c tree.c
|
||||
|
@ -34,8 +34,8 @@
|
||||
SPDK_ROOT_DIR := $(abspath $(CURDIR)/../..)
|
||||
include $(SPDK_ROOT_DIR)/mk/spdk.common.mk
|
||||
|
||||
SO_VER := 3
|
||||
SO_MINOR := 0
|
||||
SO_VER := 2
|
||||
SO_MINOR := 1
|
||||
|
||||
C_SRCS = conf.c
|
||||
LIBNAME = conf
|
||||
|
@ -34,13 +34,12 @@
|
||||
SPDK_ROOT_DIR := $(abspath $(CURDIR)/../..)
|
||||
include $(SPDK_ROOT_DIR)/mk/spdk.common.mk
|
||||
|
||||
SO_VER := 6
|
||||
SO_MINOR := 0
|
||||
SO_VER := 5
|
||||
SO_MINOR := 1
|
||||
|
||||
CFLAGS += $(ENV_CFLAGS)
|
||||
C_SRCS = env.c memory.c pci.c init.c threads.c
|
||||
C_SRCS += pci_ioat.c pci_virtio.c pci_vmd.c pci_idxd.c
|
||||
C_SRCS += pci_event.c sigbus_handler.c
|
||||
LIBNAME = env_dpdk
|
||||
|
||||
SPDK_MAP_FILE = $(abspath $(CURDIR)/spdk_env_dpdk.map)
|
||||
@ -49,18 +48,9 @@ include $(SPDK_ROOT_DIR)/mk/spdk.lib.mk
|
||||
|
||||
LIBDPDK_PKGCONFIG = $(call pkgconfig_filename,spdk_dpdklibs)
|
||||
|
||||
$(LIBDPDK_PKGCONFIG): $(PKGCONFIG) $(PKGCONFIG_INST)
|
||||
$(LIBDPDK_PKGCONFIG): $(PKGCONFIG)
|
||||
$(Q)$(SPDK_ROOT_DIR)/scripts/pc_libs.sh \
|
||||
"-L$(DPDK_ABS_DIR)/lib $(DPDK_LIB_LIST:%=-l%)" "" DPDK spdk_dpdklibs > $@
|
||||
$(Q)sed -i.bak '5s,.*,Requires: $(DEPDIRS-$(LIBNAME):%=spdk_%) spdk_dpdklibs,' $(PKGCONFIG) ; rm $(PKGCONFIG).bak
|
||||
$(Q)sed -i.bak '5s,.*,Requires: $(DEPDIRS-$(LIBNAME):%=spdk_%) spdk_dpdklibs,' $(PKGCONFIG_INST) ; rm $(PKGCONFIG_INST).bak
|
||||
$(Q)echo Requires: spdk_dpdklibs >> $(PKGCONFIG)
|
||||
|
||||
_install_dpdklibs: $(LIBDPDK_PKGCONFIG)
|
||||
@$(call pkgconfig_install,$(LIBDPDK_PKGCONFIG))
|
||||
|
||||
_uninstall_dpdklibs: $(LIBDPDK_PKGCONFIG)
|
||||
@$(call pkgconfig_uninstall,$(LIBDPDK_PKGCONFIG))
|
||||
|
||||
all: $(LIBDPDK_PKGCONFIG)
|
||||
install: _install_dpdklibs
|
||||
uninstall: _uninstall_dpdklibs
|
||||
all : $(LIBDPDK_PKGCONFIG)
|
||||
|
@ -48,7 +48,7 @@ DPDK_INC_DIR := $(DPDK_ABS_DIR)/include/dpdk
|
||||
endif
|
||||
DPDK_INC := -I$(DPDK_INC_DIR)
|
||||
|
||||
DPDK_LIB_LIST = rte_eal rte_mempool rte_ring rte_mbuf rte_bus_pci rte_pci rte_mempool_ring
|
||||
DPDK_LIB_LIST = rte_eal rte_mempool rte_ring rte_mbuf rte_pci rte_bus_pci rte_mempool_ring
|
||||
|
||||
ifeq ($(OS),Linux)
|
||||
DPDK_LIB_LIST += rte_power rte_ethdev rte_net
|
||||
|
@ -832,8 +832,10 @@ vtophys_iommu_map_dma(uint64_t vaddr, uint64_t iova, uint64_t size)
|
||||
|
||||
ret = ioctl(g_vfio.fd, VFIO_IOMMU_MAP_DMA, &dma_map->map);
|
||||
if (ret) {
|
||||
/* There are cases the vfio container doesn't have IOMMU group, it's safe for this case */
|
||||
SPDK_NOTICELOG("Cannot set up DMA mapping, error %d, ignored\n", errno);
|
||||
DEBUG_PRINT("Cannot set up DMA mapping, error %d\n", errno);
|
||||
pthread_mutex_unlock(&g_vfio.mutex);
|
||||
free(dma_map);
|
||||
return ret;
|
||||
}
|
||||
|
||||
out_insert:
|
||||
@ -890,7 +892,9 @@ vtophys_iommu_unmap_dma(uint64_t iova, uint64_t size)
|
||||
unmap.size = dma_map->map.size;
|
||||
ret = ioctl(g_vfio.fd, VFIO_IOMMU_UNMAP_DMA, &unmap);
|
||||
if (ret) {
|
||||
SPDK_NOTICELOG("Cannot clear DMA mapping, error %d, ignored\n", errno);
|
||||
DEBUG_PRINT("Cannot clear DMA mapping, error %d\n", errno);
|
||||
pthread_mutex_unlock(&g_vfio.mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
out_remove:
|
||||
@ -1464,7 +1468,6 @@ vtophys_init(void)
|
||||
g_vtophys_map = spdk_mem_map_alloc(SPDK_VTOPHYS_ERROR, &vtophys_map_ops, NULL);
|
||||
if (g_vtophys_map == NULL) {
|
||||
DEBUG_PRINT("vtophys map allocation failed\n");
|
||||
spdk_mem_map_free(&g_phys_ref_map);
|
||||
return -ENOMEM;
|
||||
}
|
||||
return 0;
|
||||
|
@ -269,17 +269,8 @@ pci_device_rte_dev_event(const char *device_name,
|
||||
|
||||
if (dev != NULL && can_detach) {
|
||||
/* if device is not attached we can remove it right away.
|
||||
* Otherwise it will be removed at detach.
|
||||
*
|
||||
* Because the user's callback is invoked in eal interrupt
|
||||
* callback, the interrupt callback need to be finished before
|
||||
* it can be unregistered when detaching device. So finish
|
||||
* callback soon and use a deferred removal to detach device
|
||||
* is need. It is a workaround, once the device detaching be
|
||||
* moved into the eal in the future, the deferred removal could
|
||||
* be deleted.
|
||||
*/
|
||||
rte_eal_alarm_set(1, detach_rte_cb, dev->dev_handle);
|
||||
* Otherwise it will be removed at detach. */
|
||||
remove_rte_dev(dev->dev_handle);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -1044,7 +1035,9 @@ spdk_pci_device_unclaim(struct spdk_pci_device *dev)
|
||||
dev->internal.claim_fd = -1;
|
||||
unlink(dev_name);
|
||||
}
|
||||
#else /* !__linux__ */
|
||||
#endif /* __linux__ */
|
||||
|
||||
#ifdef __FreeBSD__
|
||||
int
|
||||
spdk_pci_device_claim(struct spdk_pci_device *dev)
|
||||
{
|
||||
@ -1057,7 +1050,7 @@ spdk_pci_device_unclaim(struct spdk_pci_device *dev)
|
||||
{
|
||||
/* TODO */
|
||||
}
|
||||
#endif /* __linux__ */
|
||||
#endif /* __FreeBSD__ */
|
||||
|
||||
int
|
||||
spdk_pci_addr_parse(struct spdk_pci_addr *addr, const char *bdf)
|
||||
@ -1135,37 +1128,3 @@ spdk_pci_device_get_type(const struct spdk_pci_device *dev)
|
||||
{
|
||||
return dev->type;
|
||||
}
|
||||
|
||||
int
|
||||
spdk_pci_device_allow(struct spdk_pci_addr *pci_addr)
|
||||
{
|
||||
struct rte_devargs *da;
|
||||
char devargs_str[128];
|
||||
|
||||
da = calloc(1, sizeof(*da));
|
||||
if (da == NULL) {
|
||||
SPDK_ERRLOG("could not allocate rte_devargs\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
snprintf(devargs_str, sizeof(devargs_str), "pci:%04x:%02x:%02x.%x",
|
||||
pci_addr->domain, pci_addr->bus, pci_addr->dev, pci_addr->func);
|
||||
if (rte_devargs_parse(da, devargs_str) != 0) {
|
||||
SPDK_ERRLOG("rte_devargs_parse() failed on '%s'\n", devargs_str);
|
||||
free(da);
|
||||
return -EINVAL;
|
||||
}
|
||||
da->policy = RTE_DEV_ALLOWED;
|
||||
/* Note: if a devargs already exists for this device address, it just gets
|
||||
* overridden. So we do not need to check if the devargs already exists.
|
||||
* DPDK will take care of memory management for the devargs structure after
|
||||
* it has been inserted, so there's nothing SPDK needs to track.
|
||||
*/
|
||||
if (rte_devargs_insert(&da) != 0) {
|
||||
SPDK_ERRLOG("rte_devargs_insert() failed on '%s'\n", devargs_str);
|
||||
free(da);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1,137 +0,0 @@
|
||||
/*-
|
||||
* BSD LICENSE
|
||||
*
|
||||
* Copyright (c) Intel Corporation.
|
||||
* 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/env.h"
|
||||
#include "spdk/log.h"
|
||||
|
||||
struct sigbus_handler {
|
||||
spdk_pci_error_handler func;
|
||||
void *ctx;
|
||||
|
||||
TAILQ_ENTRY(sigbus_handler) tailq;
|
||||
};
|
||||
|
||||
static pthread_mutex_t g_sighandler_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
static TAILQ_HEAD(, sigbus_handler) g_sigbus_handler =
|
||||
TAILQ_HEAD_INITIALIZER(g_sigbus_handler);
|
||||
|
||||
static void
|
||||
sigbus_fault_sighandler(int signum, siginfo_t *info, void *ctx)
|
||||
{
|
||||
struct sigbus_handler *sigbus_handler;
|
||||
|
||||
pthread_mutex_lock(&g_sighandler_mutex);
|
||||
TAILQ_FOREACH(sigbus_handler, &g_sigbus_handler, tailq) {
|
||||
sigbus_handler->func(info, sigbus_handler->ctx);
|
||||
}
|
||||
pthread_mutex_unlock(&g_sighandler_mutex);
|
||||
}
|
||||
|
||||
__attribute__((constructor)) static void
|
||||
device_set_signal(void)
|
||||
{
|
||||
struct sigaction sa;
|
||||
|
||||
sa.sa_sigaction = sigbus_fault_sighandler;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_flags = SA_SIGINFO;
|
||||
sigaction(SIGBUS, &sa, NULL);
|
||||
}
|
||||
|
||||
__attribute__((destructor)) static void
|
||||
device_destroy_signal(void)
|
||||
{
|
||||
struct sigbus_handler *sigbus_handler, *tmp;
|
||||
|
||||
TAILQ_FOREACH_SAFE(sigbus_handler, &g_sigbus_handler, tailq, tmp) {
|
||||
free(sigbus_handler);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
spdk_pci_register_error_handler(spdk_pci_error_handler sighandler, void *ctx)
|
||||
{
|
||||
struct sigbus_handler *sigbus_handler;
|
||||
|
||||
if (!sighandler) {
|
||||
SPDK_ERRLOG("Error handler is NULL\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&g_sighandler_mutex);
|
||||
TAILQ_FOREACH(sigbus_handler, &g_sigbus_handler, tailq) {
|
||||
if (sigbus_handler->func == sighandler) {
|
||||
pthread_mutex_unlock(&g_sighandler_mutex);
|
||||
SPDK_ERRLOG("Error handler has been registered\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&g_sighandler_mutex);
|
||||
|
||||
sigbus_handler = calloc(1, sizeof(*sigbus_handler));
|
||||
if (!sigbus_handler) {
|
||||
SPDK_ERRLOG("Failed to allocate sigbus handler\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
sigbus_handler->func = sighandler;
|
||||
sigbus_handler->ctx = ctx;
|
||||
|
||||
pthread_mutex_lock(&g_sighandler_mutex);
|
||||
TAILQ_INSERT_TAIL(&g_sigbus_handler, sigbus_handler, tailq);
|
||||
pthread_mutex_unlock(&g_sighandler_mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
spdk_pci_unregister_error_handler(spdk_pci_error_handler sighandler)
|
||||
{
|
||||
struct sigbus_handler *sigbus_handler;
|
||||
|
||||
if (!sighandler) {
|
||||
return;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&g_sighandler_mutex);
|
||||
TAILQ_FOREACH(sigbus_handler, &g_sigbus_handler, tailq) {
|
||||
if (sigbus_handler->func == sighandler) {
|
||||
TAILQ_REMOVE(&g_sigbus_handler, sigbus_handler, tailq);
|
||||
free(sigbus_handler);
|
||||
pthread_mutex_unlock(&g_sighandler_mutex);
|
||||
return;
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&g_sighandler_mutex);
|
||||
}
|
@ -79,7 +79,6 @@
|
||||
spdk_pci_device_unclaim;
|
||||
spdk_pci_device_detach;
|
||||
spdk_pci_device_attach;
|
||||
spdk_pci_device_allow;
|
||||
spdk_pci_device_cfg_read;
|
||||
spdk_pci_device_cfg_write;
|
||||
spdk_pci_device_cfg_read8;
|
||||
@ -105,10 +104,6 @@
|
||||
spdk_mem_register;
|
||||
spdk_mem_unregister;
|
||||
spdk_mem_get_fd_and_offset;
|
||||
spdk_pci_event_listen;
|
||||
spdk_pci_get_event;
|
||||
spdk_pci_register_error_handler;
|
||||
spdk_pci_unregister_error_handler;
|
||||
|
||||
# Public functions in env_dpdk.h
|
||||
spdk_env_dpdk_post_init;
|
||||
|
@ -34,7 +34,7 @@
|
||||
SPDK_ROOT_DIR := $(abspath $(CURDIR)/../..)
|
||||
include $(SPDK_ROOT_DIR)/mk/spdk.common.mk
|
||||
|
||||
SO_VER := 8
|
||||
SO_VER := 7
|
||||
SO_MINOR := 0
|
||||
|
||||
CFLAGS += $(ENV_CFLAGS)
|
||||
|
@ -278,12 +278,7 @@ app_start_rpc(int rc, void *arg1)
|
||||
return;
|
||||
}
|
||||
|
||||
rc = spdk_rpc_initialize(g_spdk_app.rpc_addr);
|
||||
if (rc) {
|
||||
spdk_app_stop(rc);
|
||||
return;
|
||||
}
|
||||
|
||||
spdk_rpc_initialize(g_spdk_app.rpc_addr);
|
||||
if (!g_delay_subsystem_init) {
|
||||
spdk_rpc_set_state(SPDK_RPC_RUNTIME);
|
||||
app_start_application();
|
||||
@ -398,8 +393,6 @@ app_setup_trace(struct spdk_app_opts *opts)
|
||||
static void
|
||||
bootstrap_fn(void *arg1)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (g_spdk_app.json_config_file) {
|
||||
g_delay_subsystem_init = false;
|
||||
spdk_app_json_config_load(g_spdk_app.json_config_file, g_spdk_app.rpc_addr, app_start_rpc,
|
||||
@ -408,11 +401,7 @@ bootstrap_fn(void *arg1)
|
||||
if (!g_delay_subsystem_init) {
|
||||
spdk_subsystem_init(app_start_rpc, NULL);
|
||||
} else {
|
||||
rc = spdk_rpc_initialize(g_spdk_app.rpc_addr);
|
||||
if (rc) {
|
||||
spdk_app_stop(rc);
|
||||
return;
|
||||
}
|
||||
spdk_rpc_initialize(g_spdk_app.rpc_addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -429,6 +418,7 @@ app_copy_opts(struct spdk_app_opts *opts, struct spdk_app_opts *opts_user, size_
|
||||
} \
|
||||
|
||||
SET_FIELD(name);
|
||||
SET_FIELD(config_file);
|
||||
SET_FIELD(json_config_file);
|
||||
SET_FIELD(json_config_ignore_errors);
|
||||
SET_FIELD(rpc_addr);
|
||||
@ -449,6 +439,7 @@ app_copy_opts(struct spdk_app_opts *opts, struct spdk_app_opts *opts_user, size_
|
||||
SET_FIELD(pci_blocked);
|
||||
SET_FIELD(pci_allowed);
|
||||
SET_FIELD(iova_mode);
|
||||
SET_FIELD(max_delay_us);
|
||||
SET_FIELD(delay_subsystem_init);
|
||||
SET_FIELD(num_entries);
|
||||
SET_FIELD(env_context);
|
||||
@ -457,7 +448,7 @@ app_copy_opts(struct spdk_app_opts *opts, struct spdk_app_opts *opts_user, size_
|
||||
|
||||
/* You should not remove this statement, but need to update the assert statement
|
||||
* if you add a new field, and also add a corresponding SET_FIELD statement */
|
||||
SPDK_STATIC_ASSERT(sizeof(struct spdk_app_opts) == 184, "Incorrect size");
|
||||
SPDK_STATIC_ASSERT(sizeof(struct spdk_app_opts) == 200, "Incorrect size");
|
||||
|
||||
#undef SET_FIELD
|
||||
}
|
||||
@ -485,6 +476,18 @@ spdk_app_start(struct spdk_app_opts *opts_user, spdk_msg_fn start_fn,
|
||||
|
||||
app_copy_opts(opts, opts_user, opts_user->opts_size);
|
||||
|
||||
if (opts->config_file) {
|
||||
SPDK_ERRLOG("opts->config_file is deprecated. Use opts->json_config_file instead.\n");
|
||||
/* For now we will just treat config_file as json_config_file. But if both were
|
||||
* specified we will return an error here.
|
||||
*/
|
||||
if (opts->json_config_file) {
|
||||
SPDK_ERRLOG("Setting both opts->config_file and opts->json_config_file not allowed.\n");
|
||||
return 1;
|
||||
}
|
||||
opts->json_config_file = opts->config_file;
|
||||
}
|
||||
|
||||
if (!start_fn) {
|
||||
SPDK_ERRLOG("start_fn should not be NULL\n");
|
||||
return 1;
|
||||
@ -704,6 +707,11 @@ spdk_app_parse_args(int argc, char **argv, struct spdk_app_opts *opts,
|
||||
|
||||
memcpy(&g_default_opts, opts, sizeof(g_default_opts));
|
||||
|
||||
if (opts->config_file && access(opts->config_file, R_OK) != 0) {
|
||||
SPDK_WARNLOG("Can't read JSON configuration file '%s'\n", opts->config_file);
|
||||
opts->config_file = NULL;
|
||||
}
|
||||
|
||||
if (opts->json_config_file && access(opts->json_config_file, R_OK) != 0) {
|
||||
SPDK_WARNLOG("Can't read JSON configuration file '%s'\n", opts->json_config_file);
|
||||
opts->json_config_file = NULL;
|
||||
|
@ -365,12 +365,9 @@ _rpc_framework_get_reactors(void *arg1, void *arg2)
|
||||
{
|
||||
struct rpc_get_stats_ctx *ctx = arg1;
|
||||
uint32_t current_core;
|
||||
uint32_t curr_core_freq;
|
||||
struct spdk_reactor *reactor;
|
||||
struct spdk_lw_thread *lw_thread;
|
||||
struct spdk_thread *thread;
|
||||
struct spdk_governor *governor;
|
||||
struct spdk_governor_capabilities capabilities;
|
||||
|
||||
current_core = spdk_env_get_current_core();
|
||||
reactor = spdk_reactor_get(current_core);
|
||||
@ -381,16 +378,6 @@ _rpc_framework_get_reactors(void *arg1, void *arg2)
|
||||
spdk_json_write_named_uint32(ctx->w, "lcore", current_core);
|
||||
spdk_json_write_named_uint64(ctx->w, "busy", reactor->busy_tsc);
|
||||
spdk_json_write_named_uint64(ctx->w, "idle", reactor->idle_tsc);
|
||||
governor = _spdk_governor_get();
|
||||
/* We need to check whether governor can return current core frequency. */
|
||||
if (governor->get_core_capabilities && governor->get_core_freqs) {
|
||||
governor->get_core_capabilities(current_core, &capabilities);
|
||||
if (capabilities.freq_getset) {
|
||||
/* Governor returns core freqs in kHz, we want MHz. */
|
||||
curr_core_freq = governor->get_core_curr_freq(current_core) / 1000;
|
||||
spdk_json_write_named_uint32(ctx->w, "core_freq", curr_core_freq);
|
||||
}
|
||||
}
|
||||
|
||||
spdk_json_write_named_array_begin(ctx->w, "lw_threads");
|
||||
TAILQ_FOREACH(lw_thread, &reactor->threads, link) {
|
||||
@ -510,9 +497,9 @@ rpc_framework_get_scheduler(struct spdk_jsonrpc_request *request,
|
||||
|
||||
w = spdk_jsonrpc_begin_result(request);
|
||||
spdk_json_write_object_begin(w);
|
||||
spdk_json_write_named_string(w, "scheduler_name", scheduler->name);
|
||||
spdk_json_write_named_uint64(w, "scheduler_period", scheduler_period);
|
||||
spdk_json_write_named_string(w, "governor_name", governor->name);
|
||||
spdk_json_write_named_string(w, "scheduler name", scheduler->name);
|
||||
spdk_json_write_named_uint64(w, "scheduler period", scheduler_period);
|
||||
spdk_json_write_named_string(w, "governor name", governor->name);
|
||||
spdk_json_write_object_end(w);
|
||||
spdk_jsonrpc_end_result(request, w);
|
||||
}
|
||||
|
@ -49,25 +49,7 @@ _get_core_freqs(uint32_t lcore_id, uint32_t *freqs, uint32_t num)
|
||||
static uint32_t
|
||||
_get_core_curr_freq(uint32_t lcore_id)
|
||||
{
|
||||
const uint32_t MAX_CORE_FREQ_NUM = 64;
|
||||
uint32_t freqs[MAX_CORE_FREQ_NUM];
|
||||
uint32_t freq_index;
|
||||
int rc;
|
||||
|
||||
rc = rte_power_freqs(lcore_id, freqs, MAX_CORE_FREQ_NUM);
|
||||
if (!rc) {
|
||||
SPDK_ERRLOG("Unable to get current core frequency array for core %d\n.", lcore_id);
|
||||
|
||||
return 0;
|
||||
}
|
||||
freq_index = rte_power_get_freq(lcore_id);
|
||||
if (freq_index >= MAX_CORE_FREQ_NUM) {
|
||||
SPDK_ERRLOG("Unable to get current core frequency for core %d\n.", lcore_id);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return freqs[freq_index];
|
||||
return rte_power_get_freq(lcore_id);
|
||||
}
|
||||
|
||||
static int
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user