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