mfi_evt.c revision 222899
1/*-
2 * Copyright (c) 2008, 2009 Yahoo!, Inc.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
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
15 *    permission.
16 *
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
27 * SUCH DAMAGE.
28 *
29 * $FreeBSD: head/usr.sbin/mfiutil/mfi_evt.c 222899 2011-06-09 19:52:28Z bz $
30 */
31
32#include <sys/types.h>
33#include <sys/errno.h>
34#include <err.h>
35#include <stdio.h>
36#include <stdlib.h>
37#include <strings.h>
38#include <time.h>
39#include <unistd.h>
40#include "mfiutil.h"
41
42static int
43mfi_event_get_info(int fd, struct mfi_evt_log_state *info, uint8_t *statusp)
44{
45
46	return (mfi_dcmd_command(fd, MFI_DCMD_CTRL_EVENT_GETINFO, info,
47	    sizeof(struct mfi_evt_log_state), NULL, 0, statusp));
48}
49
50static int
51mfi_get_events(int fd, struct mfi_evt_list *list, int num_events,
52    union mfi_evt filter, uint32_t start_seq, uint8_t *statusp)
53{
54	uint32_t mbox[2];
55	size_t size;
56
57	mbox[0] = start_seq;
58	mbox[1] = filter.word;
59	size = sizeof(struct mfi_evt_list) + sizeof(struct mfi_evt_detail) *
60	    (num_events - 1);
61	return (mfi_dcmd_command(fd, MFI_DCMD_CTRL_EVENT_GET, list, size,
62	    (uint8_t *)&mbox, sizeof(mbox), statusp));
63}
64
65static int
66show_logstate(int ac, char **av)
67{
68	struct mfi_evt_log_state info;
69	int error, fd;
70
71	if (ac != 1) {
72		warnx("show logstate: extra arguments");
73		return (EINVAL);
74	}
75
76	fd = mfi_open(mfi_unit);
77	if (fd < 0) {
78		error = errno;
79		warn("mfi_open");
80		return (error);
81	}
82
83	if (mfi_event_get_info(fd, &info, NULL) < 0) {
84		error = errno;
85		warn("Failed to get event log info");
86		close(fd);
87		return (error);
88	}
89
90	printf("mfi%d Event Log Sequence Numbers:\n", mfi_unit);
91	printf("  Newest Seq #: %u\n", info.newest_seq_num);
92	printf("  Oldest Seq #: %u\n", info.oldest_seq_num);
93	printf("   Clear Seq #: %u\n", info.clear_seq_num);
94	printf("Shutdown Seq #: %u\n", info.shutdown_seq_num);
95	printf("    Boot Seq #: %u\n", info.boot_seq_num);
96
97	close(fd);
98
99	return (0);
100}
101MFI_COMMAND(show, logstate, show_logstate);
102
103static int
104parse_seq(struct mfi_evt_log_state *info, char *arg, uint32_t *seq)
105{
106	char *cp;
107	long val;
108
109	if (strcasecmp(arg, "newest") == 0) {
110		*seq = info->newest_seq_num;
111		return (0);
112	}
113	if (strcasecmp(arg, "oldest") == 0) {
114		*seq = info->oldest_seq_num;
115		return (0);
116	}
117	if (strcasecmp(arg, "clear") == 0) {
118		*seq = info->clear_seq_num;
119		return (0);
120	}
121	if (strcasecmp(arg, "shutdown") == 0) {
122		*seq = info->shutdown_seq_num;
123		return (0);
124	}
125	if (strcasecmp(arg, "boot") == 0) {
126		*seq = info->boot_seq_num;
127		return (0);
128	}
129	val = strtol(arg, &cp, 0);
130	if (*cp != '\0' || val < 0) {
131		errno = EINVAL;
132		return (-1);
133	}
134	*seq = val;
135	return (0);
136}
137
138static int
139parse_locale(char *arg, uint16_t *locale)
140{
141	char *cp;
142	long val;
143
144	if (strncasecmp(arg, "vol", 3) == 0 || strcasecmp(arg, "ld") == 0) {
145		*locale = MFI_EVT_LOCALE_LD;
146		return (0);
147	}
148	if (strncasecmp(arg, "drive", 5) == 0 || strcasecmp(arg, "pd") == 0) {
149		*locale = MFI_EVT_LOCALE_PD;
150		return (0);
151	}
152	if (strncasecmp(arg, "encl", 4) == 0) {
153		*locale = MFI_EVT_LOCALE_ENCL;
154		return (0);
155	}
156	if (strncasecmp(arg, "batt", 4) == 0 ||
157	    strncasecmp(arg, "bbu", 3) == 0) {
158		*locale = MFI_EVT_LOCALE_BBU;
159		return (0);
160	}
161	if (strcasecmp(arg, "sas") == 0) {
162		*locale = MFI_EVT_LOCALE_SAS;
163		return (0);
164	}
165	if (strcasecmp(arg, "ctrl") == 0 || strncasecmp(arg, "cont", 4) == 0) {
166		*locale = MFI_EVT_LOCALE_CTRL;
167		return (0);
168	}
169	if (strcasecmp(arg, "config") == 0) {
170		*locale = MFI_EVT_LOCALE_CONFIG;
171		return (0);
172	}
173	if (strcasecmp(arg, "cluster") == 0) {
174		*locale = MFI_EVT_LOCALE_CLUSTER;
175		return (0);
176	}
177	if (strcasecmp(arg, "all") == 0) {
178		*locale = MFI_EVT_LOCALE_ALL;
179		return (0);
180	}
181	val = strtol(arg, &cp, 0);
182	if (*cp != '\0' || val < 0 || val > 0xffff) {
183		errno = EINVAL;
184		return (-1);
185	}
186	*locale = val;
187	return (0);
188}
189
190static int
191parse_class(char *arg, int8_t *class)
192{
193	char *cp;
194	long val;
195
196	if (strcasecmp(arg, "debug") == 0) {
197		*class = MFI_EVT_CLASS_DEBUG;
198		return (0);
199	}
200	if (strncasecmp(arg, "prog", 4) == 0) {
201		*class = MFI_EVT_CLASS_PROGRESS;
202		return (0);
203	}
204	if (strncasecmp(arg, "info", 4) == 0) {
205		*class = MFI_EVT_CLASS_INFO;
206		return (0);
207	}
208	if (strncasecmp(arg, "warn", 4) == 0) {
209		*class = MFI_EVT_CLASS_WARNING;
210		return (0);
211	}
212	if (strncasecmp(arg, "crit", 4) == 0) {
213		*class = MFI_EVT_CLASS_CRITICAL;
214		return (0);
215	}
216	if (strcasecmp(arg, "fatal") == 0) {
217		*class = MFI_EVT_CLASS_FATAL;
218		return (0);
219	}
220	if (strcasecmp(arg, "dead") == 0) {
221		*class = MFI_EVT_CLASS_DEAD;
222		return (0);
223	}
224	val = strtol(arg, &cp, 0);
225	if (*cp != '\0' || val < -128 || val > 127) {
226		errno = EINVAL;
227		return (-1);
228	}
229	*class = val;
230	return (0);
231}
232
233/*
234 * The timestamp is the number of seconds since 00:00 Jan 1, 2000.  If
235 * the bits in 24-31 are all set, then it is the number of seconds since
236 * boot.
237 */
238static const char *
239format_timestamp(uint32_t timestamp)
240{
241	static char buffer[32];
242	static time_t base;
243	time_t t;
244	struct tm tm;
245
246	if ((timestamp & 0xff000000) == 0xff000000) {
247		snprintf(buffer, sizeof(buffer), "boot + %us", timestamp &
248		    0x00ffffff);
249		return (buffer);
250	}
251
252	if (base == 0) {
253		/* Compute 00:00 Jan 1, 2000 offset. */
254		bzero(&tm, sizeof(tm));
255		tm.tm_mday = 1;
256		tm.tm_year = (2000 - 1900);
257		base = mktime(&tm);
258	}
259	if (base == -1) {
260		snprintf(buffer, sizeof(buffer), "%us", timestamp);
261		return (buffer);
262	}
263	t = base + timestamp;
264	strftime(buffer, sizeof(buffer), "%+", localtime(&t));
265	return (buffer);
266}
267
268static const char *
269format_locale(uint16_t locale)
270{
271	static char buffer[8];
272
273	switch (locale) {
274	case MFI_EVT_LOCALE_LD:
275		return ("VOLUME");
276	case MFI_EVT_LOCALE_PD:
277		return ("DRIVE");
278	case MFI_EVT_LOCALE_ENCL:
279		return ("ENCL");
280	case MFI_EVT_LOCALE_BBU:
281		return ("BATTERY");
282	case MFI_EVT_LOCALE_SAS:
283		return ("SAS");
284	case MFI_EVT_LOCALE_CTRL:
285		return ("CTRL");
286	case MFI_EVT_LOCALE_CONFIG:
287		return ("CONFIG");
288	case MFI_EVT_LOCALE_CLUSTER:
289		return ("CLUSTER");
290	case MFI_EVT_LOCALE_ALL:
291		return ("ALL");
292	default:
293		snprintf(buffer, sizeof(buffer), "0x%04x", locale);
294		return (buffer);
295	}
296}
297
298static const char *
299format_class(int8_t class)
300{
301	static char buffer[6];
302
303	switch (class) {
304	case MFI_EVT_CLASS_DEBUG:
305		return ("debug");
306	case MFI_EVT_CLASS_PROGRESS:
307		return ("progress");
308	case MFI_EVT_CLASS_INFO:
309		return ("info");
310	case MFI_EVT_CLASS_WARNING:
311		return ("WARN");
312	case MFI_EVT_CLASS_CRITICAL:
313		return ("CRIT");
314	case MFI_EVT_CLASS_FATAL:
315		return ("FATAL");
316	case MFI_EVT_CLASS_DEAD:
317		return ("DEAD");
318	default:
319		snprintf(buffer, sizeof(buffer), "%d", class);
320		return (buffer);
321	}
322}
323
324/* Simulates %D from kernel printf(9). */
325static void
326simple_hex(void *ptr, size_t length, const char *separator)
327{
328	unsigned char *cp;
329	u_int i;
330
331	if (length == 0)
332		return;
333	cp = ptr;
334	printf("%02x", cp[0]);
335	for (i = 1; i < length; i++)
336		printf("%s%02x", separator, cp[i]);
337}
338
339static const char *
340pdrive_location(struct mfi_evt_pd *pd)
341{
342	static char buffer[16];
343
344	if (pd->enclosure_index == 0)
345		snprintf(buffer, sizeof(buffer), "%02d(s%d)", pd->device_id,
346		    pd->slot_number);
347	else
348		snprintf(buffer, sizeof(buffer), "%02d(e%d/s%d)", pd->device_id,
349		    pd->enclosure_index, pd->slot_number);
350	return (buffer);
351}
352
353static const char *
354volume_name(int fd, struct mfi_evt_ld *ld)
355{
356
357	return (mfi_volume_name(fd, ld->target_id));
358}
359
360/* Ripped from sys/dev/mfi/mfi.c. */
361static void
362mfi_decode_evt(int fd, struct mfi_evt_detail *detail, int verbose)
363{
364
365	printf("%5d (%s/%s/%s) - ", detail->seq, format_timestamp(detail->time),
366	    format_locale(detail->evt_class.members.locale),
367	    format_class(detail->evt_class.members.evt_class));
368	switch (detail->arg_type) {
369	case MR_EVT_ARGS_NONE:
370		break;
371	case MR_EVT_ARGS_CDB_SENSE:
372		if (verbose) {
373			printf("PD %s CDB ",
374			    pdrive_location(&detail->args.cdb_sense.pd)
375			    );
376			simple_hex(detail->args.cdb_sense.cdb,
377			    detail->args.cdb_sense.cdb_len, ":");
378			printf(" Sense ");
379			simple_hex(detail->args.cdb_sense.sense,
380			    detail->args.cdb_sense.sense_len, ":");
381			printf(":\n ");
382		}
383		break;
384	case MR_EVT_ARGS_LD:
385		printf("VOL %s event: ", volume_name(fd, &detail->args.ld));
386		break;
387	case MR_EVT_ARGS_LD_COUNT:
388		printf("VOL %s", volume_name(fd, &detail->args.ld_count.ld));
389		if (verbose) {
390			printf(" count %lld: ",
391			    (long long)detail->args.ld_count.count);
392		}
393		printf(": ");
394		break;
395	case MR_EVT_ARGS_LD_LBA:
396		printf("VOL %s", volume_name(fd, &detail->args.ld_count.ld));
397		if (verbose) {
398			printf(" lba %lld",
399			    (long long)detail->args.ld_lba.lba);
400		}
401		printf(": ");
402		break;
403	case MR_EVT_ARGS_LD_OWNER:
404		printf("VOL %s", volume_name(fd, &detail->args.ld_count.ld));
405		if (verbose) {
406			printf(" owner changed: prior %d, new %d",
407			    detail->args.ld_owner.pre_owner,
408			    detail->args.ld_owner.new_owner);
409		}
410		printf(": ");
411		break;
412	case MR_EVT_ARGS_LD_LBA_PD_LBA:
413		printf("VOL %s", volume_name(fd, &detail->args.ld_count.ld));
414		if (verbose) {
415			printf(" lba %lld, physical drive PD %s lba %lld",
416			    (long long)detail->args.ld_lba_pd_lba.ld_lba,
417			    pdrive_location(&detail->args.ld_lba_pd_lba.pd),
418			    (long long)detail->args.ld_lba_pd_lba.pd_lba);
419		}
420		printf(": ");
421		break;
422	case MR_EVT_ARGS_LD_PROG:
423		printf("VOL %s", volume_name(fd, &detail->args.ld_prog.ld));
424		if (verbose) {
425			printf(" progress %d%% in %ds",
426			    detail->args.ld_prog.prog.progress/655,
427			    detail->args.ld_prog.prog.elapsed_seconds);
428		}
429		printf(": ");
430		break;
431	case MR_EVT_ARGS_LD_STATE:
432		printf("VOL %s", volume_name(fd, &detail->args.ld_prog.ld));
433		if (verbose) {
434			printf(" state prior %s new %s",
435			    mfi_ldstate(detail->args.ld_state.prev_state),
436			    mfi_ldstate(detail->args.ld_state.new_state));
437		}
438		printf(": ");
439		break;
440	case MR_EVT_ARGS_LD_STRIP:
441		printf("VOL %s", volume_name(fd, &detail->args.ld_prog.ld));
442		if (verbose) {
443			printf(" strip %lld",
444			    (long long)detail->args.ld_strip.strip);
445		}
446		printf(": ");
447		break;
448	case MR_EVT_ARGS_PD:
449		if (verbose) {
450			printf("PD %s event: ",
451			    pdrive_location(&detail->args.pd));
452		}
453		break;
454	case MR_EVT_ARGS_PD_ERR:
455		if (verbose) {
456			printf("PD %s err %d: ",
457			    pdrive_location(&detail->args.pd_err.pd),
458			    detail->args.pd_err.err);
459		}
460		break;
461	case MR_EVT_ARGS_PD_LBA:
462		if (verbose) {
463			printf("PD %s lba %lld: ",
464			    pdrive_location(&detail->args.pd_lba.pd),
465			    (long long)detail->args.pd_lba.lba);
466		}
467		break;
468	case MR_EVT_ARGS_PD_LBA_LD:
469		if (verbose) {
470			printf("PD %s lba %lld VOL %s: ",
471			    pdrive_location(&detail->args.pd_lba_ld.pd),
472			    (long long)detail->args.pd_lba.lba,
473			    volume_name(fd, &detail->args.pd_lba_ld.ld));
474		}
475		break;
476	case MR_EVT_ARGS_PD_PROG:
477		if (verbose) {
478			printf("PD %s progress %d%% seconds %ds: ",
479			    pdrive_location(&detail->args.pd_prog.pd),
480			    detail->args.pd_prog.prog.progress/655,
481			    detail->args.pd_prog.prog.elapsed_seconds);
482		}
483		break;
484	case MR_EVT_ARGS_PD_STATE:
485		if (verbose) {
486			printf("PD %s state prior %s new %s: ",
487			    pdrive_location(&detail->args.pd_prog.pd),
488			    mfi_pdstate(detail->args.pd_state.prev_state),
489			    mfi_pdstate(detail->args.pd_state.new_state));
490		}
491		break;
492	case MR_EVT_ARGS_PCI:
493		if (verbose) {
494			printf("PCI 0x%04x 0x%04x 0x%04x 0x%04x: ",
495			    detail->args.pci.venderId,
496			    detail->args.pci.deviceId,
497			    detail->args.pci.subVenderId,
498			    detail->args.pci.subDeviceId);
499		}
500		break;
501	case MR_EVT_ARGS_RATE:
502		if (verbose) {
503			printf("Rebuild rate %d: ", detail->args.rate);
504		}
505		break;
506	case MR_EVT_ARGS_TIME:
507		if (verbose) {
508			printf("Adapter time %s; %d seconds since power on: ",
509			    format_timestamp(detail->args.time.rtc),
510			    detail->args.time.elapsedSeconds);
511		}
512		break;
513	case MR_EVT_ARGS_ECC:
514		if (verbose) {
515			printf("Adapter ECC %x,%x: %s: ",
516			    detail->args.ecc.ecar,
517			    detail->args.ecc.elog,
518			    detail->args.ecc.str);
519		}
520		break;
521	default:
522		if (verbose) {
523			printf("Type %d: ", detail->arg_type);
524		}
525		break;
526	}
527	printf("%s\n", detail->description);
528}
529
530static int
531show_events(int ac, char **av)
532{
533	struct mfi_evt_log_state info;
534	struct mfi_evt_list *list;
535	union mfi_evt filter;
536	long val;
537	char *cp;
538	ssize_t size;
539	uint32_t seq, start, stop;
540	uint8_t status;
541	int ch, error, fd, num_events, verbose;
542	u_int i;
543
544	fd = mfi_open(mfi_unit);
545	if (fd < 0) {
546		error = errno;
547		warn("mfi_open");
548		return (error);
549	}
550
551	if (mfi_event_get_info(fd, &info, NULL) < 0) {
552		error = errno;
553		warn("Failed to get event log info");
554		close(fd);
555		return (error);
556	}
557
558	/* Default settings. */
559	num_events = 15;
560	filter.members.reserved = 0;
561	filter.members.locale = MFI_EVT_LOCALE_ALL;
562	filter.members.evt_class = MFI_EVT_CLASS_WARNING;
563	start = info.boot_seq_num;
564	stop = info.newest_seq_num;
565	verbose = 0;
566
567	/* Parse any options. */
568	optind = 1;
569	while ((ch = getopt(ac, av, "c:l:n:v")) != -1) {
570		switch (ch) {
571		case 'c':
572			if (parse_class(optarg, &filter.members.evt_class) < 0) {
573				error = errno;
574				warn("Error parsing event class");
575				close(fd);
576				return (error);
577			}
578			break;
579		case 'l':
580			if (parse_locale(optarg, &filter.members.locale) < 0) {
581				error = errno;
582				warn("Error parsing event locale");
583				close(fd);
584				return (error);
585			}
586			break;
587		case 'n':
588			val = strtol(optarg, &cp, 0);
589			if (*cp != '\0' || val <= 0) {
590				warnx("Invalid event count");
591				close(fd);
592				return (EINVAL);
593			}
594			num_events = val;
595			break;
596		case 'v':
597			verbose = 1;
598			break;
599		case '?':
600		default:
601			close(fd);
602			return (EINVAL);
603		}
604	}
605	ac -= optind;
606	av += optind;
607
608	/* Determine buffer size and validate it. */
609	size = sizeof(struct mfi_evt_list) + sizeof(struct mfi_evt_detail) *
610	    (num_events - 1);
611	if (size > getpagesize()) {
612		warnx("Event count is too high");
613		close(fd);
614		return (EINVAL);
615	}
616
617	/* Handle optional start and stop sequence numbers. */
618	if (ac > 2) {
619		warnx("show events: extra arguments");
620		close(fd);
621		return (EINVAL);
622	}
623	if (ac > 0 && parse_seq(&info, av[0], &start) < 0) {
624		error = errno;
625		warn("Error parsing starting sequence number");
626		close(fd);
627		return (error);
628	}
629	if (ac > 1 && parse_seq(&info, av[1], &stop) < 0) {
630		error = errno;
631		warn("Error parsing ending sequence number");
632		close(fd);
633		return (error);
634	}
635
636	list = malloc(size);
637	if (list == NULL) {
638		warnx("malloc failed");
639		close(fd);
640		return (ENOMEM);
641	}
642	for (seq = start;;) {
643		if (mfi_get_events(fd, list, num_events, filter, seq,
644		    &status) < 0) {
645			error = errno;
646			warn("Failed to fetch events");
647			free(list);
648			close(fd);
649			return (error);
650		}
651		if (status == MFI_STAT_NOT_FOUND) {
652			if (seq == start)
653				warnx("No matching events found");
654			break;
655		}
656		if (status != MFI_STAT_OK) {
657			warnx("Error fetching events: %s", mfi_status(status));
658			free(list);
659			close(fd);
660			return (EIO);
661		}
662
663		for (i = 0; i < list->count; i++) {
664			/*
665			 * If this event is newer than 'stop_seq' then
666			 * break out of the loop.  Note that the log
667			 * is a circular buffer so we have to handle
668			 * the case that our stop point is earlier in
669			 * the buffer than our start point.
670			 */
671			if (list->event[i].seq >= stop) {
672				if (start <= stop)
673					break;
674				else if (list->event[i].seq < start)
675					break;
676			}
677			mfi_decode_evt(fd, &list->event[i], verbose);
678		}
679
680		/*
681		 * XXX: If the event's seq # is the end of the buffer
682		 * then this probably won't do the right thing.  We
683		 * need to know the size of the buffer somehow.
684		 */
685		seq = list->event[list->count - 1].seq + 1;
686
687	}
688
689	free(list);
690	close(fd);
691
692	return (0);
693}
694MFI_COMMAND(show, events, show_events);
695