1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright (C) 2016 Gvozden Nešković. All rights reserved.
24  */
25 
26 #include <sys/zfs_context.h>
27 #include <sys/time.h>
28 #include <sys/wait.h>
29 #include <sys/zio.h>
30 #include <sys/vdev_raidz.h>
31 #include <sys/vdev_raidz_impl.h>
32 #include <stdio.h>
33 #include <strings.h>
34 
35 #include <sys/time.h>
36 
37 #include "raidz_test.h"
38 
39 #define	GEN_BENCH_MEMORY	(((uint64_t)1ULL)<<32)
40 #define	REC_BENCH_MEMORY	(((uint64_t)1ULL)<<29)
41 #define	BENCH_ASHIFT		12
42 #define	MIN_CS_SHIFT		BENCH_ASHIFT
43 #define	MAX_CS_SHIFT		SPA_MAXBLOCKSHIFT
44 
45 static zio_t zio_bench;
46 static raidz_map_t *rm_bench;
47 static size_t max_data_size = SPA_MAXBLOCKSIZE;
48 
49 static void
bench_init_raidz_map(void)50 bench_init_raidz_map(void)
51 {
52 	zio_bench.io_offset = 0;
53 	zio_bench.io_size = max_data_size;
54 
55 	/*
56 	 * To permit larger column sizes these have to be done
57 	 * allocated using aligned alloc instead of zio_abd_buf_alloc
58 	 */
59 	zio_bench.io_abd = raidz_alloc(max_data_size);
60 
61 	init_zio_abd(&zio_bench);
62 }
63 
64 static void
bench_fini_raidz_maps(void)65 bench_fini_raidz_maps(void)
66 {
67 	/* tear down golden zio */
68 	raidz_free(zio_bench.io_abd, max_data_size);
69 	bzero(&zio_bench, sizeof (zio_t));
70 }
71 
72 static inline void
run_gen_bench_impl(const char * impl)73 run_gen_bench_impl(const char *impl)
74 {
75 	int fn, ncols;
76 	uint64_t ds, iter_cnt, iter, disksize;
77 	hrtime_t start;
78 	double elapsed, d_bw;
79 
80 	/* Benchmark generate functions */
81 	for (fn = 0; fn < RAIDZ_GEN_NUM; fn++) {
82 
83 		for (ds = MIN_CS_SHIFT; ds <= MAX_CS_SHIFT; ds++) {
84 			/* create suitable raidz_map */
85 			ncols = rto_opts.rto_dcols + fn + 1;
86 			zio_bench.io_size = 1ULL << ds;
87 			rm_bench = vdev_raidz_map_alloc(&zio_bench,
88 			    BENCH_ASHIFT, ncols, fn+1);
89 
90 			/* estimate iteration count */
91 			iter_cnt = GEN_BENCH_MEMORY;
92 			iter_cnt /= zio_bench.io_size;
93 
94 			start = gethrtime();
95 			for (iter = 0; iter < iter_cnt; iter++)
96 				vdev_raidz_generate_parity(rm_bench);
97 			elapsed = NSEC2SEC((double)(gethrtime() - start));
98 
99 			disksize = (1ULL << ds) / rto_opts.rto_dcols;
100 			d_bw = (double)iter_cnt * (double)disksize;
101 			d_bw /= (1024.0 * 1024.0 * elapsed);
102 
103 			LOG(D_ALL, "%10s, %8s, %zu, %10llu, %lf, %lf, %u\n",
104 			    impl,
105 			    raidz_gen_name[fn],
106 			    rto_opts.rto_dcols,
107 			    (1ULL<<ds),
108 			    d_bw,
109 			    d_bw * (double)(ncols),
110 			    (unsigned)iter_cnt);
111 
112 			vdev_raidz_map_free(rm_bench);
113 		}
114 	}
115 }
116 
117 void
run_gen_bench(void)118 run_gen_bench(void)
119 {
120 	char **impl_name;
121 
122 	LOG(D_INFO, DBLSEP "\nBenchmarking parity generation...\n\n");
123 	LOG(D_ALL, "impl, math, dcols, iosize, disk_bw, total_bw, iter\n");
124 
125 	for (impl_name = (char **)raidz_impl_names; *impl_name != NULL;
126 	    impl_name++) {
127 
128 		if (vdev_raidz_impl_set(*impl_name) != 0)
129 			continue;
130 
131 		run_gen_bench_impl(*impl_name);
132 	}
133 }
134 
135 static void
run_rec_bench_impl(const char * impl)136 run_rec_bench_impl(const char *impl)
137 {
138 	int fn, ncols, nbad;
139 	uint64_t ds, iter_cnt, iter, disksize;
140 	hrtime_t start;
141 	double elapsed, d_bw;
142 	static const int tgt[7][3] = {
143 		{1, 2, 3},	/* rec_p:   bad QR & D[0]	*/
144 		{0, 2, 3},	/* rec_q:   bad PR & D[0]	*/
145 		{0, 1, 3},	/* rec_r:   bad PQ & D[0]	*/
146 		{2, 3, 4},	/* rec_pq:  bad R  & D[0][1]	*/
147 		{1, 3, 4},	/* rec_pr:  bad Q  & D[0][1]	*/
148 		{0, 3, 4},	/* rec_qr:  bad P  & D[0][1]	*/
149 		{3, 4, 5}	/* rec_pqr: bad    & D[0][1][2] */
150 	};
151 
152 	for (fn = 0; fn < RAIDZ_REC_NUM; fn++) {
153 		for (ds = MIN_CS_SHIFT; ds <= MAX_CS_SHIFT; ds++) {
154 
155 			/* create suitable raidz_map */
156 			ncols = rto_opts.rto_dcols + PARITY_PQR;
157 			zio_bench.io_size = 1ULL << ds;
158 
159 			/*
160 			 * raidz block is too short to test
161 			 * the requested method
162 			 */
163 			if (zio_bench.io_size / rto_opts.rto_dcols <
164 			    (1ULL << BENCH_ASHIFT))
165 				continue;
166 
167 			rm_bench = vdev_raidz_map_alloc(&zio_bench,
168 			    BENCH_ASHIFT, ncols, PARITY_PQR);
169 
170 			/* estimate iteration count */
171 			iter_cnt = (REC_BENCH_MEMORY);
172 			iter_cnt /= zio_bench.io_size;
173 
174 			/* calculate how many bad columns there are */
175 			nbad = MIN(3, raidz_ncols(rm_bench) -
176 			    raidz_parity(rm_bench));
177 
178 			start = gethrtime();
179 			for (iter = 0; iter < iter_cnt; iter++)
180 				vdev_raidz_reconstruct(rm_bench, tgt[fn], nbad);
181 			elapsed = NSEC2SEC((double)(gethrtime() - start));
182 
183 			disksize = (1ULL << ds) / rto_opts.rto_dcols;
184 			d_bw = (double)iter_cnt * (double)(disksize);
185 			d_bw /= (1024.0 * 1024.0 * elapsed);
186 
187 			LOG(D_ALL, "%10s, %8s, %zu, %10llu, %lf, %lf, %u\n",
188 			    impl,
189 			    raidz_rec_name[fn],
190 			    rto_opts.rto_dcols,
191 			    (1ULL<<ds),
192 			    d_bw,
193 			    d_bw * (double)ncols,
194 			    (unsigned)iter_cnt);
195 
196 			vdev_raidz_map_free(rm_bench);
197 		}
198 	}
199 }
200 
201 void
run_rec_bench(void)202 run_rec_bench(void)
203 {
204 	char **impl_name;
205 
206 	LOG(D_INFO, DBLSEP "\nBenchmarking data reconstruction...\n\n");
207 	LOG(D_ALL, "impl, math, dcols, iosize, disk_bw, total_bw, iter\n");
208 
209 	for (impl_name = (char **)raidz_impl_names; *impl_name != NULL;
210 	    impl_name++) {
211 
212 		if (vdev_raidz_impl_set(*impl_name) != 0)
213 			continue;
214 
215 		run_rec_bench_impl(*impl_name);
216 	}
217 }
218 
219 void
run_raidz_benchmark(void)220 run_raidz_benchmark(void)
221 {
222 	bench_init_raidz_map();
223 
224 	run_gen_bench();
225 	run_rec_bench();
226 
227 	bench_fini_raidz_maps();
228 }
229