1 /*$NetBSD: dm_target_stripe.c,v 1.26 2019/12/03 15:47:38 tkusumi 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.26 2019/12/03 15:47:38 tkusumi 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/lwp.h> 43 44 #include "dm.h" 45 46 #ifdef DM_TARGET_MODULE 47 /* 48 * Every target can be compiled directly to dm driver or as a 49 * separate module this part of target is used for loading targets 50 * to dm driver. 51 * Target can be unloaded from kernel only if there are no users of 52 * it e.g. there are no devices which uses that target. 53 */ 54 #include <sys/kernel.h> 55 #include <sys/module.h> 56 57 MODULE(MODULE_CLASS_MISC, dm_target_stripe, NULL); 58 59 static int 60 dm_target_stripe_modcmd(modcmd_t cmd, void *arg) 61 { 62 dm_target_t *dmt; 63 int r; 64 dmt = NULL; 65 66 switch (cmd) { 67 case MODULE_CMD_INIT: 68 if ((dmt = dm_target_lookup("stripe")) != NULL) { 69 dm_target_unbusy(dmt); 70 return EEXIST; 71 } 72 dmt = dm_target_alloc("stripe"); 73 74 dmt->version[0] = 1; 75 dmt->version[1] = 0; 76 dmt->version[2] = 0; 77 strlcpy(dmt->name, "stripe", DM_MAX_TYPE_NAME); 78 dmt->init = &dm_target_stripe_init; 79 dmt->status = &dm_target_stripe_status; 80 dmt->strategy = &dm_target_stripe_strategy; 81 dmt->sync = &dm_target_stripe_sync; 82 dmt->deps = &dm_target_stripe_deps; 83 dmt->destroy = &dm_target_stripe_destroy; 84 dmt->upcall = &dm_target_stripe_upcall; 85 dmt->secsize = &dm_target_stripe_secsize; 86 87 r = dm_target_insert(dmt); 88 89 break; 90 91 case MODULE_CMD_FINI: 92 r = dm_target_rem("stripe"); 93 break; 94 95 case MODULE_CMD_STAT: 96 return ENOTTY; 97 98 default: 99 return ENOTTY; 100 } 101 102 return r; 103 } 104 #endif 105 106 static void 107 dm_target_stripe_fini(dm_target_stripe_config_t *tsc) 108 { 109 dm_target_linear_config_t *tlc; 110 111 if (tsc == NULL) 112 return; 113 114 while ((tlc = TAILQ_FIRST(&tsc->stripe_devs)) != NULL) { 115 TAILQ_REMOVE(&tsc->stripe_devs, tlc, entries); 116 dm_pdev_decr(tlc->pdev); 117 kmem_free(tlc, sizeof(*tlc)); 118 } 119 120 kmem_free(tsc, sizeof(*tsc)); 121 } 122 123 /* 124 * Init function called from dm_table_load_ioctl. 125 * DM_STRIPE_DEV_OFFSET should always hold the index of the first device-offset 126 * pair in the parameters. 127 * Example line sent to dm from lvm tools when using striped target. 128 * start length striped #stripes chunk_size device1 offset1 ... deviceN offsetN 129 * 0 65536 striped 2 512 /dev/hda 0 /dev/hdb 0 130 */ 131 int 132 dm_target_stripe_init(dm_dev_t * dmv, void **target_config, char *params) 133 { 134 dm_target_linear_config_t *tlc; 135 dm_target_stripe_config_t *tsc; 136 size_t len; 137 char **ap, *argv[10]; 138 int strpc, strpi; 139 140 if (params == NULL) 141 return EINVAL; 142 143 len = strlen(params) + 1; 144 145 /* 146 * Parse a string, containing tokens delimited by white space, 147 * into an argument vector 148 */ 149 for (ap = argv; ap <= &argv[9] && 150 (*ap = strsep(¶ms, " \t")) != NULL;) { 151 if (**ap != '\0') 152 ap++; 153 } 154 155 printf("Stripe target init function called!!\n"); 156 157 printf("Stripe target chunk size %s number of stripes %s\n", 158 argv[1], argv[0]); 159 160 tsc = kmem_alloc(sizeof(*tsc), KM_SLEEP); 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_SLEEP); 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 return 0; 190 } 191 192 /* Status routine called to get params string. */ 193 char * 194 dm_target_stripe_status(void *target_config) 195 { 196 dm_target_linear_config_t *tlc; 197 dm_target_stripe_config_t *tsc; 198 char *params, *tmp; 199 200 tsc = target_config; 201 202 params = kmem_alloc(DM_MAX_PARAMS_SIZE, KM_SLEEP); 203 tmp = kmem_alloc(DM_MAX_PARAMS_SIZE, KM_SLEEP); 204 205 snprintf(params, DM_MAX_PARAMS_SIZE, "%d %" PRIu64, 206 tsc->stripe_num, tsc->stripe_chunksize); 207 208 TAILQ_FOREACH(tlc, &tsc->stripe_devs, entries) { 209 snprintf(tmp, DM_MAX_PARAMS_SIZE, " %s %" PRIu64, 210 tlc->pdev->name, tlc->offset); 211 strcat(params, tmp); 212 } 213 214 kmem_free(tmp, DM_MAX_PARAMS_SIZE); 215 216 return params; 217 } 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 285 /* Sync underlying disk caches. */ 286 int 287 dm_target_stripe_sync(dm_table_entry_t * table_en) 288 { 289 int cmd, err; 290 dm_target_stripe_config_t *tsc; 291 dm_target_linear_config_t *tlc; 292 293 tsc = table_en->target_config; 294 295 err = 0; 296 cmd = 1; 297 298 TAILQ_FOREACH(tlc, &tsc->stripe_devs, entries) { 299 if ((err = VOP_IOCTL(tlc->pdev->pdev_vnode, DIOCCACHESYNC, 300 &cmd, FREAD|FWRITE, kauth_cred_get())) != 0) 301 return err; 302 } 303 304 return err; 305 306 } 307 308 /* Destroy target specific data. */ 309 int 310 dm_target_stripe_destroy(dm_table_entry_t * table_en) 311 { 312 dm_target_stripe_fini(table_en->target_config); 313 table_en->target_config = NULL; 314 315 /* Unbusy target so we can unload it */ 316 dm_target_unbusy(table_en->target); 317 318 return 0; 319 } 320 321 /* Doesn't not need to do anything here. */ 322 int 323 dm_target_stripe_deps(dm_table_entry_t * table_en, prop_array_t prop_array) 324 { 325 dm_target_stripe_config_t *tsc; 326 dm_target_linear_config_t *tlc; 327 328 if (table_en->target_config == NULL) 329 return ENOENT; 330 331 tsc = table_en->target_config; 332 333 TAILQ_FOREACH(tlc, &tsc->stripe_devs, entries) { 334 prop_array_add_uint64(prop_array, 335 (uint64_t) tlc->pdev->pdev_vnode->v_rdev); 336 } 337 338 return 0; 339 } 340 341 /* Unsupported for this target. */ 342 int 343 dm_target_stripe_upcall(dm_table_entry_t * table_en, struct buf * bp) 344 { 345 return 0; 346 } 347 348 /* 349 * Compute physical block size 350 * For a stripe target we chose the maximum sector size of all 351 * stripe devices. For the supported power-of-2 sizes this is equivalent 352 * to the least common multiple. 353 */ 354 int 355 dm_target_stripe_secsize(dm_table_entry_t * table_en, unsigned *secsizep) 356 { 357 dm_target_linear_config_t *tlc; 358 dm_target_stripe_config_t *tsc; 359 unsigned secsize; 360 361 secsize = 0; 362 363 tsc = table_en->target_config; 364 if (tsc != NULL) { 365 TAILQ_FOREACH(tlc, &tsc->stripe_devs, entries) { 366 if (secsize < tlc->pdev->pdev_secsize) 367 secsize = tlc->pdev->pdev_secsize; 368 } 369 } 370 371 *secsizep = secsize; 372 373 return 0; 374 } 375