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