xref: /netbsd-src/sys/dev/dm/dm_target_stripe.c (revision 5b28f239895d55856221c590945769250e289f5f)
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