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