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			    grep -ve "records in" -e "records out" -e "bytes.*copied"
119			((filenum+=1))
120		done
121	done
122
123	####################
124	# Testing showed that on some devices, unless the pool is
125	# synchronized, that the block counts will be below the
126	# anticipated sizes since not all of the blocks will be flushed
127	# to the device.  This 'sync' command prevents that from
128	# happening.
129	####################
130	sync_pool ${pool}
131}
132function histo_check_test_pool
133{
134	if [ $# -ne 1 ]; then
135		log_note "histo_check_test_pool: insufficient parameters"
136		log_fail "hctp: 1 requested $# received"
137	fi
138	typeset pool=$1
139
140	set -A recordsizes
141	set -A recordcounts
142	typeset -i rb
143	typeset -i min_rsbits=9 #512
144	typeset -i max_rsbits=SPA_MAXBLOCKSHIFT+1
145	typeset -i this_rs
146	typeset -i this_ri
147	typeset -i sum_filesizes=0
148
149	let histo_check_pool_size=$(get_pool_prop size ${pool})
150	if [[ ! ${histo_check_pool_size} =~ ${re_number} ]]; then
151		log_fail "histo_check_pool_size is not numeric ${histo_check_pool_size}"
152	fi
153	let max_pool_record_size=$(get_prop recordsize ${pool})
154	if [[ ! ${max_pool_record_size} =~ ${re_number} ]]; then
155		log_fail "hctp: max_pool_record_size is not numeric ${max_pool_record_size}"
156	fi
157
158	stripped="${TEST_BASE_DIR}/${pool}_stripped.txt"
159
160	zdb -Pbbb ${pool} | \
161	    sed -e '1,/^block[ 	][ 	]*psize[ 	][ 	]*lsize.*$/d' \
162	    -e '/^size[ 	]*Count/d' -e '/^$/,$d' \
163	    > ${stripped}
164
165	sum_filesizes=$(echo "2^21"|bc)
166
167	###################
168	# generate 10% + 20% + 30% + 31% = 91% of the filespace
169	# attempting to use 100% will lead to no space left on device
170	# attempting to use 100% will lead to no space left on device
171	# Heuristic testing showed that 91% was the practical upper
172	# bound on the default 4G zpool (mirrored) that is used in
173	# testing.
174	#
175	# In order to expedite testing, we will only fill 2G (of 4G)
176	# of the test pool.  You may want to modify this for
177	# standalone testing.
178	#
179	# In filling only 50% of the pool, we create one object on
180	# each "pass" below to achieve multiple objects per record
181	# size.  Creating one file per object would lead to
182	# excessive file creation time.
183	###################
184	# for pass in 10 20 30 31  # 91%
185	for pass in 20 20 10 # 50%
186	do
187		((thiscount=(((histo_check_pool_size*pass)/100)/sum_filesizes)))
188
189		for rb in $(seq ${min_rsbits} ${max_rsbits})
190		do
191			blksize=$(echo "2^$rb"|bc)
192			if [ $blksize -le $max_pool_record_size ]; then
193				((recordcounts[$blksize]+=thiscount))
194			fi
195		done
196	done
197
198	###################
199	# compare the above computed counts for blocks against
200	# lsize count.  Since some devices have a minimum hardware
201	# blocksize > 512, we cannot compare against the asize count.
202	# E.G., if the HWBlocksize = 4096, then the asize counts for
203	# 512, 1024 and 2048 will be zero and rolled up into the
204	# 4096 blocksize count for asize.   For verification we stick
205	# to just lsize counts.
206	#
207	# Variances are expected since this test does not account for
208	# metadata. The hardcoded limit here is empirical and should
209	# not be construed as deterministic.
210	###################
211	let max_variance=15
212	let fail_value=0
213	let error_count=0
214	log_note "Comparisons for ${pool}"
215	log_note "Bsize is the blocksize, Count is predicted value"
216	log_note "Bsize\tCount\tpsize\tlsize\tasize"
217	while read -r blksize pc pl pm lc ll lm ac al am
218	do
219		if [ $blksize -gt $max_pool_record_size ]; then
220			continue
221		fi
222		log_note \
223		    "$blksize\t${recordcounts[${blksize}]}\t$pc\t$lc\t$ac"
224
225		###################
226		# get the computer record count and compute the
227		# difference percentage in integer arithmetic
228		###################
229		rc=${recordcounts[${blksize}]}
230		((rclc=(rc-lc)<0?lc-rc:rc-lc)) # absolute value
231		((dp=(rclc*100)/rc))
232
233		###################
234		# Check against the allowed variance
235		###################
236		if [ $dp -gt ${max_variance} ]; then
237			log_note \
238			"Expected variance < ${max_variance}% observed ${dp}%"
239			if [ ${dp} -gt ${fail_value} ]; then
240				fail_value=${dp}
241				((error_count++))
242			fi
243		fi
244	done < ${stripped}
245	rm "${stripped}"
246
247	if [ ${fail_value} -gt 0 ]; then
248		if [ ${error_count} -eq 1 ]; then
249			log_note "hctp: There was ${error_count} error"
250		else
251			log_note "hctp:There were a total of ${error_count} errors"
252		fi
253		log_fail \
254		"hctp: Max variance of ${max_variance}% exceeded, saw ${fail_value}%"
255	fi
256}
257
258log_assert "Verify zdb -Pbbb (block histogram) works as expected"
259log_onexit cleanup
260verify_runnable "global"
261verify_disk_count "$DISKS" 2
262
263default_mirror_setup_noexit $DISKS
264
265histo_populate_test_pool $TESTPOOL
266
267histo_check_test_pool $TESTPOOL
268
269log_pass "Histogram for zdb"
270