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