xref: /dragonfly/sys/dev/disk/dm/dm_pdev.c (revision b1999ea8e0519ad29816e6e3462677466770be1e)
1 /*        $NetBSD: dm_pdev.c,v 1.6 2010/01/04 00:19:08 haad Exp $      */
2 
3 /*
4  * Copyright (c) 2010-2011 Alex Hornung <alex@alexhornung.com>
5  * Copyright (c) 2008 The NetBSD Foundation, Inc.
6  * All rights reserved.
7  *
8  * This code is derived from software contributed to The NetBSD Foundation
9  * by Adam Hamsik.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  * POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 #include <sys/disk.h>
34 #include <sys/fcntl.h>
35 #include <sys/malloc.h>
36 #include <sys/nlookup.h>
37 #include <sys/proc.h>
38 
39 #include <dev/disk/dm/dm.h>
40 
41 static TAILQ_HEAD(, dm_pdev) dm_pdev_list;
42 
43 static struct lock dm_pdev_mutex;
44 
45 static dm_pdev_t *dm_pdev_alloc(const char *);
46 static int dm_pdev_free(dm_pdev_t *);
47 static dm_pdev_t *dm_pdev_lookup_name(const char *);
48 
49 /*
50  * Find used pdev with name == dm_pdev_name.
51  * needs to be called with the dm_pdev_mutex held.
52  */
53 static dm_pdev_t *
dm_pdev_lookup_name(const char * dm_pdev_name)54 dm_pdev_lookup_name(const char *dm_pdev_name)
55 {
56           dm_pdev_t *dmp;
57 
58           KKASSERT(dm_pdev_name != NULL);
59 
60           TAILQ_FOREACH(dmp, &dm_pdev_list, next_pdev) {
61                     if (strcmp(dm_pdev_name, dmp->name) == 0)
62                               return dmp;
63           }
64 
65           return NULL;
66 }
67 
68 static int
dm_dk_lookup(const char * dev_name,struct vnode ** vpp)69 dm_dk_lookup(const char *dev_name, struct vnode **vpp)
70 {
71           struct nlookupdata nd;
72           int error;
73 
74           error = nlookup_init(&nd, dev_name, UIO_SYSSPACE, NLC_FOLLOW);
75           if (error)
76                     return error;
77 
78           error = vn_open(&nd, NULL, FREAD|FWRITE, 0);
79           if (error) {
80                     nlookup_done(&nd);
81                     return error;
82           }
83 
84           *vpp = nd.nl_open_vp;
85           nd.nl_open_vp = NULL;
86           nlookup_done(&nd);
87 
88           return 0;
89 }
90 
91 /*
92  * Since dm can have arbitrary stacking on any number of disks and any dm
93  * volume is at least stacked onto another disk, we need to adjust the
94  * dumping offset (which is a raw offset from the beginning of the lowest
95  * physical disk) taking into account the offset of the underlying device
96  * which in turn takes into account the offset below it, etc.
97  *
98  * This function adjusts the dumping offset that is passed to the next
99  * dev_ddump() so it is correct for that underlying device.
100  */
101 off_t
dm_pdev_correct_dump_offset(dm_pdev_t * pdev,off_t offset)102 dm_pdev_correct_dump_offset(dm_pdev_t *pdev, off_t offset)
103 {
104           off_t noffset;
105 
106           noffset = pdev->pdev_pinfo.reserved_blocks +
107               pdev->pdev_pinfo.media_offset / pdev->pdev_pinfo.media_blksize;
108           noffset *= DEV_BSIZE;
109           noffset += offset;
110 
111           return noffset;
112 }
113 
114 /*
115  * Create entry for device with name dev_name and open vnode for it.
116  * If entry already exists in global TAILQ I will only increment
117  * reference counter.
118  */
119 dm_pdev_t *
dm_pdev_insert(const char * dev_name)120 dm_pdev_insert(const char *dev_name)
121 {
122           dm_pdev_t *dmp;
123           struct vattr va;
124           int error;
125 
126           KKASSERT(dev_name != NULL);
127 
128           lockmgr(&dm_pdev_mutex, LK_EXCLUSIVE);
129           dmp = dm_pdev_lookup_name(dev_name);
130 
131           if (dmp != NULL) {
132                     dmp->ref_cnt++;
133                     dmdebug("pdev %s already in tree\n", dev_name);
134                     lockmgr(&dm_pdev_mutex, LK_RELEASE);
135                     return dmp;
136           }
137 
138           if ((dmp = dm_pdev_alloc(dev_name)) == NULL) {
139                     lockmgr(&dm_pdev_mutex, LK_RELEASE);
140                     return NULL;
141           }
142 
143           error = dm_dk_lookup(dev_name, &dmp->pdev_vnode);
144           if (error) {
145                     dmdebug("Lookup on %s failed with error %d!\n",
146                         dev_name, error);
147                     dm_pdev_free(dmp);
148                     lockmgr(&dm_pdev_mutex, LK_RELEASE);
149                     return NULL;
150           }
151           dmp->ref_cnt = 1;
152 
153           if (dm_pdev_get_vattr(dmp, &va) == -1) {
154                     dmdebug("getattr on %s failed\n", dev_name);
155                     dm_pdev_free(dmp);
156                     lockmgr(&dm_pdev_mutex, LK_RELEASE);
157                     return NULL;
158           }
159           ksnprintf(dmp->udev_name, sizeof(dmp->udev_name),
160                     "%d:%d", va.va_rmajor, va.va_rminor);
161           dmp->udev = dm_pdev_get_udev(dmp);
162 
163           /*
164            * Get us the partinfo from the underlying device, it's needed for
165            * dumps.
166            */
167           bzero(&dmp->pdev_pinfo, sizeof(dmp->pdev_pinfo));
168           error = dev_dioctl(dmp->pdev_vnode->v_rdev, DIOCGPART,
169               (void *)&dmp->pdev_pinfo, 0, proc0.p_ucred, NULL, NULL);
170           if (!error) {
171                     struct partinfo *dpart = &dmp->pdev_pinfo;
172                     dmdebug("DIOCGPART offset=%ju size=%ju blocks=%ju blksize=%d\n",
173                               dpart->media_offset,
174                               dpart->media_size,
175                               dpart->media_blocks,
176                               dpart->media_blksize);
177           } else {
178                     kprintf("dmp_pdev_insert DIOCGPART failed %d\n", error);
179           }
180 
181           TAILQ_INSERT_TAIL(&dm_pdev_list, dmp, next_pdev);
182           lockmgr(&dm_pdev_mutex, LK_RELEASE);
183 
184           dmdebug("pdev %s %s 0x%016jx\n",
185                     dmp->name, dmp->udev_name, (uintmax_t)dmp->udev);
186 
187           return dmp;
188 }
189 
190 /*
191  * Allocat new pdev structure if is not already present and
192  * set name.
193  */
194 static dm_pdev_t *
dm_pdev_alloc(const char * name)195 dm_pdev_alloc(const char *name)
196 {
197           dm_pdev_t *dmp;
198 
199           dmp = kmalloc(sizeof(*dmp), M_DM, M_WAITOK | M_ZERO);
200           if (dmp == NULL)
201                     return NULL;
202 
203           if (name)
204                     strlcpy(dmp->name, name, DM_MAX_DEV_NAME);
205 
206           return dmp;
207 }
208 
209 /*
210  * Destroy allocated dm_pdev.
211  */
212 static int
dm_pdev_free(dm_pdev_t * dmp)213 dm_pdev_free(dm_pdev_t *dmp)
214 {
215           int err;
216 
217           KKASSERT(dmp != NULL);
218 
219           if (dmp->pdev_vnode != NULL) {
220                     err = vn_close(dmp->pdev_vnode, FREAD | FWRITE, NULL);
221                     if (err != 0) {
222                               kfree(dmp, M_DM);
223                               return err;
224                     }
225           }
226           kfree(dmp, M_DM);
227 
228           return 0;
229 }
230 
231 /*
232  * This funcion is called from targets' destroy() handler.
233  * When I'm removing device from list, I have to decrement
234  * reference counter. If reference counter is 0 I will remove
235  * dmp from global list and from device list to. And I will CLOSE
236  * dmp vnode too.
237  */
238 /*
239  * Decrement pdev reference counter if 0 remove it.
240  */
241 int
dm_pdev_decr(dm_pdev_t * dmp)242 dm_pdev_decr(dm_pdev_t *dmp)
243 {
244           KKASSERT(dmp != NULL);
245           /*
246            * If this was last reference remove dmp from
247            * global list also.
248            */
249           lockmgr(&dm_pdev_mutex, LK_EXCLUSIVE);
250 
251           if (--dmp->ref_cnt == 0) {
252                     TAILQ_REMOVE(&dm_pdev_list, dmp, next_pdev);
253                     lockmgr(&dm_pdev_mutex, LK_RELEASE);
254                     dm_pdev_free(dmp);
255                     return 0;
256           }
257           lockmgr(&dm_pdev_mutex, LK_RELEASE);
258           return 0;
259 }
260 
261 uint64_t
dm_pdev_get_udev(dm_pdev_t * dmp)262 dm_pdev_get_udev(dm_pdev_t *dmp)
263 {
264           struct vattr va;
265           int ret;
266 
267           if (dmp->pdev_vnode == NULL)
268                     return (uint64_t)-1;
269 
270           ret = dm_pdev_get_vattr(dmp, &va);
271           if (ret)
272                     return (uint64_t)-1;
273 
274           ret = makeudev(va.va_rmajor, va.va_rminor);
275 
276           return ret;
277 }
278 
279 int
dm_pdev_get_vattr(dm_pdev_t * dmp,struct vattr * vap)280 dm_pdev_get_vattr(dm_pdev_t *dmp, struct vattr *vap)
281 {
282           int ret;
283 
284           if (dmp->pdev_vnode == NULL)
285                     return -1;
286 
287           KKASSERT(vap);
288           ret = VOP_GETATTR(dmp->pdev_vnode, vap);
289           if (ret)
290                     return -1;
291 
292           return 0;
293 }
294 
295 /*
296  * Initialize pdev subsystem.
297  */
298 int
dm_pdev_init(void)299 dm_pdev_init(void)
300 {
301           TAILQ_INIT(&dm_pdev_list);    /* initialize global pdev list */
302           lockinit(&dm_pdev_mutex, "dmpdev", 0, LK_CANRECURSE);
303 
304           return 0;
305 }
306 
307 /*
308  * Destroy all existing pdev's in device-mapper.
309  */
310 int
dm_pdev_uninit(void)311 dm_pdev_uninit(void)
312 {
313           dm_pdev_t *dmp;
314 
315           lockmgr(&dm_pdev_mutex, LK_EXCLUSIVE);
316 
317           while ((dmp = TAILQ_FIRST(&dm_pdev_list)) != NULL) {
318                     TAILQ_REMOVE(&dm_pdev_list, dmp, next_pdev);
319                     dm_pdev_free(dmp);
320           }
321           KKASSERT(TAILQ_EMPTY(&dm_pdev_list));
322 
323           lockmgr(&dm_pdev_mutex, LK_RELEASE);
324 
325           lockuninit(&dm_pdev_mutex);
326           return 0;
327 }
328