1#!/bin/ksh -p
2
3#
4# This file and its contents are supplied under the terms of the
5# Common Development and Distribution License ("CDDL"), version 1.0.
6# You may only use this file in accordance with the terms of version
7# 1.0 of the CDDL.
8#
9# A full copy of the text of the CDDL should have accompanied this
10# source.  A copy of the CDDL is also available via the Internet at
11# http://www.illumos.org/license/CDDL.
12#
13
14#
15# Copyright (c) 2017 by Delphix. All rights reserved.
16# Copyright (c) 2020 by Lawrence Livermore National Security LLC.
17
18. $STF_SUITE/include/libtest.shlib
19
20
21#
22# DESCRIPTION:
23#	Create a pool and populate it with files of various
24#	recordsizes
25#
26# STRATEGY:
27#	1. Create pool
28#	2. Populate it
29#	3. Run zdb -Pbbb on pool
30#	4. Verify variance on blocksizes
31#
32function cleanup
33{
34	datasetexists $TESTPOOL && destroy_pool $TESTPOOL
35}
36
37SPA_MAXBLOCKSHIFT=24
38
39function histo_populate_test_pool
40{
41	if [ $# -ne 1 ]; then
42		log_note "histo_populate_test_pool: insufficient parameters"
43		log_fail "hptp: 1 requested $# received"
44	fi
45	typeset pool=$1
46
47	set -A recordsizes
48	typeset -i min_rsbits=9 #512
49	typeset -i max_rsbits=SPA_MAXBLOCKSHIFT #16 MiB
50	typeset -i sum_filesizes=0
51	re_number='^[0-9]+$'
52
53	let histo_pool_size=$(get_pool_prop size ${pool})
54	if [[ ! ${histo_pool_size} =~ ${re_number} ]]; then
55		log_fail "histo_pool_size is not numeric ${pool_size}"
56	fi
57	let max_pool_record_size=$(get_prop recordsize ${pool})
58	if [[ ! ${max_pool_record_size} =~ ${re_number} ]]; then
59		log_fail "hptp: max_pool_record_size is not numeric ${max_pool_record_size}"
60	fi
61
62	sum_filesizes=$(echo "2^21"|bc)
63	((min_pool_size=12*sum_filesizes))
64	if [ ${histo_pool_size} -lt ${min_pool_size} ]; then
65		log_note "hptp: Your pool size ${histo_pool_size}"
66		log_fail "hptp: is less than minimum ${min_pool_size}"
67	fi
68	this_ri=min_rsbits
69	file_num=0
70	total_count=0
71	###################
72	# generate 10% + 20% + 30% + 31% = 91% of the filespace
73	# attempting to use 100% will lead to no space left on device
74	# Heuristic testing showed that 91% was the practical upper
75	# bound on the default 4G zpool (mirrored) that is used in
76	# testing.
77	#
78	# In order to expedite testing, we will only fill 2G (of 4G)
79	# of the test pool.  You may want to modify this for
80	# standalone testing.
81	#
82	# In filling only 50% of the pool, we create one object on
83	# each "pass" below to achieve multiple objects per record
84	# size.  Creating one file per object would lead to
85	# excessive file creation time.
86	###################
87	# for pass in 10 20 30 31  # 91%
88	for pass in 20 20 10 # 50%
89	do
90		((thiscount=(((histo_pool_size*pass)/100)/sum_filesizes)))
91
92		((total_count+=thiscount))
93		for rb in $(seq ${min_rsbits} ${max_rsbits})
94		do
95			this_rs=$(echo "2^${rb}" | bc)
96			if [ ${this_rs} -gt ${max_pool_record_size} ]; then
97				continue
98			fi
99
100			if [ ! -d /${pool}/B_${this_rs} ]; then
101				zfs create ${pool}/B_${this_rs}
102				zfs set recordsize=${this_rs} \
103				    ${pool}/B_${this_rs}
104			fi
105			####################
106			# Create the files in the devices and datasets
107			# of the right size.  The files are filled
108			# with random data to defeat the compression
109			#
110			# Note that the dd output is suppressed unless
111			# there are errors
112			####################
113
114			dd if=/dev/urandom \
115			    of=/${pool}/B_${this_rs}/file_${filenum} \
116			    bs=${this_rs} count=${thiscount} \
117			    iflag=fullblock 2>&1 | \
118			    egrep -v -e "records in" -e "records out" \
119				-e "bytes.*copied"
120			((filenum+=1))
121		done
122	done
123
124	####################
125	# Testing showed that on some devices, unless the pool is
126	# synchronized, that the block counts will be below the
127	# anticipated sizes since not all of the blocks will be flushed
128	# to the device.  This 'sync' command prevents that from
129	# happening.
130	####################
131	log_must zpool sync ${pool}
132}
133function histo_check_test_pool
134{
135	if [ $# -ne 1 ]; then
136		log_note "histo_check_test_pool: insufficient parameters"
137		log_fail "hctp: 1 requested $# received"
138	fi
139	typeset pool=$1
140
141	set -A recordsizes
142	set -A recordcounts
143	typeset -i rb
144	typeset -i min_rsbits=9 #512
145	typeset -i max_rsbits=SPA_MAXBLOCKSHIFT+1
146	typeset -i this_rs
147	typeset -i this_ri
148	typeset -i sum_filesizes=0
149	typeset dumped
150	typeset stripped
151
152	let histo_check_pool_size=$(get_pool_prop size ${pool})
153	if [[ ! ${histo_check_pool_size} =~ ${re_number} ]]; then
154		log_fail "histo_check_pool_size is not numeric ${histo_check_pool_size}"
155	fi
156	let max_pool_record_size=$(get_prop recordsize ${pool})
157	if [[ ! ${max_pool_record_size} =~ ${re_number} ]]; then
158		log_fail "hctp: max_pool_record_size is not numeric ${max_pool_record_size}"
159	fi
160
161	dumped="${TEST_BASE_DIR}/${pool}_dump.txt"
162	stripped="${TEST_BASE_DIR}/${pool}_stripped.txt"
163
164	zdb -Pbbb ${pool} | \
165	    tee ${dumped} | \
166	    sed -e '1,/^block[ 	][ 	]*psize[ 	][ 	]*lsize.*$/d' \
167	    -e '/^size[ 	]*Count/d' -e '/^$/,$d' \
168	    > ${stripped}
169
170	sum_filesizes=$(echo "2^21"|bc)
171
172	###################
173	# generate 10% + 20% + 30% + 31% = 91% of the filespace
174	# attempting to use 100% will lead to no space left on device
175	# attempting to use 100% will lead to no space left on device
176	# Heuristic testing showed that 91% was the practical upper
177	# bound on the default 4G zpool (mirrored) that is used in
178	# testing.
179	#
180	# In order to expedite testing, we will only fill 2G (of 4G)
181	# of the test pool.  You may want to modify this for
182	# standalone testing.
183	#
184	# In filling only 50% of the pool, we create one object on
185	# each "pass" below to achieve multiple objects per record
186	# size.  Creating one file per object would lead to
187	# excessive file creation time.
188	###################
189	# for pass in 10 20 30 31  # 91%
190	for pass in 20 20 10 # 50%
191	do
192		((thiscount=(((histo_check_pool_size*pass)/100)/sum_filesizes)))
193
194		for rb in $(seq ${min_rsbits} ${max_rsbits})
195		do
196			blksize=$(echo "2^$rb"|bc)
197			if [ $blksize -le $max_pool_record_size ]; then
198				((recordcounts[$blksize]+=thiscount))
199			fi
200		done
201	done
202
203	###################
204	# compare the above computed counts for blocks against
205	# lsize count.  Since some devices have a minimum hardware
206	# blocksize > 512, we cannot compare against the asize count.
207	# E.G., if the HWBlocksize = 4096, then the asize counts for
208	# 512, 1024 and 2048 will be zero and rolled up into the
209	# 4096 blocksize count for asize.   For verification we stick
210	# to just lsize counts.
211	#
212	# The max_variance is hard-coded here at 10%.  testing so far
213	# has shown this to be in the range of 2%-8% so we leave a
214	# generous allowance... This might need changes in the future
215	###################
216	let max_variance=10
217	let fail_value=0
218	let error_count=0
219	log_note "Comparisons for ${pool}"
220	log_note "Bsize is the blocksize, Count is predicted value"
221	log_note "Bsize\tCount\tpsize\tlsize\tasize"
222	while read -r blksize pc pl pm lc ll lm ac al am
223	do
224		if [ $blksize -gt $max_pool_record_size ]; then
225			continue
226		fi
227		log_note \
228		    "$blksize\t${recordcounts[${blksize}]}\t$pc\t$lc\t$ac"
229
230		###################
231		# get the computer record count and compute the
232		# difference percentage in integer arithmetic
233		###################
234		rc=${recordcounts[${blksize}]}
235		((rclc=(rc-lc)<0?lc-rc:rc-lc)) # absolute value
236		((dp=(rclc*100)/rc))
237
238		###################
239		# Check against the allowed variance
240		###################
241		if [ $dp -gt ${max_variance} ]; then
242			log_note \
243			"Expected variance < ${max_variance}% observed ${dp}%"
244			if [ ${dp} -gt ${fail_value} ]; then
245				fail_value=${dp}
246				((error_count++))
247			fi
248		fi
249	done < ${stripped}
250	if [ ${fail_value} -gt 0 ]; then
251		if [ ${error_count} -eq 1 ]; then
252			log_note "hctp: There was ${error_count} error"
253		else
254			log_note "hctp:There were a total of ${error_count} errors"
255		fi
256		log_fail \
257		"hctp: Max variance of ${max_variance}% exceeded, saw ${fail_value}%"
258	fi
259}
260
261log_assert "Verify zdb -Pbbb (block histogram) works as expected"
262log_onexit cleanup
263verify_runnable "global"
264verify_disk_count "$DISKS" 2
265
266default_mirror_setup_noexit $DISKS
267
268histo_populate_test_pool $TESTPOOL
269
270histo_check_test_pool $TESTPOOL
271
272log_pass "Histogram for zdb"
273