1 /*$NetBSD: dm_target_stripe.c,v 1.6 2009/06/05 19:56:40 haad 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 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 42 #include "dm.h" 43 #include "netbsd-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 56 MODULE(MODULE_CLASS_MISC, dm_target_stripe, NULL); 57 58 static int 59 dm_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 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->deps = &dm_target_stripe_deps; 82 dmt->destroy = &dm_target_stripe_destroy; 83 dmt->upcall = &dm_target_stripe_upcall; 84 85 r = dm_target_insert(dmt); 86 87 break; 88 89 case MODULE_CMD_FINI: 90 r = dm_target_rem("stripe"); 91 break; 92 93 case MODULE_CMD_STAT: 94 return ENOTTY; 95 96 default: 97 return ENOTTY; 98 } 99 100 return r; 101 } 102 103 #endif 104 105 /* 106 * Init function called from dm_table_load_ioctl. 107 * Example line sent to dm from lvm tools when using striped target. 108 * start length striped #stripes chunk_size device1 offset1 ... deviceN offsetN 109 * 0 65536 striped 2 512 /dev/hda 0 /dev/hdb 0 110 */ 111 int 112 dm_target_stripe_init(dm_dev_t *dmv, void **target_config, prop_dictionary_t dict) 113 { 114 dm_target_stripe_config_t *tsc; 115 prop_array_t dev_array; 116 prop_dictionary_t dev_dict1, dev_dict2; 117 118 uint64_t stripes, chunk_size, offset1, offset2; 119 const char *device1, *device2; 120 121 if (prop_dictionary_get_uint64(dict, DM_TARGET_STRIPE_STRIPES, 122 &stripes) == false) 123 return EINVAL; 124 125 if (prop_dictionary_get_uint64(dict, DM_TARGET_STRIPE_CHUNKSIZE, 126 &chunk_size) == false) 127 return EINVAL; 128 129 dev_array = prop_dictionary_get(dict, DM_TARGET_STRIPE_DEVARRAY); 130 131 /* XXX Support for more than 2 devices */ 132 dev_dict1 = prop_array_get(dev_array, 0); 133 dev_dict2 = prop_array_get(dev_array, 1); 134 135 if(dev_dict2 == NULL || dev_dict1 == NULL) 136 return EINVAL; 137 138 prop_dictionary_get_cstring_nocopy(dev_dict1, DM_TARGET_STRIPE_DEVICE, &device1); 139 prop_dictionary_get_uint64(dev_dict1, DM_TARGET_STRIPE_OFFSET, &offset1); 140 prop_dictionary_get_cstring_nocopy(dev_dict2, DM_TARGET_STRIPE_DEVICE, &device2); 141 prop_dictionary_get_uint64(dev_dict2, DM_TARGET_STRIPE_OFFSET, &offset2); 142 143 aprint_debug("Stripe target init function called!!\n"); 144 145 aprint_debug("Stripe target chunk size %"PRIu64" number of stripes %"PRIu64"\n", chunk_size, stripes); 146 aprint_debug("Stripe target device name %s -- offset %"PRIu64"\n", device1, offset1); 147 aprint_debug("Stripe target device name %s -- offset %"PRIu64"\n", device2, offset2); 148 149 if (stripes > MAX_STRIPES) 150 return ENOTSUP; 151 152 if ((tsc = kmem_alloc(sizeof(dm_target_stripe_config_t), KM_NOSLEEP)) 153 == NULL) 154 return ENOMEM; 155 156 /* Insert dmp to global pdev list */ 157 if ((tsc->stripe_devs[0].pdev = dm_pdev_insert(device1)) == NULL) 158 return ENOENT; 159 160 /* Insert dmp to global pdev list */ 161 if ((tsc->stripe_devs[1].pdev = dm_pdev_insert(device2)) == NULL) 162 return ENOENT; 163 164 tsc->stripe_devs[0].offset = offset1; 165 tsc->stripe_devs[1].offset = offset2; 166 167 /* Save length of param string */ 168 tsc->params_len = DM_MAX_PARAMS_SIZE; 169 tsc->stripe_chunksize = chunk_size; 170 tsc->stripe_num = (uint8_t)stripes; 171 172 *target_config = tsc; 173 174 dmv->dev_type = DM_STRIPE_DEV; 175 176 return 0; 177 } 178 179 /* Status routine called to get params string. */ 180 char * 181 dm_target_stripe_status(void *target_config) 182 { 183 dm_target_stripe_config_t *tsc; 184 char *params; 185 186 tsc = target_config; 187 188 if ((params = kmem_alloc(tsc->params_len, KM_NOSLEEP)) == NULL) 189 return NULL; 190 191 snprintf(params, tsc->params_len, "%d %"PRIu64" %s %"PRIu64" %s %"PRIu64, 192 tsc->stripe_num, tsc->stripe_chunksize, 193 tsc->stripe_devs[0].pdev->name, tsc->stripe_devs[0].offset, 194 tsc->stripe_devs[1].pdev->name, tsc->stripe_devs[1].offset); 195 196 return params; 197 } 198 199 /* Strategy routine called from dm_strategy. */ 200 int 201 dm_target_stripe_strategy(dm_table_entry_t *table_en, struct buf *bp) 202 { 203 dm_target_stripe_config_t *tsc; 204 struct buf *nestbuf; 205 uint64_t blkno, blkoff; 206 uint64_t stripe, stripe_blknr; 207 uint32_t stripe_off, stripe_rest, num_blks, issue_blks; 208 int stripe_devnr; 209 210 tsc = table_en->target_config; 211 if (tsc == NULL) 212 return 0; 213 214 /* aprint_debug("Stripe target read function called %" PRIu64 "!!\n", 215 tlc->offset);*/ 216 217 /* calculate extent of request */ 218 KASSERT(bp->b_resid % DEV_BSIZE == 0); 219 220 blkno = bp->b_blkno; 221 blkoff = 0; 222 num_blks = bp->b_resid / DEV_BSIZE; 223 for (;;) { 224 /* blockno to strip piece nr */ 225 stripe = blkno / tsc->stripe_chunksize; 226 stripe_off = blkno % tsc->stripe_chunksize; 227 228 /* where we are inside the strip */ 229 stripe_devnr = stripe % tsc->stripe_num; 230 stripe_blknr = stripe / tsc->stripe_num; 231 232 /* how much is left before we hit a boundary */ 233 stripe_rest = tsc->stripe_chunksize - stripe_off; 234 235 /* issue this piece on stripe `stripe' */ 236 issue_blks = MIN(stripe_rest, num_blks); 237 nestbuf = getiobuf(NULL, true); 238 239 nestiobuf_setup(bp, nestbuf, blkoff, issue_blks * DEV_BSIZE); 240 nestbuf->b_blkno = stripe_blknr * tsc->stripe_chunksize + stripe_off; 241 nestbuf->b_blkno += tsc->stripe_devs[stripe_devnr].offset; 242 243 VOP_STRATEGY(tsc->stripe_devs[stripe_devnr].pdev->pdev_vnode, nestbuf); 244 245 blkno += issue_blks; 246 blkoff += issue_blks * DEV_BSIZE; 247 num_blks -= issue_blks; 248 249 if (num_blks <= 0) 250 break; 251 } 252 253 return 0; 254 } 255 256 /* Doesn't do anything here. */ 257 int 258 dm_target_stripe_destroy(dm_table_entry_t *table_en) 259 { 260 dm_target_stripe_config_t *tsc; 261 262 tsc = table_en->target_config; 263 264 if (tsc == NULL) 265 return 0; 266 267 dm_pdev_decr(tsc->stripe_devs[0].pdev); 268 dm_pdev_decr(tsc->stripe_devs[1].pdev); 269 270 /* Unbusy target so we can unload it */ 271 dm_target_unbusy(table_en->target); 272 273 kmem_free(tsc, sizeof(dm_target_stripe_config_t)); 274 275 table_en->target_config = NULL; 276 277 return 0; 278 } 279 280 /* Doesn't not need to do anything here. */ 281 int 282 dm_target_stripe_deps(dm_table_entry_t *table_en, prop_array_t prop_array) 283 { 284 dm_target_stripe_config_t *tsc; 285 struct vattr va; 286 287 int error; 288 289 if (table_en->target_config == NULL) 290 return ENOENT; 291 292 tsc = table_en->target_config; 293 294 if ((error = VOP_GETATTR(tsc->stripe_devs[0].pdev->pdev_vnode, &va, curlwp->l_cred)) != 0) 295 return error; 296 297 prop_array_add_uint64(prop_array, (uint64_t)va.va_rdev); 298 299 if ((error = VOP_GETATTR(tsc->stripe_devs[1].pdev->pdev_vnode, &va, curlwp->l_cred)) != 0) 300 return error; 301 302 prop_array_add_uint64(prop_array, (uint64_t)va.va_rdev); 303 304 return 0; 305 } 306 307 /* Unsupported for this target. */ 308 int 309 dm_target_stripe_upcall(dm_table_entry_t *table_en, struct buf *bp) 310 { 311 return 0; 312 } 313