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 * 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("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 * 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 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 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 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 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 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 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