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/namei.h> 37 #include <sys/nlookup.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 * 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 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 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 * 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("dmp_pdev_insert 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("dk_lookup on device: %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("makeudev %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("dmp_pdev_insert DIOCGPART " 173 "offset=%ju size=%ju blocks=%ju blksize=%d\n", 174 dpart->media_offset, 175 dpart->media_size, 176 dpart->media_blocks, 177 dpart->media_blksize); 178 } else { 179 kprintf("dmp_pdev_insert DIOCGPART failed %d\n", error); 180 } 181 182 TAILQ_INSERT_TAIL(&dm_pdev_list, dmp, next_pdev); 183 lockmgr(&dm_pdev_mutex, LK_RELEASE); 184 185 dmdebug("dmp_pdev_insert pdev %s %s 0x%016jx\n", 186 dmp->name, dmp->udev_name, (uintmax_t)dmp->udev); 187 188 return dmp; 189 } 190 191 /* 192 * Allocat new pdev structure if is not already present and 193 * set name. 194 */ 195 static dm_pdev_t * 196 dm_pdev_alloc(const char *name) 197 { 198 dm_pdev_t *dmp; 199 200 dmp = kmalloc(sizeof(*dmp), M_DM, M_WAITOK | M_ZERO); 201 if (dmp == NULL) 202 return NULL; 203 204 if (name) 205 strlcpy(dmp->name, name, DM_MAX_DEV_NAME); 206 207 return dmp; 208 } 209 210 /* 211 * Destroy allocated dm_pdev. 212 */ 213 static int 214 dm_pdev_free(dm_pdev_t *dmp) 215 { 216 int err; 217 218 KKASSERT(dmp != NULL); 219 220 if (dmp->pdev_vnode != NULL) { 221 err = vn_close(dmp->pdev_vnode, FREAD | FWRITE, NULL); 222 if (err != 0) { 223 kfree(dmp, M_DM); 224 return err; 225 } 226 } 227 kfree(dmp, M_DM); 228 229 return 0; 230 } 231 232 /* 233 * This funcion is called from targets' destroy() handler. 234 * When I'm removing device from list, I have to decrement 235 * reference counter. If reference counter is 0 I will remove 236 * dmp from global list and from device list to. And I will CLOSE 237 * dmp vnode too. 238 */ 239 /* 240 * Decrement pdev reference counter if 0 remove it. 241 */ 242 int 243 dm_pdev_decr(dm_pdev_t *dmp) 244 { 245 KKASSERT(dmp != NULL); 246 /* 247 * If this was last reference remove dmp from 248 * global list also. 249 */ 250 lockmgr(&dm_pdev_mutex, LK_EXCLUSIVE); 251 252 if (--dmp->ref_cnt == 0) { 253 TAILQ_REMOVE(&dm_pdev_list, dmp, next_pdev); 254 lockmgr(&dm_pdev_mutex, LK_RELEASE); 255 dm_pdev_free(dmp); 256 return 0; 257 } 258 lockmgr(&dm_pdev_mutex, LK_RELEASE); 259 return 0; 260 } 261 262 uint64_t 263 dm_pdev_get_udev(dm_pdev_t *dmp) 264 { 265 struct vattr va; 266 int ret; 267 268 if (dmp->pdev_vnode == NULL) 269 return (uint64_t)-1; 270 271 ret = dm_pdev_get_vattr(dmp, &va); 272 if (ret) 273 return (uint64_t)-1; 274 275 ret = makeudev(va.va_rmajor, va.va_rminor); 276 277 return ret; 278 } 279 280 int 281 dm_pdev_get_vattr(dm_pdev_t *dmp, struct vattr *vap) 282 { 283 int ret; 284 285 if (dmp->pdev_vnode == NULL) 286 return -1; 287 288 KKASSERT(vap); 289 ret = VOP_GETATTR(dmp->pdev_vnode, vap); 290 if (ret) 291 return -1; 292 293 return 0; 294 } 295 296 /* 297 * Initialize pdev subsystem. 298 */ 299 int 300 dm_pdev_init(void) 301 { 302 TAILQ_INIT(&dm_pdev_list); /* initialize global pdev list */ 303 lockinit(&dm_pdev_mutex, "dmpdev", 0, LK_CANRECURSE); 304 305 return 0; 306 } 307 308 /* 309 * Destroy all existing pdev's in device-mapper. 310 */ 311 int 312 dm_pdev_uninit(void) 313 { 314 dm_pdev_t *dmp; 315 316 lockmgr(&dm_pdev_mutex, LK_EXCLUSIVE); 317 318 while ((dmp = TAILQ_FIRST(&dm_pdev_list)) != NULL) { 319 TAILQ_REMOVE(&dm_pdev_list, dmp, next_pdev); 320 dm_pdev_free(dmp); 321 } 322 KKASSERT(TAILQ_EMPTY(&dm_pdev_list)); 323 324 lockmgr(&dm_pdev_mutex, LK_RELEASE); 325 326 lockuninit(&dm_pdev_mutex); 327 return 0; 328 } 329