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