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