2 * Copyright (c) 2008, 2009 Yahoo!, Inc.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. The names of the authors may not be used to endorse or promote
14 * products derived from this software without specific prior written
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 #include <sys/types.h>
33 #include <sys/errno.h>
44 mfi_event_get_info(int fd, struct mfi_evt_log_state *info, uint8_t *statusp)
47 return (mfi_dcmd_command(fd, MFI_DCMD_CTRL_EVENT_GETINFO, info,
48 sizeof(struct mfi_evt_log_state), NULL, 0, statusp));
52 mfi_get_events(int fd, struct mfi_evt_list *list, int num_events,
53 union mfi_evt filter, uint32_t start_seq, uint8_t *statusp)
59 mbox[1] = filter.word;
60 size = sizeof(struct mfi_evt_list) + sizeof(struct mfi_evt_detail) *
62 return (mfi_dcmd_command(fd, MFI_DCMD_CTRL_EVENT_GET, list, size,
63 (uint8_t *)&mbox, sizeof(mbox), statusp));
67 show_logstate(int ac, char **av __unused)
69 struct mfi_evt_log_state info;
73 warnx("show logstate: extra arguments");
77 fd = mfi_open(mfi_unit, O_RDWR);
84 if (mfi_event_get_info(fd, &info, NULL) < 0) {
86 warn("Failed to get event log info");
91 printf("mfi%d Event Log Sequence Numbers:\n", mfi_unit);
92 printf(" Newest Seq #: %u\n", info.newest_seq_num);
93 printf(" Oldest Seq #: %u\n", info.oldest_seq_num);
94 printf(" Clear Seq #: %u\n", info.clear_seq_num);
95 printf("Shutdown Seq #: %u\n", info.shutdown_seq_num);
96 printf(" Boot Seq #: %u\n", info.boot_seq_num);
102 MFI_COMMAND(show, logstate, show_logstate);
105 parse_seq(struct mfi_evt_log_state *info, char *arg, uint32_t *seq)
110 if (strcasecmp(arg, "newest") == 0) {
111 *seq = info->newest_seq_num;
114 if (strcasecmp(arg, "oldest") == 0) {
115 *seq = info->oldest_seq_num;
118 if (strcasecmp(arg, "clear") == 0) {
119 *seq = info->clear_seq_num;
122 if (strcasecmp(arg, "shutdown") == 0) {
123 *seq = info->shutdown_seq_num;
126 if (strcasecmp(arg, "boot") == 0) {
127 *seq = info->boot_seq_num;
130 val = strtol(arg, &cp, 0);
131 if (*cp != '\0' || val < 0) {
140 parse_locale(char *arg, uint16_t *locale)
145 if (strncasecmp(arg, "vol", 3) == 0 || strcasecmp(arg, "ld") == 0) {
146 *locale = MFI_EVT_LOCALE_LD;
149 if (strncasecmp(arg, "drive", 5) == 0 || strcasecmp(arg, "pd") == 0) {
150 *locale = MFI_EVT_LOCALE_PD;
153 if (strncasecmp(arg, "encl", 4) == 0) {
154 *locale = MFI_EVT_LOCALE_ENCL;
157 if (strncasecmp(arg, "batt", 4) == 0 ||
158 strncasecmp(arg, "bbu", 3) == 0) {
159 *locale = MFI_EVT_LOCALE_BBU;
162 if (strcasecmp(arg, "sas") == 0) {
163 *locale = MFI_EVT_LOCALE_SAS;
166 if (strcasecmp(arg, "ctrl") == 0 || strncasecmp(arg, "cont", 4) == 0) {
167 *locale = MFI_EVT_LOCALE_CTRL;
170 if (strcasecmp(arg, "config") == 0) {
171 *locale = MFI_EVT_LOCALE_CONFIG;
174 if (strcasecmp(arg, "cluster") == 0) {
175 *locale = MFI_EVT_LOCALE_CLUSTER;
178 if (strcasecmp(arg, "all") == 0) {
179 *locale = MFI_EVT_LOCALE_ALL;
182 val = strtol(arg, &cp, 0);
183 if (*cp != '\0' || val < 0 || val > 0xffff) {
192 parse_class(char *arg, int8_t *class)
197 if (strcasecmp(arg, "debug") == 0) {
198 *class = MFI_EVT_CLASS_DEBUG;
201 if (strncasecmp(arg, "prog", 4) == 0) {
202 *class = MFI_EVT_CLASS_PROGRESS;
205 if (strncasecmp(arg, "info", 4) == 0) {
206 *class = MFI_EVT_CLASS_INFO;
209 if (strncasecmp(arg, "warn", 4) == 0) {
210 *class = MFI_EVT_CLASS_WARNING;
213 if (strncasecmp(arg, "crit", 4) == 0) {
214 *class = MFI_EVT_CLASS_CRITICAL;
217 if (strcasecmp(arg, "fatal") == 0) {
218 *class = MFI_EVT_CLASS_FATAL;
221 if (strcasecmp(arg, "dead") == 0) {
222 *class = MFI_EVT_CLASS_DEAD;
225 val = strtol(arg, &cp, 0);
226 if (*cp != '\0' || val < -128 || val > 127) {
235 * The timestamp is the number of seconds since 00:00 Jan 1, 2000. If
236 * the bits in 24-31 are all set, then it is the number of seconds since
240 format_timestamp(uint32_t timestamp)
242 static char buffer[32];
247 if ((timestamp & 0xff000000) == 0xff000000) {
248 snprintf(buffer, sizeof(buffer), "boot + %us", timestamp &
254 /* Compute 00:00 Jan 1, 2000 offset. */
255 bzero(&tm, sizeof(tm));
257 tm.tm_year = (2000 - 1900);
261 snprintf(buffer, sizeof(buffer), "%us", timestamp);
264 t = base + timestamp;
265 strftime(buffer, sizeof(buffer), "%+", localtime(&t));
270 format_locale(uint16_t locale)
272 static char buffer[8];
275 case MFI_EVT_LOCALE_LD:
277 case MFI_EVT_LOCALE_PD:
279 case MFI_EVT_LOCALE_ENCL:
281 case MFI_EVT_LOCALE_BBU:
283 case MFI_EVT_LOCALE_SAS:
285 case MFI_EVT_LOCALE_CTRL:
287 case MFI_EVT_LOCALE_CONFIG:
289 case MFI_EVT_LOCALE_CLUSTER:
291 case MFI_EVT_LOCALE_ALL:
294 snprintf(buffer, sizeof(buffer), "0x%04x", locale);
300 format_class(int8_t class)
302 static char buffer[6];
305 case MFI_EVT_CLASS_DEBUG:
307 case MFI_EVT_CLASS_PROGRESS:
309 case MFI_EVT_CLASS_INFO:
311 case MFI_EVT_CLASS_WARNING:
313 case MFI_EVT_CLASS_CRITICAL:
315 case MFI_EVT_CLASS_FATAL:
317 case MFI_EVT_CLASS_DEAD:
320 snprintf(buffer, sizeof(buffer), "%d", class);
325 /* Simulates %D from kernel printf(9). */
327 simple_hex(void *ptr, size_t length, const char *separator)
335 printf("%02x", cp[0]);
336 for (i = 1; i < length; i++)
337 printf("%s%02x", separator, cp[i]);
341 pdrive_location(struct mfi_evt_pd *pd)
343 static char buffer[16];
345 if (pd->enclosure_index == 0)
346 snprintf(buffer, sizeof(buffer), "%02d(s%d)", pd->device_id,
349 snprintf(buffer, sizeof(buffer), "%02d(e%d/s%d)", pd->device_id,
350 pd->enclosure_index, pd->slot_number);
355 volume_name(int fd, struct mfi_evt_ld *ld)
358 return (mfi_volume_name(fd, ld->target_id));
361 /* Ripped from sys/dev/mfi/mfi.c. */
363 mfi_decode_evt(int fd, struct mfi_evt_detail *detail, int verbose)
366 printf("%5d (%s/%s/%s) - ", detail->seq, format_timestamp(detail->time),
367 format_locale(detail->evt_class.members.locale),
368 format_class(detail->evt_class.members.evt_class));
369 switch (detail->arg_type) {
370 case MR_EVT_ARGS_NONE:
372 case MR_EVT_ARGS_CDB_SENSE:
375 pdrive_location(&detail->args.cdb_sense.pd)
377 simple_hex(detail->args.cdb_sense.cdb,
378 detail->args.cdb_sense.cdb_len, ":");
380 simple_hex(detail->args.cdb_sense.sense,
381 detail->args.cdb_sense.sense_len, ":");
386 printf("VOL %s event: ", volume_name(fd, &detail->args.ld));
388 case MR_EVT_ARGS_LD_COUNT:
389 printf("VOL %s", volume_name(fd, &detail->args.ld_count.ld));
391 printf(" count %lld: ",
392 (long long)detail->args.ld_count.count);
396 case MR_EVT_ARGS_LD_LBA:
397 printf("VOL %s", volume_name(fd, &detail->args.ld_count.ld));
400 (long long)detail->args.ld_lba.lba);
404 case MR_EVT_ARGS_LD_OWNER:
405 printf("VOL %s", volume_name(fd, &detail->args.ld_count.ld));
407 printf(" owner changed: prior %d, new %d",
408 detail->args.ld_owner.pre_owner,
409 detail->args.ld_owner.new_owner);
413 case MR_EVT_ARGS_LD_LBA_PD_LBA:
414 printf("VOL %s", volume_name(fd, &detail->args.ld_count.ld));
416 printf(" lba %lld, physical drive PD %s lba %lld",
417 (long long)detail->args.ld_lba_pd_lba.ld_lba,
418 pdrive_location(&detail->args.ld_lba_pd_lba.pd),
419 (long long)detail->args.ld_lba_pd_lba.pd_lba);
423 case MR_EVT_ARGS_LD_PROG:
424 printf("VOL %s", volume_name(fd, &detail->args.ld_prog.ld));
426 printf(" progress %d%% in %ds",
427 detail->args.ld_prog.prog.progress/655,
428 detail->args.ld_prog.prog.elapsed_seconds);
432 case MR_EVT_ARGS_LD_STATE:
433 printf("VOL %s", volume_name(fd, &detail->args.ld_prog.ld));
435 printf(" state prior %s new %s",
436 mfi_ldstate(detail->args.ld_state.prev_state),
437 mfi_ldstate(detail->args.ld_state.new_state));
441 case MR_EVT_ARGS_LD_STRIP:
442 printf("VOL %s", volume_name(fd, &detail->args.ld_strip.ld));
444 printf(" strip %lld",
445 (long long)detail->args.ld_strip.strip);
451 printf("PD %s event: ",
452 pdrive_location(&detail->args.pd));
455 case MR_EVT_ARGS_PD_ERR:
457 printf("PD %s err %d: ",
458 pdrive_location(&detail->args.pd_err.pd),
459 detail->args.pd_err.err);
462 case MR_EVT_ARGS_PD_LBA:
464 printf("PD %s lba %lld: ",
465 pdrive_location(&detail->args.pd_lba.pd),
466 (long long)detail->args.pd_lba.lba);
469 case MR_EVT_ARGS_PD_LBA_LD:
471 printf("PD %s lba %lld VOL %s: ",
472 pdrive_location(&detail->args.pd_lba_ld.pd),
473 (long long)detail->args.pd_lba.lba,
474 volume_name(fd, &detail->args.pd_lba_ld.ld));
477 case MR_EVT_ARGS_PD_PROG:
479 printf("PD %s progress %d%% seconds %ds: ",
480 pdrive_location(&detail->args.pd_prog.pd),
481 detail->args.pd_prog.prog.progress/655,
482 detail->args.pd_prog.prog.elapsed_seconds);
485 case MR_EVT_ARGS_PD_STATE:
487 printf("PD %s state prior %s new %s: ",
488 pdrive_location(&detail->args.pd_prog.pd),
489 mfi_pdstate(detail->args.pd_state.prev_state),
490 mfi_pdstate(detail->args.pd_state.new_state));
493 case MR_EVT_ARGS_PCI:
495 printf("PCI 0x%04x 0x%04x 0x%04x 0x%04x: ",
496 detail->args.pci.venderId,
497 detail->args.pci.deviceId,
498 detail->args.pci.subVenderId,
499 detail->args.pci.subDeviceId);
502 case MR_EVT_ARGS_RATE:
504 printf("Rebuild rate %d: ", detail->args.rate);
507 case MR_EVT_ARGS_TIME:
509 printf("Adapter time %s; %d seconds since power on: ",
510 format_timestamp(detail->args.time.rtc),
511 detail->args.time.elapsedSeconds);
514 case MR_EVT_ARGS_ECC:
516 printf("Adapter ECC %x,%x: %s: ",
517 detail->args.ecc.ecar,
518 detail->args.ecc.elog,
519 detail->args.ecc.str);
524 printf("Type %d: ", detail->arg_type);
528 printf("%s\n", detail->description);
532 show_events(int ac, char **av)
534 struct mfi_evt_log_state info;
535 struct mfi_evt_list *list;
536 union mfi_evt filter;
540 uint32_t seq, start, stop;
542 int ch, error, fd, num_events, verbose;
545 fd = mfi_open(mfi_unit, O_RDWR);
552 if (mfi_event_get_info(fd, &info, NULL) < 0) {
554 warn("Failed to get event log info");
559 /* Default settings. */
561 filter.members.reserved = 0;
562 filter.members.locale = MFI_EVT_LOCALE_ALL;
563 filter.members.evt_class = MFI_EVT_CLASS_WARNING;
564 start = info.boot_seq_num;
565 stop = info.newest_seq_num;
568 /* Parse any options. */
570 while ((ch = getopt(ac, av, "c:l:n:v")) != -1) {
573 if (parse_class(optarg, &filter.members.evt_class) < 0) {
575 warn("Error parsing event class");
581 if (parse_locale(optarg, &filter.members.locale) < 0) {
583 warn("Error parsing event locale");
589 val = strtol(optarg, &cp, 0);
590 if (*cp != '\0' || val <= 0) {
591 warnx("Invalid event count");
609 /* Determine buffer size and validate it. */
610 size = sizeof(struct mfi_evt_list) + sizeof(struct mfi_evt_detail) *
612 if (size > getpagesize()) {
613 warnx("Event count is too high");
618 /* Handle optional start and stop sequence numbers. */
620 warnx("show events: extra arguments");
624 if (ac > 0 && parse_seq(&info, av[0], &start) < 0) {
626 warn("Error parsing starting sequence number");
630 if (ac > 1 && parse_seq(&info, av[1], &stop) < 0) {
632 warn("Error parsing ending sequence number");
639 warnx("malloc failed");
643 for (seq = start;;) {
644 if (mfi_get_events(fd, list, num_events, filter, seq,
647 warn("Failed to fetch events");
652 if (status == MFI_STAT_NOT_FOUND) {
654 warnx("No matching events found");
657 if (status != MFI_STAT_OK) {
658 warnx("Error fetching events: %s", mfi_status(status));
664 for (i = 0; i < list->count; i++) {
666 * If this event is newer than 'stop_seq' then
667 * break out of the loop. Note that the log
668 * is a circular buffer so we have to handle
669 * the case that our stop point is earlier in
670 * the buffer than our start point.
672 if (list->event[i].seq >= stop) {
675 else if (list->event[i].seq < start)
678 mfi_decode_evt(fd, &list->event[i], verbose);
682 * XXX: If the event's seq # is the end of the buffer
683 * then this probably won't do the right thing. We
684 * need to know the size of the buffer somehow.
686 seq = list->event[list->count - 1].seq + 1;
695 MFI_COMMAND(show, events, show_events);