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