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