libc: Fix up compat-43 a bit.
[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("device=%d error=%d\n", devnr, bp->b_error);
251         }
252
253         obio = pop_bio(bio);
254         biodone(obio);
255 }
256
257 static __inline
258 struct bio *get_stripe_bio(struct bio *bio, void *priv)
259 {
260         struct bio *nbio;
261
262         nbio = push_bio(bio);
263         nbio->bio_caller_info1.ptr = priv;
264         nbio->bio_done = dm_target_stripe_iodone;
265
266         return nbio;
267 }
268 #else
269 static __inline
270 struct bio *get_stripe_bio(struct bio *bio, void *priv __unused)
271 {
272         return bio;
273 }
274 #endif
275
276 /*
277  * Strategy routine called from dm_strategy.
278  */
279 static int
280 dm_target_stripe_strategy(dm_table_entry_t *table_en, struct buf *bp)
281 {
282         dm_target_stripe_config_t *tsc;
283         struct bio *bio = &bp->b_bio1;
284         struct bio *nbio;
285         struct buf *nestbuf;
286         struct target_stripe_dev *dev;
287         uint64_t blkno, blkoff;
288         uint64_t stripe, blknr;
289         uint32_t stripe_off, stripe_rest, num_blks, issue_blks;
290         int devnr;
291
292         tsc = table_en->target_config;
293         if (tsc == NULL)
294                 return 0;
295
296         /* calculate extent of request */
297         KKASSERT(bp->b_resid % DEV_BSIZE == 0);
298
299         switch(bp->b_cmd) {
300         case BUF_CMD_READ:
301         case BUF_CMD_WRITE:
302         case BUF_CMD_FREEBLKS:
303                 /*
304                  * Loop through to individual operations
305                  */
306                 blkno = bio->bio_offset / DEV_BSIZE;
307                 blkoff = 0;
308                 num_blks = bp->b_resid / DEV_BSIZE;
309                 nestiobuf_init(bio);
310
311                 while (num_blks > 0) {
312                         /* blockno to stripe piece nr */
313                         stripe = blkno / tsc->stripe_chunksize;
314                         stripe_off = blkno % tsc->stripe_chunksize;
315
316                         /* where we are inside the stripe */
317                         devnr = stripe % tsc->stripe_num;
318                         blknr = stripe / tsc->stripe_num;
319                         dev = &tsc->stripe_devs[devnr];
320
321                         /* how much is left before we hit a boundary */
322                         stripe_rest = tsc->stripe_chunksize - stripe_off;
323
324                         /* issue this piece on stripe `stripe' */
325                         issue_blks = MIN(stripe_rest, num_blks);
326                         nestbuf = getpbuf(NULL);
327                         nestbuf->b_flags |= bio->bio_buf->b_flags & B_HASBOGUS;
328
329                         nestiobuf_add(bio, nestbuf, blkoff,
330                                         issue_blks * DEV_BSIZE, NULL);
331
332                         nbio = get_stripe_bio(&nestbuf->b_bio1, tsc);
333                         nbio->bio_offset = blknr * tsc->stripe_chunksize;
334                         nbio->bio_offset += stripe_off;
335                         nbio->bio_offset += dev->offset;
336                         nbio->bio_offset *= DEV_BSIZE;
337
338                         vn_strategy(dev->pdev->pdev_vnode, nbio);
339
340                         blkno += issue_blks;
341                         blkoff += issue_blks * DEV_BSIZE;
342                         num_blks -= issue_blks;
343                 }
344                 nestiobuf_start(bio);
345                 break;
346         case BUF_CMD_FLUSH:
347                 nestiobuf_init(bio);
348                 for (devnr = 0; devnr < tsc->stripe_num; ++devnr) {
349                         dev = &tsc->stripe_devs[devnr];
350                         nestbuf = getpbuf(NULL);
351                         nestbuf->b_flags |= bio->bio_buf->b_flags & B_HASBOGUS;
352
353                         nestiobuf_add(bio, nestbuf, 0, 0, NULL);
354
355                         nbio = get_stripe_bio(&nestbuf->b_bio1, tsc);
356                         nbio->bio_offset = 0;
357
358                         vn_strategy(dev->pdev->pdev_vnode, nbio);
359                 }
360                 nestiobuf_start(bio);
361                 break;
362         default:
363                 bp->b_flags |= B_ERROR;
364                 bp->b_error = EIO;
365                 biodone(bio);
366                 break;
367         }
368         return 0;
369 }
370
371
372 static int
373 dm_target_stripe_dump(dm_table_entry_t *table_en, void *data, size_t length, off_t offset)
374 {
375         dm_target_stripe_config_t *tsc;
376         uint64_t blkno, blkoff;
377         uint64_t stripe, blknr;
378         uint32_t stripe_off, stripe_rest, num_blks, issue_blks;
379         uint64_t off2, len2;
380         int devnr;
381
382         tsc = table_en->target_config;
383         if (tsc == NULL)
384                 return 0;
385
386         /* calculate extent of request */
387         KKASSERT(length % DEV_BSIZE == 0);
388
389         blkno = offset / DEV_BSIZE;
390         blkoff = 0;
391         num_blks = length / DEV_BSIZE;
392
393         /*
394          * 0 length means flush buffers and return
395          */
396         if (length == 0) {
397                 for (devnr = 0; devnr < tsc->stripe_num; ++devnr) {
398                         if (tsc->stripe_devs[devnr].pdev->pdev_vnode->v_rdev == NULL)
399                                 return ENXIO;
400
401                         dev_ddump(tsc->stripe_devs[devnr].pdev->pdev_vnode->v_rdev,
402                             data, 0, offset, 0);
403                 }
404                 return 0;
405         }
406
407         while (num_blks > 0) {
408                 /* blockno to stripe piece nr */
409                 stripe = blkno / tsc->stripe_chunksize;
410                 stripe_off = blkno % tsc->stripe_chunksize;
411
412                 /* where we are inside the stripe */
413                 devnr = stripe % tsc->stripe_num;
414                 blknr = stripe / tsc->stripe_num;
415
416                 /* how much is left before we hit a boundary */
417                 stripe_rest = tsc->stripe_chunksize - stripe_off;
418
419                 /* issue this piece on stripe `stripe' */
420                 issue_blks = MIN(stripe_rest, num_blks);
421
422 #if 0
423                 nestiobuf_add(bio, nestbuf, blkoff,
424                                 issue_blks * DEV_BSIZE);
425 #endif
426                 len2 = issue_blks * DEV_BSIZE;
427
428                 /* I need number of bytes. */
429                 off2 = blknr * tsc->stripe_chunksize + stripe_off;
430                 off2 += tsc->stripe_devs[devnr].offset;
431                 off2 *= DEV_BSIZE;
432                 off2 = dm_pdev_correct_dump_offset(tsc->stripe_devs[devnr].pdev,
433                     off2);
434
435                 if (tsc->stripe_devs[devnr].pdev->pdev_vnode->v_rdev == NULL)
436                         return ENXIO;
437
438                 dev_ddump(tsc->stripe_devs[devnr].pdev->pdev_vnode->v_rdev,
439                     (char *)data + blkoff, 0, off2, len2);
440
441                 blkno += issue_blks;
442                 blkoff += issue_blks * DEV_BSIZE;
443                 num_blks -= issue_blks;
444         }
445
446         return 0;
447 }
448
449 /*
450  * Destroy a dm table entry for stripes.
451  */
452 static int
453 dm_target_stripe_destroy(dm_table_entry_t *table_en)
454 {
455         if (table_en->target_config != NULL)
456                 dm_target_stripe_destroy_config(table_en->target_config);
457
458         return 0;
459 }
460
461 static void
462 dm_target_stripe_destroy_config(dm_target_stripe_config_t *tsc)
463 {
464         int n;
465
466         for (n = 0; n < tsc->stripe_num; ++n) {
467                 if (tsc->stripe_devs[n].pdev) {
468                         dm_pdev_decr(tsc->stripe_devs[n].pdev);
469                         tsc->stripe_devs[n].pdev = NULL;
470                 }
471         }
472         kfree(tsc, M_DMSTRIPE);
473 }
474
475 static int
476 dmts_mod_handler(module_t mod, int type, void *unused)
477 {
478         dm_target_t *dmt = NULL;
479         int err = 0;
480
481         switch(type) {
482         case MOD_LOAD:
483                 if ((dmt = dm_target_lookup("striped")) != NULL) {
484                         dm_target_unbusy(dmt);
485                         return EEXIST;
486                 }
487                 dmt = dm_target_alloc("striped");
488                 dmt->version[0] = 1;
489                 dmt->version[1] = 0;
490                 dmt->version[2] = 3;
491                 dmt->init = &dm_target_stripe_init;
492                 dmt->destroy = &dm_target_stripe_destroy;
493                 dmt->strategy = &dm_target_stripe_strategy;
494                 dmt->table = &dm_target_stripe_table;
495                 dmt->info = &dm_target_stripe_info;
496                 dmt->dump = &dm_target_stripe_dump;
497                 dmt->max_argc = 2 + (MAX_STRIPES * 2);
498
499                 err = dm_target_insert(dmt);
500                 if (err == 0)
501                         kprintf("dm_target_striped: Successfully initialized\n");
502                 break;
503
504         case MOD_UNLOAD:
505                 err = dm_target_remove("striped");
506                 if (err == 0)
507                         kprintf("dm_target_striped: unloaded\n");
508                 break;
509         }
510
511         return err;
512 }
513
514 DM_TARGET_MODULE(dm_target_striped, dmts_mod_handler);