xref: /spdk/test/common/nvme/functions.sh (revision 83ba9086796471697a4975a58f60e2392bccd08c)
1#!/usr/bin/env bash
2#  SPDX-License-Identifier: BSD-3-Clause
3#  Copyright (C) 2023 Intel Corporation
4#  All rights reserved.
5#
6
7rootdir=$(readlink -f "$(dirname "$BASH_SOURCE")/../../../")
8source "$rootdir/scripts/common.sh"
9
10declare -A ctrls=()
11declare -A nvmes=()
12declare -A bdfs=()
13declare -a ordered_ctrls=()
14nvme_name=""
15
16nvme_get() {
17	local ref=$1 reg val
18	shift
19
20	local -gA "$ref=()"
21	while IFS=":" read -r reg val; do
22		[[ -n $val ]] \
23			&& eval "${ref}[${reg//[[:space:]]/}]=\"${val##+([[:space:]])}\""
24	done < <("${NVME_CMD:-nvme}" "$@")
25}
26
27scan_nvme_ctrls() {
28	# Create set of references and bundle them together in global assoc arrays.
29	# Each controller's regs are mapped onto a separate array named as the target
30	# ctrl. Each ctrl also gets a dedicated array which holds references to each
31	# namespace device. E.g.:
32	#
33	#  ctrls["nvme0"]=nvme0
34	#    nvme0["mn"] nvme0["vid"]
35	#  nvmes["nvme0"]=nvme0_ns
36	#    nvme0_ns[1]=nvme0n1 nvme0_ns[2]=nvme0n2
37	#     nvme0n1["lbaf0"] nvme0n1["ncap"]
38	#  bdf["nvme0"]=0000:00:42.0
39	#
40	# Each of these can be accessed via get*() helpers defined below. E.g.:
41	# get_nvme_ctrl_feature nvme0 vid -> 0x8086
42	# get_nvme_ns_feature nvme0 1 ncap -> 0x2e9390b0
43	# get_nvme_nss nvme0 -> 1 2
44
45	local ctrl ctrl_dev reg val ns pci
46
47	for ctrl in /sys/class/nvme/nvme*; do
48		[[ -e $ctrl ]] || continue
49		pci=$(< "$ctrl/address")
50		pci_can_use "$pci" || continue
51		ctrl_dev=${ctrl##*/}
52		nvme_get "$ctrl_dev" id-ctrl "/dev/$ctrl_dev"
53		local -n _ctrl_ns=${ctrl_dev}_ns
54		for ns in "$ctrl/${ctrl##*/}n"*; do
55			[[ -e $ns ]] || continue
56			ns_dev=${ns##*/}
57			nvme_get "$ns_dev" id-ns "/dev/$ns_dev"
58			_ctrl_ns[${ns##*n}]=$ns_dev
59		done
60		ctrls["$ctrl_dev"]=$ctrl_dev
61		nvmes["$ctrl_dev"]=${ctrl_dev}_ns
62		bdfs["$ctrl_dev"]=$pci
63		ordered_ctrls[${ctrl_dev/nvme/}]=$ctrl_dev
64	done
65	((${#ctrls[@]} > 0))
66}
67
68get_nvme_ctrl_feature() {
69	local ctrl=$1 reg=${2:-cntlid}
70
71	[[ -n ${ctrls["$ctrl"]} ]] || return 1
72
73	local -n _ctrl=${ctrls["$ctrl"]}
74
75	[[ -n ${_ctrl["$reg"]} ]] || return 1
76	echo "${_ctrl["$reg"]}"
77}
78
79get_nvme_ns_feature() {
80	local ctrl=$1 ns=$2 reg=${3:-nsze}
81
82	[[ -n ${nvmes["$ctrl"]} ]] || return 1
83
84	local -n _nss=${nvmes["$ctrl"]}
85	[[ -n ${_nss[ns]} ]] || return 1
86
87	local -n _ns=${_nss[ns]}
88
89	[[ -n ${_ns["$reg"]} ]] || return 1
90	echo "${_ns["$reg"]}"
91}
92
93get_nvme_nss() {
94	local ctrl=$1
95
96	[[ -n ${nvmes["$ctrl"]} ]] || return 1
97	local -n _nss=${nvmes["$ctrl"]}
98
99	echo "${!_nss[@]}"
100}
101
102get_active_lbaf() {
103	local ctrl=$1 ns=$2 reg lbaf
104
105	[[ -n ${nvmes["$ctrl"]} ]] || return 1
106
107	local -n _nss=${nvmes["$ctrl"]}
108	[[ -n ${_nss[ns]} ]] || return 1
109
110	local -n _ns=${_nss[ns]}
111
112	for reg in "${!_ns[@]}"; do
113		[[ $reg == lbaf* ]] || continue
114		[[ ${_ns["$reg"]} == *"in use"* ]] || continue
115		echo "${reg/lbaf/}" && return 0
116	done
117	return 1
118}
119
120get_oacs() {
121	local ctrl=${1:-nvme0} bit=${2:-nsmgt}
122	local -A bits
123
124	# Figure 275: Identify – Identify Controller Data Structure, I/O Command Set Independent
125	bits["ss/sr"]=$((1 << 0))
126	bits["fnvme"]=$((1 << 1))
127	bits["fc/fi"]=$((1 << 2))
128	bits["nsmgt"]=$((1 << 3))
129	bits["self-test"]=$((1 << 4))
130	bits["directives"]=$((1 << 5))
131	bits["nvme-mi-s/r"]=$((1 << 6))
132	bits["virtmgt"]=$((1 << 7))
133	bits["doorbellbuf"]=$((1 << 8))
134	bits["getlba"]=$((1 << 9))
135	bits["commfeatlock"]=$((1 << 10))
136
137	bit=${bit,,}
138	[[ -n ${bits["$bit"]} ]] || return 1
139
140	(($(get_nvme_ctrl_feature "$ctrl" oacs) & bits["$bit"]))
141}
142
143get_nvmes_with_ns_management() {
144	((${#ctrls[@]} == 0)) && scan_nvme_ctrls
145
146	local ctrl
147	for ctrl in "${!ctrls[@]}"; do
148		get_oacs "$ctrl" nsmgt && echo "$ctrl"
149	done
150
151	return 0
152}
153
154get_nvme_with_ns_management() {
155	local _ctrls
156
157	_ctrls=($(get_nvmes_with_ns_management))
158	if ((${#_ctrls[@]} > 0)); then
159		echo "${_ctrls[0]}"
160		return 0
161	fi
162	return 1
163}
164
165get_ctratt() {
166	local ctrl=$1
167	get_nvme_ctrl_feature "$ctrl" ctratt
168}
169
170get_oncs() {
171	local ctrl=$1
172	get_nvme_ctrl_feature "$ctrl" oncs
173}
174
175ctrl_has_fdp() {
176	local ctrl=$1 ctratt
177
178	ctratt=$(get_ctratt "$ctrl")
179	# See include/spdk/nvme_spec.h
180	((ctratt & 1 << 19))
181}
182
183ctrl_has_scc() {
184	local ctrl=$1 oncs
185
186	oncs=$(get_oncs "$ctrl")
187	# See include/spdk/nvme_spec.h
188	((oncs & 1 << 8))
189}
190
191get_ctrls_with_feature() {
192	((${#ctrls[@]} == 0)) && scan_nvme_ctrls
193
194	local ctrl feature=${1:-fdp}
195
196	[[ $(type -t "ctrl_has_$feature") == function ]] || return 1
197
198	for ctrl in "${!ctrls[@]}"; do
199		"ctrl_has_$feature" "$ctrl" && echo "$ctrl"
200	done
201}
202
203get_ctrl_with_feature() {
204	local _ctrls feature=${1:-fdp}
205
206	_ctrls=($(get_ctrls_with_feature "$feature"))
207	if ((${#_ctrls[@]} > 0)); then
208		echo "${_ctrls[0]}"
209		return 0
210	fi
211	return 1
212}
213