mfi_evt.c revision 196200
1183631Sdanger/*-
2183631Sdanger * Copyright (c) 2008, 2009 Yahoo!, Inc.
3183631Sdanger * All rights reserved.
4183631Sdanger *
5183631Sdanger * Redistribution and use in source and binary forms, with or without
6183631Sdanger * modification, are permitted provided that the following conditions
7183631Sdanger * are met:
8183631Sdanger * 1. Redistributions of source code must retain the above copyright
9183631Sdanger *    notice, this list of conditions and the following disclaimer.
10183631Sdanger * 2. Redistributions in binary form must reproduce the above copyright
11183631Sdanger *    notice, this list of conditions and the following disclaimer in the
12183631Sdanger *    documentation and/or other materials provided with the distribution.
13183631Sdanger * 3. The names of the authors may not be used to endorse or promote
14183631Sdanger *    products derived from this software without specific prior written
15183631Sdanger *    permission.
16183631Sdanger *
17183631Sdanger * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18183631Sdanger * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19183631Sdanger * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20183631Sdanger * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21183631Sdanger * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22183631Sdanger * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23183631Sdanger * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24183631Sdanger * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25183631Sdanger * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26183631Sdanger * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27278651Sgjb * SUCH DAMAGE.
28183631Sdanger *
29183631Sdanger * $FreeBSD: head/usr.sbin/mfiutil/mfi_evt.c 196200 2009-08-13 23:18:45Z scottl $
30183631Sdanger */
31183631Sdanger
32183631Sdanger#include <sys/types.h>
33183631Sdanger#include <sys/errno.h>
34278651Sgjb#include <err.h>
35183631Sdanger//#include <libutil.h>
36183631Sdanger#include <stdio.h>
37183631Sdanger#include <stdlib.h>
38183631Sdanger#include <strings.h>
39183631Sdanger#include <time.h>
40183631Sdanger#include <unistd.h>
41183631Sdanger#include "mfiutil.h"
42183631Sdanger
43183631Sdangerstatic int
44183631Sdangermfi_event_get_info(int fd, struct mfi_evt_log_state *info, uint8_t *statusp)
45183631Sdanger{
46183631Sdanger
47183631Sdanger	return (mfi_dcmd_command(fd, MFI_DCMD_CTRL_EVENT_GETINFO, info,
48183631Sdanger	    sizeof(struct mfi_evt_log_state), NULL, 0, statusp));
49183631Sdanger}
50183631Sdanger
51183631Sdangerstatic int
52183631Sdangermfi_get_events(int fd, struct mfi_evt_list *list, int num_events,
53183631Sdanger    union mfi_evt filter, uint32_t start_seq, uint8_t *statusp)
54183631Sdanger{
55183631Sdanger	uint32_t mbox[2];
56183631Sdanger	size_t size;
57183631Sdanger
58183631Sdanger	mbox[0] = start_seq;
59183631Sdanger	mbox[1] = filter.word;
60183631Sdanger	size = sizeof(struct mfi_evt_list) + sizeof(struct mfi_evt_detail) *
61183631Sdanger	    (num_events - 1);
62183631Sdanger	return (mfi_dcmd_command(fd, MFI_DCMD_CTRL_EVENT_GET, list, size,
63183631Sdanger	    (uint8_t *)&mbox, sizeof(mbox), statusp));
64183631Sdanger}
65183631Sdanger
66183631Sdangerstatic int
67183631Sdangershow_logstate(int ac, char **av)
68183631Sdanger{
69183631Sdanger	struct mfi_evt_log_state info;
70183631Sdanger	int fd;
71183631Sdanger
72183631Sdanger	if (ac != 1) {
73183631Sdanger		warnx("show logstate: extra arguments");
74183631Sdanger		return (EINVAL);
75	}
76
77	fd = mfi_open(mfi_unit);
78	if (fd < 0) {
79		warn("mfi_open");
80		return (errno);
81	}
82
83	if (mfi_event_get_info(fd, &info, NULL) < 0) {
84		warn("Failed to get event log info");
85		return (errno);
86	}
87
88	printf("mfi%d Event Log Sequence Numbers:\n", mfi_unit);
89	printf("  Newest Seq #: %u\n", info.newest_seq_num);
90	printf("  Oldest Seq #: %u\n", info.oldest_seq_num);
91	printf("   Clear Seq #: %u\n", info.clear_seq_num);
92	printf("Shutdown Seq #: %u\n", info.shutdown_seq_num);
93	printf("    Boot Seq #: %u\n", info.boot_seq_num);
94
95	close(fd);
96
97	return (0);
98}
99MFI_COMMAND(show, logstate, show_logstate);
100
101static int
102parse_seq(struct mfi_evt_log_state *info, char *arg, uint32_t *seq)
103{
104	char *cp;
105	long val;
106
107	if (strcasecmp(arg, "newest") == 0) {
108		*seq = info->newest_seq_num;
109		return (0);
110	}
111	if (strcasecmp(arg, "oldest") == 0) {
112		*seq = info->oldest_seq_num;
113		return (0);
114	}
115	if (strcasecmp(arg, "clear") == 0) {
116		*seq = info->clear_seq_num;
117		return (0);
118	}
119	if (strcasecmp(arg, "shutdown") == 0) {
120		*seq = info->shutdown_seq_num;
121		return (0);
122	}
123	if (strcasecmp(arg, "boot") == 0) {
124		*seq = info->boot_seq_num;
125		return (0);
126	}
127	val = strtol(arg, &cp, 0);
128	if (*cp != '\0' || val < 0) {
129		errno = EINVAL;
130		return (-1);
131	}
132	*seq = val;
133	return (0);
134}
135
136static int
137parse_locale(char *arg, uint16_t *locale)
138{
139	char *cp;
140	long val;
141
142	if (strncasecmp(arg, "vol", 3) == 0 || strcasecmp(arg, "ld") == 0) {
143		*locale = MFI_EVT_LOCALE_LD;
144		return (0);
145	}
146	if (strncasecmp(arg, "drive", 5) == 0 || strcasecmp(arg, "pd") == 0) {
147		*locale = MFI_EVT_LOCALE_PD;
148		return (0);
149	}
150	if (strncasecmp(arg, "encl", 4) == 0) {
151		*locale = MFI_EVT_LOCALE_ENCL;
152		return (0);
153	}
154	if (strncasecmp(arg, "batt", 4) == 0 ||
155	    strncasecmp(arg, "bbu", 3) == 0) {
156		*locale = MFI_EVT_LOCALE_BBU;
157		return (0);
158	}
159	if (strcasecmp(arg, "sas") == 0) {
160		*locale = MFI_EVT_LOCALE_SAS;
161		return (0);
162	}
163	if (strcasecmp(arg, "ctrl") == 0 || strncasecmp(arg, "cont", 4) == 0) {
164		*locale = MFI_EVT_LOCALE_CTRL;
165		return (0);
166	}
167	if (strcasecmp(arg, "config") == 0) {
168		*locale = MFI_EVT_LOCALE_CONFIG;
169		return (0);
170	}
171	if (strcasecmp(arg, "cluster") == 0) {
172		*locale = MFI_EVT_LOCALE_CLUSTER;
173		return (0);
174	}
175	if (strcasecmp(arg, "all") == 0) {
176		*locale = MFI_EVT_LOCALE_ALL;
177		return (0);
178	}
179	val = strtol(arg, &cp, 0);
180	if (*cp != '\0' || val < 0 || val > 0xffff) {
181		errno = EINVAL;
182		return (-1);
183	}
184	*locale = val;
185	return (0);
186}
187
188static int
189parse_class(char *arg, int8_t *class)
190{
191	char *cp;
192	long val;
193
194	if (strcasecmp(arg, "debug") == 0) {
195		*class = MFI_EVT_CLASS_DEBUG;
196		return (0);
197	}
198	if (strncasecmp(arg, "prog", 4) == 0) {
199		*class = MFI_EVT_CLASS_PROGRESS;
200		return (0);
201	}
202	if (strncasecmp(arg, "info", 4) == 0) {
203		*class = MFI_EVT_CLASS_INFO;
204		return (0);
205	}
206	if (strncasecmp(arg, "warn", 4) == 0) {
207		*class = MFI_EVT_CLASS_WARNING;
208		return (0);
209	}
210	if (strncasecmp(arg, "crit", 4) == 0) {
211		*class = MFI_EVT_CLASS_CRITICAL;
212		return (0);
213	}
214	if (strcasecmp(arg, "fatal") == 0) {
215		*class = MFI_EVT_CLASS_FATAL;
216		return (0);
217	}
218	if (strcasecmp(arg, "dead") == 0) {
219		*class = MFI_EVT_CLASS_DEAD;
220		return (0);
221	}
222	val = strtol(arg, &cp, 0);
223	if (*cp != '\0' || val < -128 || val > 127) {
224		errno = EINVAL;
225		return (-1);
226	}
227	*class = val;
228	return (0);
229}
230
231/*
232 * The timestamp is the number of seconds since 00:00 Jan 1, 2000.  If
233 * the bits in 24-31 are all set, then it is the number of seconds since
234 * boot.
235 */
236static const char *
237format_timestamp(uint32_t timestamp)
238{
239	static char buffer[32];
240	static time_t base;
241	time_t t;
242	struct tm tm;
243
244	if ((timestamp & 0xff000000) == 0xff000000) {
245		snprintf(buffer, sizeof(buffer), "boot + %us", timestamp &
246		    0x00ffffff);
247		return (buffer);
248	}
249
250	if (base == 0) {
251		/* Compute 00:00 Jan 1, 2000 offset. */
252		bzero(&tm, sizeof(tm));
253		tm.tm_mday = 1;
254		tm.tm_year = (2000 - 1900);
255		base = mktime(&tm);
256	}
257	if (base == -1) {
258		snprintf(buffer, sizeof(buffer), "%us", timestamp);
259		return (buffer);
260	}
261	t = base + timestamp;
262	strftime(buffer, sizeof(buffer), "%+", localtime(&t));
263	return (buffer);
264}
265
266static const char *
267format_locale(uint16_t locale)
268{
269	static char buffer[8];
270
271	switch (locale) {
272	case MFI_EVT_LOCALE_LD:
273		return ("VOLUME");
274	case MFI_EVT_LOCALE_PD:
275		return ("DRIVE");
276	case MFI_EVT_LOCALE_ENCL:
277		return ("ENCL");
278	case MFI_EVT_LOCALE_BBU:
279		return ("BATTERY");
280	case MFI_EVT_LOCALE_SAS:
281		return ("SAS");
282	case MFI_EVT_LOCALE_CTRL:
283		return ("CTRL");
284	case MFI_EVT_LOCALE_CONFIG:
285		return ("CONFIG");
286	case MFI_EVT_LOCALE_CLUSTER:
287		return ("CLUSTER");
288	case MFI_EVT_LOCALE_ALL:
289		return ("ALL");
290	default:
291		snprintf(buffer, sizeof(buffer), "0x%04x", locale);
292		return (buffer);
293	}
294}
295
296static const char *
297format_class(int8_t class)
298{
299	static char buffer[6];
300
301	switch (class) {
302	case MFI_EVT_CLASS_DEBUG:
303		return ("debug");
304	case MFI_EVT_CLASS_PROGRESS:
305		return ("progress");
306	case MFI_EVT_CLASS_INFO:
307		return ("info");
308	case MFI_EVT_CLASS_WARNING:
309		return ("WARN");
310	case MFI_EVT_CLASS_CRITICAL:
311		return ("CRIT");
312	case MFI_EVT_CLASS_FATAL:
313		return ("FATAL");
314	case MFI_EVT_CLASS_DEAD:
315		return ("DEAD");
316	default:
317		snprintf(buffer, sizeof(buffer), "%d", class);
318		return (buffer);
319	}
320}
321
322/* Simulates %D from kernel printf(9). */
323static void
324simple_hex(void *ptr, size_t length, const char *separator)
325{
326	unsigned char *cp;
327	u_int i;
328
329	if (length == 0)
330		return;
331	cp = ptr;
332	printf("%02x", cp[0]);
333	for (i = 1; i < length; i++)
334		printf("%s%02x", separator, cp[i]);
335}
336
337static const char *
338pdrive_location(struct mfi_evt_pd *pd)
339{
340	static char buffer[16];
341
342	if (pd->enclosure_index == 0)
343		snprintf(buffer, sizeof(buffer), "%02d(s%d)", pd->device_id,
344		    pd->slot_number);
345	else
346		snprintf(buffer, sizeof(buffer), "%02d(e%d/s%d)", pd->device_id,
347		    pd->enclosure_index, pd->slot_number);
348	return (buffer);
349}
350
351static const char *
352volume_name(int fd, struct mfi_evt_ld *ld)
353{
354
355	return (mfi_volume_name(fd, ld->target_id));
356}
357
358/* Ripped from sys/dev/mfi/mfi.c. */
359static void
360mfi_decode_evt(int fd, struct mfi_evt_detail *detail, int verbose)
361{
362
363	printf("%5d (%s/%s/%s) - ", detail->seq, format_timestamp(detail->time),
364	    format_locale(detail->class.members.locale),
365	    format_class(detail->class.members.class));
366	switch (detail->arg_type) {
367	case MR_EVT_ARGS_NONE:
368		break;
369	case MR_EVT_ARGS_CDB_SENSE:
370		if (verbose) {
371			printf("PD %s CDB ",
372			    pdrive_location(&detail->args.cdb_sense.pd)
373			    );
374			simple_hex(detail->args.cdb_sense.cdb,
375			    detail->args.cdb_sense.cdb_len, ":");
376			printf(" Sense ");
377			simple_hex(detail->args.cdb_sense.sense,
378			    detail->args.cdb_sense.sense_len, ":");
379			printf(":\n ");
380		}
381		break;
382	case MR_EVT_ARGS_LD:
383		printf("VOL %s event: ", volume_name(fd, &detail->args.ld));
384		break;
385	case MR_EVT_ARGS_LD_COUNT:
386		printf("VOL %s", volume_name(fd, &detail->args.ld_count.ld));
387		if (verbose) {
388			printf(" count %lld: ",
389			    (long long)detail->args.ld_count.count);
390		}
391		printf(": ");
392		break;
393	case MR_EVT_ARGS_LD_LBA:
394		printf("VOL %s", volume_name(fd, &detail->args.ld_count.ld));
395		if (verbose) {
396			printf(" lba %lld",
397			    (long long)detail->args.ld_lba.lba);
398		}
399		printf(": ");
400		break;
401	case MR_EVT_ARGS_LD_OWNER:
402		printf("VOL %s", volume_name(fd, &detail->args.ld_count.ld));
403		if (verbose) {
404			printf(" owner changed: prior %d, new %d",
405			    detail->args.ld_owner.pre_owner,
406			    detail->args.ld_owner.new_owner);
407		}
408		printf(": ");
409		break;
410	case MR_EVT_ARGS_LD_LBA_PD_LBA:
411		printf("VOL %s", volume_name(fd, &detail->args.ld_count.ld));
412		if (verbose) {
413			printf(" lba %lld, physical drive PD %s lba %lld",
414			    (long long)detail->args.ld_lba_pd_lba.ld_lba,
415			    pdrive_location(&detail->args.ld_lba_pd_lba.pd),
416			    (long long)detail->args.ld_lba_pd_lba.pd_lba);
417		}
418		printf(": ");
419		break;
420	case MR_EVT_ARGS_LD_PROG:
421		printf("VOL %s", volume_name(fd, &detail->args.ld_prog.ld));
422		if (verbose) {
423			printf(" progress %d%% in %ds",
424			    detail->args.ld_prog.prog.progress/655,
425			    detail->args.ld_prog.prog.elapsed_seconds);
426		}
427		printf(": ");
428		break;
429	case MR_EVT_ARGS_LD_STATE:
430		printf("VOL %s", volume_name(fd, &detail->args.ld_prog.ld));
431		if (verbose) {
432			printf(" state prior %s new %s",
433			    mfi_ldstate(detail->args.ld_state.prev_state),
434			    mfi_ldstate(detail->args.ld_state.new_state));
435		}
436		printf(": ");
437		break;
438	case MR_EVT_ARGS_LD_STRIP:
439		printf("VOL %s", volume_name(fd, &detail->args.ld_prog.ld));
440		if (verbose) {
441			printf(" strip %lld",
442			    (long long)detail->args.ld_strip.strip);
443		}
444		printf(": ");
445		break;
446	case MR_EVT_ARGS_PD:
447		if (verbose) {
448			printf("PD %s event: ",
449			    pdrive_location(&detail->args.pd));
450		}
451		break;
452	case MR_EVT_ARGS_PD_ERR:
453		if (verbose) {
454			printf("PD %s err %d: ",
455			    pdrive_location(&detail->args.pd_err.pd),
456			    detail->args.pd_err.err);
457		}
458		break;
459	case MR_EVT_ARGS_PD_LBA:
460		if (verbose) {
461			printf("PD %s lba %lld: ",
462			    pdrive_location(&detail->args.pd_lba.pd),
463			    (long long)detail->args.pd_lba.lba);
464		}
465		break;
466	case MR_EVT_ARGS_PD_LBA_LD:
467		if (verbose) {
468			printf("PD %s lba %lld VOL %s: ",
469			    pdrive_location(&detail->args.pd_lba_ld.pd),
470			    (long long)detail->args.pd_lba.lba,
471			    volume_name(fd, &detail->args.pd_lba_ld.ld));
472		}
473		break;
474	case MR_EVT_ARGS_PD_PROG:
475		if (verbose) {
476			printf("PD %s progress %d%% seconds %ds: ",
477			    pdrive_location(&detail->args.pd_prog.pd),
478			    detail->args.pd_prog.prog.progress/655,
479			    detail->args.pd_prog.prog.elapsed_seconds);
480		}
481		break;
482	case MR_EVT_ARGS_PD_STATE:
483		if (verbose) {
484			printf("PD %s state prior %s new %s: ",
485			    pdrive_location(&detail->args.pd_prog.pd),
486			    mfi_pdstate(detail->args.pd_state.prev_state),
487			    mfi_pdstate(detail->args.pd_state.new_state));
488		}
489		break;
490	case MR_EVT_ARGS_PCI:
491		if (verbose) {
492			printf("PCI 0x%04x 0x%04x 0x%04x 0x%04x: ",
493			    detail->args.pci.venderId,
494			    detail->args.pci.deviceId,
495			    detail->args.pci.subVenderId,
496			    detail->args.pci.subDeviceId);
497		}
498		break;
499	case MR_EVT_ARGS_RATE:
500		if (verbose) {
501			printf("Rebuild rate %d: ", detail->args.rate);
502		}
503		break;
504	case MR_EVT_ARGS_TIME:
505		if (verbose) {
506			printf("Adapter time %s; %d seconds since power on: ",
507			    format_timestamp(detail->args.time.rtc),
508			    detail->args.time.elapsedSeconds);
509		}
510		break;
511	case MR_EVT_ARGS_ECC:
512		if (verbose) {
513			printf("Adapter ECC %x,%x: %s: ",
514			    detail->args.ecc.ecar,
515			    detail->args.ecc.elog,
516			    detail->args.ecc.str);
517		}
518		break;
519	default:
520		if (verbose) {
521			printf("Type %d: ", detail->arg_type);
522		}
523		break;
524	}
525	printf("%s\n", detail->description);
526}
527
528static int
529show_events(int ac, char **av)
530{
531	struct mfi_evt_log_state info;
532	struct mfi_evt_list *list;
533	union mfi_evt filter;
534	long val;
535	char *cp;
536	ssize_t size;
537	uint32_t seq, start, stop;
538	uint8_t status;
539	int ch, fd, num_events, verbose;
540	u_int i;
541
542	fd = mfi_open(mfi_unit);
543	if (fd < 0) {
544		warn("mfi_open");
545		return (errno);
546	}
547
548	if (mfi_event_get_info(fd, &info, NULL) < 0) {
549		warn("Failed to get event log info");
550		return (errno);
551	}
552
553	/* Default settings. */
554	num_events = 15;
555	filter.members.reserved = 0;
556	filter.members.locale = MFI_EVT_LOCALE_ALL;
557	filter.members.class = MFI_EVT_CLASS_WARNING;
558	start = info.boot_seq_num;
559	stop = info.newest_seq_num;
560	verbose = 0;
561
562	/* Parse any options. */
563	optind = 1;
564	while ((ch = getopt(ac, av, "c:l:n:v")) != -1) {
565		switch (ch) {
566		case 'c':
567			if (parse_class(optarg, &filter.members.class) < 0) {
568				warn("Error parsing event class");
569				return (errno);
570			}
571			break;
572		case 'l':
573			if (parse_locale(optarg, &filter.members.locale) < 0) {
574				warn("Error parsing event locale");
575				return (errno);
576			}
577			break;
578		case 'n':
579			val = strtol(optarg, &cp, 0);
580			if (*cp != '\0' || val <= 0) {
581				warnx("Invalid event count");
582				return (EINVAL);
583			}
584			num_events = val;
585			break;
586		case 'v':
587			verbose = 1;
588			break;
589		case '?':
590		default:
591			return (EINVAL);
592		}
593	}
594	ac -= optind;
595	av += optind;
596
597	/* Determine buffer size and validate it. */
598	size = sizeof(struct mfi_evt_list) + sizeof(struct mfi_evt_detail) *
599	    (num_events - 1);
600	if (size > getpagesize()) {
601		warnx("Event count is too high");
602		return (EINVAL);
603	}
604
605	/* Handle optional start and stop sequence numbers. */
606	if (ac > 2) {
607		warnx("show events: extra arguments");
608		return (EINVAL);
609	}
610	if (ac > 0 && parse_seq(&info, av[0], &start) < 0) {
611		warn("Error parsing starting sequence number");
612		return (errno);
613	}
614	if (ac > 1 && parse_seq(&info, av[1], &stop) < 0) {
615		warn("Error parsing ending sequence number");
616		return (errno);
617	}
618
619	list = malloc(size);
620	for (seq = start;;) {
621		if (mfi_get_events(fd, list, num_events, filter, seq,
622		    &status) < 0) {
623			warn("Failed to fetch events");
624			return (errno);
625		}
626		if (status == MFI_STAT_NOT_FOUND) {
627			if (seq == start)
628				warnx("No matching events found");
629			break;
630		}
631		if (status != MFI_STAT_OK) {
632			warnx("Error fetching events: %s", mfi_status(status));
633			return (EIO);
634		}
635
636		for (i = 0; i < list->count; i++) {
637			/*
638			 * If this event is newer than 'stop_seq' then
639			 * break out of the loop.  Note that the log
640			 * is a circular buffer so we have to handle
641			 * the case that our stop point is earlier in
642			 * the buffer than our start point.
643			 */
644			if (list->event[i].seq >= stop) {
645				if (start <= stop)
646					break;
647				else if (list->event[i].seq < start)
648					break;
649			}
650			mfi_decode_evt(fd, &list->event[i], verbose);
651		}
652
653		/*
654		 * XXX: If the event's seq # is the end of the buffer
655		 * then this probably won't do the right thing.  We
656		 * need to know the size of the buffer somehow.
657		 */
658		seq = list->event[list->count - 1].seq + 1;
659
660	}
661
662	free(list);
663	close(fd);
664
665	return (0);
666}
667MFI_COMMAND(show, events, show_events);
668