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