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 https://opensource.org/licenses/CDDL-1.0.
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 
34 #include "raidz_test.h"
35 
36 #define	GEN_BENCH_MEMORY	(((uint64_t)1ULL)<<32)
37 #define	REC_BENCH_MEMORY	(((uint64_t)1ULL)<<29)
38 #define	BENCH_ASHIFT		12
39 #define	MIN_CS_SHIFT		BENCH_ASHIFT
40 #define	MAX_CS_SHIFT		SPA_MAXBLOCKSHIFT
41 
42 static zio_t zio_bench;
43 static raidz_map_t *rm_bench;
44 static size_t max_data_size = SPA_MAXBLOCKSIZE;
45 
46 static void
47 bench_init_raidz_map(void)
48 {
49 	zio_bench.io_offset = 0;
50 	zio_bench.io_size = max_data_size;
51 
52 	/*
53 	 * To permit larger column sizes these have to be done
54 	 * allocated using aligned alloc instead of zio_abd_buf_alloc
55 	 */
56 	zio_bench.io_abd = raidz_alloc(max_data_size);
57 
58 	init_zio_abd(&zio_bench);
59 }
60 
61 static void
62 bench_fini_raidz_maps(void)
63 {
64 	/* tear down golden zio */
65 	raidz_free(zio_bench.io_abd, max_data_size);
66 	memset(&zio_bench, 0, sizeof (zio_t));
67 }
68 
69 static inline void
70 run_gen_bench_impl(const char *impl)
71 {
72 	int fn, ncols;
73 	uint64_t ds, iter_cnt, iter, disksize;
74 	hrtime_t start;
75 	double elapsed, d_bw;
76 
77 	/* Benchmark generate functions */
78 	for (fn = 0; fn < RAIDZ_GEN_NUM; fn++) {
79 
80 		for (ds = MIN_CS_SHIFT; ds <= MAX_CS_SHIFT; ds++) {
81 			/* create suitable raidz_map */
82 			ncols = rto_opts.rto_dcols + fn + 1;
83 			zio_bench.io_size = 1ULL << ds;
84 
85 			if (rto_opts.rto_expand) {
86 				rm_bench = vdev_raidz_map_alloc_expanded(
87 				    &zio_bench,
88 				    rto_opts.rto_ashift, ncols+1, ncols,
89 				    fn+1, rto_opts.rto_expand_offset,
90 				    0, B_FALSE);
91 			} else {
92 				rm_bench = vdev_raidz_map_alloc(&zio_bench,
93 				    BENCH_ASHIFT, ncols, fn+1);
94 			}
95 
96 			/* estimate iteration count */
97 			iter_cnt = GEN_BENCH_MEMORY;
98 			iter_cnt /= zio_bench.io_size;
99 
100 			start = gethrtime();
101 			for (iter = 0; iter < iter_cnt; iter++)
102 				vdev_raidz_generate_parity(rm_bench);
103 			elapsed = NSEC2SEC((double)(gethrtime() - start));
104 
105 			disksize = (1ULL << ds) / rto_opts.rto_dcols;
106 			d_bw = (double)iter_cnt * (double)disksize;
107 			d_bw /= (1024.0 * 1024.0 * elapsed);
108 
109 			LOG(D_ALL, "%10s, %8s, %zu, %10llu, %lf, %lf, %u\n",
110 			    impl,
111 			    raidz_gen_name[fn],
112 			    rto_opts.rto_dcols,
113 			    (1ULL<<ds),
114 			    d_bw,
115 			    d_bw * (double)(ncols),
116 			    (unsigned)iter_cnt);
117 
118 			vdev_raidz_map_free(rm_bench);
119 		}
120 	}
121 }
122 
123 static void
124 run_gen_bench(void)
125 {
126 	char **impl_name;
127 
128 	LOG(D_INFO, DBLSEP "\nBenchmarking parity generation...\n\n");
129 	LOG(D_ALL, "impl, math, dcols, iosize, disk_bw, total_bw, iter\n");
130 
131 	for (impl_name = (char **)raidz_impl_names; *impl_name != NULL;
132 	    impl_name++) {
133 
134 		if (vdev_raidz_impl_set(*impl_name) != 0)
135 			continue;
136 
137 		run_gen_bench_impl(*impl_name);
138 	}
139 }
140 
141 static void
142 run_rec_bench_impl(const char *impl)
143 {
144 	int fn, ncols, nbad;
145 	uint64_t ds, iter_cnt, iter, disksize;
146 	hrtime_t start;
147 	double elapsed, d_bw;
148 	static const int tgt[7][3] = {
149 		{1, 2, 3},	/* rec_p:   bad QR & D[0]	*/
150 		{0, 2, 3},	/* rec_q:   bad PR & D[0]	*/
151 		{0, 1, 3},	/* rec_r:   bad PQ & D[0]	*/
152 		{2, 3, 4},	/* rec_pq:  bad R  & D[0][1]	*/
153 		{1, 3, 4},	/* rec_pr:  bad Q  & D[0][1]	*/
154 		{0, 3, 4},	/* rec_qr:  bad P  & D[0][1]	*/
155 		{3, 4, 5}	/* rec_pqr: bad    & D[0][1][2] */
156 	};
157 
158 	for (fn = 0; fn < RAIDZ_REC_NUM; fn++) {
159 		for (ds = MIN_CS_SHIFT; ds <= MAX_CS_SHIFT; ds++) {
160 
161 			/* create suitable raidz_map */
162 			ncols = rto_opts.rto_dcols + PARITY_PQR;
163 			zio_bench.io_size = 1ULL << ds;
164 
165 			/*
166 			 * raidz block is too short to test
167 			 * the requested method
168 			 */
169 			if (zio_bench.io_size / rto_opts.rto_dcols <
170 			    (1ULL << BENCH_ASHIFT))
171 				continue;
172 
173 			if (rto_opts.rto_expand) {
174 				rm_bench = vdev_raidz_map_alloc_expanded(
175 				    &zio_bench,
176 				    BENCH_ASHIFT, ncols+1, ncols,
177 				    PARITY_PQR,
178 				    rto_opts.rto_expand_offset, 0, B_FALSE);
179 			} else {
180 				rm_bench = vdev_raidz_map_alloc(&zio_bench,
181 				    BENCH_ASHIFT, ncols, PARITY_PQR);
182 			}
183 
184 			/* estimate iteration count */
185 			iter_cnt = (REC_BENCH_MEMORY);
186 			iter_cnt /= zio_bench.io_size;
187 
188 			/* calculate how many bad columns there are */
189 			nbad = MIN(3, raidz_ncols(rm_bench) -
190 			    raidz_parity(rm_bench));
191 
192 			start = gethrtime();
193 			for (iter = 0; iter < iter_cnt; iter++)
194 				vdev_raidz_reconstruct(rm_bench, tgt[fn], nbad);
195 			elapsed = NSEC2SEC((double)(gethrtime() - start));
196 
197 			disksize = (1ULL << ds) / rto_opts.rto_dcols;
198 			d_bw = (double)iter_cnt * (double)(disksize);
199 			d_bw /= (1024.0 * 1024.0 * elapsed);
200 
201 			LOG(D_ALL, "%10s, %8s, %zu, %10llu, %lf, %lf, %u\n",
202 			    impl,
203 			    raidz_rec_name[fn],
204 			    rto_opts.rto_dcols,
205 			    (1ULL<<ds),
206 			    d_bw,
207 			    d_bw * (double)ncols,
208 			    (unsigned)iter_cnt);
209 
210 			vdev_raidz_map_free(rm_bench);
211 		}
212 	}
213 }
214 
215 static void
216 run_rec_bench(void)
217 {
218 	char **impl_name;
219 
220 	LOG(D_INFO, DBLSEP "\nBenchmarking data reconstruction...\n\n");
221 	LOG(D_ALL, "impl, math, dcols, iosize, disk_bw, total_bw, iter\n");
222 
223 	for (impl_name = (char **)raidz_impl_names; *impl_name != NULL;
224 	    impl_name++) {
225 
226 		if (vdev_raidz_impl_set(*impl_name) != 0)
227 			continue;
228 
229 		run_rec_bench_impl(*impl_name);
230 	}
231 }
232 
233 void
234 run_raidz_benchmark(void)
235 {
236 	bench_init_raidz_map();
237 
238 	run_gen_bench();
239 	run_rec_bench();
240 
241 	bench_fini_raidz_maps();
242 }
243