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