1 /* $NetBSD: dm_target.c,v 1.12 2010/01/04 00:14:41 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/malloc.h> 34 #include <sys/module.h> 35 #include <sys/linker.h> 36 #include <cpu/atomic.h> 37 #include <dev/disk/dm/dm.h> 38 #include <dev/disk/dm/netbsd-dm.h> 39 40 static dm_target_t *dm_target_lookup_name(const char *); 41 42 static TAILQ_HEAD(, dm_target) dm_target_list; 43 44 static struct lock dm_target_mutex; 45 46 void 47 dm_target_busy(dm_target_t *target) 48 { 49 atomic_add_int(&target->ref_cnt, 1); 50 } 51 52 /* 53 * Release reference counter on target. 54 */ 55 void 56 dm_target_unbusy(dm_target_t *target) 57 { 58 KKASSERT(target->ref_cnt > 0); 59 atomic_subtract_int(&target->ref_cnt, 1); 60 } 61 62 /* 63 * Try to autoload the module for the requested target. 64 */ 65 dm_target_t * 66 dm_target_autoload(const char *dm_target_name) 67 { 68 char mod_name[128]; 69 dm_target_t *dmt; 70 linker_file_t linker_file; 71 int error; 72 73 ksnprintf(mod_name, sizeof(mod_name), "dm_target_%s", dm_target_name); 74 error = linker_reference_module(mod_name, NULL, &linker_file); 75 if (error != 0) { 76 kprintf("dm: could not autoload module for target %s\n", 77 dm_target_name); 78 return NULL; 79 } 80 81 dmt = dm_target_lookup(dm_target_name); 82 if (dmt == NULL) { 83 linker_release_module(NULL, NULL, linker_file); 84 return NULL; 85 } 86 87 /* XXX: extra-big hack to allow users to kldunload the module */ 88 linker_file->userrefs = 1; 89 90 return dmt; 91 } 92 93 /* 94 * Lookup for target in global target list. 95 */ 96 dm_target_t * 97 dm_target_lookup(const char *dm_target_name) 98 { 99 dm_target_t *dmt; 100 101 dmt = NULL; 102 103 if (dm_target_name == NULL) 104 return NULL; 105 106 lockmgr(&dm_target_mutex, LK_EXCLUSIVE); 107 108 dmt = dm_target_lookup_name(dm_target_name); 109 if (dmt != NULL) 110 dm_target_busy(dmt); 111 112 lockmgr(&dm_target_mutex, LK_RELEASE); 113 114 return dmt; 115 } 116 117 /* 118 * Search for name in TAILQ and return apropriate pointer. 119 */ 120 static dm_target_t * 121 dm_target_lookup_name(const char *dm_target_name) 122 { 123 dm_target_t *dm_target; 124 125 TAILQ_FOREACH(dm_target, &dm_target_list, dm_target_next) { 126 if (strcmp(dm_target_name, dm_target->name) == 0) 127 return dm_target; 128 } 129 130 return NULL; 131 } 132 133 /* 134 * Insert new target struct into the TAILQ. 135 * dm_target contains name, version, function pointer to specific target 136 * functions. 137 */ 138 int 139 dm_target_insert(dm_target_t *dm_target) 140 { 141 dm_target_t *dmt; 142 143 if (dm_target->strategy == NULL) { 144 kprintf("dm: %s missing strategy\n", dm_target->name); 145 return EINVAL; 146 } 147 148 lockmgr(&dm_target_mutex, LK_EXCLUSIVE); 149 150 dmt = dm_target_lookup_name(dm_target->name); 151 if (dmt != NULL) { 152 kprintf("uhoh, target_insert EEXIST\n"); 153 lockmgr(&dm_target_mutex, LK_RELEASE); 154 return EEXIST; 155 } 156 TAILQ_INSERT_TAIL(&dm_target_list, dm_target, dm_target_next); 157 158 lockmgr(&dm_target_mutex, LK_RELEASE); 159 160 return 0; 161 } 162 163 /* 164 * Remove target from TAILQ, target is selected with it's name. 165 */ 166 int 167 dm_target_remove(char *dm_target_name) 168 { 169 dm_target_t *dmt; 170 171 KKASSERT(dm_target_name != NULL); 172 173 lockmgr(&dm_target_mutex, LK_EXCLUSIVE); 174 175 dmt = dm_target_lookup_name(dm_target_name); 176 if (dmt == NULL) { 177 lockmgr(&dm_target_mutex, LK_RELEASE); 178 return ENOENT; 179 } 180 if (dmt->ref_cnt > 0) { 181 lockmgr(&dm_target_mutex, LK_RELEASE); 182 return EBUSY; 183 } 184 TAILQ_REMOVE(&dm_target_list, dmt, dm_target_next); 185 186 lockmgr(&dm_target_mutex, LK_RELEASE); 187 188 dm_target_free(dmt); 189 190 return 0; 191 } 192 193 /* 194 * Allocate new target entry. 195 */ 196 dm_target_t * 197 dm_target_alloc(const char *name) 198 { 199 dm_target_t *dmt; 200 201 dmt = kmalloc(sizeof(*dmt), M_DM, M_WAITOK | M_ZERO); 202 if (dmt == NULL) 203 return NULL; 204 205 if (name) 206 strlcpy(dmt->name, name, sizeof(dmt->name)); 207 208 return dmt; 209 } 210 211 int 212 dm_target_free(dm_target_t *dmt) 213 { 214 KKASSERT(dmt != NULL); 215 216 kfree(dmt, M_DM); 217 218 return 0; 219 } 220 221 /* 222 * Return prop_array of dm_target dictionaries. 223 */ 224 prop_array_t 225 dm_target_prop_list(void) 226 { 227 prop_array_t target_array, ver; 228 prop_dictionary_t target_dict; 229 dm_target_t *dm_target; 230 231 size_t i; 232 233 target_array = prop_array_create(); 234 235 lockmgr(&dm_target_mutex, LK_EXCLUSIVE); 236 237 TAILQ_FOREACH(dm_target, &dm_target_list, dm_target_next) { 238 239 target_dict = prop_dictionary_create(); 240 ver = prop_array_create(); 241 prop_dictionary_set_cstring(target_dict, DM_TARGETS_NAME, 242 dm_target->name); 243 244 for (i = 0; i < 3; i++) 245 prop_array_add_uint32(ver, dm_target->version[i]); 246 247 prop_dictionary_set(target_dict, DM_TARGETS_VERSION, ver); 248 prop_array_add(target_array, target_dict); 249 250 prop_object_release(ver); 251 prop_object_release(target_dict); 252 } 253 254 lockmgr(&dm_target_mutex, LK_RELEASE); 255 256 return target_array; 257 } 258 259 /* 260 * Initialize dm_target subsystem. 261 */ 262 int 263 dm_target_init(void) 264 { 265 TAILQ_INIT(&dm_target_list); /* initialize global target list */ 266 lockinit(&dm_target_mutex, "dmtrgt", 0, LK_CANRECURSE); 267 268 return 0; 269 } 270 271 /* 272 * Destroy all targets and remove them from queue. 273 * This routine is called from dmdestroy, before module 274 * is unloaded. 275 */ 276 int 277 dm_target_uninit(void) 278 { 279 dm_target_t *dm_target; 280 281 lockmgr(&dm_target_mutex, LK_EXCLUSIVE); 282 283 while ((dm_target = TAILQ_FIRST(&dm_target_list)) != NULL) { 284 TAILQ_REMOVE(&dm_target_list, dm_target, dm_target_next); 285 dm_target_free(dm_target); 286 } 287 KKASSERT(TAILQ_EMPTY(&dm_target_list)); 288 289 lockmgr(&dm_target_mutex, LK_RELEASE); 290 291 lockuninit(&dm_target_mutex); 292 293 return 0; 294 } 295