1 1.45 rillig /*$NetBSD: dm_target_stripe.c,v 1.45 2024/09/08 09:36:50 rillig Exp $*/ 2 1.1 haad 3 1.1 haad /* 4 1.1 haad * Copyright (c) 2009 The NetBSD Foundation, Inc. 5 1.1 haad * All rights reserved. 6 1.1 haad * 7 1.1 haad * This code is derived from software contributed to The NetBSD Foundation 8 1.1 haad * by Adam Hamsik. 9 1.1 haad * 10 1.1 haad * Redistribution and use in source and binary forms, with or without 11 1.1 haad * modification, are permitted provided that the following conditions 12 1.1 haad * are met: 13 1.1 haad * 1. Redistributions of source code must retain the above copyright 14 1.1 haad * notice, this list of conditions and the following disclaimer. 15 1.1 haad * 2. Redistributions in binary form must reproduce the above copyright 16 1.1 haad * notice, this list of conditions and the following disclaimer in the 17 1.1 haad * documentation and/or other materials provided with the distribution. 18 1.1 haad * 19 1.1 haad * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 1.1 haad * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 1.1 haad * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 1.1 haad * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 1.1 haad * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 1.1 haad * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 1.1 haad * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 1.1 haad * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 1.1 haad * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 1.1 haad * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 1.1 haad * POSSIBILITY OF SUCH DAMAGE. 30 1.1 haad */ 31 1.23 christos #include <sys/cdefs.h> 32 1.45 rillig __KERNEL_RCSID(0, "$NetBSD: dm_target_stripe.c,v 1.45 2024/09/08 09:36:50 rillig Exp $"); 33 1.1 haad 34 1.1 haad /* 35 1.1 haad * This file implements initial version of device-mapper stripe target. 36 1.1 haad */ 37 1.1 haad #include <sys/types.h> 38 1.1 haad #include <sys/param.h> 39 1.1 haad #include <sys/buf.h> 40 1.3 haad #include <sys/kmem.h> 41 1.12 uebayasi #include <sys/lwp.h> 42 1.1 haad 43 1.1 haad #include "dm.h" 44 1.1 haad 45 1.32 tkusumi typedef struct target_stripe_config { 46 1.32 tkusumi #define DM_STRIPE_DEV_OFFSET 2 47 1.32 tkusumi struct target_linear_devs stripe_devs; 48 1.32 tkusumi uint8_t stripe_num; 49 1.32 tkusumi uint64_t stripe_chunksize; 50 1.32 tkusumi } dm_target_stripe_config_t; 51 1.32 tkusumi 52 1.1 haad #ifdef DM_TARGET_MODULE 53 1.1 haad /* 54 1.1 haad * Every target can be compiled directly to dm driver or as a 55 1.1 haad * separate module this part of target is used for loading targets 56 1.1 haad * to dm driver. 57 1.1 haad * Target can be unloaded from kernel only if there are no users of 58 1.1 haad * it e.g. there are no devices which uses that target. 59 1.1 haad */ 60 1.1 haad #include <sys/kernel.h> 61 1.1 haad #include <sys/module.h> 62 1.1 haad 63 1.1 haad MODULE(MODULE_CLASS_MISC, dm_target_stripe, NULL); 64 1.1 haad 65 1.1 haad static int 66 1.1 haad dm_target_stripe_modcmd(modcmd_t cmd, void *arg) 67 1.1 haad { 68 1.1 haad dm_target_t *dmt; 69 1.1 haad int r; 70 1.9 haad 71 1.1 haad switch (cmd) { 72 1.1 haad case MODULE_CMD_INIT: 73 1.33 tkusumi if ((dmt = dm_target_lookup("striped")) != NULL) { 74 1.3 haad dm_target_unbusy(dmt); 75 1.1 haad return EEXIST; 76 1.3 haad } 77 1.33 tkusumi dmt = dm_target_alloc("striped"); 78 1.9 haad 79 1.1 haad dmt->version[0] = 1; 80 1.1 haad dmt->version[1] = 0; 81 1.1 haad dmt->version[2] = 0; 82 1.1 haad dmt->init = &dm_target_stripe_init; 83 1.43 tkusumi dmt->info = &dm_target_stripe_info; 84 1.36 tkusumi dmt->table = &dm_target_stripe_table; 85 1.1 haad dmt->strategy = &dm_target_stripe_strategy; 86 1.10 haad dmt->sync = &dm_target_stripe_sync; 87 1.1 haad dmt->destroy = &dm_target_stripe_destroy; 88 1.44 tkusumi //dmt->upcall = &dm_target_stripe_upcall; 89 1.15 ahoka dmt->secsize = &dm_target_stripe_secsize; 90 1.1 haad 91 1.1 haad r = dm_target_insert(dmt); 92 1.9 haad 93 1.1 haad break; 94 1.1 haad 95 1.1 haad case MODULE_CMD_FINI: 96 1.33 tkusumi r = dm_target_rem("striped"); 97 1.1 haad break; 98 1.1 haad 99 1.1 haad case MODULE_CMD_STAT: 100 1.1 haad return ENOTTY; 101 1.1 haad 102 1.1 haad default: 103 1.1 haad return ENOTTY; 104 1.1 haad } 105 1.1 haad 106 1.1 haad return r; 107 1.1 haad } 108 1.1 haad #endif 109 1.1 haad 110 1.21 christos static void 111 1.21 christos dm_target_stripe_fini(dm_target_stripe_config_t *tsc) 112 1.21 christos { 113 1.21 christos dm_target_linear_config_t *tlc; 114 1.21 christos 115 1.21 christos if (tsc == NULL) 116 1.21 christos return; 117 1.21 christos 118 1.21 christos while ((tlc = TAILQ_FIRST(&tsc->stripe_devs)) != NULL) { 119 1.21 christos TAILQ_REMOVE(&tsc->stripe_devs, tlc, entries); 120 1.21 christos dm_pdev_decr(tlc->pdev); 121 1.21 christos kmem_free(tlc, sizeof(*tlc)); 122 1.21 christos } 123 1.21 christos 124 1.21 christos kmem_free(tsc, sizeof(*tsc)); 125 1.21 christos } 126 1.21 christos 127 1.3 haad /* 128 1.3 haad * Init function called from dm_table_load_ioctl. 129 1.11 haad * DM_STRIPE_DEV_OFFSET should always hold the index of the first device-offset 130 1.11 haad * pair in the parameters. 131 1.3 haad * Example line sent to dm from lvm tools when using striped target. 132 1.3 haad * start length striped #stripes chunk_size device1 offset1 ... deviceN offsetN 133 1.3 haad * 0 65536 striped 2 512 /dev/hda 0 /dev/hdb 0 134 1.3 haad */ 135 1.1 haad int 136 1.34 tkusumi dm_target_stripe_init(dm_table_entry_t *table_en, int argc, char **argv) 137 1.1 haad { 138 1.11 haad dm_target_linear_config_t *tlc; 139 1.3 haad dm_target_stripe_config_t *tsc; 140 1.11 haad int strpc, strpi; 141 1.8 haad 142 1.40 tkusumi if (argc < 2) { 143 1.40 tkusumi printf("Stripe target takes at least 2 args, %d given\n", argc); 144 1.3 haad return EINVAL; 145 1.8 haad } 146 1.9 haad 147 1.8 haad printf("Stripe target init function called!!\n"); 148 1.11 haad printf("Stripe target chunk size %s number of stripes %s\n", 149 1.11 haad argv[1], argv[0]); 150 1.3 haad 151 1.24 chs tsc = kmem_alloc(sizeof(*tsc), KM_SLEEP); 152 1.9 haad 153 1.11 haad /* Initialize linked list for striping devices */ 154 1.11 haad TAILQ_INIT(&tsc->stripe_devs); 155 1.3 haad 156 1.8 haad /* Save length of param string */ 157 1.39 tkusumi tsc->stripe_chunksize = atoi64(argv[1]); 158 1.39 tkusumi tsc->stripe_num = (uint8_t) atoi64(argv[0]); 159 1.9 haad 160 1.11 haad strpc = DM_STRIPE_DEV_OFFSET + (tsc->stripe_num * 2); 161 1.11 haad for (strpi = DM_STRIPE_DEV_OFFSET; strpi < strpc; strpi += 2) { 162 1.11 haad printf("Stripe target device name %s -- offset %s\n", 163 1.11 haad argv[strpi], argv[strpi+1]); 164 1.11 haad 165 1.24 chs tlc = kmem_alloc(sizeof(*tlc), KM_SLEEP); 166 1.20 agc if ((tlc->pdev = dm_pdev_insert(argv[strpi])) == NULL) { 167 1.20 agc kmem_free(tlc, sizeof(*tlc)); 168 1.21 christos dm_target_stripe_fini(tsc); 169 1.15 ahoka return ENOENT; 170 1.20 agc } 171 1.39 tkusumi tlc->offset = atoi64(argv[strpi+1]); 172 1.42 tkusumi dm_table_add_deps(table_en, tlc->pdev); 173 1.11 haad 174 1.11 haad /* Insert striping device to linked list. */ 175 1.11 haad TAILQ_INSERT_TAIL(&tsc->stripe_devs, tlc, entries); 176 1.11 haad } 177 1.11 haad 178 1.31 tkusumi table_en->target_config = tsc; 179 1.1 haad 180 1.3 haad return 0; 181 1.1 haad } 182 1.23 christos 183 1.43 tkusumi /* Info routine called to get params string. */ 184 1.43 tkusumi char * 185 1.43 tkusumi dm_target_stripe_info(void *target_config) 186 1.43 tkusumi { 187 1.43 tkusumi dm_target_linear_config_t *tlc; 188 1.43 tkusumi dm_target_stripe_config_t *tsc; 189 1.43 tkusumi char *params, *ptr, buf[256]; 190 1.43 tkusumi int ret, i = 0; 191 1.43 tkusumi size_t len; 192 1.43 tkusumi 193 1.43 tkusumi tsc = target_config; 194 1.43 tkusumi 195 1.43 tkusumi len = DM_MAX_PARAMS_SIZE; 196 1.43 tkusumi params = kmem_alloc(len, KM_SLEEP); 197 1.43 tkusumi ptr = params; 198 1.43 tkusumi 199 1.43 tkusumi ret = snprintf(ptr, len, "%d ", tsc->stripe_num); 200 1.43 tkusumi ptr += ret; 201 1.43 tkusumi len -= ret; 202 1.43 tkusumi 203 1.43 tkusumi memset(buf, 0, sizeof(buf)); 204 1.43 tkusumi TAILQ_FOREACH(tlc, &tsc->stripe_devs, entries) { 205 1.43 tkusumi ret = snprintf(ptr, len, "%s ", tlc->pdev->udev_name); 206 1.43 tkusumi if (0 /*tlc->num_error*/) 207 1.43 tkusumi buf[i] = 'D'; 208 1.43 tkusumi else 209 1.43 tkusumi buf[i] = 'A'; 210 1.43 tkusumi i++; 211 1.43 tkusumi ptr += ret; 212 1.43 tkusumi len -= ret; 213 1.43 tkusumi } 214 1.43 tkusumi 215 1.43 tkusumi ret = snprintf(ptr, len, "1 %s", buf); 216 1.43 tkusumi ptr += ret; 217 1.43 tkusumi len -= ret; 218 1.43 tkusumi 219 1.43 tkusumi return params; 220 1.43 tkusumi } 221 1.43 tkusumi 222 1.36 tkusumi /* Table routine called to get params string. */ 223 1.1 haad char * 224 1.36 tkusumi dm_target_stripe_table(void *target_config) 225 1.1 haad { 226 1.11 haad dm_target_linear_config_t *tlc; 227 1.3 haad dm_target_stripe_config_t *tsc; 228 1.11 haad char *params, *tmp; 229 1.3 haad 230 1.3 haad tsc = target_config; 231 1.9 haad 232 1.22 chs params = kmem_alloc(DM_MAX_PARAMS_SIZE, KM_SLEEP); 233 1.22 chs tmp = kmem_alloc(DM_MAX_PARAMS_SIZE, KM_SLEEP); 234 1.11 haad 235 1.11 haad snprintf(params, DM_MAX_PARAMS_SIZE, "%d %" PRIu64, 236 1.11 haad tsc->stripe_num, tsc->stripe_chunksize); 237 1.11 haad 238 1.11 haad TAILQ_FOREACH(tlc, &tsc->stripe_devs, entries) { 239 1.11 haad snprintf(tmp, DM_MAX_PARAMS_SIZE, " %s %" PRIu64, 240 1.41 tkusumi tlc->pdev->udev_name, tlc->offset); 241 1.11 haad strcat(params, tmp); 242 1.11 haad } 243 1.11 haad 244 1.11 haad kmem_free(tmp, DM_MAX_PARAMS_SIZE); 245 1.9 haad 246 1.3 haad return params; 247 1.9 haad } 248 1.23 christos 249 1.1 haad /* Strategy routine called from dm_strategy. */ 250 1.1 haad int 251 1.30 tkusumi dm_target_stripe_strategy(dm_table_entry_t *table_en, struct buf *bp) 252 1.1 haad { 253 1.11 haad dm_target_linear_config_t *tlc; 254 1.4 reinoud dm_target_stripe_config_t *tsc; 255 1.4 reinoud struct buf *nestbuf; 256 1.4 reinoud uint64_t blkno, blkoff; 257 1.4 reinoud uint64_t stripe, stripe_blknr; 258 1.4 reinoud uint32_t stripe_off, stripe_rest, num_blks, issue_blks; 259 1.11 haad int i, stripe_devnr; 260 1.4 reinoud 261 1.4 reinoud tsc = table_en->target_config; 262 1.4 reinoud if (tsc == NULL) 263 1.4 reinoud return 0; 264 1.4 reinoud 265 1.4 reinoud /* calculate extent of request */ 266 1.4 reinoud KASSERT(bp->b_resid % DEV_BSIZE == 0); 267 1.4 reinoud 268 1.9 haad blkno = bp->b_blkno; 269 1.4 reinoud blkoff = 0; 270 1.4 reinoud num_blks = bp->b_resid / DEV_BSIZE; 271 1.4 reinoud for (;;) { 272 1.29 tkusumi /* blockno to stripe piece nr */ 273 1.9 haad stripe = blkno / tsc->stripe_chunksize; 274 1.4 reinoud stripe_off = blkno % tsc->stripe_chunksize; 275 1.4 reinoud 276 1.29 tkusumi /* where we are inside the stripe */ 277 1.4 reinoud stripe_devnr = stripe % tsc->stripe_num; 278 1.4 reinoud stripe_blknr = stripe / tsc->stripe_num; 279 1.4 reinoud 280 1.4 reinoud /* how much is left before we hit a boundary */ 281 1.4 reinoud stripe_rest = tsc->stripe_chunksize - stripe_off; 282 1.4 reinoud 283 1.4 reinoud /* issue this piece on stripe `stripe' */ 284 1.4 reinoud issue_blks = MIN(stripe_rest, num_blks); 285 1.4 reinoud nestbuf = getiobuf(NULL, true); 286 1.4 reinoud 287 1.4 reinoud nestiobuf_setup(bp, nestbuf, blkoff, issue_blks * DEV_BSIZE); 288 1.4 reinoud nestbuf->b_blkno = stripe_blknr * tsc->stripe_chunksize + stripe_off; 289 1.4 reinoud 290 1.11 haad tlc = TAILQ_FIRST(&tsc->stripe_devs); 291 1.14 haad for (i = 0; i < stripe_devnr && tlc != NULL; i++) 292 1.11 haad tlc = TAILQ_NEXT(tlc, entries); 293 1.11 haad 294 1.45 rillig /* by this point we should have a tlc */ 295 1.17 haad KASSERT(tlc != NULL); 296 1.11 haad 297 1.11 haad nestbuf->b_blkno += tlc->offset; 298 1.11 haad 299 1.11 haad VOP_STRATEGY(tlc->pdev->pdev_vnode, nestbuf); 300 1.4 reinoud 301 1.9 haad blkno += issue_blks; 302 1.9 haad blkoff += issue_blks * DEV_BSIZE; 303 1.4 reinoud num_blks -= issue_blks; 304 1.4 reinoud 305 1.4 reinoud if (num_blks <= 0) 306 1.4 reinoud break; 307 1.4 reinoud } 308 1.1 haad 309 1.1 haad return 0; 310 1.1 haad } 311 1.23 christos 312 1.10 haad /* Sync underlying disk caches. */ 313 1.10 haad int 314 1.30 tkusumi dm_target_stripe_sync(dm_table_entry_t *table_en) 315 1.10 haad { 316 1.11 haad int cmd, err; 317 1.10 haad dm_target_stripe_config_t *tsc; 318 1.11 haad dm_target_linear_config_t *tlc; 319 1.10 haad 320 1.10 haad tsc = table_en->target_config; 321 1.10 haad 322 1.10 haad err = 0; 323 1.10 haad cmd = 1; 324 1.10 haad 325 1.11 haad TAILQ_FOREACH(tlc, &tsc->stripe_devs, entries) { 326 1.11 haad if ((err = VOP_IOCTL(tlc->pdev->pdev_vnode, DIOCCACHESYNC, 327 1.10 haad &cmd, FREAD|FWRITE, kauth_cred_get())) != 0) 328 1.10 haad return err; 329 1.10 haad } 330 1.15 ahoka 331 1.10 haad return err; 332 1.10 haad 333 1.10 haad } 334 1.23 christos 335 1.10 haad /* Destroy target specific data. */ 336 1.1 haad int 337 1.30 tkusumi dm_target_stripe_destroy(dm_table_entry_t *table_en) 338 1.1 haad { 339 1.38 tkusumi 340 1.21 christos dm_target_stripe_fini(table_en->target_config); 341 1.1 haad 342 1.1 haad /* Unbusy target so we can unload it */ 343 1.1 haad dm_target_unbusy(table_en->target); 344 1.9 haad 345 1.1 haad return 0; 346 1.1 haad } 347 1.23 christos 348 1.44 tkusumi #if 0 349 1.1 haad /* Unsupported for this target. */ 350 1.1 haad int 351 1.30 tkusumi dm_target_stripe_upcall(dm_table_entry_t *table_en, struct buf *bp) 352 1.1 haad { 353 1.38 tkusumi 354 1.1 haad return 0; 355 1.1 haad } 356 1.44 tkusumi #endif 357 1.23 christos 358 1.13 mlelstv /* 359 1.13 mlelstv * Compute physical block size 360 1.15 ahoka * For a stripe target we chose the maximum sector size of all 361 1.13 mlelstv * stripe devices. For the supported power-of-2 sizes this is equivalent 362 1.13 mlelstv * to the least common multiple. 363 1.13 mlelstv */ 364 1.13 mlelstv int 365 1.37 tkusumi dm_target_stripe_secsize(dm_table_entry_t *table_en, unsigned int *secsizep) 366 1.13 mlelstv { 367 1.13 mlelstv dm_target_linear_config_t *tlc; 368 1.13 mlelstv dm_target_stripe_config_t *tsc; 369 1.37 tkusumi unsigned int secsize; 370 1.13 mlelstv 371 1.13 mlelstv secsize = 0; 372 1.13 mlelstv 373 1.13 mlelstv tsc = table_en->target_config; 374 1.13 mlelstv if (tsc != NULL) { 375 1.13 mlelstv TAILQ_FOREACH(tlc, &tsc->stripe_devs, entries) { 376 1.13 mlelstv if (secsize < tlc->pdev->pdev_secsize) 377 1.13 mlelstv secsize = tlc->pdev->pdev_secsize; 378 1.13 mlelstv } 379 1.13 mlelstv } 380 1.13 mlelstv 381 1.13 mlelstv *secsizep = secsize; 382 1.13 mlelstv 383 1.13 mlelstv return 0; 384 1.13 mlelstv } 385