| 1 | /* $NetBSD: dm_target.c,v 1.19 2014/10/18 08:33:27 snj 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/kmem.h> |
| 36 | #include <sys/module.h> |
| 37 | |
| 38 | |
| 39 | #include "netbsd-dm.h" |
| 40 | #include "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 | kmutex_t 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_inc_32(&target->ref_cnt); |
| 58 | } |
| 59 | /* |
| 60 | * Release reference counter on target. |
| 61 | */ |
| 62 | void |
| 63 | dm_target_unbusy(dm_target_t * target) |
| 64 | { |
| 65 | KASSERT(target->ref_cnt > 0); |
| 66 | atomic_dec_32(&target->ref_cnt); |
| 67 | } |
| 68 | /* |
| 69 | * Try to autoload target module if it was not found in current |
| 70 | * target list. |
| 71 | */ |
| 72 | dm_target_t * |
| 73 | dm_target_autoload(const char *dm_target_name) |
| 74 | { |
| 75 | char name[30]; |
| 76 | u_int gen; |
| 77 | dm_target_t *dmt; |
| 78 | |
| 79 | snprintf(name, sizeof(name), "dm_target_%s" , dm_target_name); |
| 80 | name[29] = '\0'; |
| 81 | |
| 82 | do { |
| 83 | gen = module_gen; |
| 84 | |
| 85 | /* Try to autoload target module */ |
| 86 | (void) module_autoload(name, MODULE_CLASS_MISC); |
| 87 | } while (gen != module_gen); |
| 88 | |
| 89 | mutex_enter(&dm_target_mutex); |
| 90 | dmt = dm_target_lookup_name(dm_target_name); |
| 91 | if (dmt != NULL) |
| 92 | dm_target_busy(dmt); |
| 93 | mutex_exit(&dm_target_mutex); |
| 94 | |
| 95 | return dmt; |
| 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 | mutex_enter(&dm_target_mutex); |
| 111 | |
| 112 | dmt = dm_target_lookup_name(dm_target_name); |
| 113 | if (dmt != NULL) |
| 114 | dm_target_busy(dmt); |
| 115 | |
| 116 | mutex_exit(&dm_target_mutex); |
| 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 | int dlen; |
| 128 | int slen; |
| 129 | |
| 130 | slen = strlen(dm_target_name) + 1; |
| 131 | |
| 132 | TAILQ_FOREACH(dm_target, &dm_target_list, dm_target_next) { |
| 133 | dlen = strlen(dm_target->name) + 1; |
| 134 | if (dlen != slen) |
| 135 | continue; |
| 136 | |
| 137 | if (strncmp(dm_target_name, dm_target->name, slen) == 0) |
| 138 | return dm_target; |
| 139 | } |
| 140 | |
| 141 | return NULL; |
| 142 | } |
| 143 | /* |
| 144 | * Insert new target struct into the TAIL. |
| 145 | * dm_target |
| 146 | * contains name, version, function pointer to specifif target functions. |
| 147 | */ |
| 148 | int |
| 149 | dm_target_insert(dm_target_t * dm_target) |
| 150 | { |
| 151 | dm_target_t *dmt; |
| 152 | |
| 153 | /* Sanity check for any missing function */ |
| 154 | KASSERT(dm_target->init != NULL); |
| 155 | KASSERT(dm_target->status != NULL); |
| 156 | KASSERT(dm_target->strategy != NULL); |
| 157 | KASSERT(dm_target->deps != NULL); |
| 158 | KASSERT(dm_target->destroy != NULL); |
| 159 | KASSERT(dm_target->upcall != NULL); |
| 160 | KASSERT(dm_target->sync != NULL); |
| 161 | KASSERT(dm_target->secsize != NULL); |
| 162 | |
| 163 | mutex_enter(&dm_target_mutex); |
| 164 | |
| 165 | dmt = dm_target_lookup_name(dm_target->name); |
| 166 | if (dmt != NULL) { |
| 167 | mutex_exit(&dm_target_mutex); |
| 168 | return EEXIST; |
| 169 | } |
| 170 | TAILQ_INSERT_TAIL(&dm_target_list, dm_target, dm_target_next); |
| 171 | |
| 172 | mutex_exit(&dm_target_mutex); |
| 173 | |
| 174 | return 0; |
| 175 | } |
| 176 | |
| 177 | |
| 178 | /* |
| 179 | * Remove target from TAIL, target is selected with its name. |
| 180 | */ |
| 181 | int |
| 182 | dm_target_rem(char *dm_target_name) |
| 183 | { |
| 184 | dm_target_t *dmt; |
| 185 | |
| 186 | KASSERT(dm_target_name != NULL); |
| 187 | |
| 188 | mutex_enter(&dm_target_mutex); |
| 189 | |
| 190 | dmt = dm_target_lookup_name(dm_target_name); |
| 191 | if (dmt == NULL) { |
| 192 | mutex_exit(&dm_target_mutex); |
| 193 | return ENOENT; |
| 194 | } |
| 195 | if (dmt->ref_cnt > 0) { |
| 196 | mutex_exit(&dm_target_mutex); |
| 197 | return EBUSY; |
| 198 | } |
| 199 | TAILQ_REMOVE(&dm_target_list, |
| 200 | dmt, dm_target_next); |
| 201 | |
| 202 | mutex_exit(&dm_target_mutex); |
| 203 | |
| 204 | (void) kmem_free(dmt, sizeof(dm_target_t)); |
| 205 | |
| 206 | return 0; |
| 207 | } |
| 208 | /* |
| 209 | * Destroy all targets and remove them from queue. |
| 210 | * This routine is called from dm_detach, before module |
| 211 | * is unloaded. |
| 212 | */ |
| 213 | int |
| 214 | dm_target_destroy(void) |
| 215 | { |
| 216 | dm_target_t *dm_target; |
| 217 | |
| 218 | mutex_enter(&dm_target_mutex); |
| 219 | while (TAILQ_FIRST(&dm_target_list) != NULL) { |
| 220 | |
| 221 | dm_target = TAILQ_FIRST(&dm_target_list); |
| 222 | |
| 223 | TAILQ_REMOVE(&dm_target_list, TAILQ_FIRST(&dm_target_list), |
| 224 | dm_target_next); |
| 225 | |
| 226 | (void) kmem_free(dm_target, sizeof(dm_target_t)); |
| 227 | } |
| 228 | mutex_exit(&dm_target_mutex); |
| 229 | |
| 230 | mutex_destroy(&dm_target_mutex); |
| 231 | |
| 232 | return 0; |
| 233 | } |
| 234 | /* |
| 235 | * Allocate new target entry. |
| 236 | */ |
| 237 | dm_target_t * |
| 238 | dm_target_alloc(const char *name) |
| 239 | { |
| 240 | return kmem_zalloc(sizeof(dm_target_t), KM_SLEEP); |
| 241 | } |
| 242 | /* |
| 243 | * Return prop_array of dm_target dictionaries. |
| 244 | */ |
| 245 | prop_array_t |
| 246 | dm_target_prop_list(void) |
| 247 | { |
| 248 | prop_array_t target_array, ver; |
| 249 | prop_dictionary_t target_dict; |
| 250 | dm_target_t *dm_target; |
| 251 | |
| 252 | size_t i; |
| 253 | |
| 254 | target_array = prop_array_create(); |
| 255 | |
| 256 | mutex_enter(&dm_target_mutex); |
| 257 | |
| 258 | TAILQ_FOREACH(dm_target, &dm_target_list, dm_target_next) { |
| 259 | |
| 260 | target_dict = prop_dictionary_create(); |
| 261 | ver = prop_array_create(); |
| 262 | prop_dictionary_set_cstring(target_dict, DM_TARGETS_NAME, |
| 263 | dm_target->name); |
| 264 | |
| 265 | for (i = 0; i < 3; i++) |
| 266 | prop_array_add_uint32(ver, dm_target->version[i]); |
| 267 | |
| 268 | prop_dictionary_set(target_dict, DM_TARGETS_VERSION, ver); |
| 269 | prop_array_add(target_array, target_dict); |
| 270 | |
| 271 | prop_object_release(ver); |
| 272 | prop_object_release(target_dict); |
| 273 | } |
| 274 | |
| 275 | mutex_exit(&dm_target_mutex); |
| 276 | |
| 277 | return target_array; |
| 278 | } |
| 279 | /* Initialize dm_target subsystem. */ |
| 280 | int |
| 281 | dm_target_init(void) |
| 282 | { |
| 283 | dm_target_t *dmt, *dmt3; |
| 284 | int r; |
| 285 | |
| 286 | r = 0; |
| 287 | |
| 288 | mutex_init(&dm_target_mutex, MUTEX_DEFAULT, IPL_NONE); |
| 289 | |
| 290 | dmt = dm_target_alloc("linear" ); |
| 291 | dmt3 = dm_target_alloc("striped" ); |
| 292 | |
| 293 | dmt->version[0] = 1; |
| 294 | dmt->version[1] = 0; |
| 295 | dmt->version[2] = 2; |
| 296 | strlcpy(dmt->name, "linear" , DM_MAX_TYPE_NAME); |
| 297 | dmt->init = &dm_target_linear_init; |
| 298 | dmt->status = &dm_target_linear_status; |
| 299 | dmt->strategy = &dm_target_linear_strategy; |
| 300 | dmt->sync = &dm_target_linear_sync; |
| 301 | dmt->deps = &dm_target_linear_deps; |
| 302 | dmt->destroy = &dm_target_linear_destroy; |
| 303 | dmt->upcall = &dm_target_linear_upcall; |
| 304 | dmt->secsize = &dm_target_linear_secsize; |
| 305 | |
| 306 | r = dm_target_insert(dmt); |
| 307 | |
| 308 | dmt3->version[0] = 1; |
| 309 | dmt3->version[1] = 0; |
| 310 | dmt3->version[2] = 3; |
| 311 | strlcpy(dmt3->name, "striped" , DM_MAX_TYPE_NAME); |
| 312 | dmt3->init = &dm_target_stripe_init; |
| 313 | dmt3->status = &dm_target_stripe_status; |
| 314 | dmt3->strategy = &dm_target_stripe_strategy; |
| 315 | dmt3->sync = &dm_target_stripe_sync; |
| 316 | dmt3->deps = &dm_target_stripe_deps; |
| 317 | dmt3->destroy = &dm_target_stripe_destroy; |
| 318 | dmt3->upcall = &dm_target_stripe_upcall; |
| 319 | dmt3->secsize = &dm_target_stripe_secsize; |
| 320 | |
| 321 | r = dm_target_insert(dmt3); |
| 322 | |
| 323 | return r; |
| 324 | } |
| 325 | |