dm_target_stripe.c revision 1.12 1 /*$NetBSD: dm_target_stripe.c,v 1.12 2010/11/15 05:54:38 uebayasi 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(¶ms, " \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