xref: /dragonfly/sys/dev/disk/dm/dm_pdev.c (revision b1999ea8)
1ff56536eSAlex Hornung /*        $NetBSD: dm_pdev.c,v 1.6 2010/01/04 00:19:08 haad Exp $      */
2ff56536eSAlex Hornung 
3ff56536eSAlex Hornung /*
4ba2ce341SAlex Hornung  * Copyright (c) 2010-2011 Alex Hornung <alex@alexhornung.com>
5ff56536eSAlex Hornung  * Copyright (c) 2008 The NetBSD Foundation, Inc.
6ff56536eSAlex Hornung  * All rights reserved.
7ff56536eSAlex Hornung  *
8ff56536eSAlex Hornung  * This code is derived from software contributed to The NetBSD Foundation
9ff56536eSAlex Hornung  * by Adam Hamsik.
10ff56536eSAlex Hornung  *
11ff56536eSAlex Hornung  * Redistribution and use in source and binary forms, with or without
12ff56536eSAlex Hornung  * modification, are permitted provided that the following conditions
13ff56536eSAlex Hornung  * are met:
14ff56536eSAlex Hornung  * 1. Redistributions of source code must retain the above copyright
15ff56536eSAlex Hornung  *    notice, this list of conditions and the following disclaimer.
16ff56536eSAlex Hornung  * 2. Redistributions in binary form must reproduce the above copyright
17ff56536eSAlex Hornung  *    notice, this list of conditions and the following disclaimer in the
18ff56536eSAlex Hornung  *    documentation and/or other materials provided with the distribution.
19ff56536eSAlex Hornung  *
20ff56536eSAlex Hornung  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21ff56536eSAlex Hornung  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22ff56536eSAlex Hornung  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23ff56536eSAlex Hornung  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24ff56536eSAlex Hornung  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25ff56536eSAlex Hornung  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26ff56536eSAlex Hornung  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27ff56536eSAlex Hornung  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28ff56536eSAlex Hornung  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29ff56536eSAlex Hornung  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30ff56536eSAlex Hornung  * POSSIBILITY OF SUCH DAMAGE.
31ff56536eSAlex Hornung  */
32ff56536eSAlex Hornung 
33ff56536eSAlex Hornung #include <sys/disk.h>
34ff56536eSAlex Hornung #include <sys/fcntl.h>
355b279a20SAlex Hornung #include <sys/malloc.h>
365b279a20SAlex Hornung #include <sys/nlookup.h>
37*d2348894SSascha Wildner #include <sys/proc.h>
38ff56536eSAlex Hornung 
39a84e173eSAlex Hornung #include <dev/disk/dm/dm.h>
40ff56536eSAlex Hornung 
414e359672STomohiro Kusumi static TAILQ_HEAD(, dm_pdev) dm_pdev_list;
42ff56536eSAlex Hornung 
43637b454aSTomohiro Kusumi static struct lock dm_pdev_mutex;
44ff56536eSAlex Hornung 
45ff56536eSAlex Hornung static dm_pdev_t *dm_pdev_alloc(const char *);
4641d37b86STomohiro Kusumi static int dm_pdev_free(dm_pdev_t *);
47ff56536eSAlex Hornung static dm_pdev_t *dm_pdev_lookup_name(const char *);
48ff56536eSAlex Hornung 
49ff56536eSAlex Hornung /*
50ff56536eSAlex Hornung  * Find used pdev with name == dm_pdev_name.
51add6c27fSAlex Hornung  * needs to be called with the dm_pdev_mutex held.
52ff56536eSAlex Hornung  */
53add6c27fSAlex Hornung static dm_pdev_t *
dm_pdev_lookup_name(const char * dm_pdev_name)54ff56536eSAlex Hornung dm_pdev_lookup_name(const char *dm_pdev_name)
55ff56536eSAlex Hornung {
56bdf2de7bSTomohiro Kusumi 	dm_pdev_t *dmp;
57ff56536eSAlex Hornung 
585b279a20SAlex Hornung 	KKASSERT(dm_pdev_name != NULL);
59ff56536eSAlex Hornung 
60b2ecd1aaSTomohiro Kusumi 	TAILQ_FOREACH(dmp, &dm_pdev_list, next_pdev) {
61bdf2de7bSTomohiro Kusumi 		if (strcmp(dm_pdev_name, dmp->name) == 0)
62bdf2de7bSTomohiro Kusumi 			return dmp;
63ff56536eSAlex Hornung 	}
64ff56536eSAlex Hornung 
65ff56536eSAlex Hornung 	return NULL;
66ff56536eSAlex Hornung }
675b279a20SAlex Hornung 
685b279a20SAlex Hornung static int
dm_dk_lookup(const char * dev_name,struct vnode ** vpp)695b279a20SAlex Hornung dm_dk_lookup(const char *dev_name, struct vnode **vpp)
705b279a20SAlex Hornung {
715b279a20SAlex Hornung 	struct nlookupdata nd;
725b279a20SAlex Hornung 	int error;
735b279a20SAlex Hornung 
745b279a20SAlex Hornung 	error = nlookup_init(&nd, dev_name, UIO_SYSSPACE, NLC_FOLLOW);
755b279a20SAlex Hornung 	if (error)
765b279a20SAlex Hornung 		return error;
775b279a20SAlex Hornung 
785b279a20SAlex Hornung 	error = vn_open(&nd, NULL, FREAD|FWRITE, 0);
79776654afSAlex Hornung 	if (error) {
80c9d49f70SAlex Hornung 		nlookup_done(&nd);
81776654afSAlex Hornung 		return error;
82776654afSAlex Hornung 	}
83776654afSAlex Hornung 
845b279a20SAlex Hornung 	*vpp = nd.nl_open_vp;
855b279a20SAlex Hornung 	nd.nl_open_vp = NULL;
865b279a20SAlex Hornung 	nlookup_done(&nd);
875b279a20SAlex Hornung 
885b279a20SAlex Hornung 	return 0;
895b279a20SAlex Hornung }
905b279a20SAlex Hornung 
91add6c27fSAlex Hornung /*
92add6c27fSAlex Hornung  * Since dm can have arbitrary stacking on any number of disks and any dm
93add6c27fSAlex Hornung  * volume is at least stacked onto another disk, we need to adjust the
94add6c27fSAlex Hornung  * dumping offset (which is a raw offset from the beginning of the lowest
95add6c27fSAlex Hornung  * physical disk) taking into account the offset of the underlying device
96add6c27fSAlex Hornung  * which in turn takes into account the offset below it, etc.
97add6c27fSAlex Hornung  *
98add6c27fSAlex Hornung  * This function adjusts the dumping offset that is passed to the next
99add6c27fSAlex Hornung  * dev_ddump() so it is correct for that underlying device.
100add6c27fSAlex Hornung  */
10179b7159fSAlex Hornung off_t
dm_pdev_correct_dump_offset(dm_pdev_t * pdev,off_t offset)10279b7159fSAlex Hornung dm_pdev_correct_dump_offset(dm_pdev_t *pdev, off_t offset)
10379b7159fSAlex Hornung {
10479b7159fSAlex Hornung 	off_t noffset;
10579b7159fSAlex Hornung 
10679b7159fSAlex Hornung 	noffset = pdev->pdev_pinfo.reserved_blocks +
10779b7159fSAlex Hornung 	    pdev->pdev_pinfo.media_offset / pdev->pdev_pinfo.media_blksize;
10879b7159fSAlex Hornung 	noffset *= DEV_BSIZE;
10979b7159fSAlex Hornung 	noffset += offset;
11079b7159fSAlex Hornung 
11179b7159fSAlex Hornung 	return noffset;
11279b7159fSAlex Hornung }
11379b7159fSAlex Hornung 
114ff56536eSAlex Hornung /*
115ff56536eSAlex Hornung  * Create entry for device with name dev_name and open vnode for it.
116b2ecd1aaSTomohiro Kusumi  * If entry already exists in global TAILQ I will only increment
117ff56536eSAlex Hornung  * reference counter.
118ff56536eSAlex Hornung  */
119ff56536eSAlex Hornung dm_pdev_t *
dm_pdev_insert(const char * dev_name)120ff56536eSAlex Hornung dm_pdev_insert(const char *dev_name)
121ff56536eSAlex Hornung {
122ff56536eSAlex Hornung 	dm_pdev_t *dmp;
12376d3bec1STomohiro Kusumi 	struct vattr va;
124ff56536eSAlex Hornung 	int error;
125ff56536eSAlex Hornung 
1265b279a20SAlex Hornung 	KKASSERT(dev_name != NULL);
127ff56536eSAlex Hornung 
1285b279a20SAlex Hornung 	lockmgr(&dm_pdev_mutex, LK_EXCLUSIVE);
129ff56536eSAlex Hornung 	dmp = dm_pdev_lookup_name(dev_name);
130ff56536eSAlex Hornung 
131ff56536eSAlex Hornung 	if (dmp != NULL) {
132ff56536eSAlex Hornung 		dmp->ref_cnt++;
13328d082ddSTomohiro Kusumi 		dmdebug("pdev %s already in tree\n", dev_name);
1345b279a20SAlex Hornung 		lockmgr(&dm_pdev_mutex, LK_RELEASE);
135ff56536eSAlex Hornung 		return dmp;
136ff56536eSAlex Hornung 	}
137ff56536eSAlex Hornung 
138ac816675STomohiro Kusumi 	if ((dmp = dm_pdev_alloc(dev_name)) == NULL) {
139ac816675STomohiro Kusumi 		lockmgr(&dm_pdev_mutex, LK_RELEASE);
140ff56536eSAlex Hornung 		return NULL;
141ac816675STomohiro Kusumi 	}
142ff56536eSAlex Hornung 
1435b279a20SAlex Hornung 	error = dm_dk_lookup(dev_name, &dmp->pdev_vnode);
144ff56536eSAlex Hornung 	if (error) {
14528d082ddSTomohiro Kusumi 		dmdebug("Lookup on %s failed with error %d!\n",
146ff56536eSAlex Hornung 		    dev_name, error);
14741d37b86STomohiro Kusumi 		dm_pdev_free(dmp);
148ac816675STomohiro Kusumi 		lockmgr(&dm_pdev_mutex, LK_RELEASE);
149ff56536eSAlex Hornung 		return NULL;
150ff56536eSAlex Hornung 	}
151ff56536eSAlex Hornung 	dmp->ref_cnt = 1;
152ff56536eSAlex Hornung 
15376d3bec1STomohiro Kusumi 	if (dm_pdev_get_vattr(dmp, &va) == -1) {
15428d082ddSTomohiro Kusumi 		dmdebug("getattr on %s failed\n", dev_name);
15541d37b86STomohiro Kusumi 		dm_pdev_free(dmp);
15676d3bec1STomohiro Kusumi 		lockmgr(&dm_pdev_mutex, LK_RELEASE);
15776d3bec1STomohiro Kusumi 		return NULL;
15876d3bec1STomohiro Kusumi 	}
15976d3bec1STomohiro Kusumi 	ksnprintf(dmp->udev_name, sizeof(dmp->udev_name),
16076d3bec1STomohiro Kusumi 		"%d:%d", va.va_rmajor, va.va_rminor);
1614f398b7bSTomohiro Kusumi 	dmp->udev = dm_pdev_get_udev(dmp);
16276d3bec1STomohiro Kusumi 
16379b7159fSAlex Hornung 	/*
16479b7159fSAlex Hornung 	 * Get us the partinfo from the underlying device, it's needed for
16579b7159fSAlex Hornung 	 * dumps.
16679b7159fSAlex Hornung 	 */
16779b7159fSAlex Hornung 	bzero(&dmp->pdev_pinfo, sizeof(dmp->pdev_pinfo));
16879b7159fSAlex Hornung 	error = dev_dioctl(dmp->pdev_vnode->v_rdev, DIOCGPART,
1698c530b23SJohannes Hofmann 	    (void *)&dmp->pdev_pinfo, 0, proc0.p_ucred, NULL, NULL);
170cec31096STomohiro Kusumi 	if (!error) {
171cec31096STomohiro Kusumi 		struct partinfo *dpart = &dmp->pdev_pinfo;
17228d082ddSTomohiro Kusumi 		dmdebug("DIOCGPART offset=%ju size=%ju blocks=%ju blksize=%d\n",
173cec31096STomohiro Kusumi 			dpart->media_offset,
174cec31096STomohiro Kusumi 			dpart->media_size,
175cec31096STomohiro Kusumi 			dpart->media_blocks,
176cec31096STomohiro Kusumi 			dpart->media_blksize);
177cec31096STomohiro Kusumi 	} else {
1789842ce30STomohiro Kusumi 		kprintf("dmp_pdev_insert DIOCGPART failed %d\n", error);
179cec31096STomohiro Kusumi 	}
18079b7159fSAlex Hornung 
181b2ecd1aaSTomohiro Kusumi 	TAILQ_INSERT_TAIL(&dm_pdev_list, dmp, next_pdev);
1825b279a20SAlex Hornung 	lockmgr(&dm_pdev_mutex, LK_RELEASE);
183ff56536eSAlex Hornung 
18428d082ddSTomohiro Kusumi 	dmdebug("pdev %s %s 0x%016jx\n",
1854f398b7bSTomohiro Kusumi 		dmp->name, dmp->udev_name, (uintmax_t)dmp->udev);
18676d3bec1STomohiro Kusumi 
187ff56536eSAlex Hornung 	return dmp;
188ff56536eSAlex Hornung }
189ff56536eSAlex Hornung 
190ff56536eSAlex Hornung /*
191ff56536eSAlex Hornung  * Allocat new pdev structure if is not already present and
192ff56536eSAlex Hornung  * set name.
193ff56536eSAlex Hornung  */
194ff56536eSAlex Hornung static dm_pdev_t *
dm_pdev_alloc(const char * name)195ff56536eSAlex Hornung dm_pdev_alloc(const char *name)
196ff56536eSAlex Hornung {
197ff56536eSAlex Hornung 	dm_pdev_t *dmp;
198ff56536eSAlex Hornung 
19941d37b86STomohiro Kusumi 	dmp = kmalloc(sizeof(*dmp), M_DM, M_WAITOK | M_ZERO);
20041d37b86STomohiro Kusumi 	if (dmp == NULL)
201ff56536eSAlex Hornung 		return NULL;
202ff56536eSAlex Hornung 
20341d37b86STomohiro Kusumi 	if (name)
204b7c11cdaSTomohiro Kusumi 		strlcpy(dmp->name, name, DM_MAX_DEV_NAME);
205ff56536eSAlex Hornung 
206ff56536eSAlex Hornung 	return dmp;
207ff56536eSAlex Hornung }
208b7c11cdaSTomohiro Kusumi 
209ff56536eSAlex Hornung /*
210ff56536eSAlex Hornung  * Destroy allocated dm_pdev.
211ff56536eSAlex Hornung  */
212ff56536eSAlex Hornung static int
dm_pdev_free(dm_pdev_t * dmp)21341d37b86STomohiro Kusumi dm_pdev_free(dm_pdev_t *dmp)
214ff56536eSAlex Hornung {
215ff56536eSAlex Hornung 	int err;
216ff56536eSAlex Hornung 
2175b279a20SAlex Hornung 	KKASSERT(dmp != NULL);
218ff56536eSAlex Hornung 
219ff56536eSAlex Hornung 	if (dmp->pdev_vnode != NULL) {
2203596743eSMarkus Pfeiffer 		err = vn_close(dmp->pdev_vnode, FREAD | FWRITE, NULL);
22141d37b86STomohiro Kusumi 		if (err != 0) {
22241d37b86STomohiro Kusumi 			kfree(dmp, M_DM);
223ff56536eSAlex Hornung 			return err;
224ff56536eSAlex Hornung 		}
22541d37b86STomohiro Kusumi 	}
2265b279a20SAlex Hornung 	kfree(dmp, M_DM);
227ff56536eSAlex Hornung 
228ff56536eSAlex Hornung 	return 0;
229ff56536eSAlex Hornung }
230ff56536eSAlex Hornung 
231ff56536eSAlex Hornung /*
232b7c11cdaSTomohiro Kusumi  * This funcion is called from targets' destroy() handler.
233ff56536eSAlex Hornung  * When I'm removing device from list, I have to decrement
234ff56536eSAlex Hornung  * reference counter. If reference counter is 0 I will remove
235ff56536eSAlex Hornung  * dmp from global list and from device list to. And I will CLOSE
236ff56536eSAlex Hornung  * dmp vnode too.
237ff56536eSAlex Hornung  */
238ff56536eSAlex Hornung /*
239ff56536eSAlex Hornung  * Decrement pdev reference counter if 0 remove it.
240ff56536eSAlex Hornung  */
241ff56536eSAlex Hornung int
dm_pdev_decr(dm_pdev_t * dmp)242ff56536eSAlex Hornung dm_pdev_decr(dm_pdev_t *dmp)
243ff56536eSAlex Hornung {
2445b279a20SAlex Hornung 	KKASSERT(dmp != NULL);
245ff56536eSAlex Hornung 	/*
246ff56536eSAlex Hornung 	 * If this was last reference remove dmp from
247ff56536eSAlex Hornung 	 * global list also.
248ff56536eSAlex Hornung 	 */
2495b279a20SAlex Hornung 	lockmgr(&dm_pdev_mutex, LK_EXCLUSIVE);
250ff56536eSAlex Hornung 
251ff56536eSAlex Hornung 	if (--dmp->ref_cnt == 0) {
252b2ecd1aaSTomohiro Kusumi 		TAILQ_REMOVE(&dm_pdev_list, dmp, next_pdev);
2535b279a20SAlex Hornung 		lockmgr(&dm_pdev_mutex, LK_RELEASE);
25441d37b86STomohiro Kusumi 		dm_pdev_free(dmp);
255ff56536eSAlex Hornung 		return 0;
256ff56536eSAlex Hornung 	}
2575b279a20SAlex Hornung 	lockmgr(&dm_pdev_mutex, LK_RELEASE);
258ff56536eSAlex Hornung 	return 0;
259ff56536eSAlex Hornung }
2609fada28aSAlex Hornung 
261d471f1f9STomohiro Kusumi uint64_t
dm_pdev_get_udev(dm_pdev_t * dmp)262d471f1f9STomohiro Kusumi dm_pdev_get_udev(dm_pdev_t *dmp)
263d471f1f9STomohiro Kusumi {
264d471f1f9STomohiro Kusumi 	struct vattr va;
265d471f1f9STomohiro Kusumi 	int ret;
266d471f1f9STomohiro Kusumi 
267d471f1f9STomohiro Kusumi 	if (dmp->pdev_vnode == NULL)
268d471f1f9STomohiro Kusumi 		return (uint64_t)-1;
269d471f1f9STomohiro Kusumi 
27076d3bec1STomohiro Kusumi 	ret = dm_pdev_get_vattr(dmp, &va);
271d471f1f9STomohiro Kusumi 	if (ret)
272d471f1f9STomohiro Kusumi 		return (uint64_t)-1;
273d471f1f9STomohiro Kusumi 
274d471f1f9STomohiro Kusumi 	ret = makeudev(va.va_rmajor, va.va_rminor);
275d471f1f9STomohiro Kusumi 
276d471f1f9STomohiro Kusumi 	return ret;
277d471f1f9STomohiro Kusumi }
278d471f1f9STomohiro Kusumi 
27976d3bec1STomohiro Kusumi int
dm_pdev_get_vattr(dm_pdev_t * dmp,struct vattr * vap)28076d3bec1STomohiro Kusumi dm_pdev_get_vattr(dm_pdev_t *dmp, struct vattr *vap)
28176d3bec1STomohiro Kusumi {
28276d3bec1STomohiro Kusumi 	int ret;
28376d3bec1STomohiro Kusumi 
28476d3bec1STomohiro Kusumi 	if (dmp->pdev_vnode == NULL)
28576d3bec1STomohiro Kusumi 		return -1;
28676d3bec1STomohiro Kusumi 
28776d3bec1STomohiro Kusumi 	KKASSERT(vap);
28876d3bec1STomohiro Kusumi 	ret = VOP_GETATTR(dmp->pdev_vnode, vap);
28976d3bec1STomohiro Kusumi 	if (ret)
29076d3bec1STomohiro Kusumi 		return -1;
29176d3bec1STomohiro Kusumi 
29276d3bec1STomohiro Kusumi 	return 0;
29376d3bec1STomohiro Kusumi }
29476d3bec1STomohiro Kusumi 
2959fada28aSAlex Hornung /*
2969fada28aSAlex Hornung  * Initialize pdev subsystem.
2979fada28aSAlex Hornung  */
2989fada28aSAlex Hornung int
dm_pdev_init(void)2999fada28aSAlex Hornung dm_pdev_init(void)
3009fada28aSAlex Hornung {
301b2ecd1aaSTomohiro Kusumi 	TAILQ_INIT(&dm_pdev_list);	/* initialize global pdev list */
3029fada28aSAlex Hornung 	lockinit(&dm_pdev_mutex, "dmpdev", 0, LK_CANRECURSE);
3039fada28aSAlex Hornung 
3049fada28aSAlex Hornung 	return 0;
3059fada28aSAlex Hornung }
3069fada28aSAlex Hornung 
3079fada28aSAlex Hornung /*
3089fada28aSAlex Hornung  * Destroy all existing pdev's in device-mapper.
3099fada28aSAlex Hornung  */
3109fada28aSAlex Hornung int
dm_pdev_uninit(void)3119fada28aSAlex Hornung dm_pdev_uninit(void)
3129fada28aSAlex Hornung {
31364c24ea9STomohiro Kusumi 	dm_pdev_t *dmp;
3149fada28aSAlex Hornung 
3159fada28aSAlex Hornung 	lockmgr(&dm_pdev_mutex, LK_EXCLUSIVE);
3169fada28aSAlex Hornung 
317b2ecd1aaSTomohiro Kusumi 	while ((dmp = TAILQ_FIRST(&dm_pdev_list)) != NULL) {
318b2ecd1aaSTomohiro Kusumi 		TAILQ_REMOVE(&dm_pdev_list, dmp, next_pdev);
31941d37b86STomohiro Kusumi 		dm_pdev_free(dmp);
3209fada28aSAlex Hornung 	}
321b2ecd1aaSTomohiro Kusumi 	KKASSERT(TAILQ_EMPTY(&dm_pdev_list));
32264c24ea9STomohiro Kusumi 
3239fada28aSAlex Hornung 	lockmgr(&dm_pdev_mutex, LK_RELEASE);
3249fada28aSAlex Hornung 
3259fada28aSAlex Hornung 	lockuninit(&dm_pdev_mutex);
3269fada28aSAlex Hornung 	return 0;
3279fada28aSAlex Hornung }
328