1 /*$NetBSD: dm_target_stripe.c,v 1.22 2017/06/01 02:45:09 chs 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 dmt->secsize = &dm_target_stripe_secsize; 85 86 r = dm_target_insert(dmt); 87 88 break; 89 90 case MODULE_CMD_FINI: 91 r = dm_target_rem("stripe"); 92 break; 93 94 case MODULE_CMD_STAT: 95 return ENOTTY; 96 97 default: 98 return ENOTTY; 99 } 100 101 return r; 102 } 103 #endif 104 105 static void 106 dm_target_stripe_fini(dm_target_stripe_config_t *tsc) 107 { 108 dm_target_linear_config_t *tlc; 109 110 if (tsc == NULL) 111 return; 112 113 while ((tlc = TAILQ_FIRST(&tsc->stripe_devs)) != NULL) { 114 TAILQ_REMOVE(&tsc->stripe_devs, tlc, entries); 115 dm_pdev_decr(tlc->pdev); 116 kmem_free(tlc, sizeof(*tlc)); 117 } 118 119 kmem_free(tsc, sizeof(*tsc)); 120 } 121 122 /* 123 * Init function called from dm_table_load_ioctl. 124 * DM_STRIPE_DEV_OFFSET should always hold the index of the first device-offset 125 * pair in the parameters. 126 * Example line sent to dm from lvm tools when using striped target. 127 * start length striped #stripes chunk_size device1 offset1 ... deviceN offsetN 128 * 0 65536 striped 2 512 /dev/hda 0 /dev/hdb 0 129 */ 130 int 131 dm_target_stripe_init(dm_dev_t * dmv, void **target_config, char *params) 132 { 133 dm_target_linear_config_t *tlc; 134 dm_target_stripe_config_t *tsc; 135 size_t len; 136 char **ap, *argv[10]; 137 int strpc, strpi; 138 139 if (params == NULL) 140 return EINVAL; 141 142 len = strlen(params) + 1; 143 144 /* 145 * Parse a string, containing tokens delimited by white space, 146 * into an argument vector 147 */ 148 for (ap = argv; ap <= &argv[9] && 149 (*ap = strsep(¶ms, " \t")) != NULL;) { 150 if (**ap != '\0') 151 ap++; 152 } 153 154 printf("Stripe target init function called!!\n"); 155 156 printf("Stripe target chunk size %s number of stripes %s\n", 157 argv[1], argv[0]); 158 159 if ((tsc = kmem_alloc(sizeof(*tsc), KM_NOSLEEP)) == NULL) 160 return ENOMEM; 161 162 /* Initialize linked list for striping devices */ 163 TAILQ_INIT(&tsc->stripe_devs); 164 165 /* Save length of param string */ 166 tsc->params_len = len; 167 tsc->stripe_chunksize = atoi(argv[1]); 168 tsc->stripe_num = (uint8_t) atoi(argv[0]); 169 170 strpc = DM_STRIPE_DEV_OFFSET + (tsc->stripe_num * 2); 171 for (strpi = DM_STRIPE_DEV_OFFSET; strpi < strpc; strpi += 2) { 172 printf("Stripe target device name %s -- offset %s\n", 173 argv[strpi], argv[strpi+1]); 174 175 tlc = kmem_alloc(sizeof(*tlc), KM_NOSLEEP); 176 if ((tlc->pdev = dm_pdev_insert(argv[strpi])) == NULL) { 177 kmem_free(tlc, sizeof(*tlc)); 178 dm_target_stripe_fini(tsc); 179 return ENOENT; 180 } 181 tlc->offset = atoi(argv[strpi+1]); 182 183 /* Insert striping device to linked list. */ 184 TAILQ_INSERT_TAIL(&tsc->stripe_devs, tlc, entries); 185 } 186 187 *target_config = tsc; 188 189 dmv->dev_type = DM_STRIPE_DEV; 190 191 return 0; 192 } 193 /* Status routine called to get params string. */ 194 char * 195 dm_target_stripe_status(void *target_config) 196 { 197 dm_target_linear_config_t *tlc; 198 dm_target_stripe_config_t *tsc; 199 char *params, *tmp; 200 201 tsc = target_config; 202 203 params = kmem_alloc(DM_MAX_PARAMS_SIZE, KM_SLEEP); 204 tmp = kmem_alloc(DM_MAX_PARAMS_SIZE, KM_SLEEP); 205 206 snprintf(params, DM_MAX_PARAMS_SIZE, "%d %" PRIu64, 207 tsc->stripe_num, tsc->stripe_chunksize); 208 209 TAILQ_FOREACH(tlc, &tsc->stripe_devs, entries) { 210 snprintf(tmp, DM_MAX_PARAMS_SIZE, " %s %" PRIu64, 211 tlc->pdev->name, tlc->offset); 212 strcat(params, tmp); 213 } 214 215 kmem_free(tmp, DM_MAX_PARAMS_SIZE); 216 217 return params; 218 } 219 /* Strategy routine called from dm_strategy. */ 220 int 221 dm_target_stripe_strategy(dm_table_entry_t * table_en, struct buf * bp) 222 { 223 dm_target_linear_config_t *tlc; 224 dm_target_stripe_config_t *tsc; 225 struct buf *nestbuf; 226 uint64_t blkno, blkoff; 227 uint64_t stripe, stripe_blknr; 228 uint32_t stripe_off, stripe_rest, num_blks, issue_blks; 229 int i, stripe_devnr; 230 231 tsc = table_en->target_config; 232 if (tsc == NULL) 233 return 0; 234 235 /* printf("Stripe target read function called %" PRIu64 "!!\n", 236 tlc->offset);*/ 237 238 /* calculate extent of request */ 239 KASSERT(bp->b_resid % DEV_BSIZE == 0); 240 241 blkno = bp->b_blkno; 242 blkoff = 0; 243 num_blks = bp->b_resid / DEV_BSIZE; 244 for (;;) { 245 /* blockno to strip piece nr */ 246 stripe = blkno / tsc->stripe_chunksize; 247 stripe_off = blkno % tsc->stripe_chunksize; 248 249 /* where we are inside the strip */ 250 stripe_devnr = stripe % tsc->stripe_num; 251 stripe_blknr = stripe / tsc->stripe_num; 252 253 /* how much is left before we hit a boundary */ 254 stripe_rest = tsc->stripe_chunksize - stripe_off; 255 256 /* issue this piece on stripe `stripe' */ 257 issue_blks = MIN(stripe_rest, num_blks); 258 nestbuf = getiobuf(NULL, true); 259 260 nestiobuf_setup(bp, nestbuf, blkoff, issue_blks * DEV_BSIZE); 261 nestbuf->b_blkno = stripe_blknr * tsc->stripe_chunksize + stripe_off; 262 263 tlc = TAILQ_FIRST(&tsc->stripe_devs); 264 for (i = 0; i < stripe_devnr && tlc != NULL; i++) 265 tlc = TAILQ_NEXT(tlc, entries); 266 267 /* by this point we should have an tlc */ 268 KASSERT(tlc != NULL); 269 270 nestbuf->b_blkno += tlc->offset; 271 272 VOP_STRATEGY(tlc->pdev->pdev_vnode, nestbuf); 273 274 blkno += issue_blks; 275 blkoff += issue_blks * DEV_BSIZE; 276 num_blks -= issue_blks; 277 278 if (num_blks <= 0) 279 break; 280 } 281 282 return 0; 283 } 284 /* Sync underlying disk caches. */ 285 int 286 dm_target_stripe_sync(dm_table_entry_t * table_en) 287 { 288 int cmd, err; 289 dm_target_stripe_config_t *tsc; 290 dm_target_linear_config_t *tlc; 291 292 tsc = table_en->target_config; 293 294 err = 0; 295 cmd = 1; 296 297 TAILQ_FOREACH(tlc, &tsc->stripe_devs, entries) { 298 if ((err = VOP_IOCTL(tlc->pdev->pdev_vnode, DIOCCACHESYNC, 299 &cmd, FREAD|FWRITE, kauth_cred_get())) != 0) 300 return err; 301 } 302 303 return err; 304 305 } 306 /* Destroy target specific data. */ 307 int 308 dm_target_stripe_destroy(dm_table_entry_t * table_en) 309 { 310 dm_target_stripe_fini(table_en->target_config); 311 312 /* Unbusy target so we can unload it */ 313 dm_target_unbusy(table_en->target); 314 315 table_en->target_config = NULL; 316 return 0; 317 } 318 /* Doesn't not need to do anything here. */ 319 int 320 dm_target_stripe_deps(dm_table_entry_t * table_en, prop_array_t prop_array) 321 { 322 dm_target_stripe_config_t *tsc; 323 dm_target_linear_config_t *tlc; 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 prop_array_add_uint64(prop_array, 332 (uint64_t) tlc->pdev->pdev_vnode->v_rdev); 333 } 334 335 return 0; 336 } 337 /* Unsupported for this target. */ 338 int 339 dm_target_stripe_upcall(dm_table_entry_t * table_en, struct buf * bp) 340 { 341 return 0; 342 } 343 /* 344 * Compute physical block size 345 * For a stripe target we chose the maximum sector size of all 346 * stripe devices. For the supported power-of-2 sizes this is equivalent 347 * to the least common multiple. 348 */ 349 int 350 dm_target_stripe_secsize(dm_table_entry_t * table_en, unsigned *secsizep) 351 { 352 dm_target_linear_config_t *tlc; 353 dm_target_stripe_config_t *tsc; 354 unsigned secsize; 355 356 secsize = 0; 357 358 tsc = table_en->target_config; 359 if (tsc != NULL) { 360 TAILQ_FOREACH(tlc, &tsc->stripe_devs, entries) { 361 if (secsize < tlc->pdev->pdev_secsize) 362 secsize = tlc->pdev->pdev_secsize; 363 } 364 } 365 366 *secsizep = secsize; 367 368 return 0; 369 } 370