Home | History | Annotate | Download | only in os
      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