1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #include <sys/ddi.h> 27 #include <sys/sunddi.h> 28 #include <sys/sunndi.h> 29 #include <sys/ddi_impldefs.h> 30 #include <sys/ddi_implfuncs.h> 31 #include <sys/list.h> 32 #include <sys/reboot.h> 33 #include <sys/sysmacros.h> 34 #include <sys/console.h> 35 #include <sys/devcache.h> 36 37 /* 38 * The nvpair name in the I/O retire specific sub-nvlist 39 */ 40 #define RIO_STORE_VERSION_STR "rio-store-version" 41 #define RIO_STORE_MAGIC_STR "rio-store-magic" 42 #define RIO_STORE_FLAGS_STR "rio-store-flags" 43 44 #define RIO_STORE_VERSION_1 1 45 #define RIO_STORE_VERSION RIO_STORE_VERSION_1 46 47 /* 48 * decoded retire list element 49 */ 50 51 typedef enum rio_store_flags { 52 RIO_STORE_F_INVAL = 0, 53 RIO_STORE_F_RETIRED = 1, 54 RIO_STORE_F_BYPASS = 2 55 } rio_store_flags_t; 56 57 typedef struct rio_store { 58 char *rst_devpath; 59 rio_store_flags_t rst_flags; 60 list_node_t rst_next; 61 } rio_store_t; 62 63 #define RIO_STORE_MAGIC 0x601fcace /* retire */ 64 65 static int rio_store_decode(nvf_handle_t nvfh, nvlist_t *line_nvl, char *name); 66 static int rio_store_encode(nvf_handle_t nvfh, nvlist_t **ret_nvl); 67 static void retire_list_free(nvf_handle_t nvfh); 68 69 70 /* 71 * Retire I/O persistent store registration info 72 */ 73 static nvf_ops_t rio_store_ops = { 74 "/etc/devices/retire_store", /* path to store */ 75 rio_store_decode, /* decode nvlist into retire_list */ 76 rio_store_encode, /* encode retire_list into nvlist */ 77 retire_list_free, /* free retire_list */ 78 NULL /* write complete callback */ 79 }; 80 81 static nvf_handle_t rio_store_handle; 82 static char store_path[MAXPATHLEN]; 83 static int store_debug = 0; 84 static int bypass_msg = 0; 85 static int retire_msg = 0; 86 87 #define STORE_DEBUG 0x0001 88 #define STORE_TRACE 0x0002 89 90 #define STORE_DBG(args) if (store_debug & STORE_DEBUG) cmn_err args 91 #define STORE_TRC(args) if (store_debug & STORE_TRACE) cmn_err args 92 93 /* 94 * We don't use the simple read disable offered by the 95 * caching framework (see devcache.c) as it will not 96 * have the desired effect of bypassing the persistent 97 * store. A simple read disable will 98 * 99 * 1. cause any additions to the cache to destroy the 100 * existing on-disk cache 101 * 102 * 2. prevent deletions from the existing on-disk 103 * cache which is needed for recovery from bad 104 * retire decisions. 105 * 106 * Use the following tunable instead 107 * 108 */ 109 int ddi_retire_store_bypass = 0; 110 111 112 113 /* 114 * Initialize retire store data structures 115 */ 116 void 117 retire_store_init(void) 118 { 119 if (boothowto & RB_ASKNAME) { 120 121 printf("Retire store [%s] (/dev/null to bypass): ", 122 rio_store_ops.nvfr_cache_path); 123 console_gets(store_path, sizeof (store_path) - 1); 124 store_path[sizeof (store_path) - 1] = '\0'; 125 126 if (strcmp(store_path, "/dev/null") == 0) { 127 ddi_retire_store_bypass = 1; 128 } else if (store_path[0] != '\0') { 129 if (store_path[0] != '/') { 130 printf("Invalid store path: %s. Using default" 131 "\n", store_path); 132 } else { 133 rio_store_ops.nvfr_cache_path = store_path; 134 } 135 } 136 } 137 138 rio_store_handle = nvf_register_file(&rio_store_ops); 139 140 list_create(nvf_list(rio_store_handle), sizeof (rio_store_t), 141 offsetof(rio_store_t, rst_next)); 142 } 143 144 /* 145 * Read and populate the in-core retire store 146 */ 147 void 148 retire_store_read(void) 149 { 150 rw_enter(nvf_lock(rio_store_handle), RW_WRITER); 151 ASSERT(list_head(nvf_list(rio_store_handle)) == NULL); 152 (void) nvf_read_file(rio_store_handle); 153 rw_exit(nvf_lock(rio_store_handle)); 154 STORE_DBG((CE_NOTE, "Read on-disk retire store")); 155 } 156 157 static void 158 rio_store_free(rio_store_t *rsp) 159 { 160 int flag_mask = RIO_STORE_F_RETIRED|RIO_STORE_F_BYPASS; 161 162 ASSERT(rsp); 163 ASSERT(rsp->rst_devpath); 164 ASSERT(rsp->rst_flags & RIO_STORE_F_RETIRED); 165 ASSERT(!(rsp->rst_flags & ~flag_mask)); 166 167 STORE_TRC((CE_NOTE, "store: freed path: %s", rsp->rst_devpath)); 168 169 kmem_free(rsp->rst_devpath, strlen(rsp->rst_devpath) + 1); 170 kmem_free(rsp, sizeof (*rsp)); 171 } 172 173 static void 174 retire_list_free(nvf_handle_t nvfh) 175 { 176 list_t *listp; 177 rio_store_t *rsp; 178 179 ASSERT(nvfh == rio_store_handle); 180 ASSERT(RW_WRITE_HELD(nvf_lock(nvfh))); 181 182 listp = nvf_list(nvfh); 183 while (rsp = list_head(listp)) { 184 list_remove(listp, rsp); 185 rio_store_free(rsp); 186 } 187 188 STORE_DBG((CE_NOTE, "store: freed retire list")); 189 } 190 191 static int 192 rio_store_decode(nvf_handle_t nvfh, nvlist_t *line_nvl, char *name) 193 { 194 rio_store_t *rsp; 195 int32_t version; 196 int32_t magic; 197 int32_t flags; 198 int rval; 199 200 ASSERT(nvfh == rio_store_handle); 201 ASSERT(RW_WRITE_HELD(nvf_lock(nvfh))); 202 ASSERT(name); 203 204 version = 0; 205 rval = nvlist_lookup_int32(line_nvl, RIO_STORE_VERSION_STR, &version); 206 if (rval != 0 || version != RIO_STORE_VERSION) { 207 return (EINVAL); 208 } 209 210 magic = 0; 211 rval = nvlist_lookup_int32(line_nvl, RIO_STORE_MAGIC_STR, &magic); 212 if (rval != 0 || magic != RIO_STORE_MAGIC) { 213 return (EINVAL); 214 } 215 216 flags = 0; 217 rval = nvlist_lookup_int32(line_nvl, RIO_STORE_FLAGS_STR, &flags); 218 if (rval != 0 || flags != RIO_STORE_F_RETIRED) { 219 return (EINVAL); 220 } 221 222 if (ddi_retire_store_bypass) { 223 flags |= RIO_STORE_F_BYPASS; 224 if (!bypass_msg) { 225 bypass_msg = 1; 226 cmn_err(CE_WARN, 227 "Bypassing retire store /etc/devices/retire_store"); 228 } 229 } 230 231 rsp = kmem_zalloc(sizeof (rio_store_t), KM_SLEEP); 232 rsp->rst_devpath = i_ddi_strdup(name, KM_SLEEP); 233 rsp->rst_flags = flags; 234 list_insert_tail(nvf_list(nvfh), rsp); 235 236 STORE_TRC((CE_NOTE, "store: added to retire list: %s", name)); 237 if (!retire_msg) { 238 retire_msg = 1; 239 cmn_err(CE_NOTE, "One or more I/O devices have been retired"); 240 } 241 242 return (0); 243 } 244 245 static int 246 rio_store_encode(nvf_handle_t nvfh, nvlist_t **ret_nvl) 247 { 248 nvlist_t *nvl; 249 nvlist_t *line_nvl; 250 list_t *listp; 251 rio_store_t *rsp; 252 int rval; 253 254 ASSERT(nvfh == rio_store_handle); 255 ASSERT(RW_WRITE_HELD(nvf_lock(nvfh))); 256 257 *ret_nvl = NULL; 258 259 nvl = NULL; 260 rval = nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP); 261 if (rval != 0) { 262 return (DDI_FAILURE); 263 } 264 265 listp = nvf_list(nvfh); 266 for (rsp = list_head(listp); rsp; rsp = list_next(listp, rsp)) { 267 int flag_mask = RIO_STORE_F_RETIRED|RIO_STORE_F_BYPASS; 268 int flags; 269 ASSERT(rsp->rst_devpath); 270 ASSERT(!(rsp->rst_flags & ~flag_mask)); 271 272 line_nvl = NULL; 273 rval = nvlist_alloc(&line_nvl, NV_UNIQUE_NAME, KM_SLEEP); 274 if (rval != 0) { 275 line_nvl = NULL; 276 goto error; 277 } 278 279 rval = nvlist_add_int32(line_nvl, RIO_STORE_VERSION_STR, 280 RIO_STORE_VERSION); 281 if (rval != 0) { 282 goto error; 283 } 284 rval = nvlist_add_int32(line_nvl, RIO_STORE_MAGIC_STR, 285 RIO_STORE_MAGIC); 286 if (rval != 0) { 287 goto error; 288 } 289 290 /* don't save the bypass flag */ 291 flags = RIO_STORE_F_RETIRED; 292 rval = nvlist_add_int32(line_nvl, RIO_STORE_FLAGS_STR, 293 flags); 294 if (rval != 0) { 295 goto error; 296 } 297 298 rval = nvlist_add_nvlist(nvl, rsp->rst_devpath, line_nvl); 299 if (rval != 0) { 300 goto error; 301 } 302 nvlist_free(line_nvl); 303 line_nvl = NULL; 304 } 305 306 *ret_nvl = nvl; 307 STORE_DBG((CE_NOTE, "packed retire list into nvlist")); 308 return (DDI_SUCCESS); 309 310 error: 311 nvlist_free(line_nvl); 312 ASSERT(nvl); 313 nvlist_free(nvl); 314 return (DDI_FAILURE); 315 } 316 317 int 318 e_ddi_retire_persist(char *devpath) 319 { 320 rio_store_t *rsp; 321 rio_store_t *new_rsp; 322 list_t *listp; 323 char *new_path; 324 325 STORE_DBG((CE_NOTE, "e_ddi_retire_persist: entered: %s", devpath)); 326 327 new_rsp = kmem_zalloc(sizeof (*new_rsp), KM_SLEEP); 328 new_rsp->rst_devpath = new_path = i_ddi_strdup(devpath, KM_SLEEP); 329 new_rsp->rst_flags = RIO_STORE_F_RETIRED; 330 331 rw_enter(nvf_lock(rio_store_handle), RW_WRITER); 332 333 listp = nvf_list(rio_store_handle); 334 for (rsp = list_head(listp); rsp; rsp = list_next(listp, rsp)) { 335 int flag_mask = RIO_STORE_F_RETIRED|RIO_STORE_F_BYPASS; 336 ASSERT(!(rsp->rst_flags & ~flag_mask)); 337 338 /* already there */ 339 if (strcmp(devpath, rsp->rst_devpath) == 0) { 340 /* explicit retire, clear bypass flag (if any) */ 341 rsp->rst_flags &= ~RIO_STORE_F_BYPASS; 342 ASSERT(rsp->rst_flags == RIO_STORE_F_RETIRED); 343 rw_exit(nvf_lock(rio_store_handle)); 344 kmem_free(new_path, strlen(new_path) + 1); 345 kmem_free(new_rsp, sizeof (*new_rsp)); 346 STORE_DBG((CE_NOTE, "store: already in. Clear bypass " 347 ": %s", devpath)); 348 return (0); 349 } 350 351 } 352 353 ASSERT(rsp == NULL); 354 list_insert_tail(listp, new_rsp); 355 356 nvf_mark_dirty(rio_store_handle); 357 358 rw_exit(nvf_lock(rio_store_handle)); 359 360 nvf_wake_daemon(); 361 362 STORE_DBG((CE_NOTE, "store: New, added to list, dirty: %s", devpath)); 363 364 return (0); 365 } 366 367 int 368 e_ddi_retire_unpersist(char *devpath) 369 { 370 rio_store_t *rsp; 371 rio_store_t *next; 372 list_t *listp; 373 int is_dirty = 0; 374 375 STORE_DBG((CE_NOTE, "e_ddi_retire_unpersist: entered: %s", devpath)); 376 377 rw_enter(nvf_lock(rio_store_handle), RW_WRITER); 378 379 listp = nvf_list(rio_store_handle); 380 for (rsp = list_head(listp); rsp; rsp = next) { 381 next = list_next(listp, rsp); 382 if (strcmp(devpath, rsp->rst_devpath) != 0) 383 continue; 384 385 list_remove(listp, rsp); 386 rio_store_free(rsp); 387 388 STORE_DBG((CE_NOTE, "store: found in list. Freed: %s", 389 devpath)); 390 391 nvf_mark_dirty(rio_store_handle); 392 is_dirty = 1; 393 } 394 395 rw_exit(nvf_lock(rio_store_handle)); 396 397 if (is_dirty) 398 nvf_wake_daemon(); 399 400 return (is_dirty); 401 } 402 403 int 404 e_ddi_device_retired(char *devpath) 405 { 406 list_t *listp; 407 rio_store_t *rsp; 408 size_t len; 409 int retired; 410 411 retired = 0; 412 413 rw_enter(nvf_lock(rio_store_handle), RW_READER); 414 415 listp = nvf_list(rio_store_handle); 416 for (rsp = list_head(listp); rsp; rsp = list_next(listp, rsp)) { 417 int flag_mask = RIO_STORE_F_RETIRED|RIO_STORE_F_BYPASS; 418 ASSERT(!(rsp->rst_flags & ~flag_mask)); 419 420 /* 421 * If the "bypass" flag is set, then the device 422 * is *not* retired for the current boot of the 423 * system. It indicates that the retire store 424 * was read but the devices in the retire store 425 * were not retired i.e. effectively the store 426 * was bypassed. For why we bother to even read 427 * the store when we bypass it, see the comments 428 * for the tunable ddi_retire_store_bypass. 429 */ 430 if (rsp->rst_flags & RIO_STORE_F_BYPASS) { 431 STORE_TRC((CE_NOTE, "store: found & bypassed: %s", 432 rsp->rst_devpath)); 433 continue; 434 } 435 436 /* 437 * device is retired, if it or a parent exists 438 * in the in-core list 439 */ 440 len = strlen(rsp->rst_devpath); 441 if (strncmp(devpath, rsp->rst_devpath, len) != 0) 442 continue; 443 if (devpath[len] == '\0' || devpath[len] == '/') { 444 /* exact match or a child */ 445 retired = 1; 446 STORE_TRC((CE_NOTE, "store: found & !bypassed: %s", 447 devpath)); 448 break; 449 } 450 } 451 rw_exit(nvf_lock(rio_store_handle)); 452 453 return (retired); 454 } 455