1*5b28f239Srillig /*$NetBSD: dm_target_stripe.c,v 1.45 2024/09/08 09:36:50 rillig Exp $*/ 232013fd9Shaad 332013fd9Shaad /* 432013fd9Shaad * Copyright (c) 2009 The NetBSD Foundation, Inc. 532013fd9Shaad * All rights reserved. 632013fd9Shaad * 732013fd9Shaad * This code is derived from software contributed to The NetBSD Foundation 832013fd9Shaad * by Adam Hamsik. 932013fd9Shaad * 1032013fd9Shaad * Redistribution and use in source and binary forms, with or without 1132013fd9Shaad * modification, are permitted provided that the following conditions 1232013fd9Shaad * are met: 1332013fd9Shaad * 1. Redistributions of source code must retain the above copyright 1432013fd9Shaad * notice, this list of conditions and the following disclaimer. 1532013fd9Shaad * 2. Redistributions in binary form must reproduce the above copyright 1632013fd9Shaad * notice, this list of conditions and the following disclaimer in the 1732013fd9Shaad * documentation and/or other materials provided with the distribution. 1832013fd9Shaad * 1932013fd9Shaad * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 2032013fd9Shaad * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 2132013fd9Shaad * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 2232013fd9Shaad * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 2332013fd9Shaad * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 2432013fd9Shaad * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 2532013fd9Shaad * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 2632013fd9Shaad * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 2732013fd9Shaad * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 2832013fd9Shaad * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 2932013fd9Shaad * POSSIBILITY OF SUCH DAMAGE. 3032013fd9Shaad */ 31249cf593Schristos #include <sys/cdefs.h> 32*5b28f239Srillig __KERNEL_RCSID(0, "$NetBSD: dm_target_stripe.c,v 1.45 2024/09/08 09:36:50 rillig Exp $"); 3332013fd9Shaad 3432013fd9Shaad /* 3532013fd9Shaad * This file implements initial version of device-mapper stripe target. 3632013fd9Shaad */ 3732013fd9Shaad #include <sys/types.h> 3832013fd9Shaad #include <sys/param.h> 3932013fd9Shaad #include <sys/buf.h> 40f048f56fShaad #include <sys/kmem.h> 410666f65eSuebayasi #include <sys/lwp.h> 4232013fd9Shaad 4332013fd9Shaad #include "dm.h" 4432013fd9Shaad 45837fc044Stkusumi typedef struct target_stripe_config { 46837fc044Stkusumi #define DM_STRIPE_DEV_OFFSET 2 47837fc044Stkusumi struct target_linear_devs stripe_devs; 48837fc044Stkusumi uint8_t stripe_num; 49837fc044Stkusumi uint64_t stripe_chunksize; 50837fc044Stkusumi } dm_target_stripe_config_t; 51837fc044Stkusumi 5232013fd9Shaad #ifdef DM_TARGET_MODULE 5332013fd9Shaad /* 5432013fd9Shaad * Every target can be compiled directly to dm driver or as a 5532013fd9Shaad * separate module this part of target is used for loading targets 5632013fd9Shaad * to dm driver. 5732013fd9Shaad * Target can be unloaded from kernel only if there are no users of 5832013fd9Shaad * it e.g. there are no devices which uses that target. 5932013fd9Shaad */ 6032013fd9Shaad #include <sys/kernel.h> 6132013fd9Shaad #include <sys/module.h> 6232013fd9Shaad 6332013fd9Shaad MODULE(MODULE_CLASS_MISC, dm_target_stripe, NULL); 6432013fd9Shaad 6532013fd9Shaad static int 6632013fd9Shaad dm_target_stripe_modcmd(modcmd_t cmd, void *arg) 6732013fd9Shaad { 6832013fd9Shaad dm_target_t *dmt; 6932013fd9Shaad int r; 7032013fd9Shaad 7132013fd9Shaad switch (cmd) { 7232013fd9Shaad case MODULE_CMD_INIT: 73943f3792Stkusumi if ((dmt = dm_target_lookup("striped")) != NULL) { 74f048f56fShaad dm_target_unbusy(dmt); 7532013fd9Shaad return EEXIST; 76f048f56fShaad } 77943f3792Stkusumi dmt = dm_target_alloc("striped"); 7832013fd9Shaad 7932013fd9Shaad dmt->version[0] = 1; 8032013fd9Shaad dmt->version[1] = 0; 8132013fd9Shaad dmt->version[2] = 0; 8232013fd9Shaad dmt->init = &dm_target_stripe_init; 83c2813277Stkusumi dmt->info = &dm_target_stripe_info; 848026110eStkusumi dmt->table = &dm_target_stripe_table; 8532013fd9Shaad dmt->strategy = &dm_target_stripe_strategy; 8658b64727Shaad dmt->sync = &dm_target_stripe_sync; 8732013fd9Shaad dmt->destroy = &dm_target_stripe_destroy; 8867de4a93Stkusumi //dmt->upcall = &dm_target_stripe_upcall; 8958dc1a2fSahoka dmt->secsize = &dm_target_stripe_secsize; 9032013fd9Shaad 9132013fd9Shaad r = dm_target_insert(dmt); 9232013fd9Shaad 9332013fd9Shaad break; 9432013fd9Shaad 9532013fd9Shaad case MODULE_CMD_FINI: 96943f3792Stkusumi r = dm_target_rem("striped"); 9732013fd9Shaad break; 9832013fd9Shaad 9932013fd9Shaad case MODULE_CMD_STAT: 10032013fd9Shaad return ENOTTY; 10132013fd9Shaad 10232013fd9Shaad default: 10332013fd9Shaad return ENOTTY; 10432013fd9Shaad } 10532013fd9Shaad 10632013fd9Shaad return r; 10732013fd9Shaad } 10832013fd9Shaad #endif 10932013fd9Shaad 110eceaff9aSchristos static void 111eceaff9aSchristos dm_target_stripe_fini(dm_target_stripe_config_t *tsc) 112eceaff9aSchristos { 113eceaff9aSchristos dm_target_linear_config_t *tlc; 114eceaff9aSchristos 115eceaff9aSchristos if (tsc == NULL) 116eceaff9aSchristos return; 117eceaff9aSchristos 118eceaff9aSchristos while ((tlc = TAILQ_FIRST(&tsc->stripe_devs)) != NULL) { 119eceaff9aSchristos TAILQ_REMOVE(&tsc->stripe_devs, tlc, entries); 120eceaff9aSchristos dm_pdev_decr(tlc->pdev); 121eceaff9aSchristos kmem_free(tlc, sizeof(*tlc)); 122eceaff9aSchristos } 123eceaff9aSchristos 124eceaff9aSchristos kmem_free(tsc, sizeof(*tsc)); 125eceaff9aSchristos } 126eceaff9aSchristos 127f048f56fShaad /* 128f048f56fShaad * Init function called from dm_table_load_ioctl. 1291a571ae4Shaad * DM_STRIPE_DEV_OFFSET should always hold the index of the first device-offset 1301a571ae4Shaad * pair in the parameters. 131f048f56fShaad * Example line sent to dm from lvm tools when using striped target. 132f048f56fShaad * start length striped #stripes chunk_size device1 offset1 ... deviceN offsetN 133f048f56fShaad * 0 65536 striped 2 512 /dev/hda 0 /dev/hdb 0 134f048f56fShaad */ 13532013fd9Shaad int 136d14bb027Stkusumi dm_target_stripe_init(dm_table_entry_t *table_en, int argc, char **argv) 13732013fd9Shaad { 1381a571ae4Shaad dm_target_linear_config_t *tlc; 139f048f56fShaad dm_target_stripe_config_t *tsc; 1401a571ae4Shaad int strpc, strpi; 141f048f56fShaad 142945ce57bStkusumi if (argc < 2) { 143945ce57bStkusumi printf("Stripe target takes at least 2 args, %d given\n", argc); 144d14bb027Stkusumi return EINVAL; 145f26bfe28Shaad } 14632013fd9Shaad 147f26bfe28Shaad printf("Stripe target init function called!!\n"); 1481a571ae4Shaad printf("Stripe target chunk size %s number of stripes %s\n", 1491a571ae4Shaad argv[1], argv[0]); 150f048f56fShaad 1510a827a3fSchs tsc = kmem_alloc(sizeof(*tsc), KM_SLEEP); 152f048f56fShaad 1531a571ae4Shaad /* Initialize linked list for striping devices */ 1541a571ae4Shaad TAILQ_INIT(&tsc->stripe_devs); 155f048f56fShaad 156f26bfe28Shaad /* Save length of param string */ 157e4ac7035Stkusumi tsc->stripe_chunksize = atoi64(argv[1]); 158e4ac7035Stkusumi tsc->stripe_num = (uint8_t) atoi64(argv[0]); 159f048f56fShaad 1601a571ae4Shaad strpc = DM_STRIPE_DEV_OFFSET + (tsc->stripe_num * 2); 1611a571ae4Shaad for (strpi = DM_STRIPE_DEV_OFFSET; strpi < strpc; strpi += 2) { 1621a571ae4Shaad printf("Stripe target device name %s -- offset %s\n", 1631a571ae4Shaad argv[strpi], argv[strpi+1]); 1641a571ae4Shaad 1650a827a3fSchs tlc = kmem_alloc(sizeof(*tlc), KM_SLEEP); 166f37d41b3Sagc if ((tlc->pdev = dm_pdev_insert(argv[strpi])) == NULL) { 167f37d41b3Sagc kmem_free(tlc, sizeof(*tlc)); 168eceaff9aSchristos dm_target_stripe_fini(tsc); 1691a571ae4Shaad return ENOENT; 170f37d41b3Sagc } 171e4ac7035Stkusumi tlc->offset = atoi64(argv[strpi+1]); 172ee87a3caStkusumi dm_table_add_deps(table_en, tlc->pdev); 1731a571ae4Shaad 1741a571ae4Shaad /* Insert striping device to linked list. */ 1751a571ae4Shaad TAILQ_INSERT_TAIL(&tsc->stripe_devs, tlc, entries); 1761a571ae4Shaad } 1771a571ae4Shaad 1782cb93e57Stkusumi table_en->target_config = tsc; 17932013fd9Shaad 180f048f56fShaad return 0; 18132013fd9Shaad } 182249cf593Schristos 183c2813277Stkusumi /* Info routine called to get params string. */ 184c2813277Stkusumi char * 185c2813277Stkusumi dm_target_stripe_info(void *target_config) 186c2813277Stkusumi { 187c2813277Stkusumi dm_target_linear_config_t *tlc; 188c2813277Stkusumi dm_target_stripe_config_t *tsc; 189c2813277Stkusumi char *params, *ptr, buf[256]; 190c2813277Stkusumi int ret, i = 0; 191c2813277Stkusumi size_t len; 192c2813277Stkusumi 193c2813277Stkusumi tsc = target_config; 194c2813277Stkusumi 195c2813277Stkusumi len = DM_MAX_PARAMS_SIZE; 196c2813277Stkusumi params = kmem_alloc(len, KM_SLEEP); 197c2813277Stkusumi ptr = params; 198c2813277Stkusumi 199c2813277Stkusumi ret = snprintf(ptr, len, "%d ", tsc->stripe_num); 200c2813277Stkusumi ptr += ret; 201c2813277Stkusumi len -= ret; 202c2813277Stkusumi 203c2813277Stkusumi memset(buf, 0, sizeof(buf)); 204c2813277Stkusumi TAILQ_FOREACH(tlc, &tsc->stripe_devs, entries) { 205c2813277Stkusumi ret = snprintf(ptr, len, "%s ", tlc->pdev->udev_name); 206c2813277Stkusumi if (0 /*tlc->num_error*/) 207c2813277Stkusumi buf[i] = 'D'; 208c2813277Stkusumi else 209c2813277Stkusumi buf[i] = 'A'; 210c2813277Stkusumi i++; 211c2813277Stkusumi ptr += ret; 212c2813277Stkusumi len -= ret; 213c2813277Stkusumi } 214c2813277Stkusumi 215c2813277Stkusumi ret = snprintf(ptr, len, "1 %s", buf); 216c2813277Stkusumi ptr += ret; 217c2813277Stkusumi len -= ret; 218c2813277Stkusumi 219c2813277Stkusumi return params; 220c2813277Stkusumi } 221c2813277Stkusumi 2228026110eStkusumi /* Table routine called to get params string. */ 22332013fd9Shaad char * 2248026110eStkusumi dm_target_stripe_table(void *target_config) 22532013fd9Shaad { 2261a571ae4Shaad dm_target_linear_config_t *tlc; 227f048f56fShaad dm_target_stripe_config_t *tsc; 2281a571ae4Shaad char *params, *tmp; 229f048f56fShaad 230f048f56fShaad tsc = target_config; 231f048f56fShaad 232fd34ea77Schs params = kmem_alloc(DM_MAX_PARAMS_SIZE, KM_SLEEP); 233fd34ea77Schs tmp = kmem_alloc(DM_MAX_PARAMS_SIZE, KM_SLEEP); 2341a571ae4Shaad 2351a571ae4Shaad snprintf(params, DM_MAX_PARAMS_SIZE, "%d %" PRIu64, 2361a571ae4Shaad tsc->stripe_num, tsc->stripe_chunksize); 2371a571ae4Shaad 2381a571ae4Shaad TAILQ_FOREACH(tlc, &tsc->stripe_devs, entries) { 2391a571ae4Shaad snprintf(tmp, DM_MAX_PARAMS_SIZE, " %s %" PRIu64, 240aecedc2eStkusumi tlc->pdev->udev_name, tlc->offset); 2411a571ae4Shaad strcat(params, tmp); 2421a571ae4Shaad } 2431a571ae4Shaad 2441a571ae4Shaad kmem_free(tmp, DM_MAX_PARAMS_SIZE); 245f048f56fShaad 246f048f56fShaad return params; 24732013fd9Shaad } 248249cf593Schristos 24932013fd9Shaad /* Strategy routine called from dm_strategy. */ 25032013fd9Shaad int 25132013fd9Shaad dm_target_stripe_strategy(dm_table_entry_t *table_en, struct buf *bp) 25232013fd9Shaad { 2531a571ae4Shaad dm_target_linear_config_t *tlc; 254664af01cSreinoud dm_target_stripe_config_t *tsc; 255664af01cSreinoud struct buf *nestbuf; 256664af01cSreinoud uint64_t blkno, blkoff; 257664af01cSreinoud uint64_t stripe, stripe_blknr; 258664af01cSreinoud uint32_t stripe_off, stripe_rest, num_blks, issue_blks; 2591a571ae4Shaad int i, stripe_devnr; 26032013fd9Shaad 261664af01cSreinoud tsc = table_en->target_config; 262664af01cSreinoud if (tsc == NULL) 263664af01cSreinoud return 0; 26432013fd9Shaad 265664af01cSreinoud /* calculate extent of request */ 266664af01cSreinoud KASSERT(bp->b_resid % DEV_BSIZE == 0); 267664af01cSreinoud 268664af01cSreinoud blkno = bp->b_blkno; 269664af01cSreinoud blkoff = 0; 270664af01cSreinoud num_blks = bp->b_resid / DEV_BSIZE; 271664af01cSreinoud for (;;) { 272354a4ac4Stkusumi /* blockno to stripe piece nr */ 273664af01cSreinoud stripe = blkno / tsc->stripe_chunksize; 274664af01cSreinoud stripe_off = blkno % tsc->stripe_chunksize; 275664af01cSreinoud 276354a4ac4Stkusumi /* where we are inside the stripe */ 277664af01cSreinoud stripe_devnr = stripe % tsc->stripe_num; 278664af01cSreinoud stripe_blknr = stripe / tsc->stripe_num; 279664af01cSreinoud 280664af01cSreinoud /* how much is left before we hit a boundary */ 281664af01cSreinoud stripe_rest = tsc->stripe_chunksize - stripe_off; 282664af01cSreinoud 283664af01cSreinoud /* issue this piece on stripe `stripe' */ 284664af01cSreinoud issue_blks = MIN(stripe_rest, num_blks); 285664af01cSreinoud nestbuf = getiobuf(NULL, true); 286664af01cSreinoud 287664af01cSreinoud nestiobuf_setup(bp, nestbuf, blkoff, issue_blks * DEV_BSIZE); 288664af01cSreinoud nestbuf->b_blkno = stripe_blknr * tsc->stripe_chunksize + stripe_off; 289664af01cSreinoud 2901a571ae4Shaad tlc = TAILQ_FIRST(&tsc->stripe_devs); 291b08f5943Shaad for (i = 0; i < stripe_devnr && tlc != NULL; i++) 2921a571ae4Shaad tlc = TAILQ_NEXT(tlc, entries); 2931a571ae4Shaad 294*5b28f239Srillig /* by this point we should have a tlc */ 29562688f91Shaad KASSERT(tlc != NULL); 2961a571ae4Shaad 2971a571ae4Shaad nestbuf->b_blkno += tlc->offset; 2981a571ae4Shaad 2991a571ae4Shaad VOP_STRATEGY(tlc->pdev->pdev_vnode, nestbuf); 300664af01cSreinoud 301664af01cSreinoud blkno += issue_blks; 302664af01cSreinoud blkoff += issue_blks * DEV_BSIZE; 303664af01cSreinoud num_blks -= issue_blks; 304664af01cSreinoud 305664af01cSreinoud if (num_blks <= 0) 306664af01cSreinoud break; 307664af01cSreinoud } 30832013fd9Shaad 30932013fd9Shaad return 0; 31032013fd9Shaad } 311249cf593Schristos 31258b64727Shaad /* Sync underlying disk caches. */ 31358b64727Shaad int 31458b64727Shaad dm_target_stripe_sync(dm_table_entry_t *table_en) 31558b64727Shaad { 3161a571ae4Shaad int cmd, err; 31758b64727Shaad dm_target_stripe_config_t *tsc; 3181a571ae4Shaad dm_target_linear_config_t *tlc; 31958b64727Shaad 32058b64727Shaad tsc = table_en->target_config; 32158b64727Shaad 32258b64727Shaad err = 0; 32358b64727Shaad cmd = 1; 32458b64727Shaad 3251a571ae4Shaad TAILQ_FOREACH(tlc, &tsc->stripe_devs, entries) { 3261a571ae4Shaad if ((err = VOP_IOCTL(tlc->pdev->pdev_vnode, DIOCCACHESYNC, 32758b64727Shaad &cmd, FREAD|FWRITE, kauth_cred_get())) != 0) 32858b64727Shaad return err; 32958b64727Shaad } 33058b64727Shaad 33158b64727Shaad return err; 33258b64727Shaad 33358b64727Shaad } 334249cf593Schristos 33558b64727Shaad /* Destroy target specific data. */ 33632013fd9Shaad int 33732013fd9Shaad dm_target_stripe_destroy(dm_table_entry_t *table_en) 33832013fd9Shaad { 33946f14759Stkusumi 340eceaff9aSchristos dm_target_stripe_fini(table_en->target_config); 34132013fd9Shaad 34232013fd9Shaad /* Unbusy target so we can unload it */ 34332013fd9Shaad dm_target_unbusy(table_en->target); 34432013fd9Shaad 34532013fd9Shaad return 0; 34632013fd9Shaad } 347249cf593Schristos 34867de4a93Stkusumi #if 0 34932013fd9Shaad /* Unsupported for this target. */ 35032013fd9Shaad int 35132013fd9Shaad dm_target_stripe_upcall(dm_table_entry_t *table_en, struct buf *bp) 35232013fd9Shaad { 35346f14759Stkusumi 35432013fd9Shaad return 0; 35532013fd9Shaad } 35667de4a93Stkusumi #endif 357249cf593Schristos 35875268cffSmlelstv /* 35975268cffSmlelstv * Compute physical block size 36075268cffSmlelstv * For a stripe target we chose the maximum sector size of all 36175268cffSmlelstv * stripe devices. For the supported power-of-2 sizes this is equivalent 36275268cffSmlelstv * to the least common multiple. 36375268cffSmlelstv */ 36475268cffSmlelstv int 365c3c15a28Stkusumi dm_target_stripe_secsize(dm_table_entry_t *table_en, unsigned int *secsizep) 36675268cffSmlelstv { 36775268cffSmlelstv dm_target_linear_config_t *tlc; 36875268cffSmlelstv dm_target_stripe_config_t *tsc; 369c3c15a28Stkusumi unsigned int secsize; 37075268cffSmlelstv 37175268cffSmlelstv secsize = 0; 37275268cffSmlelstv 37375268cffSmlelstv tsc = table_en->target_config; 37475268cffSmlelstv if (tsc != NULL) { 37575268cffSmlelstv TAILQ_FOREACH(tlc, &tsc->stripe_devs, entries) { 37675268cffSmlelstv if (secsize < tlc->pdev->pdev_secsize) 37775268cffSmlelstv secsize = tlc->pdev->pdev_secsize; 37875268cffSmlelstv } 37975268cffSmlelstv } 38075268cffSmlelstv 38175268cffSmlelstv *secsizep = secsize; 38275268cffSmlelstv 38375268cffSmlelstv return 0; 38475268cffSmlelstv } 385