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(¶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 /* 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