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