one-core-peak: Adding option to reporting latencies
[fio.git] / t / one-core-peak.sh
1 #!/bin/bash
2
3 args=$*
4 first_cores=""
5 taskset_cores=""
6 first_cores_count=0
7 nb_threads=4 #default from the benchmark
8 drives=""
9
10 # Default options
11 latency_cmdline=""
12
13 fatal() {
14   echo "$@"
15   exit 1
16 }
17
18 hint() {
19   echo "Warning: $*"
20 }
21
22 info() {
23   item=$1
24   shift
25   echo "${item}: $*"
26 }
27
28 check_root() {
29   [[ ${EUID} -eq 0 ]] || fatal "You should be root to run this tool"
30 }
31
32 check_binary() {
33   # Ensure the binaries are present and executable
34   for bin in "$@"; do
35     if [ ! -x ${bin} ]; then
36       which ${bin} >/dev/null
37       [ $? -eq 0 ] || fatal "${bin} doesn't exists or is not executable"
38     fi
39   done
40 }
41
42
43 detect_first_core() {
44   # Detect which logical cpus belongs to the first physical core
45   # If Hyperthreading is enabled, two cores are returned
46   cpus=$(lscpu  --all -pSOCKET,CORE,CPU |grep "0,0")
47   for cpu in ${cpus}; do
48     IFS=','
49     # shellcheck disable=SC2206
50     array=(${cpu})
51     if [ ${first_cores_count} -eq 0 ]; then
52       first_cores="${array[2]}"
53     else
54       first_cores="${first_cores} ${array[2]}"
55     fi
56
57     first_cores_count=$((first_cores_count + 1))
58     unset IFS
59   done
60   [ ${first_cores_count} -eq 0 ] && fatal "Cannot detect first core"
61   taskset_cores=$(echo "${first_cores}" | tr ' ' ',')
62 }
63
64 usage() {
65   echo "usage: [options] block_device [other_block_devices]
66
67    -h         : print help
68    -l         : enable latency reporting
69
70    example:
71       t/one-core-peak.sh /dev/nvme0n1
72       t/one-core-peak.sh -l /dev/nvme0n1 /dev/nvme1n1
73   "
74   exit 0
75 }
76
77 check_args() {
78   local OPTIND h option
79   while getopts "hl" option; do
80     case "${option}" in
81         h) # Show help
82             usage
83             ;;
84         l) # Report latency
85             latency_cmdline="1"
86             ;;
87         *)
88             fatal "Unsupported ${option} option"
89             ;;
90     esac
91   done
92   shift $((OPTIND-1))
93   [ $# -eq 0 ] && fatal "Missing drive(s) as argument"
94   drives="$@"
95 }
96
97 check_drive_exists() {
98   # Ensure the block device exists
99   [ -b $1 ] || fatal "$1 is not a valid block device"
100 }
101
102 is_nvme() {
103   [[ ${*} == *"nvme"* ]]
104 }
105
106 check_poll_queue() {
107   # Print a warning if the nvme poll queues aren't enabled
108   is_nvme ${drives} || return
109   poll_queue=$(cat /sys/module/nvme/parameters/poll_queues)
110   [ ${poll_queue} -eq 0 ] && hint "For better performance, you should enable nvme poll queues by setting nvme.poll_queues=32 on the kernel commande line"
111 }
112
113 block_dev_name() {
114   echo ${1#"/dev/"}
115 }
116
117 get_sys_block_dir() {
118   # Returns the /sys/block/ directory of a given block device
119   device_name=$1
120   sys_block_dir="/sys/block/${device_name}"
121   [ -d "${sys_block_dir}" ] || fatal "Cannot find ${sys_block_dir} directory"
122   echo ${sys_block_dir}
123 }
124
125 check_io_scheduler() {
126   # Ensure io_sched is set to none
127   device_name=$(block_dev_name $1)
128   sys_block_dir=$(get_sys_block_dir ${device_name})
129   sched_file="${sys_block_dir}/queue/scheduler"
130   [ -f "${sched_file}" ] || fatal "Cannot find IO scheduler for ${device_name}"
131   grep -q '\[none\]' ${sched_file}
132   if [ $? -ne 0 ]; then
133     info "${device_name}" "set none as io scheduler"
134     echo "none" > ${sched_file}
135   fi
136
137 }
138
139 check_sysblock_value() {
140   device_name=$(block_dev_name $1)
141   sys_block_dir=$(get_sys_block_dir ${device_name})
142   target_file="${sys_block_dir}/$2"
143   value=$3
144   [ -f "${target_file}" ] || fatal "Cannot find ${target_file} for ${device_name}"
145   content=$(cat ${target_file})
146   if [ "${content}" != "${value}" ]; then
147     info "${device_name}" "${target_file} set to ${value}."
148     echo ${value} > ${target_file} 2>/dev/null || hint "${device_name}: Cannot set ${value} on ${target_file}"
149   fi
150 }
151
152 compute_nb_threads() {
153   # Increase the number of threads if there is more devices or cores than the default value
154   [ $# -gt ${nb_threads} ] && nb_threads=$#
155   [ ${first_cores_count} -gt ${nb_threads} ] && nb_threads=${first_cores_count}
156 }
157
158 check_scaling_governor() {
159   driver=$(LC_ALL=C cpupower frequency-info |grep "driver:" |awk '{print $2}')
160   if [ -z "${driver}" ]; then
161     hint "Cannot detect processor scaling driver"
162     return
163   fi
164   cpupower frequency-set -g performance >/dev/null 2>&1 || fatal "Cannot set scaling processor governor"
165 }
166
167 check_idle_governor() {
168   filename="/sys/devices/system/cpu/cpuidle/current_governor"
169   if [ ! -f "${filename}" ]; then
170     hint "Cannot detect cpu idle governor"
171     return
172   fi
173   echo "menu" > ${filename} 2>/dev/null || fatal "Cannot set cpu idle governor to menu"
174 }
175
176 show_nvme() {
177   device_name=$(block_dev_name $1)
178   device_dir="/sys/block/${device_name}/device/"
179   pci_addr=$(cat ${device_dir}/address)
180   pci_dir="/sys/bus/pci/devices/${pci_addr}/"
181   link_speed=$(cat ${pci_dir}/current_link_speed)
182   irq=$(cat ${pci_dir}/irq)
183   numa=$(cat ${pci_dir}/numa_node)
184   cpus=$(cat ${pci_dir}/local_cpulist)
185   model=$(cat ${device_dir}/model | xargs) #xargs for trimming spaces
186   fw=$(cat ${device_dir}/firmware_rev | xargs) #xargs for trimming spaces
187   serial=$(cat ${device_dir}/serial | xargs) #xargs for trimming spaces
188   info ${device_name} "MODEL=${model} FW=${fw} serial=${serial} PCI=${pci_addr}@${link_speed} IRQ=${irq} NUMA=${numa} CPUS=${cpus} "
189 }
190
191 show_device() {
192   device_name=$(block_dev_name $1)
193   is_nvme $1 && show_nvme $1
194 }
195
196 show_system() {
197   CPU_MODEL=$(grep -m1 "model name" /proc/cpuinfo | awk '{print substr($0, index($0,$4))}')
198   MEMORY_SPEED=$(dmidecode -t 17 -q | grep -m 1 "Configured Memory Speed: [0-9]" | awk '{print substr($0, index($0,$4))}')
199   KERNEL=$(uname -r)
200   info "system" "CPU: ${CPU_MODEL}"
201   info "system" "MEMORY: ${MEMORY_SPEED}"
202   info "system" "KERNEL: ${KERNEL}"
203   tsc=$(journalctl -k | grep 'tsc: Refined TSC clocksource calibration:' | awk '{print $11'})
204   if [ -n "${tsc}" ]; then
205     info "system" "TSC: ${tsc} Mhz"
206     tsc=$(echo ${tsc} | tr -d '.')
207     [ -n "${latency_cmdline}" ] && latency_cmdline="-t1 -T${tsc}000"
208   fi
209 }
210
211 ### MAIN
212 check_args ${args}
213 check_root
214 check_binary t/io_uring lscpu grep taskset cpupower awk tr xargs dmidecode
215 detect_first_core
216
217 info "##################################################"
218 show_system
219 for drive in ${drives}; do
220   check_drive_exists ${drive}
221   check_io_scheduler ${drive}
222   check_sysblock_value ${drive} "queue/iostats" 0 # Ensure iostats are disabled
223   check_sysblock_value ${drive} "queue/nomerges" 2 # Ensure merge are disabled
224   check_sysblock_value ${drive} "queue/io_poll" 1 # Ensure io_poll is enabled
225   show_device ${drive}
226 done
227
228 check_poll_queue
229 compute_nb_threads ${drives}
230 check_scaling_governor
231 check_idle_governor
232
233 info "##################################################"
234 echo
235
236 cmdline="taskset -c ${taskset_cores} t/io_uring -b512 -d128 -c32 -s32 -p1 -F1 -B1 -n${nb_threads} ${latency_cmdline} ${drives}"
237 info "io_uring" "Running ${cmdline}"
238 ${cmdline}