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]
23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
30 * Checkpoint the ereport events for persitence across fmd restart.
32 * Each ereport is stored in a named buffer. Each ereport is uniquely
33 * indentified by a id which is consists of a number of ereport fields. The
34 * name of the buffer is derived from the id.
36 * All ereport ids are stored in the circular list which is saved in a
44 #include <sys/types.h>
46 #include <sys/fm/ldom.h>
47 #include <sys/fm/protocol.h>
48 #include <fm/fmd_api.h>
49 #include <fm/libtopo.h>
50 #include <fm/topo_hc.h>
52 #include "etm_etm_proto.h"
53 #include "etm_iosvc.h"
55 #include "etm_filter.h"
57 #define ETM_ATTR_PRIMARY "primary"
58 #define ETM_ATTR_TOD "__tod"
59 #define ETM_LDOM_PRIMARY "primary"
62 * -------------------------- private variables ------------------------------
65 static etm_ckpt_id_list_t
*etm_id_lst
= NULL
; /* list of ereports ids */
67 static pthread_mutex_t etm_id_lst_lock
; /* list lock */
70 * -------------------------- functions --------------------------------------
76 * Hash a class name to a number
79 etm_ckpt_str_hash(char *str
)
81 uint_t hash
= 0; /* hash value */
95 * Get the string of an ereport id. It is used as the named buffer that
99 etm_ckpt_id2str(etm_ckpt_erpt_id_t
*id
, char *str
, size_t size
) {
100 (void) snprintf(str
, size
, "%s_%llx_%d_%x_%d", ETM_CKPT_ERPT_PREFIX
,
101 id
->ei_ena
, id
->ei_hash
, id
->ei_tod1
, id
->ei_pri
);
107 * Get the buffer name and ereport id of a given ereport
110 etm_ckpt_erpt2id(fmd_hdl_t
*hdl
, nvlist_t
*erpt
, etm_ckpt_erpt_id_t
*id
,
111 char *str
, int size
) {
115 boolean_t pri
= B_FALSE
;
117 bzero(id
, sizeof (etm_ckpt_erpt_id_t
));
120 if (nvlist_lookup_uint64(erpt
, FM_EREPORT_ENA
, &id
->ei_ena
) != 0) {
121 fmd_hdl_debug(hdl
, "Ena not found\n");
126 (void) nvlist_lookup_string(erpt
, FM_CLASS
, &class);
128 fmd_hdl_debug(hdl
, "%s not found\n", FM_CLASS
);
131 if (strncmp(class, FM_EREPORT_CLASS
, strlen(FM_EREPORT_CLASS
)) != 0) {
132 fmd_hdl_debug(hdl
, "Only support checkpointing %s\n",
136 id
->ei_hash
= etm_ckpt_str_hash(class);
138 /* tod[1]: fractional of a second */
139 if (nvlist_lookup_uint64_array(erpt
, ETM_ATTR_TOD
, &tod
, &sz
) == 0) {
141 id
->ei_tod1
= (uint32_t)tod
[1];
146 if (nvlist_lookup_boolean_value(erpt
, ETM_ATTR_PRIMARY
, &pri
) == 0) {
147 id
->ei_pri
= pri
? 1 : 0;
150 etm_ckpt_id2str(id
, str
, size
);
156 * etm_ckpt_il_equal()
158 * Test if two ereport ids are equal.
161 etm_ckpt_il_equal(etm_ckpt_erpt_id_t
*i1
, etm_ckpt_erpt_id_t
*i2
)
163 return ((i1
->ei_ena
== i2
->ei_ena
) && (i1
->ei_tod1
== i2
->ei_tod1
) &&
164 (i1
->ei_pri
== i2
->ei_pri
) && (i1
->ei_hash
== i2
->ei_hash
));
168 * etm_ckpt_il_resize()
170 * Increase the size of the circular list and pack its entries.
173 etm_ckpt_il_resize(fmd_hdl_t
*hdl
, uint_t factor
)
175 etm_ckpt_id_list_t
*il1
, *il2
; /* temp lists */
176 size_t sz1
, sz2
; /* sizes of lists */
177 int i
, next
; /* temp counters */
178 etm_ckpt_erpt_id_t
*p1
, *p2
, *s1
, *s2
; /* temp id pointers */
179 etm_ckpt_erpt_id_t blank
; /* blank ereport id */
184 /* the present queue */
186 sz1
= sizeof (etm_ckpt_id_list_t
) + il1
->il_ids_sz
;
188 /* Create an empty queue with a new size */
189 sz2
= sizeof (etm_ckpt_id_list_t
) + (factor
* il1
->il_ids_sz
);
190 il2
= fmd_hdl_zalloc(hdl
, sz2
, FMD_SLEEP
);
191 il2
->il_ver
= ETM_CKPT_VERSION
;
192 il2
->il_max
= factor
* etm_id_lst
->il_max
;
193 il2
->il_ids_sz
= factor
* il1
->il_ids_sz
;
195 /* pointers to the two arrays of entries */
196 bzero(&blank
, sizeof (blank
));
197 s1
= (etm_ckpt_erpt_id_t
*)
198 ((ptrdiff_t)il1
+ sizeof (etm_ckpt_id_list_t
));
199 s2
= (etm_ckpt_erpt_id_t
*)
200 ((ptrdiff_t)il2
+ sizeof (etm_ckpt_id_list_t
));
202 /* copy non-empty ereport ids from list il1 to il2. Toss the blank. */
203 if (il1
->il_head
!= il1
->il_tail
) {
204 for (i
= il1
->il_head
; i
!= il1
->il_tail
; i
= next
) {
205 next
= (i
+ 1) % il1
->il_max
;
207 if (!etm_ckpt_il_equal(p1
, &blank
)) {
208 /* copy non-empty entries */
209 il2
->il_tail
= (il2
->il_tail
+ 1) % il2
->il_max
;
210 fmd_hdl_debug(hdl
, "Copying entry %d to %d\n",
212 p2
= s2
+ il2
->il_tail
;
220 /* both lists have the same size, update the present list */
221 bcopy(il2
, il1
, sz1
);
222 fmd_hdl_free(hdl
, il2
, sz2
);
223 fmd_buf_write(hdl
, NULL
, ETM_CKPT_IL_BUF
, (void *) il1
, sz1
);
225 /* replace the present list */
227 fmd_hdl_free(hdl
, il1
, sz1
);
228 /* write to new buffer */
229 fmd_buf_destroy(hdl
, NULL
, ETM_CKPT_IL_BUF
);
230 fmd_buf_create(hdl
, NULL
, ETM_CKPT_IL_BUF
, sz2
);
231 fmd_buf_write(hdl
, NULL
, ETM_CKPT_IL_BUF
, (void *) il2
, sz2
);
238 * Find the ereport id in the list.
242 etm_ckpt_il_find(fmd_hdl_t
*hdl
, etm_ckpt_erpt_id_t
*id
)
244 int i
, next
; /* temp counter */
245 etm_ckpt_erpt_id_t
*p
, *s
; /* temp erpt id */
247 fmd_hdl_debug(hdl
, "etm_ckpt_il_find()\n");
250 if (etm_id_lst
->il_head
== etm_id_lst
->il_tail
) {
251 fmd_hdl_debug(hdl
, "find an empty list\n");
254 s
= (etm_ckpt_erpt_id_t
*)((ptrdiff_t)etm_id_lst
+
255 sizeof (etm_ckpt_id_list_t
));
256 for (i
= etm_id_lst
->il_head
; i
!= etm_id_lst
->il_tail
; i
= next
) {
257 next
= (i
+ 1) % etm_id_lst
->il_max
;
259 if (etm_ckpt_il_equal(p
, id
))
269 * Add an ereport id in the list.
272 etm_ckpt_il_add(fmd_hdl_t
*hdl
, etm_ckpt_erpt_id_t
*id
) {
274 etm_ckpt_erpt_id_t
*p
, *s
; /* temp id */
277 * resize the q if it is full.
278 * If the capacity is less 80%, purge the emtpy entries to make more
279 * room for new entries. Otherwise, double the queue size.
281 next
= (etm_id_lst
->il_tail
+ 1) % etm_id_lst
->il_max
;
282 if (next
== etm_id_lst
->il_head
) {
283 if ((etm_id_lst
->il_cnt
* 1.0 / etm_id_lst
->il_max
) < 0.8) {
284 etm_ckpt_il_resize(hdl
, 1);
286 etm_ckpt_il_resize(hdl
, 2);
289 /* test if the list again */
290 next
= (etm_id_lst
->il_tail
+ 1) % etm_id_lst
->il_max
;
291 if (next
== etm_id_lst
->il_head
) {
292 fmd_hdl_error(hdl
, "List is full %d %d\n",
293 etm_id_lst
->il_head
, etm_id_lst
->il_tail
);
297 /* Add the id entry at the head */
298 s
= (etm_ckpt_erpt_id_t
*)((ptrdiff_t)etm_id_lst
+
299 sizeof (etm_ckpt_id_list_t
));
300 etm_id_lst
->il_tail
= (etm_id_lst
->il_tail
+ 1) % etm_id_lst
->il_max
;
301 p
= s
+ etm_id_lst
->il_tail
;
303 etm_id_lst
->il_cnt
++;
305 return (etm_id_lst
->il_tail
);
309 * etm_ckpt_il_delete()
311 * Delete an ereport id from the list.
314 etm_ckpt_il_delete(fmd_hdl_t
*hdl
, etm_ckpt_erpt_id_t
*id
) {
316 int i
, next
; /* temp counter */
317 etm_ckpt_erpt_id_t
*p
, *s
; /* temp id pointers */
318 etm_ckpt_erpt_id_t blank
; /* blank id */
321 if (etm_id_lst
->il_tail
== etm_id_lst
->il_head
) {
322 fmd_hdl_debug(hdl
, "Empty queue(%d)\n", etm_id_lst
->il_head
);
326 bzero(&blank
, sizeof (blank
));
327 s
= (etm_ckpt_erpt_id_t
*)((ptrdiff_t)etm_id_lst
+
328 sizeof (etm_ckpt_id_list_t
));
330 /* delete leading empty entries */
331 for (i
= etm_id_lst
->il_head
; i
!= etm_id_lst
->il_tail
; i
= next
) {
332 next
= (i
+ 1) % etm_id_lst
->il_max
;
334 if (!etm_ckpt_il_equal(p
, &blank
)) {
337 etm_id_lst
->il_cnt
--;
338 etm_id_lst
->il_head
= next
;
342 if (etm_id_lst
->il_head
== etm_id_lst
->il_tail
) {
343 fmd_hdl_debug(hdl
, "Empty queue(%d)\n", etm_id_lst
->il_head
);
347 /* find the entry and clear it */
348 for (i
= etm_id_lst
->il_head
; i
!= etm_id_lst
->il_tail
; i
= next
) {
349 next
= (i
+ 1) % etm_id_lst
->il_max
;
351 if (etm_ckpt_il_equal(p
, id
)) {
352 /* clear the entry */
354 etm_id_lst
->il_cnt
--;
356 /* remove the entry if it is the last one */
357 if (i
== etm_id_lst
->il_head
) {
358 etm_id_lst
->il_head
= next
;
369 * etm_ckpt_il_restore()
371 * Restore the idlist named buffer which is the circular list of the
375 etm_ckpt_il_restore(fmd_hdl_t
*hdl
)
377 size_t size
; /* buffer size */
379 /* get the buffer of the id list */
380 size
= fmd_buf_size(hdl
, NULL
, ETM_CKPT_IL_BUF
);
381 if (size
< sizeof (etm_ckpt_id_list_t
)) {
382 fmd_hdl_debug(hdl
, "Buffer name %s do not exist\n",
386 etm_id_lst
= (etm_ckpt_id_list_t
*)fmd_hdl_zalloc(hdl
, size
, FMD_SLEEP
);
387 fmd_buf_read(hdl
, NULL
, ETM_CKPT_IL_BUF
, (void *) etm_id_lst
, size
);
390 if (etm_id_lst
->il_ver
> ETM_CKPT_VERSION
) {
392 fmd_hdl_error(hdl
, "Unsupport checkpoint version (%#x)\n",
394 fmd_hdl_free(hdl
, (void *) etm_id_lst
, size
);
399 /* check the length */
400 if (etm_id_lst
->il_ids_sz
!= (size
- sizeof (etm_ckpt_id_list_t
))) {
401 fmd_hdl_debug(hdl
, "Invalid ids buffer size (%d, %d)\n",
402 etm_id_lst
->il_ids_sz
, size
);
403 fmd_hdl_free(hdl
, (void *) etm_id_lst
, size
);
412 * Recover ereports from the checkpointed data and dispatch them to the
416 etm_ckpt_recover(fmd_hdl_t
*hdl
)
418 int size
; /* buffer size */
419 int i
, next
; /* temp counter */
420 boolean_t dirty
= B_FALSE
; /* dirty flag */
421 uint64_t did
; /* domain id */
422 char name
[ETM_LINE_LEN
]; /* temp str */
423 char ldom
[ETM_LINE_LEN
]; /* ldom id */
424 etm_ckpt_erpt_id_t
*p
, *s
; /* temp ereport id */
425 etm_ckpt_erpt_id_t blank
; /* blank ereport id */
426 etm_ckpt_erpt_buf_t
*ep
; /* ereport buffer */
427 size_t sz
; /* size of ep */
428 char *buf
; /* temp buf */
429 nvlist_t
*nvl
; /* ereport */
430 etm_iosvc_t
*iosvc
; /* iosvc data struct */
433 * restore the circular list of ereport ids
435 etm_ckpt_il_restore(hdl
);
436 if (etm_id_lst
== NULL
) {
437 fmd_hdl_debug(hdl
, "Initialize a new id list\n");
438 size
= sizeof (etm_ckpt_id_list_t
) +
439 ETM_CKPT_IL_MIN_SIZE
* sizeof (etm_ckpt_erpt_id_t
);
440 etm_id_lst
= fmd_hdl_zalloc(hdl
, size
, FMD_SLEEP
);
441 etm_id_lst
->il_ver
= ETM_CKPT_VERSION
;
442 etm_id_lst
->il_max
= ETM_CKPT_IL_MIN_SIZE
;
443 etm_id_lst
->il_head
= 0;
444 etm_id_lst
->il_tail
= 0;
445 etm_id_lst
->il_ids_sz
=
446 ETM_CKPT_IL_MIN_SIZE
* sizeof (etm_ckpt_erpt_id_t
);
447 fmd_buf_destroy(hdl
, NULL
, ETM_CKPT_IL_BUF
);
448 fmd_buf_create(hdl
, NULL
, ETM_CKPT_IL_BUF
, size
);
449 fmd_buf_write(hdl
, NULL
, ETM_CKPT_IL_BUF
, (void *) etm_id_lst
,
453 fmd_thr_checkpoint(hdl
);
459 if ((etm_id_lst
->il_head
== etm_id_lst
->il_tail
) ||
460 (etm_id_lst
->il_cnt
== 0)) {
464 /* Visit all the entries in the list */
465 bzero(&blank
, sizeof (blank
));
466 s
= (etm_ckpt_erpt_id_t
*)((ptrdiff_t)etm_id_lst
+
467 sizeof (etm_ckpt_id_list_t
));
468 for (i
= etm_id_lst
->il_head
; i
!= etm_id_lst
->il_tail
; i
= next
) {
469 next
= (i
+ 1) % etm_id_lst
->il_max
;
471 if (etm_ckpt_il_equal(p
, &blank
)) {
472 fmd_hdl_debug(hdl
, "Skip empty entry %d\n", i
);
476 etm_ckpt_id2str(p
, name
, sizeof (name
));
477 fmd_hdl_debug(hdl
, "Restoring entry %s\n", name
);
478 if ((sz
= fmd_buf_size(hdl
, NULL
, name
)) == 0) {
479 fmd_hdl_error(hdl
, "Clear the stale entry %s\n", name
);
483 ep
= (etm_ckpt_erpt_buf_t
*)fmd_hdl_zalloc(hdl
, sz
, FMD_SLEEP
);
484 fmd_buf_read(hdl
, NULL
, name
, (void *) ep
, sz
);
485 buf
= (char *)((ptrdiff_t)ep
+ sizeof (etm_ckpt_erpt_buf_t
));
487 if (nvlist_unpack(buf
, ep
->eb_len
, &nvl
, 0)) {
488 fmd_hdl_debug(hdl
, "failed to unpack %s\n", name
);
489 fmd_hdl_free(hdl
, ep
, sz
);
492 fmd_hdl_free(hdl
, ep
, sz
);
493 if (etm_filter_find_ldom_id(hdl
, nvl
, ldom
, ETM_LINE_LEN
,
494 &did
) || (strcmp(name
, ETM_LDOM_PRIMARY
) == 0)) {
495 fmd_hdl_debug(hdl
, "Discard event %s\n", name
);
496 fmd_buf_destroy(hdl
, NULL
, name
);
503 fmd_hdl_debug(hdl
, "Dispatch %s to ldom %s\n", name
, ldom
);
506 * Find the queue of the ldom, create it if not exist.
507 * Then insert this event into the queue.
509 iosvc
= etm_iosvc_lookup(hdl
, ldom
, DS_INVALID_HDL
, B_TRUE
);
511 (void) etm_pack_ds_msg(hdl
, iosvc
, NULL
, 0, nvl
, SP_MSG
,
517 /* update the buffer of the queue */
518 size
= sizeof (etm_ckpt_id_list_t
) + etm_id_lst
->il_ids_sz
;
519 fmd_buf_write(hdl
, NULL
, ETM_CKPT_IL_BUF
, (void *) etm_id_lst
,
523 fmd_thr_checkpoint(hdl
);
526 } /* etm_ckpt_recover */
530 * etm_ckpt_add_entry()
532 * Save an ereport for persistence.
535 etm_ckpt_add_entry(fmd_hdl_t
*hdl
, nvlist_t
*erpt
) {
536 etm_ckpt_erpt_id_t id
;
537 char name
[ETM_LINE_LEN
];
538 int rc
; /* gen use */
539 size_t sz
; /* size */
540 size_t buflen
; /* sz of packed erpt */
541 uint8_t *buf
; /* buffer of erpt */
542 etm_ckpt_erpt_buf_t
*hdr
;
544 /* map ereport to id */
545 bzero(name
, ETM_LINE_LEN
);
546 rc
= etm_ckpt_erpt2id(hdl
, erpt
, &id
, name
, ETM_LINE_LEN
);
548 fmd_hdl_debug(hdl
, "Invalid ereport\n");
553 * check for a duplicate entry in the id list
554 * find the ereport buffer and search for the id
556 if (fmd_buf_size(hdl
, NULL
, name
) > 0 &&
557 etm_ckpt_il_find(hdl
, &id
) >= 0) {
558 fmd_hdl_debug(hdl
, "Duplicate id %s\n", name
);
562 /* Create the ereport buffer */
563 if (nvlist_size(erpt
, &buflen
, NV_ENCODE_XDR
) != 0) {
564 fmd_hdl_debug(hdl
, "nvlist_size fails\n");
567 sz
= sizeof (etm_ckpt_erpt_buf_t
) + buflen
;
568 hdr
= (etm_ckpt_erpt_buf_t
*)fmd_hdl_zalloc(hdl
, sz
, FMD_SLEEP
);
569 buf
= (uint8_t *)((ptrdiff_t)hdr
+ sizeof (etm_ckpt_erpt_buf_t
));
570 hdr
->eb_ver
= ETM_CKPT_VERSION
;
571 hdr
->eb_len
= buflen
;
572 if (nvlist_pack(erpt
, (char **)&buf
, &buflen
, NV_ENCODE_XDR
, 0) != 0) {
573 fmd_hdl_free(hdl
, hdr
, sz
);
574 fmd_hdl_debug(hdl
, "unpack fails\n");
577 fmd_hdl_debug(hdl
, "Add ckpt event(%s, %d)\n", name
, sz
);
578 fmd_buf_create(hdl
, NULL
, name
, sz
);
579 fmd_buf_write(hdl
, NULL
, name
, hdr
, sz
);
580 fmd_hdl_free(hdl
, hdr
, sz
);
582 /* Insert the ereport id into the id list */
583 if (etm_ckpt_il_add(hdl
, &id
) < 0) {
584 fmd_hdl_debug(hdl
, "Insert id %s failed\n", name
);
585 fmd_buf_destroy(hdl
, NULL
, name
);
589 /* update the buffer of the queue */
590 sz
= sizeof (etm_ckpt_id_list_t
) + etm_id_lst
->il_ids_sz
;
591 fmd_buf_write(hdl
, NULL
, ETM_CKPT_IL_BUF
, (void *) etm_id_lst
, sz
);
594 fmd_thr_checkpoint(hdl
);
600 * etm_ckpt_delete_entry()
602 * Delete an ereport id in the list.
605 etm_ckpt_delete_entry(fmd_hdl_t
*hdl
, nvlist_t
*erpt
) {
606 etm_ckpt_erpt_id_t id
;
607 char name
[ETM_LINE_LEN
];
608 int rc
; /* return code */
609 size_t sz
; /* size */
611 /* get id, id name */
612 bzero(name
, ETM_LINE_LEN
);
613 if (etm_ckpt_erpt2id(hdl
, erpt
, &id
, name
, ETM_LINE_LEN
) != 0) {
614 fmd_hdl_debug(hdl
, "Invalid ereport\n");
617 fmd_hdl_debug(hdl
, "Delete ckpt event(%s)\n", name
);
619 /* delete the ereport buffer */
620 if (fmd_buf_size(hdl
, NULL
, name
) > 0) {
621 fmd_buf_destroy(hdl
, NULL
, name
);
624 rc
= etm_ckpt_il_delete(hdl
, &id
);
626 fmd_hdl_debug(hdl
, "Delete id %s failed\n", name
);
630 /* update the buffer of the queue */
631 sz
= sizeof (etm_ckpt_id_list_t
) + etm_id_lst
->il_ids_sz
;
632 fmd_buf_write(hdl
, NULL
, ETM_CKPT_IL_BUF
, (void *) etm_id_lst
, sz
);
635 fmd_thr_checkpoint(hdl
);
641 etm_ckpt_add(fmd_hdl_t
*hdl
, nvlist_t
*erpt
) {
643 int rc
; /* return code */
645 (void) pthread_mutex_lock(&etm_id_lst_lock
);
647 rc
= etm_ckpt_add_entry(hdl
, erpt
);
649 (void) pthread_mutex_unlock(&etm_id_lst_lock
);
651 return (rc
>= 0 ? 0 : rc
);
655 etm_ckpt_delete(fmd_hdl_t
*hdl
, nvlist_t
*erpt
) {
656 int rc
; /* return code */
658 (void) pthread_mutex_lock(&etm_id_lst_lock
);
660 rc
= etm_ckpt_delete_entry(hdl
, erpt
);
662 (void) pthread_mutex_unlock(&etm_id_lst_lock
);
664 return (rc
>= 0 ? 0 : rc
);
669 etm_ckpt_init(fmd_hdl_t
*hdl
) {
670 (void) pthread_mutex_init(&etm_id_lst_lock
, NULL
);
675 etm_ckpt_fini(fmd_hdl_t
*hdl
) {
676 if (etm_id_lst
!= NULL
) {
677 fmd_hdl_free(hdl
, etm_id_lst
,
678 sizeof (etm_ckpt_id_list_t
) + etm_id_lst
->il_ids_sz
);
680 (void) pthread_mutex_destroy(&etm_id_lst_lock
);