xref: /netbsd/sys/dev/dm/dm_target_stripe.c (revision ac70dd37)
1 /*$NetBSD: dm_target_stripe.c,v 1.26 2019/12/03 15:47:38 tkusumi Exp $*/
2 
3 /*
4  * Copyright (c) 2009 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Adam Hamsik.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 #include <sys/cdefs.h>
32 __KERNEL_RCSID(0, "$NetBSD: dm_target_stripe.c,v 1.26 2019/12/03 15:47:38 tkusumi Exp $");
33 
34 /*
35  * This file implements initial version of device-mapper stripe target.
36  */
37 #include <sys/types.h>
38 #include <sys/param.h>
39 
40 #include <sys/buf.h>
41 #include <sys/kmem.h>
42 #include <sys/lwp.h>
43 
44 #include "dm.h"
45 
46 #ifdef DM_TARGET_MODULE
47 /*
48  * Every target can be compiled directly to dm driver or as a
49  * separate module this part of target is used for loading targets
50  * to dm driver.
51  * Target can be unloaded from kernel only if there are no users of
52  * it e.g. there are no devices which uses that target.
53  */
54 #include <sys/kernel.h>
55 #include <sys/module.h>
56 
57 MODULE(MODULE_CLASS_MISC, dm_target_stripe, NULL);
58 
59 static int
60 dm_target_stripe_modcmd(modcmd_t cmd, void *arg)
61 {
62 	dm_target_t *dmt;
63 	int r;
64 	dmt = NULL;
65 
66 	switch (cmd) {
67 	case MODULE_CMD_INIT:
68 		if ((dmt = dm_target_lookup("stripe")) != NULL) {
69 			dm_target_unbusy(dmt);
70 			return EEXIST;
71 		}
72 		dmt = dm_target_alloc("stripe");
73 
74 		dmt->version[0] = 1;
75 		dmt->version[1] = 0;
76 		dmt->version[2] = 0;
77 		strlcpy(dmt->name, "stripe", DM_MAX_TYPE_NAME);
78 		dmt->init = &dm_target_stripe_init;
79 		dmt->status = &dm_target_stripe_status;
80 		dmt->strategy = &dm_target_stripe_strategy;
81 		dmt->sync = &dm_target_stripe_sync;
82 		dmt->deps = &dm_target_stripe_deps;
83 		dmt->destroy = &dm_target_stripe_destroy;
84 		dmt->upcall = &dm_target_stripe_upcall;
85 		dmt->secsize = &dm_target_stripe_secsize;
86 
87 		r = dm_target_insert(dmt);
88 
89 		break;
90 
91 	case MODULE_CMD_FINI:
92 		r = dm_target_rem("stripe");
93 		break;
94 
95 	case MODULE_CMD_STAT:
96 		return ENOTTY;
97 
98 	default:
99 		return ENOTTY;
100 	}
101 
102 	return r;
103 }
104 #endif
105 
106 static void
107 dm_target_stripe_fini(dm_target_stripe_config_t *tsc)
108 {
109 	dm_target_linear_config_t *tlc;
110 
111 	if (tsc == NULL)
112 		return;
113 
114 	while ((tlc = TAILQ_FIRST(&tsc->stripe_devs)) != NULL) {
115 		TAILQ_REMOVE(&tsc->stripe_devs, tlc, entries);
116 		dm_pdev_decr(tlc->pdev);
117 		kmem_free(tlc, sizeof(*tlc));
118 	}
119 
120 	kmem_free(tsc, sizeof(*tsc));
121 }
122 
123 /*
124  * Init function called from dm_table_load_ioctl.
125  * DM_STRIPE_DEV_OFFSET should always hold the index of the first device-offset
126  * pair in the parameters.
127  * Example line sent to dm from lvm tools when using striped target.
128  * start length striped #stripes chunk_size device1 offset1 ... deviceN offsetN
129  * 0 65536 striped 2 512 /dev/hda 0 /dev/hdb 0
130  */
131 int
132 dm_target_stripe_init(dm_dev_t * dmv, void **target_config, char *params)
133 {
134 	dm_target_linear_config_t *tlc;
135 	dm_target_stripe_config_t *tsc;
136 	size_t len;
137 	char **ap, *argv[10];
138 	int strpc, strpi;
139 
140 	if (params == NULL)
141 		return EINVAL;
142 
143 	len = strlen(params) + 1;
144 
145 	/*
146 	 * Parse a string, containing tokens delimited by white space,
147 	 * into an argument vector
148 	 */
149 	for (ap = argv; ap <= &argv[9] &&
150 	    (*ap = strsep(&params, " \t")) != NULL;) {
151 		if (**ap != '\0')
152 			ap++;
153 	}
154 
155 	printf("Stripe target init function called!!\n");
156 
157 	printf("Stripe target chunk size %s number of stripes %s\n",
158 	    argv[1], argv[0]);
159 
160 	tsc = kmem_alloc(sizeof(*tsc), KM_SLEEP);
161 
162 	/* Initialize linked list for striping devices */
163 	TAILQ_INIT(&tsc->stripe_devs);
164 
165 	/* Save length of param string */
166 	tsc->params_len = len;
167 	tsc->stripe_chunksize = atoi(argv[1]);
168 	tsc->stripe_num = (uint8_t) atoi(argv[0]);
169 
170 	strpc = DM_STRIPE_DEV_OFFSET + (tsc->stripe_num * 2);
171 	for (strpi = DM_STRIPE_DEV_OFFSET; strpi < strpc; strpi += 2) {
172 		printf("Stripe target device name %s -- offset %s\n",
173 		       argv[strpi], argv[strpi+1]);
174 
175 		tlc = kmem_alloc(sizeof(*tlc), KM_SLEEP);
176 		if ((tlc->pdev = dm_pdev_insert(argv[strpi])) == NULL) {
177 			kmem_free(tlc, sizeof(*tlc));
178 			dm_target_stripe_fini(tsc);
179 			return ENOENT;
180 		}
181 		tlc->offset = atoi(argv[strpi+1]);
182 
183 		/* Insert striping device to linked list. */
184 		TAILQ_INSERT_TAIL(&tsc->stripe_devs, tlc, entries);
185 	}
186 
187 	*target_config = tsc;
188 
189 	return 0;
190 }
191 
192 /* Status routine called to get params string. */
193 char *
194 dm_target_stripe_status(void *target_config)
195 {
196 	dm_target_linear_config_t *tlc;
197 	dm_target_stripe_config_t *tsc;
198 	char *params, *tmp;
199 
200 	tsc = target_config;
201 
202 	params = kmem_alloc(DM_MAX_PARAMS_SIZE, KM_SLEEP);
203 	tmp = kmem_alloc(DM_MAX_PARAMS_SIZE, KM_SLEEP);
204 
205 	snprintf(params, DM_MAX_PARAMS_SIZE, "%d %" PRIu64,
206 	    tsc->stripe_num, tsc->stripe_chunksize);
207 
208 	TAILQ_FOREACH(tlc, &tsc->stripe_devs, entries) {
209 		snprintf(tmp, DM_MAX_PARAMS_SIZE, " %s %" PRIu64,
210 		    tlc->pdev->name, tlc->offset);
211 		strcat(params, tmp);
212 	}
213 
214 	kmem_free(tmp, DM_MAX_PARAMS_SIZE);
215 
216 	return params;
217 }
218 
219 /* Strategy routine called from dm_strategy. */
220 int
221 dm_target_stripe_strategy(dm_table_entry_t * table_en, struct buf * bp)
222 {
223 	dm_target_linear_config_t *tlc;
224 	dm_target_stripe_config_t *tsc;
225 	struct buf *nestbuf;
226 	uint64_t blkno, blkoff;
227 	uint64_t stripe, stripe_blknr;
228 	uint32_t stripe_off, stripe_rest, num_blks, issue_blks;
229 	int i, stripe_devnr;
230 
231 	tsc = table_en->target_config;
232 	if (tsc == NULL)
233 		return 0;
234 
235 /*	printf("Stripe target read function called %" PRIu64 "!!\n",
236 	tlc->offset);*/
237 
238 	/* calculate extent of request */
239 	KASSERT(bp->b_resid % DEV_BSIZE == 0);
240 
241 	blkno = bp->b_blkno;
242 	blkoff = 0;
243 	num_blks = bp->b_resid / DEV_BSIZE;
244 	for (;;) {
245 		/* blockno to strip piece nr */
246 		stripe = blkno / tsc->stripe_chunksize;
247 		stripe_off = blkno % tsc->stripe_chunksize;
248 
249 		/* where we are inside the strip */
250 		stripe_devnr = stripe % tsc->stripe_num;
251 		stripe_blknr = stripe / tsc->stripe_num;
252 
253 		/* how much is left before we hit a boundary */
254 		stripe_rest = tsc->stripe_chunksize - stripe_off;
255 
256 		/* issue this piece on stripe `stripe' */
257 		issue_blks = MIN(stripe_rest, num_blks);
258 		nestbuf = getiobuf(NULL, true);
259 
260 		nestiobuf_setup(bp, nestbuf, blkoff, issue_blks * DEV_BSIZE);
261 		nestbuf->b_blkno = stripe_blknr * tsc->stripe_chunksize + stripe_off;
262 
263 		tlc = TAILQ_FIRST(&tsc->stripe_devs);
264 		for (i = 0; i < stripe_devnr && tlc != NULL; i++)
265 			tlc = TAILQ_NEXT(tlc, entries);
266 
267 		/* by this point we should have an tlc */
268 		KASSERT(tlc != NULL);
269 
270 		nestbuf->b_blkno += tlc->offset;
271 
272 		VOP_STRATEGY(tlc->pdev->pdev_vnode, nestbuf);
273 
274 		blkno += issue_blks;
275 		blkoff += issue_blks * DEV_BSIZE;
276 		num_blks -= issue_blks;
277 
278 		if (num_blks <= 0)
279 			break;
280 	}
281 
282 	return 0;
283 }
284 
285 /* Sync underlying disk caches. */
286 int
287 dm_target_stripe_sync(dm_table_entry_t * table_en)
288 {
289 	int cmd, err;
290 	dm_target_stripe_config_t *tsc;
291 	dm_target_linear_config_t *tlc;
292 
293 	tsc = table_en->target_config;
294 
295 	err = 0;
296 	cmd = 1;
297 
298 	TAILQ_FOREACH(tlc, &tsc->stripe_devs, entries) {
299 		if ((err = VOP_IOCTL(tlc->pdev->pdev_vnode, DIOCCACHESYNC,
300 			    &cmd, FREAD|FWRITE, kauth_cred_get())) != 0)
301 			return err;
302 	}
303 
304 	return err;
305 
306 }
307 
308 /* Destroy target specific data. */
309 int
310 dm_target_stripe_destroy(dm_table_entry_t * table_en)
311 {
312 	dm_target_stripe_fini(table_en->target_config);
313 	table_en->target_config = NULL;
314 
315 	/* Unbusy target so we can unload it */
316 	dm_target_unbusy(table_en->target);
317 
318 	return 0;
319 }
320 
321 /* Doesn't not need to do anything here. */
322 int
323 dm_target_stripe_deps(dm_table_entry_t * table_en, prop_array_t prop_array)
324 {
325 	dm_target_stripe_config_t *tsc;
326 	dm_target_linear_config_t *tlc;
327 
328 	if (table_en->target_config == NULL)
329 		return ENOENT;
330 
331 	tsc = table_en->target_config;
332 
333 	TAILQ_FOREACH(tlc, &tsc->stripe_devs, entries) {
334 		prop_array_add_uint64(prop_array,
335 		    (uint64_t) tlc->pdev->pdev_vnode->v_rdev);
336 	}
337 
338 	return 0;
339 }
340 
341 /* Unsupported for this target. */
342 int
343 dm_target_stripe_upcall(dm_table_entry_t * table_en, struct buf * bp)
344 {
345 	return 0;
346 }
347 
348 /*
349  * Compute physical block size
350  * For a stripe target we chose the maximum sector size of all
351  * stripe devices. For the supported power-of-2 sizes this is equivalent
352  * to the least common multiple.
353  */
354 int
355 dm_target_stripe_secsize(dm_table_entry_t * table_en, unsigned *secsizep)
356 {
357 	dm_target_linear_config_t *tlc;
358 	dm_target_stripe_config_t *tsc;
359 	unsigned secsize;
360 
361 	secsize = 0;
362 
363 	tsc = table_en->target_config;
364 	if (tsc != NULL) {
365 		TAILQ_FOREACH(tlc, &tsc->stripe_devs, entries) {
366 			if (secsize < tlc->pdev->pdev_secsize)
367 				secsize = tlc->pdev->pdev_secsize;
368 		}
369 	}
370 
371 	*secsizep = secsize;
372 
373 	return 0;
374 }
375