xref: /netbsd-src/sys/dev/dm/dm_target_stripe.c (revision a0698ed9d41653d7a2378819ad501a285ca0d401)
1 /*$NetBSD: dm_target_stripe.c,v 1.23 2018/01/05 14:22:26 christos 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.23 2018/01/05 14:22:26 christos 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/vnode.h>
43 #include <sys/lwp.h>
44 
45 #include "dm.h"
46 
47 #ifdef DM_TARGET_MODULE
48 /*
49  * Every target can be compiled directly to dm driver or as a
50  * separate module this part of target is used for loading targets
51  * to dm driver.
52  * Target can be unloaded from kernel only if there are no users of
53  * it e.g. there are no devices which uses that target.
54  */
55 #include <sys/kernel.h>
56 #include <sys/module.h>
57 
58 MODULE(MODULE_CLASS_MISC, dm_target_stripe, NULL);
59 
60 static int
61 dm_target_stripe_modcmd(modcmd_t cmd, void *arg)
62 {
63 	dm_target_t *dmt;
64 	int r;
65 	dmt = NULL;
66 
67 	switch (cmd) {
68 	case MODULE_CMD_INIT:
69 		if ((dmt = dm_target_lookup("stripe")) != NULL) {
70 			dm_target_unbusy(dmt);
71 			return EEXIST;
72 		}
73 		dmt = dm_target_alloc("stripe");
74 
75 		dmt->version[0] = 1;
76 		dmt->version[1] = 0;
77 		dmt->version[2] = 0;
78 		strlcpy(dmt->name, "stripe", DM_MAX_TYPE_NAME);
79 		dmt->init = &dm_target_stripe_init;
80 		dmt->status = &dm_target_stripe_status;
81 		dmt->strategy = &dm_target_stripe_strategy;
82 		dmt->sync = &dm_target_stripe_sync;
83 		dmt->deps = &dm_target_stripe_deps;
84 		dmt->destroy = &dm_target_stripe_destroy;
85 		dmt->upcall = &dm_target_stripe_upcall;
86 		dmt->secsize = &dm_target_stripe_secsize;
87 
88 		r = dm_target_insert(dmt);
89 
90 		break;
91 
92 	case MODULE_CMD_FINI:
93 		r = dm_target_rem("stripe");
94 		break;
95 
96 	case MODULE_CMD_STAT:
97 		return ENOTTY;
98 
99 	default:
100 		return ENOTTY;
101 	}
102 
103 	return r;
104 }
105 #endif
106 
107 static void
108 dm_target_stripe_fini(dm_target_stripe_config_t *tsc)
109 {
110 	dm_target_linear_config_t *tlc;
111 
112 	if (tsc == NULL)
113 		return;
114 
115 	while ((tlc = TAILQ_FIRST(&tsc->stripe_devs)) != NULL) {
116 		TAILQ_REMOVE(&tsc->stripe_devs, tlc, entries);
117 		dm_pdev_decr(tlc->pdev);
118 		kmem_free(tlc, sizeof(*tlc));
119 	}
120 
121 	kmem_free(tsc, sizeof(*tsc));
122 }
123 
124 /*
125  * Init function called from dm_table_load_ioctl.
126  * DM_STRIPE_DEV_OFFSET should always hold the index of the first device-offset
127  * pair in the parameters.
128  * Example line sent to dm from lvm tools when using striped target.
129  * start length striped #stripes chunk_size device1 offset1 ... deviceN offsetN
130  * 0 65536 striped 2 512 /dev/hda 0 /dev/hdb 0
131  */
132 int
133 dm_target_stripe_init(dm_dev_t * dmv, void **target_config, char *params)
134 {
135 	dm_target_linear_config_t *tlc;
136 	dm_target_stripe_config_t *tsc;
137 	size_t len;
138 	char **ap, *argv[10];
139 	int strpc, strpi;
140 
141 	if (params == NULL)
142 		return EINVAL;
143 
144 	len = strlen(params) + 1;
145 
146 	/*
147 	 * Parse a string, containing tokens delimited by white space,
148 	 * into an argument vector
149 	 */
150 	for (ap = argv; ap <= &argv[9] &&
151 	    (*ap = strsep(&params, " \t")) != NULL;) {
152 		if (**ap != '\0')
153 			ap++;
154 	}
155 
156 	printf("Stripe target init function called!!\n");
157 
158 	printf("Stripe target chunk size %s number of stripes %s\n",
159 	    argv[1], argv[0]);
160 
161 	if ((tsc = kmem_alloc(sizeof(*tsc), KM_NOSLEEP)) == NULL)
162 		return ENOMEM;
163 
164 	/* Initialize linked list for striping devices */
165 	TAILQ_INIT(&tsc->stripe_devs);
166 
167 	/* Save length of param string */
168 	tsc->params_len = len;
169 	tsc->stripe_chunksize = atoi(argv[1]);
170 	tsc->stripe_num = (uint8_t) atoi(argv[0]);
171 
172 	strpc = DM_STRIPE_DEV_OFFSET + (tsc->stripe_num * 2);
173 	for (strpi = DM_STRIPE_DEV_OFFSET; strpi < strpc; strpi += 2) {
174 		printf("Stripe target device name %s -- offset %s\n",
175 		       argv[strpi], argv[strpi+1]);
176 
177 		tlc = kmem_alloc(sizeof(*tlc), KM_NOSLEEP);
178 		if ((tlc->pdev = dm_pdev_insert(argv[strpi])) == NULL) {
179 			kmem_free(tlc, sizeof(*tlc));
180 			dm_target_stripe_fini(tsc);
181 			return ENOENT;
182 		}
183 		tlc->offset = atoi(argv[strpi+1]);
184 
185 		/* Insert striping device to linked list. */
186 		TAILQ_INSERT_TAIL(&tsc->stripe_devs, tlc, entries);
187 	}
188 
189 	*target_config = tsc;
190 
191 	dmv->dev_type = DM_STRIPE_DEV;
192 
193 	return 0;
194 }
195 
196 /* Status routine called to get params string. */
197 char *
198 dm_target_stripe_status(void *target_config)
199 {
200 	dm_target_linear_config_t *tlc;
201 	dm_target_stripe_config_t *tsc;
202 	char *params, *tmp;
203 
204 	tsc = target_config;
205 
206 	params = kmem_alloc(DM_MAX_PARAMS_SIZE, KM_SLEEP);
207 	tmp = kmem_alloc(DM_MAX_PARAMS_SIZE, KM_SLEEP);
208 
209 	snprintf(params, DM_MAX_PARAMS_SIZE, "%d %" PRIu64,
210 	    tsc->stripe_num, tsc->stripe_chunksize);
211 
212 	TAILQ_FOREACH(tlc, &tsc->stripe_devs, entries) {
213 		snprintf(tmp, DM_MAX_PARAMS_SIZE, " %s %" PRIu64,
214 		    tlc->pdev->name, tlc->offset);
215 		strcat(params, tmp);
216 	}
217 
218 	kmem_free(tmp, DM_MAX_PARAMS_SIZE);
219 
220 	return params;
221 }
222 
223 /* Strategy routine called from dm_strategy. */
224 int
225 dm_target_stripe_strategy(dm_table_entry_t * table_en, struct buf * bp)
226 {
227 	dm_target_linear_config_t *tlc;
228 	dm_target_stripe_config_t *tsc;
229 	struct buf *nestbuf;
230 	uint64_t blkno, blkoff;
231 	uint64_t stripe, stripe_blknr;
232 	uint32_t stripe_off, stripe_rest, num_blks, issue_blks;
233 	int i, stripe_devnr;
234 
235 	tsc = table_en->target_config;
236 	if (tsc == NULL)
237 		return 0;
238 
239 /*	printf("Stripe target read function called %" PRIu64 "!!\n",
240 	tlc->offset);*/
241 
242 	/* calculate extent of request */
243 	KASSERT(bp->b_resid % DEV_BSIZE == 0);
244 
245 	blkno = bp->b_blkno;
246 	blkoff = 0;
247 	num_blks = bp->b_resid / DEV_BSIZE;
248 	for (;;) {
249 		/* blockno to strip piece nr */
250 		stripe = blkno / tsc->stripe_chunksize;
251 		stripe_off = blkno % tsc->stripe_chunksize;
252 
253 		/* where we are inside the strip */
254 		stripe_devnr = stripe % tsc->stripe_num;
255 		stripe_blknr = stripe / tsc->stripe_num;
256 
257 		/* how much is left before we hit a boundary */
258 		stripe_rest = tsc->stripe_chunksize - stripe_off;
259 
260 		/* issue this piece on stripe `stripe' */
261 		issue_blks = MIN(stripe_rest, num_blks);
262 		nestbuf = getiobuf(NULL, true);
263 
264 		nestiobuf_setup(bp, nestbuf, blkoff, issue_blks * DEV_BSIZE);
265 		nestbuf->b_blkno = stripe_blknr * tsc->stripe_chunksize + stripe_off;
266 
267 		tlc = TAILQ_FIRST(&tsc->stripe_devs);
268 		for (i = 0; i < stripe_devnr && tlc != NULL; i++)
269 			tlc = TAILQ_NEXT(tlc, entries);
270 
271 		/* by this point we should have an tlc */
272 		KASSERT(tlc != NULL);
273 
274 		nestbuf->b_blkno += tlc->offset;
275 
276 		VOP_STRATEGY(tlc->pdev->pdev_vnode, nestbuf);
277 
278 		blkno += issue_blks;
279 		blkoff += issue_blks * DEV_BSIZE;
280 		num_blks -= issue_blks;
281 
282 		if (num_blks <= 0)
283 			break;
284 	}
285 
286 	return 0;
287 }
288 
289 /* Sync underlying disk caches. */
290 int
291 dm_target_stripe_sync(dm_table_entry_t * table_en)
292 {
293 	int cmd, err;
294 	dm_target_stripe_config_t *tsc;
295 	dm_target_linear_config_t *tlc;
296 
297 	tsc = table_en->target_config;
298 
299 	err = 0;
300 	cmd = 1;
301 
302 	TAILQ_FOREACH(tlc, &tsc->stripe_devs, entries) {
303 		if ((err = VOP_IOCTL(tlc->pdev->pdev_vnode, DIOCCACHESYNC,
304 			    &cmd, FREAD|FWRITE, kauth_cred_get())) != 0)
305 			return err;
306 	}
307 
308 	return err;
309 
310 }
311 
312 /* Destroy target specific data. */
313 int
314 dm_target_stripe_destroy(dm_table_entry_t * table_en)
315 {
316 	dm_target_stripe_fini(table_en->target_config);
317 	table_en->target_config = NULL;
318 
319 	/* Unbusy target so we can unload it */
320 	dm_target_unbusy(table_en->target);
321 
322 	return 0;
323 }
324 
325 /* Doesn't not need to do anything here. */
326 int
327 dm_target_stripe_deps(dm_table_entry_t * table_en, prop_array_t prop_array)
328 {
329 	dm_target_stripe_config_t *tsc;
330 	dm_target_linear_config_t *tlc;
331 
332 	if (table_en->target_config == NULL)
333 		return ENOENT;
334 
335 	tsc = table_en->target_config;
336 
337 	TAILQ_FOREACH(tlc, &tsc->stripe_devs, entries) {
338 		prop_array_add_uint64(prop_array,
339 		    (uint64_t) tlc->pdev->pdev_vnode->v_rdev);
340 	}
341 
342 	return 0;
343 }
344 
345 /* Unsupported for this target. */
346 int
347 dm_target_stripe_upcall(dm_table_entry_t * table_en, struct buf * bp)
348 {
349 	return 0;
350 }
351 
352 /*
353  * Compute physical block size
354  * For a stripe target we chose the maximum sector size of all
355  * stripe devices. For the supported power-of-2 sizes this is equivalent
356  * to the least common multiple.
357  */
358 int
359 dm_target_stripe_secsize(dm_table_entry_t * table_en, unsigned *secsizep)
360 {
361 	dm_target_linear_config_t *tlc;
362 	dm_target_stripe_config_t *tsc;
363 	unsigned secsize;
364 
365 	secsize = 0;
366 
367 	tsc = table_en->target_config;
368 	if (tsc != NULL) {
369 		TAILQ_FOREACH(tlc, &tsc->stripe_devs, entries) {
370 			if (secsize < tlc->pdev->pdev_secsize)
371 				secsize = tlc->pdev->pdev_secsize;
372 		}
373 	}
374 
375 	*secsizep = secsize;
376 
377 	return 0;
378 }
379