31d475e21d86b7ee9e47a70e50269dfef6798b4a
[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/types.h>
41
42 #include <sys/buf.h>
43 #include <sys/malloc.h>
44 #include <sys/vnode.h>
45
46 #include <dev/disk/dm/dm.h>
47 MALLOC_DEFINE(M_DMSTRIPE, "dm_striped", "Device Mapper Target Striped");
48
49 #define MAX_STRIPES 32
50 /* #define USE_NUM_ERROR */
51
52 struct target_stripe_dev {
53         dm_pdev_t *pdev;
54         uint64_t offset;
55         int num_error;
56 };
57
58 typedef struct target_stripe_config {
59         int stripe_num;
60         uint64_t stripe_chunksize;
61         struct target_stripe_dev stripe_devs[0];
62 } dm_target_stripe_config_t;
63
64 static void dm_target_stripe_destroy_config(dm_target_stripe_config_t *tsc);
65
66 /*
67  * Init function called from dm_table_load_ioctl.
68  *
69  * Example line sent to dm from lvm tools when using striped target.
70  * start length striped #stripes chunk_size device1 offset1 ... deviceN offsetN
71  */
72 static int
73 dm_target_stripe_init(dm_table_entry_t *table_en, int argc, char **argv)
74 {
75         dm_target_stripe_config_t *tsc;
76         char *arg;
77         int i, n, siz, chunksize;
78
79         if (argc < 4) {
80                 kprintf("Striped target takes 4 or more args\n");
81                 return EINVAL;
82         }
83
84         n = (int)atoi64(argv[0]);
85         if (n <= 0 || n > MAX_STRIPES) {
86                 kprintf("dm: Error %d stripes not supported (%d max)\n",
87                         n, MAX_STRIPES);
88                 return ENOTSUP;
89         }
90 #if 0
91         if (table_en->length % n) {
92                 kprintf("dm: Target device size not multiple of stripes\n");
93                 return EINVAL;
94         }
95 #endif
96         if (argc != (2 + n * 2)) {
97                 kprintf("dm: Invalid argc %d for %d stripe devices\n",
98                         argc, n);
99                 return EINVAL;
100         }
101
102         chunksize = atoi64(argv[1]);
103         if (chunksize < 1 || chunksize * DEV_BSIZE > MAXPHYS) {
104                 kprintf("dm: Error unsupported chunk size %jdKB\n",
105                         (intmax_t)chunksize * DEV_BSIZE / 1024);
106                 return EINVAL;
107         }
108 #if 0
109         if ((table_en->length / n) % chunksize) {
110                 kprintf("dm: Stripe device size not multiple of chunk size\n");
111                 return EINVAL;
112         }
113 #endif
114
115         siz = sizeof(dm_target_stripe_config_t) +
116                 n * sizeof(struct target_stripe_dev);
117         tsc = kmalloc(siz, M_DMSTRIPE, M_WAITOK | M_ZERO);
118         tsc->stripe_num = n;
119         tsc->stripe_chunksize = chunksize;
120
121         /*
122          * Parse the devices
123          */
124
125         kprintf("dm: Stripe %d devices chunk size %dKB\n",
126                 (int)tsc->stripe_num,
127                 (int)tsc->stripe_chunksize
128         );
129
130         argv += 2;
131         for (n = 0, i = 0; n < tsc->stripe_num; ++n) {
132                 arg = argv[i++];
133                 KKASSERT(arg);
134                 tsc->stripe_devs[n].pdev = dm_pdev_insert(arg);
135                 if (tsc->stripe_devs[n].pdev == NULL)
136                         break;
137                 arg = argv[i++];
138                 KKASSERT(arg);
139                 tsc->stripe_devs[n].offset = atoi64(arg);
140                 dm_table_add_deps(table_en, tsc->stripe_devs[n].pdev);
141         }
142         if (n != tsc->stripe_num) {
143                 dm_target_stripe_destroy_config(tsc);
144                 return (ENOENT);
145         }
146
147         dm_table_init_target(table_en, DM_STRIPE_DEV, tsc);
148
149         return 0;
150 }
151
152 /*
153  * Info routine called to get params string.
154  */
155 static char *
156 dm_target_stripe_info(void *target_config)
157 {
158         dm_target_stripe_config_t *tsc;
159         char *params;
160         char *ptr;
161         char buf[MAX_STRIPES + 1];
162         size_t len;
163         int ret;
164         int i;
165
166         tsc = target_config;
167
168         len = DM_MAX_PARAMS_SIZE;
169         params = dm_alloc_string(len);
170         ptr = params;
171
172         ret = ksnprintf(ptr, len, "%d ", tsc->stripe_num);
173         ptr += ret;
174         len -= ret;
175
176         memset(buf, 0, sizeof(buf));
177         for (i = 0; i < tsc->stripe_num; i++) {
178                 ret = ksnprintf(ptr, len, "%s ",
179                         tsc->stripe_devs[i].pdev->udev_name);
180                 if (tsc->stripe_devs[i].num_error) /* no lock */
181                         buf[i] = 'D';
182                 else
183                         buf[i] = 'A';
184                 ptr += ret;
185                 len -= ret;
186         }
187
188         ret = ksnprintf(ptr, len, "1 %s", buf);
189         ptr += ret;
190         len -= ret;
191
192         return params;
193 }
194
195 /*
196  * Table routine called to get params string.
197  */
198 static char *
199 dm_target_stripe_table(void *target_config)
200 {
201         dm_target_stripe_config_t *tsc;
202         char *params;
203         char *ptr;
204         size_t len;
205         int ret;
206         int i;
207
208         tsc = target_config;
209
210         len = DM_MAX_PARAMS_SIZE;
211         params = dm_alloc_string(len);
212         ptr = params;
213
214         ret = ksnprintf(ptr, len, "%d %jd",
215                 tsc->stripe_num,
216                 (intmax_t)tsc->stripe_chunksize);
217         ptr += ret;
218         len -= ret;
219
220         for (i = 0; i < tsc->stripe_num; i++) {
221                 ret = ksnprintf(ptr, len, " %s %jd",
222                         tsc->stripe_devs[i].pdev->udev_name,
223                         (intmax_t)tsc->stripe_devs[i].offset);
224                 ptr += ret;
225                 len -= ret;
226         }
227
228         return params;
229 }
230
231 #ifdef USE_NUM_ERROR
232 static void
233 dm_target_stripe_iodone(struct bio *bio)
234 {
235         struct bio *obio;
236         struct buf *bp;
237         dm_target_stripe_config_t *tsc;
238
239         bp = bio->bio_buf;
240         tsc = bio->bio_caller_info1.ptr;
241
242         if (bp->b_error) {
243                 int devnr;
244                 uint64_t blkno, stripe;
245
246                 blkno = bio->bio_offset / DEV_BSIZE;
247                 stripe = blkno / tsc->stripe_chunksize;
248                 devnr = stripe % tsc->stripe_num;
249                 KKASSERT(devnr < MAX_STRIPES);
250                 tsc->stripe_devs[devnr].num_error++;
251
252                 aprint_debug("stripe_iodone: device=%d error=%d\n",
253                         devnr, bp->b_error);
254         }
255
256         obio = pop_bio(bio);
257         biodone(obio);
258 }
259
260 static __inline
261 struct bio *get_stripe_bio(struct bio *bio, void *priv)
262 {
263         struct bio *nbio;
264
265         nbio = push_bio(bio);
266         nbio->bio_caller_info1.ptr = priv;
267         nbio->bio_done = dm_target_stripe_iodone;
268
269         return nbio;
270 }
271 #else
272 static __inline
273 struct bio *get_stripe_bio(struct bio *bio, void *priv __unused)
274 {
275         return bio;
276 }
277 #endif
278
279 /*
280  * Strategy routine called from dm_strategy.
281  */
282 static int
283 dm_target_stripe_strategy(dm_table_entry_t *table_en, struct buf *bp)
284 {
285         dm_target_stripe_config_t *tsc;
286         struct bio *bio = &bp->b_bio1;
287         struct bio *nbio;
288         struct buf *nestbuf;
289         struct target_stripe_dev *dev;
290         uint64_t blkno, blkoff;
291         uint64_t stripe, blknr;
292         uint32_t stripe_off, stripe_rest, num_blks, issue_blks;
293         int devnr;
294
295         tsc = table_en->target_config;
296         if (tsc == NULL)
297                 return 0;
298
299         /* calculate extent of request */
300         KKASSERT(bp->b_resid % DEV_BSIZE == 0);
301
302         switch(bp->b_cmd) {
303         case BUF_CMD_READ:
304         case BUF_CMD_WRITE:
305         case BUF_CMD_FREEBLKS:
306                 /*
307                  * Loop through to individual operations
308                  */
309                 blkno = bio->bio_offset / DEV_BSIZE;
310                 blkoff = 0;
311                 num_blks = bp->b_resid / DEV_BSIZE;
312                 nestiobuf_init(bio);
313
314                 while (num_blks > 0) {
315                         /* blockno to stripe piece nr */
316                         stripe = blkno / tsc->stripe_chunksize;
317                         stripe_off = blkno % tsc->stripe_chunksize;
318
319                         /* where we are inside the stripe */
320                         devnr = stripe % tsc->stripe_num;
321                         blknr = stripe / tsc->stripe_num;
322                         dev = &tsc->stripe_devs[devnr];
323
324                         /* how much is left before we hit a boundary */
325                         stripe_rest = tsc->stripe_chunksize - stripe_off;
326
327                         /* issue this piece on stripe `stripe' */
328                         issue_blks = MIN(stripe_rest, num_blks);
329                         nestbuf = getpbuf(NULL);
330                         nestbuf->b_flags |= bio->bio_buf->b_flags & B_HASBOGUS;
331
332                         nestiobuf_add(bio, nestbuf, blkoff,
333                                         issue_blks * DEV_BSIZE, NULL);
334
335                         nbio = get_stripe_bio(&nestbuf->b_bio1, tsc);
336                         nbio->bio_offset = blknr * tsc->stripe_chunksize;
337                         nbio->bio_offset += stripe_off;
338                         nbio->bio_offset += dev->offset;
339                         nbio->bio_offset *= DEV_BSIZE;
340
341                         vn_strategy(dev->pdev->pdev_vnode, nbio);
342
343                         blkno += issue_blks;
344                         blkoff += issue_blks * DEV_BSIZE;
345                         num_blks -= issue_blks;
346                 }
347                 nestiobuf_start(bio);
348                 break;
349         case BUF_CMD_FLUSH:
350                 nestiobuf_init(bio);
351                 for (devnr = 0; devnr < tsc->stripe_num; ++devnr) {
352                         dev = &tsc->stripe_devs[devnr];
353                         nestbuf = getpbuf(NULL);
354                         nestbuf->b_flags |= bio->bio_buf->b_flags & B_HASBOGUS;
355
356                         nestiobuf_add(bio, nestbuf, 0, 0, NULL);
357
358                         nbio = get_stripe_bio(&nestbuf->b_bio1, tsc);
359                         nbio->bio_offset = 0;
360
361                         vn_strategy(dev->pdev->pdev_vnode, nbio);
362                 }
363                 nestiobuf_start(bio);
364                 break;
365         default:
366                 bp->b_flags |= B_ERROR;
367                 bp->b_error = EIO;
368                 biodone(bio);
369                 break;
370         }
371         return 0;
372 }
373
374
375 static int
376 dm_target_stripe_dump(dm_table_entry_t *table_en, void *data, size_t length, off_t offset)
377 {
378         dm_target_stripe_config_t *tsc;
379         uint64_t blkno, blkoff;
380         uint64_t stripe, blknr;
381         uint32_t stripe_off, stripe_rest, num_blks, issue_blks;
382         uint64_t off2, len2;
383         int devnr;
384
385         tsc = table_en->target_config;
386         if (tsc == NULL)
387                 return 0;
388
389         /* calculate extent of request */
390         KKASSERT(length % DEV_BSIZE == 0);
391
392         blkno = offset / DEV_BSIZE;
393         blkoff = 0;
394         num_blks = length / DEV_BSIZE;
395
396         /*
397          * 0 length means flush buffers and return
398          */
399         if (length == 0) {
400                 for (devnr = 0; devnr < tsc->stripe_num; ++devnr) {
401                         if (tsc->stripe_devs[devnr].pdev->pdev_vnode->v_rdev == NULL)
402                                 return ENXIO;
403
404                         dev_ddump(tsc->stripe_devs[devnr].pdev->pdev_vnode->v_rdev,
405                             data, 0, offset, 0);
406                 }
407                 return 0;
408         }
409
410         while (num_blks > 0) {
411                 /* blockno to stripe piece nr */
412                 stripe = blkno / tsc->stripe_chunksize;
413                 stripe_off = blkno % tsc->stripe_chunksize;
414
415                 /* where we are inside the stripe */
416                 devnr = stripe % tsc->stripe_num;
417                 blknr = stripe / tsc->stripe_num;
418
419                 /* how much is left before we hit a boundary */
420                 stripe_rest = tsc->stripe_chunksize - stripe_off;
421
422                 /* issue this piece on stripe `stripe' */
423                 issue_blks = MIN(stripe_rest, num_blks);
424
425 #if 0
426                 nestiobuf_add(bio, nestbuf, blkoff,
427                                 issue_blks * DEV_BSIZE);
428 #endif
429                 len2 = issue_blks * DEV_BSIZE;
430
431                 /* I need number of bytes. */
432                 off2 = blknr * tsc->stripe_chunksize + stripe_off;
433                 off2 += tsc->stripe_devs[devnr].offset;
434                 off2 *= DEV_BSIZE;
435                 off2 = dm_pdev_correct_dump_offset(tsc->stripe_devs[devnr].pdev,
436                     off2);
437
438                 if (tsc->stripe_devs[devnr].pdev->pdev_vnode->v_rdev == NULL)
439                         return ENXIO;
440
441                 dev_ddump(tsc->stripe_devs[devnr].pdev->pdev_vnode->v_rdev,
442                     (char *)data + blkoff, 0, off2, len2);
443
444                 blkno += issue_blks;
445                 blkoff += issue_blks * DEV_BSIZE;
446                 num_blks -= issue_blks;
447         }
448
449         return 0;
450 }
451
452 /*
453  * Destroy a dm table entry for stripes.
454  */
455 static int
456 dm_target_stripe_destroy(dm_table_entry_t *table_en)
457 {
458         dm_target_stripe_config_t *tsc;
459
460         if ((tsc = table_en->target_config) != NULL) {
461                 table_en->target_config = NULL;
462                 dm_target_stripe_destroy_config(tsc);
463         }
464
465         return 0;
466 }
467
468 static void
469 dm_target_stripe_destroy_config(dm_target_stripe_config_t *tsc)
470 {
471         int n;
472
473         for (n = 0; n < tsc->stripe_num; ++n) {
474                 if (tsc->stripe_devs[n].pdev) {
475                         dm_pdev_decr(tsc->stripe_devs[n].pdev);
476                         tsc->stripe_devs[n].pdev = NULL;
477                 }
478         }
479         kfree(tsc, M_DMSTRIPE);
480 }
481
482 static int
483 dmts_mod_handler(module_t mod, int type, void *unused)
484 {
485         dm_target_t *dmt = NULL;
486         int err = 0;
487
488         switch(type) {
489         case MOD_LOAD:
490                 if ((dmt = dm_target_lookup("striped")) != NULL) {
491                         dm_target_unbusy(dmt);
492                         return EEXIST;
493                 }
494                 dmt = dm_target_alloc("striped");
495                 dmt->version[0] = 1;
496                 dmt->version[1] = 0;
497                 dmt->version[2] = 3;
498                 strlcpy(dmt->name, "striped", DM_MAX_TYPE_NAME);
499                 dmt->init = &dm_target_stripe_init;
500                 dmt->info = &dm_target_stripe_info;
501                 dmt->table = &dm_target_stripe_table;
502                 dmt->strategy = &dm_target_stripe_strategy;
503                 dmt->destroy = &dm_target_stripe_destroy;
504                 dmt->dump = &dm_target_stripe_dump;
505                 dmt->max_argc = 2 + (MAX_STRIPES * 2);
506
507                 err = dm_target_insert(dmt);
508                 if (err == 0)
509                         kprintf("dm_target_striped: Successfully initialized\n");
510                 break;
511
512         case MOD_UNLOAD:
513                 err = dm_target_rem("striped");
514                 if (err == 0)
515                         kprintf("dm_target_striped: unloaded\n");
516                 break;
517
518         default:
519                 break;
520         }
521
522         return err;
523 }
524
525 DM_TARGET_MODULE(dm_target_striped, dmts_mod_handler);