Merge branch 'vendor/OPENSSL'
[dragonfly.git] / sys / dev / disk / dm / targets / striped / dm_target_striped.c
1 /*
2  * Copyright (c) 2009 The NetBSD Foundation, Inc.
3  * All rights reserved.
4  *
5  * This code is derived from software contributed to The NetBSD Foundation
6  * by Adam Hamsik.
7  *
8  * This code is further derived from software contributed to the
9  * DragonFly project by Alex Hornung and Matthew Dillon
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  * POSSIBILITY OF SUCH DAMAGE.
31  *
32  * $NetBSD: dm_target_stripe.c,v 1.9 2010/01/04 00:14:41 haad Exp $
33  */
34
35 /*
36  * This file implements initial version of device-mapper stripe target.
37  *
38  * DragonFly changes: Increase to an unlimited number of stripes
39  */
40 #include <sys/malloc.h>
41
42 #include <dev/disk/dm/dm.h>
43 MALLOC_DEFINE(M_DMSTRIPE, "dm_striped", "Device Mapper Target Striped");
44
45 #define MAX_STRIPES 32
46 /* #define USE_NUM_ERROR */
47
48 struct target_stripe_dev {
49         dm_pdev_t *pdev;
50         uint64_t offset;
51         int num_error;
52 };
53
54 typedef struct target_stripe_config {
55         int stripe_num;
56         uint64_t stripe_chunksize;
57         struct target_stripe_dev stripe_devs[0];
58 } dm_target_stripe_config_t;
59
60 static void dm_target_stripe_destroy_config(dm_target_stripe_config_t *tsc);
61
62 /*
63  * Init function called from dm_table_load_ioctl.
64  *
65  * Example line sent to dm from lvm tools when using striped target.
66  * start length striped #stripes chunk_size device1 offset1 ... deviceN offsetN
67  */
68 static int
69 dm_target_stripe_init(dm_table_entry_t *table_en, int argc, char **argv)
70 {
71         dm_target_stripe_config_t *tsc;
72         char *arg;
73         int i, n, siz, chunksize;
74
75         if (argc < 4) {
76                 kprintf("Striped target takes 4 or more args\n");
77                 return EINVAL;
78         }
79
80         n = (int)atoi64(argv[0]);
81         if (n <= 0 || n > MAX_STRIPES) {
82                 kprintf("dm: Error %d stripes not supported (%d max)\n",
83                         n, MAX_STRIPES);
84                 return ENOTSUP;
85         }
86 #if 0
87         if (table_en->length % n) {
88                 kprintf("dm: Target device size not multiple of stripes\n");
89                 return EINVAL;
90         }
91 #endif
92         if (argc != (2 + n * 2)) {
93                 kprintf("dm: Invalid argc %d for %d stripe devices\n",
94                         argc, n);
95                 return EINVAL;
96         }
97
98         chunksize = atoi64(argv[1]);
99         if (chunksize < 1 || chunksize * DEV_BSIZE > MAXPHYS) {
100                 kprintf("dm: Error unsupported chunk size %jdKB\n",
101                         (intmax_t)chunksize * DEV_BSIZE / 1024);
102                 return EINVAL;
103         }
104 #if 0
105         if ((table_en->length / n) % chunksize) {
106                 kprintf("dm: Stripe device size not multiple of chunk size\n");
107                 return EINVAL;
108         }
109 #endif
110
111         siz = sizeof(dm_target_stripe_config_t) +
112                 n * sizeof(struct target_stripe_dev);
113         tsc = kmalloc(siz, M_DMSTRIPE, M_WAITOK | M_ZERO);
114         if (tsc == NULL)
115                 return ENOMEM;
116         tsc->stripe_num = n;
117         tsc->stripe_chunksize = chunksize;
118
119         /*
120          * Parse the devices
121          */
122
123         kprintf("dm: Stripe %d devices chunk size %dKB\n",
124                 (int)tsc->stripe_num,
125                 (int)tsc->stripe_chunksize
126         );
127
128         argv += 2;
129         for (n = 0, i = 0; n < tsc->stripe_num; ++n) {
130                 arg = argv[i++];
131                 KKASSERT(arg);
132                 tsc->stripe_devs[n].pdev = dm_pdev_insert(arg);
133                 if (tsc->stripe_devs[n].pdev == NULL)
134                         break;
135                 arg = argv[i++];
136                 KKASSERT(arg);
137                 tsc->stripe_devs[n].offset = atoi64(arg);
138                 dm_table_add_deps(table_en, tsc->stripe_devs[n].pdev);
139         }
140         if (n != tsc->stripe_num) {
141                 dm_target_stripe_destroy_config(tsc);
142                 return (ENOENT);
143         }
144
145         dm_table_init_target(table_en, DM_STRIPE_DEV, tsc);
146
147         return 0;
148 }
149
150 /*
151  * Info routine called to get params string.
152  */
153 static char *
154 dm_target_stripe_info(void *target_config)
155 {
156         dm_target_stripe_config_t *tsc;
157         char *params;
158         char *ptr;
159         char buf[MAX_STRIPES + 1];
160         size_t len;
161         int ret;
162         int i;
163
164         tsc = target_config;
165
166         len = DM_MAX_PARAMS_SIZE;
167         params = dm_alloc_string(len);
168         ptr = params;
169
170         ret = ksnprintf(ptr, len, "%d ", tsc->stripe_num);
171         ptr += ret;
172         len -= ret;
173
174         memset(buf, 0, sizeof(buf));
175         for (i = 0; i < tsc->stripe_num; i++) {
176                 ret = ksnprintf(ptr, len, "%s ",
177                         tsc->stripe_devs[i].pdev->udev_name);
178                 if (tsc->stripe_devs[i].num_error) /* no lock */
179                         buf[i] = 'D';
180                 else
181                         buf[i] = 'A';
182                 ptr += ret;
183                 len -= ret;
184         }
185
186         ret = ksnprintf(ptr, len, "1 %s", buf);
187         ptr += ret;
188         len -= ret;
189
190         return params;
191 }
192
193 /*
194  * Table routine called to get params string.
195  */
196 static char *
197 dm_target_stripe_table(void *target_config)
198 {
199         dm_target_stripe_config_t *tsc;
200         char *params;
201         char *ptr;
202         size_t len;
203         int ret;
204         int i;
205
206         tsc = target_config;
207
208         len = DM_MAX_PARAMS_SIZE;
209         params = dm_alloc_string(len);
210         ptr = params;
211
212         ret = ksnprintf(ptr, len, "%d %jd",
213                 tsc->stripe_num,
214                 (intmax_t)tsc->stripe_chunksize);
215         ptr += ret;
216         len -= ret;
217
218         for (i = 0; i < tsc->stripe_num; i++) {
219                 ret = ksnprintf(ptr, len, " %s %jd",
220                         tsc->stripe_devs[i].pdev->udev_name,
221                         (intmax_t)tsc->stripe_devs[i].offset);
222                 ptr += ret;
223                 len -= ret;
224         }
225
226         return params;
227 }
228
229 #ifdef USE_NUM_ERROR
230 static void
231 dm_target_stripe_iodone(struct bio *bio)
232 {
233         struct bio *obio;
234         struct buf *bp;
235         dm_target_stripe_config_t *tsc;
236
237         bp = bio->bio_buf;
238         tsc = bio->bio_caller_info1.ptr;
239
240         if (bp->b_error) {
241                 int devnr;
242                 uint64_t blkno, stripe;
243
244                 blkno = bio->bio_offset / DEV_BSIZE;
245                 stripe = blkno / tsc->stripe_chunksize;
246                 devnr = stripe % tsc->stripe_num;
247                 KKASSERT(devnr < MAX_STRIPES);
248                 tsc->stripe_devs[devnr].num_error++;
249
250                 dmdebug("stripe_iodone: device=%d error=%d\n",
251                         devnr, bp->b_error);
252         }
253
254         obio = pop_bio(bio);
255         biodone(obio);
256 }
257
258 static __inline
259 struct bio *get_stripe_bio(struct bio *bio, void *priv)
260 {
261         struct bio *nbio;
262
263         nbio = push_bio(bio);
264         nbio->bio_caller_info1.ptr = priv;
265         nbio->bio_done = dm_target_stripe_iodone;
266
267         return nbio;
268 }
269 #else
270 static __inline
271 struct bio *get_stripe_bio(struct bio *bio, void *priv __unused)
272 {
273         return bio;
274 }
275 #endif
276
277 /*
278  * Strategy routine called from dm_strategy.
279  */
280 static int
281 dm_target_stripe_strategy(dm_table_entry_t *table_en, struct buf *bp)
282 {
283         dm_target_stripe_config_t *tsc;
284         struct bio *bio = &bp->b_bio1;
285         struct bio *nbio;
286         struct buf *nestbuf;
287         struct target_stripe_dev *dev;
288         uint64_t blkno, blkoff;
289         uint64_t stripe, blknr;
290         uint32_t stripe_off, stripe_rest, num_blks, issue_blks;
291         int devnr;
292
293         tsc = table_en->target_config;
294         if (tsc == NULL)
295                 return 0;
296
297         /* calculate extent of request */
298         KKASSERT(bp->b_resid % DEV_BSIZE == 0);
299
300         switch(bp->b_cmd) {
301         case BUF_CMD_READ:
302         case BUF_CMD_WRITE:
303         case BUF_CMD_FREEBLKS:
304                 /*
305                  * Loop through to individual operations
306                  */
307                 blkno = bio->bio_offset / DEV_BSIZE;
308                 blkoff = 0;
309                 num_blks = bp->b_resid / DEV_BSIZE;
310                 nestiobuf_init(bio);
311
312                 while (num_blks > 0) {
313                         /* blockno to stripe piece nr */
314                         stripe = blkno / tsc->stripe_chunksize;
315                         stripe_off = blkno % tsc->stripe_chunksize;
316
317                         /* where we are inside the stripe */
318                         devnr = stripe % tsc->stripe_num;
319                         blknr = stripe / tsc->stripe_num;
320                         dev = &tsc->stripe_devs[devnr];
321
322                         /* how much is left before we hit a boundary */
323                         stripe_rest = tsc->stripe_chunksize - stripe_off;
324
325                         /* issue this piece on stripe `stripe' */
326                         issue_blks = MIN(stripe_rest, num_blks);
327                         nestbuf = getpbuf(NULL);
328                         nestbuf->b_flags |= bio->bio_buf->b_flags & B_HASBOGUS;
329
330                         nestiobuf_add(bio, nestbuf, blkoff,
331                                         issue_blks * DEV_BSIZE, NULL);
332
333                         nbio = get_stripe_bio(&nestbuf->b_bio1, tsc);
334                         nbio->bio_offset = blknr * tsc->stripe_chunksize;
335                         nbio->bio_offset += stripe_off;
336                         nbio->bio_offset += dev->offset;
337                         nbio->bio_offset *= DEV_BSIZE;
338
339                         vn_strategy(dev->pdev->pdev_vnode, nbio);
340
341                         blkno += issue_blks;
342                         blkoff += issue_blks * DEV_BSIZE;
343                         num_blks -= issue_blks;
344                 }
345                 nestiobuf_start(bio);
346                 break;
347         case BUF_CMD_FLUSH:
348                 nestiobuf_init(bio);
349                 for (devnr = 0; devnr < tsc->stripe_num; ++devnr) {
350                         dev = &tsc->stripe_devs[devnr];
351                         nestbuf = getpbuf(NULL);
352                         nestbuf->b_flags |= bio->bio_buf->b_flags & B_HASBOGUS;
353
354                         nestiobuf_add(bio, nestbuf, 0, 0, NULL);
355
356                         nbio = get_stripe_bio(&nestbuf->b_bio1, tsc);
357                         nbio->bio_offset = 0;
358
359                         vn_strategy(dev->pdev->pdev_vnode, nbio);
360                 }
361                 nestiobuf_start(bio);
362                 break;
363         default:
364                 bp->b_flags |= B_ERROR;
365                 bp->b_error = EIO;
366                 biodone(bio);
367                 break;
368         }
369         return 0;
370 }
371
372
373 static int
374 dm_target_stripe_dump(dm_table_entry_t *table_en, void *data, size_t length, off_t offset)
375 {
376         dm_target_stripe_config_t *tsc;
377         uint64_t blkno, blkoff;
378         uint64_t stripe, blknr;
379         uint32_t stripe_off, stripe_rest, num_blks, issue_blks;
380         uint64_t off2, len2;
381         int devnr;
382
383         tsc = table_en->target_config;
384         if (tsc == NULL)
385                 return 0;
386
387         /* calculate extent of request */
388         KKASSERT(length % DEV_BSIZE == 0);
389
390         blkno = offset / DEV_BSIZE;
391         blkoff = 0;
392         num_blks = length / DEV_BSIZE;
393
394         /*
395          * 0 length means flush buffers and return
396          */
397         if (length == 0) {
398                 for (devnr = 0; devnr < tsc->stripe_num; ++devnr) {
399                         if (tsc->stripe_devs[devnr].pdev->pdev_vnode->v_rdev == NULL)
400                                 return ENXIO;
401
402                         dev_ddump(tsc->stripe_devs[devnr].pdev->pdev_vnode->v_rdev,
403                             data, 0, offset, 0);
404                 }
405                 return 0;
406         }
407
408         while (num_blks > 0) {
409                 /* blockno to stripe piece nr */
410                 stripe = blkno / tsc->stripe_chunksize;
411                 stripe_off = blkno % tsc->stripe_chunksize;
412
413                 /* where we are inside the stripe */
414                 devnr = stripe % tsc->stripe_num;
415                 blknr = stripe / tsc->stripe_num;
416
417                 /* how much is left before we hit a boundary */
418                 stripe_rest = tsc->stripe_chunksize - stripe_off;
419
420                 /* issue this piece on stripe `stripe' */
421                 issue_blks = MIN(stripe_rest, num_blks);
422
423 #if 0
424                 nestiobuf_add(bio, nestbuf, blkoff,
425                                 issue_blks * DEV_BSIZE);
426 #endif
427                 len2 = issue_blks * DEV_BSIZE;
428
429                 /* I need number of bytes. */
430                 off2 = blknr * tsc->stripe_chunksize + stripe_off;
431                 off2 += tsc->stripe_devs[devnr].offset;
432                 off2 *= DEV_BSIZE;
433                 off2 = dm_pdev_correct_dump_offset(tsc->stripe_devs[devnr].pdev,
434                     off2);
435
436                 if (tsc->stripe_devs[devnr].pdev->pdev_vnode->v_rdev == NULL)
437                         return ENXIO;
438
439                 dev_ddump(tsc->stripe_devs[devnr].pdev->pdev_vnode->v_rdev,
440                     (char *)data + blkoff, 0, off2, len2);
441
442                 blkno += issue_blks;
443                 blkoff += issue_blks * DEV_BSIZE;
444                 num_blks -= issue_blks;
445         }
446
447         return 0;
448 }
449
450 /*
451  * Destroy a dm table entry for stripes.
452  */
453 static int
454 dm_target_stripe_destroy(dm_table_entry_t *table_en)
455 {
456         if (table_en->target_config != NULL)
457                 dm_target_stripe_destroy_config(table_en->target_config);
458
459         return 0;
460 }
461
462 static void
463 dm_target_stripe_destroy_config(dm_target_stripe_config_t *tsc)
464 {
465         int n;
466
467         for (n = 0; n < tsc->stripe_num; ++n) {
468                 if (tsc->stripe_devs[n].pdev) {
469                         dm_pdev_decr(tsc->stripe_devs[n].pdev);
470                         tsc->stripe_devs[n].pdev = NULL;
471                 }
472         }
473         kfree(tsc, M_DMSTRIPE);
474 }
475
476 static int
477 dmts_mod_handler(module_t mod, int type, void *unused)
478 {
479         dm_target_t *dmt = NULL;
480         int err = 0;
481
482         switch(type) {
483         case MOD_LOAD:
484                 if ((dmt = dm_target_lookup("striped")) != NULL) {
485                         dm_target_unbusy(dmt);
486                         return EEXIST;
487                 }
488                 dmt = dm_target_alloc("striped");
489                 dmt->version[0] = 1;
490                 dmt->version[1] = 0;
491                 dmt->version[2] = 3;
492                 dmt->init = &dm_target_stripe_init;
493                 dmt->destroy = &dm_target_stripe_destroy;
494                 dmt->strategy = &dm_target_stripe_strategy;
495                 dmt->table = &dm_target_stripe_table;
496                 dmt->info = &dm_target_stripe_info;
497                 dmt->dump = &dm_target_stripe_dump;
498                 dmt->max_argc = 2 + (MAX_STRIPES * 2);
499
500                 err = dm_target_insert(dmt);
501                 if (err == 0)
502                         kprintf("dm_target_striped: Successfully initialized\n");
503                 break;
504
505         case MOD_UNLOAD:
506                 err = dm_target_remove("striped");
507                 if (err == 0)
508                         kprintf("dm_target_striped: unloaded\n");
509                 break;
510
511         default:
512                 break;
513         }
514
515         return err;
516 }
517
518 DM_TARGET_MODULE(dm_target_striped, dmts_mod_handler);