Print this page
8074 need to add FMA event for SSD wearout
Split |
Close |
Expand all |
Collapse all |
--- old/usr/src/cmd/fm/modules/common/disk-transport/disk_transport.c
+++ new/usr/src/cmd/fm/modules/common/disk-transport/disk_transport.c
1 1 /*
2 2 * CDDL HEADER START
3 3 *
4 4 * The contents of this file are subject to the terms of the
5 5 * Common Development and Distribution License (the "License").
6 6 * You may not use this file except in compliance with the License.
7 7 *
8 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 9 * or http://www.opensolaris.org/os/licensing.
10 10 * See the License for the specific language governing permissions
11 11 * and limitations under the License.
12 12 *
13 13 * When distributing Covered Code, include this CDDL HEADER in each
↓ open down ↓ |
13 lines elided |
↑ open up ↑ |
14 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 15 * If applicable, add the following below this CDDL HEADER, with the
16 16 * fields enclosed by brackets "[]" replaced with your own identifying
17 17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 18 *
19 19 * CDDL HEADER END
20 20 */
21 21 /*
22 22 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
23 23 * Use is subject to license terms.
24 + * Copyright 2016 Nexenta Systems, Inc. All rights reserved.
24 25 */
25 26
26 -#pragma ident "%Z%%M% %I% %E% SMI"
27 -
28 27 /*
29 28 * Disk error transport module
30 29 *
31 30 * This transport module is responsible for translating between disk errors
32 31 * and FMA ereports. It is a read-only transport module, and checks for the
33 32 * following failures:
34 33 *
35 - * - overtemp
36 - * - predictive failure
37 - * - self-test failure
34 + * - overtemp
35 + * - predictive failure
36 + * - self-test failure
37 + * - solid state media wearout
38 38 *
39 39 * These failures are detected via the TOPO_METH_DISK_STATUS method, which
40 40 * leverages libdiskstatus to do the actual analysis. This transport module is
41 41 * in charge of the following tasks:
42 42 *
43 - * - discovering available devices
44 - * - periodically checking devices
45 - * - managing device addition/removal
43 + * - discovering available devices
44 + * - periodically checking devices
45 + * - managing device addition/removal
46 46 */
47 47
48 48 #include <ctype.h>
49 49 #include <fm/fmd_api.h>
50 50 #include <fm/libdiskstatus.h>
51 51 #include <fm/libtopo.h>
52 52 #include <fm/topo_hc.h>
53 53 #include <fm/topo_mod.h>
54 54 #include <limits.h>
55 55 #include <string.h>
56 56 #include <sys/fm/io/scsi.h>
57 57 #include <sys/fm/protocol.h>
58 58
59 59 static struct dt_stat {
60 60 fmd_stat_t dropped;
61 61 } dt_stats = {
62 62 { "dropped", FMD_TYPE_UINT64, "number of dropped ereports" }
63 63 };
64 64
65 65 typedef struct disk_monitor {
66 66 fmd_hdl_t *dm_hdl;
67 67 fmd_xprt_t *dm_xprt;
68 68 id_t dm_timer;
69 69 hrtime_t dm_interval;
70 70 char *dm_sim_search;
71 71 char *dm_sim_file;
72 72 boolean_t dm_timer_istopo;
73 73 } disk_monitor_t;
74 74
75 75 static void
76 76 dt_post_ereport(fmd_hdl_t *hdl, fmd_xprt_t *xprt, const char *protocol,
77 77 const char *faultname, uint64_t ena, nvlist_t *detector, nvlist_t *payload)
78 78 {
79 79 nvlist_t *nvl;
80 80 int e = 0;
81 81 char fullclass[PATH_MAX];
82 82
83 83 (void) snprintf(fullclass, sizeof (fullclass), "%s.io.%s.disk.%s",
84 84 FM_EREPORT_CLASS, protocol, faultname);
85 85
86 86 if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) == 0) {
87 87 e |= nvlist_add_string(nvl, FM_CLASS, fullclass);
88 88 e |= nvlist_add_uint8(nvl, FM_VERSION, FM_EREPORT_VERSION);
89 89 e |= nvlist_add_uint64(nvl, FM_EREPORT_ENA, ena);
90 90 e |= nvlist_add_nvlist(nvl, FM_EREPORT_DETECTOR, detector);
91 91 e |= nvlist_merge(nvl, payload, 0);
92 92
93 93 if (e == 0) {
94 94 fmd_xprt_post(hdl, xprt, nvl, 0);
95 95 } else {
96 96 nvlist_free(nvl);
97 97 dt_stats.dropped.fmds_value.ui64++;
98 98 }
99 99 } else {
100 100 dt_stats.dropped.fmds_value.ui64++;
101 101 }
102 102 }
103 103
104 104 /*
105 105 * Check a single topo node for failure. This simply invokes the disk status
↓ open down ↓ |
50 lines elided |
↑ open up ↑ |
106 106 * method, and generates any ereports as necessary.
107 107 */
108 108 static int
109 109 dt_analyze_disk(topo_hdl_t *thp, tnode_t *node, void *arg)
110 110 {
111 111 nvlist_t *result;
112 112 nvlist_t *fmri, *faults;
113 113 char *protocol;
114 114 int err;
115 115 disk_monitor_t *dmp = arg;
116 - uint64_t ena;
117 116 nvpair_t *elem;
118 117 boolean_t fault;
119 118 nvlist_t *details;
120 119 char *fmristr;
121 120 nvlist_t *in = NULL;
122 121
123 122 if (topo_node_resource(node, &fmri, &err) != 0) {
124 123 fmd_hdl_error(dmp->dm_hdl, "failed to get fmri: %s\n",
125 124 topo_strerror(err));
126 125 return (TOPO_WALK_ERR);
127 126 }
128 127
129 128 if (topo_hdl_nvalloc(thp, &in, NV_UNIQUE_NAME) != 0) {
130 129 nvlist_free(fmri);
131 130 return (TOPO_WALK_ERR);
132 131 }
133 132
134 133 if (dmp->dm_sim_search) {
135 134 fmristr = NULL;
136 135 if (topo_fmri_nvl2str(thp, fmri, &fmristr, &err) == 0 &&
137 136 strstr(fmristr, dmp->dm_sim_search) != 0)
138 137 (void) nvlist_add_string(in, "path", dmp->dm_sim_file);
139 138 topo_hdl_strfree(thp, fmristr);
140 139 }
141 140
142 141 /*
143 142 * Try to invoke the method. If this fails (most likely because the
144 143 * method is not supported), then ignore this node.
↓ open down ↓ |
18 lines elided |
↑ open up ↑ |
145 144 */
146 145 if (topo_method_invoke(node, TOPO_METH_DISK_STATUS,
147 146 TOPO_METH_DISK_STATUS_VERSION, in, &result, &err) != 0) {
148 147 nvlist_free(fmri);
149 148 nvlist_free(in);
150 149 return (TOPO_WALK_NEXT);
151 150 }
152 151
153 152 nvlist_free(in);
154 153
155 - ena = fmd_event_ena_create(dmp->dm_hdl);
156 -
157 154 /*
158 - * Add any faults.
155 + * Check for faults and post ereport(s) if needed
159 156 */
160 157 if (nvlist_lookup_nvlist(result, "faults", &faults) == 0 &&
161 158 nvlist_lookup_string(result, "protocol", &protocol) == 0) {
162 159 elem = NULL;
163 160 while ((elem = nvlist_next_nvpair(faults, elem)) != NULL) {
164 161 if (nvpair_type(elem) != DATA_TYPE_BOOLEAN_VALUE)
165 162 continue;
166 163
167 164 (void) nvpair_value_boolean_value(elem, &fault);
168 165 if (!fault ||
169 166 nvlist_lookup_nvlist(result, nvpair_name(elem),
170 167 &details) != 0)
171 168 continue;
172 169
170 + if (strcmp(nvpair_name(elem),
171 + FM_EREPORT_SCSI_SSMWEAROUT) == 0 &&
172 + fmd_prop_get_int32(dmp->dm_hdl,
173 + "ignore-ssm-wearout") == FMD_B_TRUE)
174 + continue;
175 +
173 176 dt_post_ereport(dmp->dm_hdl, dmp->dm_xprt, protocol,
174 - nvpair_name(elem), ena, fmri, details);
177 + nvpair_name(elem),
178 + fmd_event_ena_create(dmp->dm_hdl), fmri, details);
175 179 }
176 180 }
177 181
178 182 nvlist_free(result);
179 183 nvlist_free(fmri);
180 184
181 185 return (TOPO_WALK_NEXT);
182 186 }
183 187
184 188 /*
185 189 * Periodic timeout. Iterates over all hc:// topo nodes, calling
186 190 * dt_analyze_disk() for each one.
187 191 */
188 192 /*ARGSUSED*/
189 193 static void
190 194 dt_timeout(fmd_hdl_t *hdl, id_t id, void *data)
191 195 {
192 196 topo_hdl_t *thp;
193 197 topo_walk_t *twp;
194 198 int err;
195 199 disk_monitor_t *dmp = fmd_hdl_getspecific(hdl);
196 200
197 201 dmp->dm_hdl = hdl;
198 202
199 203 thp = fmd_hdl_topo_hold(hdl, TOPO_VERSION);
200 204 if ((twp = topo_walk_init(thp, FM_FMRI_SCHEME_HC, dt_analyze_disk,
201 205 dmp, &err)) == NULL) {
202 206 fmd_hdl_topo_rele(hdl, thp);
203 207 fmd_hdl_error(hdl, "failed to get topology: %s\n",
204 208 topo_strerror(err));
205 209 return;
206 210 }
207 211
208 212 if (topo_walk_step(twp, TOPO_WALK_CHILD) == TOPO_WALK_ERR) {
209 213 topo_walk_fini(twp);
210 214 fmd_hdl_topo_rele(hdl, thp);
211 215 fmd_hdl_error(hdl, "failed to walk topology\n");
212 216 return;
213 217 }
214 218
215 219 topo_walk_fini(twp);
216 220 fmd_hdl_topo_rele(hdl, thp);
217 221
218 222 dmp->dm_timer = fmd_timer_install(hdl, NULL, NULL, dmp->dm_interval);
219 223 dmp->dm_timer_istopo = B_FALSE;
220 224 }
221 225
222 226 /*
223 227 * Called when the topology may have changed. We want to examine all disks in
224 228 * case a new one has been inserted, but we don't want to overwhelm the system
225 229 * in the event of a flurry of topology changes, as most likely only a small
226 230 * number of disks are changing. To avoid this, we set the timer for a small
227 231 * but non-trivial interval (by default 1 minute), and ignore intervening
228 232 * changes during this period. This still gives us a reasonable response time
229 233 * to newly inserted devices without overwhelming the system if lots of hotplug
230 234 * activity is going on.
231 235 */
232 236 /*ARGSUSED*/
233 237 static void
234 238 dt_topo_change(fmd_hdl_t *hdl, topo_hdl_t *thp)
235 239 {
236 240 disk_monitor_t *dmp = fmd_hdl_getspecific(hdl);
237 241
238 242 if (dmp->dm_timer_istopo)
239 243 return;
240 244
↓ open down ↓ |
56 lines elided |
↑ open up ↑ |
241 245 fmd_timer_remove(hdl, dmp->dm_timer);
242 246 dmp->dm_timer = fmd_timer_install(hdl, NULL, NULL,
243 247 fmd_prop_get_int64(hdl, "min-interval"));
244 248 dmp->dm_timer_istopo = B_TRUE;
245 249 }
246 250
247 251 static const fmd_prop_t fmd_props[] = {
248 252 { "interval", FMD_TYPE_TIME, "1h" },
249 253 { "min-interval", FMD_TYPE_TIME, "1min" },
250 254 { "simulate", FMD_TYPE_STRING, "" },
255 + { "ignore-ssm-wearout", FMD_TYPE_BOOL, "false"},
251 256 { NULL, 0, NULL }
252 257 };
253 258
254 259 static const fmd_hdl_ops_t fmd_ops = {
255 260 NULL, /* fmdo_recv */
256 261 dt_timeout, /* fmdo_timeout */
257 262 NULL, /* fmdo_close */
258 263 NULL, /* fmdo_stats */
259 264 NULL, /* fmdo_gc */
260 265 NULL, /* fmdo_send */
261 266 dt_topo_change, /* fmdo_topo_change */
262 267 };
263 268
264 269 static const fmd_hdl_info_t fmd_info = {
265 - "Disk Transport Agent", "1.0", &fmd_ops, fmd_props
270 + "Disk Transport Agent", "1.1", &fmd_ops, fmd_props
266 271 };
267 272
268 273 void
269 274 _fmd_init(fmd_hdl_t *hdl)
270 275 {
271 276 disk_monitor_t *dmp;
272 277 char *simulate;
273 278
274 279 if (fmd_hdl_register(hdl, FMD_API_VERSION, &fmd_info) != 0)
275 280 return;
276 281
277 282 (void) fmd_stat_create(hdl, FMD_STAT_NOALLOC,
278 283 sizeof (dt_stats) / sizeof (fmd_stat_t),
279 284 (fmd_stat_t *)&dt_stats);
280 285
281 286 dmp = fmd_hdl_zalloc(hdl, sizeof (disk_monitor_t), FMD_SLEEP);
↓ open down ↓ |
6 lines elided |
↑ open up ↑ |
282 287 fmd_hdl_setspecific(hdl, dmp);
283 288
284 289 dmp->dm_xprt = fmd_xprt_open(hdl, FMD_XPRT_RDONLY, NULL, NULL);
285 290 dmp->dm_interval = fmd_prop_get_int64(hdl, "interval");
286 291
287 292 /*
288 293 * Determine if we have the simulate property set. This property allows
289 294 * the developer to substitute a faulty device based off all or part of
290 295 * an FMRI string. For example, one could do:
291 296 *
292 - * setprop simulate "bay=4/disk=4 /path/to/sim.so"
297 + * setprop simulate "bay=4/disk=4 /path/to/sim.so"
293 298 *
294 299 * When the transport module encounters an FMRI containing the given
295 300 * string, then it will open the simulator file instead of the
296 301 * corresponding device. This can be any file, but is intended to be a
297 302 * libdiskstatus simulator shared object, capable of faking up SCSI
298 303 * responses.
299 304 *
300 305 * The property consists of two strings, an FMRI fragment and an
301 306 * absolute path, separated by whitespace.
302 307 */
303 308 simulate = fmd_prop_get_string(hdl, "simulate");
304 309 if (simulate[0] != '\0') {
305 310 const char *sep;
306 311 size_t len;
307 312
308 313 for (sep = simulate; *sep != '\0'; sep++) {
309 314 if (isspace(*sep))
310 315 break;
311 316 }
312 317
313 318 if (*sep != '\0') {
314 319 len = sep - simulate;
315 320
316 321 dmp->dm_sim_search = fmd_hdl_alloc(hdl,
317 322 len + 1, FMD_SLEEP);
318 323 (void) memcpy(dmp->dm_sim_search, simulate, len);
319 324 dmp->dm_sim_search[len] = '\0';
320 325 }
321 326
322 327 for (; *sep != '\0'; sep++) {
323 328 if (!isspace(*sep))
324 329 break;
325 330 }
326 331
327 332 if (*sep != '\0') {
328 333 dmp->dm_sim_file = fmd_hdl_strdup(hdl, sep, FMD_SLEEP);
329 334 } else if (dmp->dm_sim_search) {
330 335 fmd_hdl_strfree(hdl, dmp->dm_sim_search);
331 336 dmp->dm_sim_search = NULL;
332 337 }
333 338 }
334 339 fmd_prop_free_string(hdl, simulate);
335 340
336 341 /*
337 342 * Call our initial timer routine. This will do an initial check of all
338 343 * the disks, and then start the periodic timeout.
339 344 */
340 345 dmp->dm_timer = fmd_timer_install(hdl, NULL, NULL, 0);
341 346 }
342 347
343 348 void
344 349 _fmd_fini(fmd_hdl_t *hdl)
345 350 {
346 351 disk_monitor_t *dmp;
347 352
348 353 dmp = fmd_hdl_getspecific(hdl);
349 354 if (dmp) {
350 355 fmd_xprt_close(hdl, dmp->dm_xprt);
351 356 fmd_hdl_strfree(hdl, dmp->dm_sim_search);
352 357 fmd_hdl_strfree(hdl, dmp->dm_sim_file);
353 358 fmd_hdl_free(hdl, dmp, sizeof (*dmp));
354 359 }
355 360 }
↓ open down ↓ |
53 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX