1 /*
2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
5 * 1.0 of the CDDL.
6 *
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
10 */
11
12 /*
13 * Copyright 2018 Nexenta Systems, Inc.
14 */
15
16 #include <fm/fmd_api.h>
17 #include <sys/note.h>
18 #include <fm/libtopo.h>
19 #include <sys/fm/protocol.h>
20 #include <sys/fm/io/disk.h>
21 #include <strings.h>
22
23 typedef struct disk_sense_stat {
24 fmd_stat_t bad_fmri;
25 fmd_stat_t bad_scheme;
26 } disk_sense_stat_t;
27
28 disk_sense_stat_t disk_sense_stats = {
29 { "bad_FMRI", FMD_TYPE_UINT64,
30 "event FMRI is missing or invalid" },
31 { "bad_scheme", FMD_TYPE_UINT64,
32 "event does not contain a valid detector"},
33 };
34
35 static const fmd_prop_t fmd_props [] = {
36 { "io_N", FMD_TYPE_INT32, "10" },
37 { "io_T", FMD_TYPE_TIME, "10min"},
38 { NULL, 0, NULL }
39 };
40
41 void
42 disk_sense_close(fmd_hdl_t *hdl, fmd_case_t *c)
43 {
44 char *devid = fmd_case_getspecific(hdl, c);
45 if (devid != NULL && (fmd_serd_exists(hdl, devid) == FMD_B_TRUE)) {
46 fmd_hdl_debug(hdl, "Destroying serd: %s", devid);
47 fmd_serd_destroy(hdl, devid);
48 }
49 }
50
51 static void
52 disk_sense_case_solve(fmd_hdl_t *hdl, const char *faultclass, fmd_case_t *c,
53 char *devid, nvlist_t *detector)
54 {
55 fmd_hdl_topo_node_info_t *node;
56 char faultname[PATH_MAX];
57 nvlist_t *fault = NULL;
58
59 (void) snprintf(faultname, sizeof (faultname),
60 "fault.io.disk.%s", faultclass);
61
62 if ((node = fmd_hdl_topo_node_get_by_devid(hdl, devid)) == NULL) {
63 fault = fmd_nvl_create_fault(hdl, faultname, 100,
64 detector, NULL, NULL);
65 } else {
66 fault = fmd_nvl_create_fault(hdl, faultname, 100,
67 detector, node->fru, node->resource);
68 nvlist_free(node->fru);
69 nvlist_free(node->resource);
70 fmd_hdl_free(hdl, node, sizeof (fmd_hdl_topo_node_info_t));
71 }
72
73 fmd_case_add_suspect(hdl, c, fault);
74 fmd_case_setspecific(hdl, c, devid);
75 fmd_case_solve(hdl, c);
76 }
77
78 void
79 disk_sense_recv(fmd_hdl_t *hdl, fmd_event_t *event, nvlist_t *nvl,
80 const char *class)
81 {
82 nvlist_t *detector = NULL;
83 char *devid = NULL;
84 uint8_t key = 0;
85 uint8_t asc = 0;
86 uint8_t ascq = 0;
87 _NOTE(ARGUNUSED(class));
88
89 if (nvlist_lookup_nvlist(nvl, "detector", &detector) != 0) {
90 disk_sense_stats.bad_scheme.fmds_value.ui64++;
91 return;
92 }
93
94 if (nvlist_lookup_string(detector, "devid", &devid) != 0) {
95 disk_sense_stats.bad_fmri.fmds_value.ui64++;
96 return;
97 }
98
99 if (nvlist_lookup_uint8(nvl, "key", &key) != 0) {
100 disk_sense_stats.bad_fmri.fmds_value.ui64++;
101 fmd_hdl_debug(hdl, "Invalid ereport payload");
102 return;
103 }
104
105 (void) nvlist_lookup_uint8(nvl, "asc", &asc);
106 (void) nvlist_lookup_uint8(nvl, "ascq", &ascq);
107
108 if (key == 0x1 && asc == 0xb && ascq == 0x1) {
109 /* over temp reported by drive sense data */
110 fmd_case_t *c = fmd_case_open(hdl, NULL);
111 fmd_case_add_ereport(hdl, c, event);
112 disk_sense_case_solve(hdl, FM_FAULT_DISK_OVERTEMP, c,
113 devid, detector);
114 fmd_hdl_debug(hdl, "Disk sense data reports drive over-temp");
115 return;
116 }
117 }
118
119 static const fmd_hdl_ops_t fmd_ops = {
120 disk_sense_recv,
121 NULL,
122 disk_sense_close,
123 NULL,
124 NULL,
125 };
126
127 static const fmd_hdl_info_t fmd_info = {
128 "disk-sense-de", "0.5", &fmd_ops, fmd_props
129 };
130
131 void
132 _fmd_init(fmd_hdl_t *hdl)
133 {
134 if (fmd_hdl_register(hdl, FMD_API_VERSION, &fmd_info) != 0) {
135 fmd_hdl_debug(hdl, "Internal error\n");
136 return;
137 }
138
139 (void) fmd_stat_create(hdl, FMD_STAT_NOALLOC,
140 sizeof (disk_sense_stats) / sizeof (fmd_stat_t),
141 (fmd_stat_t *)&disk_sense_stats);
142 }
143
144 void
145 _fmd_fini(fmd_hdl_t *hdl)
146 {
147 _NOTE(ARGUNUSED(hdl));
148 }