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.
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.
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]
22 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 #pragma ident "%Z%%M% %I% %E% SMI"
29 #include <sys/sunddi.h>
30 #include <sys/sunndi.h>
31 #include <sys/ddi_impldefs.h>
32 #include <sys/ddi_implfuncs.h>
34 #include <sys/reboot.h>
35 #include <sys/sysmacros.h>
36 #include <sys/console.h>
37 #include <sys/devcache.h>
40 * The nvpair name in the I/O retire specific sub-nvlist
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"
46 #define RIO_STORE_VERSION_1 1
47 #define RIO_STORE_VERSION RIO_STORE_VERSION_1
50 * decoded retire list element
53 typedef enum rio_store_flags
{
54 RIO_STORE_F_INVAL
= 0,
55 RIO_STORE_F_RETIRED
= 1,
56 RIO_STORE_F_BYPASS
= 2
59 typedef struct rio_store
{
61 rio_store_flags_t rst_flags
;
65 #define RIO_STORE_MAGIC 0x601fcace /* retire */
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
);
73 * Retire I/O persistent store registration info
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 */
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;
89 #define STORE_DEBUG 0x0001
90 #define STORE_TRACE 0x0002
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
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
101 * 1. cause any additions to the cache to destroy the
102 * existing on-disk cache
104 * 2. prevent deletions from the existing on-disk
105 * cache which is needed for recovery from bad
108 * Use the following tunable instead
111 int ddi_retire_store_bypass
= 0;
116 * Initialize retire store data structures
119 retire_store_init(void)
121 if (boothowto
& RB_ASKNAME
) {
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';
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"
135 rio_store_ops
.nvfr_cache_path
= store_path
;
140 rio_store_handle
= nvf_register_file(&rio_store_ops
);
142 list_create(nvf_list(rio_store_handle
), sizeof (rio_store_t
),
143 offsetof(rio_store_t
, rst_next
));
147 * Read and populate the in-core retire store
150 retire_store_read(void)
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"));
160 rio_store_free(rio_store_t
*rsp
)
162 int flag_mask
= RIO_STORE_F_RETIRED
|RIO_STORE_F_BYPASS
;
165 ASSERT(rsp
->rst_devpath
);
166 ASSERT(rsp
->rst_flags
& RIO_STORE_F_RETIRED
);
167 ASSERT(!(rsp
->rst_flags
& ~flag_mask
));
169 STORE_TRC((CE_NOTE
, "store: freed path: %s", rsp
->rst_devpath
));
171 kmem_free(rsp
->rst_devpath
, strlen(rsp
->rst_devpath
) + 1);
172 kmem_free(rsp
, sizeof (*rsp
));
176 retire_list_free(nvf_handle_t nvfh
)
181 ASSERT(nvfh
== rio_store_handle
);
182 ASSERT(RW_WRITE_HELD(nvf_lock(nvfh
)));
184 listp
= nvf_list(nvfh
);
185 while (rsp
= list_head(listp
)) {
186 list_remove(listp
, rsp
);
190 STORE_DBG((CE_NOTE
, "store: freed retire list"));
194 rio_store_decode(nvf_handle_t nvfh
, nvlist_t
*line_nvl
, char *name
)
202 ASSERT(nvfh
== rio_store_handle
);
203 ASSERT(RW_WRITE_HELD(nvf_lock(nvfh
)));
207 rval
= nvlist_lookup_int32(line_nvl
, RIO_STORE_VERSION_STR
, &version
);
208 if (rval
!= 0 || version
!= RIO_STORE_VERSION
) {
213 rval
= nvlist_lookup_int32(line_nvl
, RIO_STORE_MAGIC_STR
, &magic
);
214 if (rval
!= 0 || magic
!= RIO_STORE_MAGIC
) {
219 rval
= nvlist_lookup_int32(line_nvl
, RIO_STORE_FLAGS_STR
, &flags
);
220 if (rval
!= 0 || flags
!= RIO_STORE_F_RETIRED
) {
224 if (ddi_retire_store_bypass
) {
225 flags
|= RIO_STORE_F_BYPASS
;
229 "Bypassing retire store /etc/devices/retire_store");
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
);
238 STORE_TRC((CE_NOTE
, "store: added to retire list: %s", name
));
241 cmn_err(CE_NOTE
, "One or more I/O devices have been retired");
248 rio_store_encode(nvf_handle_t nvfh
, nvlist_t
**ret_nvl
)
256 ASSERT(nvfh
== rio_store_handle
);
257 ASSERT(RW_WRITE_HELD(nvf_lock(nvfh
)));
262 rval
= nvlist_alloc(&nvl
, NV_UNIQUE_NAME
, KM_SLEEP
);
264 return (DDI_FAILURE
);
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
;
271 ASSERT(rsp
->rst_devpath
);
272 ASSERT(!(rsp
->rst_flags
& ~flag_mask
));
275 rval
= nvlist_alloc(&line_nvl
, NV_UNIQUE_NAME
, KM_SLEEP
);
281 rval
= nvlist_add_int32(line_nvl
, RIO_STORE_VERSION_STR
,
286 rval
= nvlist_add_int32(line_nvl
, RIO_STORE_MAGIC_STR
,
292 /* don't save the bypass flag */
293 flags
= RIO_STORE_F_RETIRED
;
294 rval
= nvlist_add_int32(line_nvl
, RIO_STORE_FLAGS_STR
,
300 rval
= nvlist_add_nvlist(nvl
, rsp
->rst_devpath
, line_nvl
);
304 nvlist_free(line_nvl
);
309 STORE_DBG((CE_NOTE
, "packed retire list into nvlist"));
310 return (DDI_SUCCESS
);
314 nvlist_free(line_nvl
);
317 return (DDI_FAILURE
);
321 e_ddi_retire_persist(char *devpath
)
324 rio_store_t
*new_rsp
;
328 STORE_DBG((CE_NOTE
, "e_ddi_retire_persist: entered: %s", devpath
));
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
;
334 rw_enter(nvf_lock(rio_store_handle
), RW_WRITER
);
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
));
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 "
357 list_insert_tail(listp
, new_rsp
);
359 nvf_mark_dirty(rio_store_handle
);
361 rw_exit(nvf_lock(rio_store_handle
));
365 STORE_DBG((CE_NOTE
, "store: New, added to list, dirty: %s", devpath
));
371 e_ddi_retire_unpersist(char *devpath
)
378 STORE_DBG((CE_NOTE
, "e_ddi_retire_unpersist: entered: %s", devpath
));
380 rw_enter(nvf_lock(rio_store_handle
), RW_WRITER
);
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)
388 list_remove(listp
, rsp
);
391 STORE_DBG((CE_NOTE
, "store: found in list. Freed: %s",
394 nvf_mark_dirty(rio_store_handle
);
398 rw_exit(nvf_lock(rio_store_handle
));
407 e_ddi_device_retired(char *devpath
)
416 rw_enter(nvf_lock(rio_store_handle
), RW_READER
);
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
));
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.
433 if (rsp
->rst_flags
& RIO_STORE_F_BYPASS
) {
434 STORE_TRC((CE_NOTE
, "store: found & bypassed: %s",
440 * device is retired, if it or a parent exists
441 * in the in-core list
443 len
= strlen(rsp
->rst_devpath
);
444 if (strncmp(devpath
, rsp
->rst_devpath
, len
) != 0)
446 if (devpath
[len
] == '\0' || devpath
[len
] == '/') {
447 /* exact match or a child */
449 STORE_TRC((CE_NOTE
, "store: found & !bypassed: %s",
454 rw_exit(nvf_lock(rio_store_handle
));