1*1410cb93SJoshua M. Clulow /*
2*1410cb93SJoshua M. Clulow * This file and its contents are supplied under the terms of the
3*1410cb93SJoshua M. Clulow * Common Development and Distribution License ("CDDL"), version 1.0.
4*1410cb93SJoshua M. Clulow * You may only use this file in accordance with the terms of version
5*1410cb93SJoshua M. Clulow * 1.0 of the CDDL.
6*1410cb93SJoshua M. Clulow *
7*1410cb93SJoshua M. Clulow * A full copy of the text of the CDDL should have accompanied this
8*1410cb93SJoshua M. Clulow * source. A copy of the CDDL is also available via the Internet at
9*1410cb93SJoshua M. Clulow * http://www.illumos.org/license/CDDL.
10*1410cb93SJoshua M. Clulow */
11*1410cb93SJoshua M. Clulow
12*1410cb93SJoshua M. Clulow /*
13*1410cb93SJoshua M. Clulow * Copyright (c) 2013, Joyent, Inc. All rights reserved.
14*1410cb93SJoshua M. Clulow */
15*1410cb93SJoshua M. Clulow
16*1410cb93SJoshua M. Clulow /*
17*1410cb93SJoshua M. Clulow * Disk Lights Agent (FMA)
18*1410cb93SJoshua M. Clulow *
19*1410cb93SJoshua M. Clulow * This Fault Management Daemon (fmd) module periodically scans the topology
20*1410cb93SJoshua M. Clulow * tree, enumerates all disks with associated fault indicators, and then
21*1410cb93SJoshua M. Clulow * synchronises the fault status of resources in the FMA Resource Cache with
22*1410cb93SJoshua M. Clulow * the indicators. In short: it turns the fault light on for befallen disks.
23*1410cb93SJoshua M. Clulow *
24*1410cb93SJoshua M. Clulow * Presently, we recognise associated fault indicators for disks by looking
25*1410cb93SJoshua M. Clulow * for the following structure in the topology tree:
26*1410cb93SJoshua M. Clulow *
27*1410cb93SJoshua M. Clulow * /bay=N
28*1410cb93SJoshua M. Clulow * |
29*1410cb93SJoshua M. Clulow * +---- /disk=0 <---------------- our Disk
30*1410cb93SJoshua M. Clulow * |
31*1410cb93SJoshua M. Clulow * +---- /bay=N?indicator=fail <---- the Fault Light
32*1410cb93SJoshua M. Clulow * \---- /bay=N?indicator=ident
33*1410cb93SJoshua M. Clulow *
34*1410cb93SJoshua M. Clulow * That is: a DISK node will have a parent BAY; that BAY will itself have
35*1410cb93SJoshua M. Clulow * child Facility nodes, one of which will be called "fail". If any of the
36*1410cb93SJoshua M. Clulow * above does not hold, we simply do nothing for this disk.
37*1410cb93SJoshua M. Clulow */
38*1410cb93SJoshua M. Clulow
39*1410cb93SJoshua M. Clulow #include <string.h>
40*1410cb93SJoshua M. Clulow #include <strings.h>
41*1410cb93SJoshua M. Clulow #include <libnvpair.h>
42*1410cb93SJoshua M. Clulow #include <fm/libtopo.h>
43*1410cb93SJoshua M. Clulow #include <fm/topo_list.h>
44*1410cb93SJoshua M. Clulow #include <fm/topo_hc.h>
45*1410cb93SJoshua M. Clulow #include <fm/fmd_api.h>
46*1410cb93SJoshua M. Clulow #include <sys/fm/protocol.h>
47*1410cb93SJoshua M. Clulow
48*1410cb93SJoshua M. Clulow
49*1410cb93SJoshua M. Clulow typedef struct disk_lights {
50*1410cb93SJoshua M. Clulow fmd_hdl_t *dl_fmd;
51*1410cb93SJoshua M. Clulow uint64_t dl_poll_interval;
52*1410cb93SJoshua M. Clulow uint64_t dl_coalesce_interval;
53*1410cb93SJoshua M. Clulow id_t dl_timer;
54*1410cb93SJoshua M. Clulow boolean_t dl_triggered;
55*1410cb93SJoshua M. Clulow } disk_lights_t;
56*1410cb93SJoshua M. Clulow
57*1410cb93SJoshua M. Clulow static void disklights_topo(fmd_hdl_t *, topo_hdl_t *);
58*1410cb93SJoshua M. Clulow static void disklights_recv(fmd_hdl_t *, fmd_event_t *, nvlist_t *,
59*1410cb93SJoshua M. Clulow const char *);
60*1410cb93SJoshua M. Clulow static void disklights_timeout(fmd_hdl_t *, id_t, void *);
61*1410cb93SJoshua M. Clulow
62*1410cb93SJoshua M. Clulow static const fmd_hdl_ops_t fmd_ops = {
63*1410cb93SJoshua M. Clulow disklights_recv, /* fmdo_recv */
64*1410cb93SJoshua M. Clulow disklights_timeout, /* fmdo_timeout */
65*1410cb93SJoshua M. Clulow NULL, /* fmdo_close */
66*1410cb93SJoshua M. Clulow NULL, /* fmdo_stats */
67*1410cb93SJoshua M. Clulow NULL, /* fmdo_gc */
68*1410cb93SJoshua M. Clulow NULL, /* fmdo_send */
69*1410cb93SJoshua M. Clulow disklights_topo, /* fmdo_topo */
70*1410cb93SJoshua M. Clulow };
71*1410cb93SJoshua M. Clulow
72*1410cb93SJoshua M. Clulow /*
73*1410cb93SJoshua M. Clulow * POLL_INTERVAL is the period after which we perform an unsolicited poll
74*1410cb93SJoshua M. Clulow * to ensure we remain in sync with reality.
75*1410cb93SJoshua M. Clulow */
76*1410cb93SJoshua M. Clulow #define DL_PROP_POLL_INTERVAL "poll-interval"
77*1410cb93SJoshua M. Clulow
78*1410cb93SJoshua M. Clulow /*
79*1410cb93SJoshua M. Clulow * COALESCE_INTERVAL is how long we wait after we are trigged by either a
80*1410cb93SJoshua M. Clulow * topology change or a relevant list.* event, in order to allow a series
81*1410cb93SJoshua M. Clulow * of events to coalesce.
82*1410cb93SJoshua M. Clulow */
83*1410cb93SJoshua M. Clulow #define DL_PROP_COALESCE_INTERVAL "coalesce-interval"
84*1410cb93SJoshua M. Clulow
85*1410cb93SJoshua M. Clulow static const fmd_prop_t fmd_props[] = {
86*1410cb93SJoshua M. Clulow { DL_PROP_POLL_INTERVAL, FMD_TYPE_TIME, "5min" },
87*1410cb93SJoshua M. Clulow { DL_PROP_COALESCE_INTERVAL, FMD_TYPE_TIME, "3s" },
88*1410cb93SJoshua M. Clulow { NULL, 0, NULL }
89*1410cb93SJoshua M. Clulow };
90*1410cb93SJoshua M. Clulow
91*1410cb93SJoshua M. Clulow static const fmd_hdl_info_t fmd_info = {
92*1410cb93SJoshua M. Clulow "Disk Lights Agent",
93*1410cb93SJoshua M. Clulow "1.0",
94*1410cb93SJoshua M. Clulow &fmd_ops,
95*1410cb93SJoshua M. Clulow fmd_props
96*1410cb93SJoshua M. Clulow };
97*1410cb93SJoshua M. Clulow
98*1410cb93SJoshua M. Clulow /*
99*1410cb93SJoshua M. Clulow * Fetch the Facility Node properties (name, type) from the FMRI
100*1410cb93SJoshua M. Clulow * for this node, or return -1 if we can't.
101*1410cb93SJoshua M. Clulow */
102*1410cb93SJoshua M. Clulow static int
get_facility_props(topo_hdl_t * hdl,tnode_t * node,char ** facname,char ** factype)103*1410cb93SJoshua M. Clulow get_facility_props(topo_hdl_t *hdl, tnode_t *node, char **facname,
104*1410cb93SJoshua M. Clulow char **factype)
105*1410cb93SJoshua M. Clulow {
106*1410cb93SJoshua M. Clulow int e, ret = -1;
107*1410cb93SJoshua M. Clulow nvlist_t *fmri = NULL, *fnvl;
108*1410cb93SJoshua M. Clulow char *nn = NULL, *tt = NULL;
109*1410cb93SJoshua M. Clulow
110*1410cb93SJoshua M. Clulow if (topo_node_resource(node, &fmri, &e) != 0)
111*1410cb93SJoshua M. Clulow goto out;
112*1410cb93SJoshua M. Clulow
113*1410cb93SJoshua M. Clulow if (nvlist_lookup_nvlist(fmri, FM_FMRI_FACILITY, &fnvl) != 0)
114*1410cb93SJoshua M. Clulow goto out;
115*1410cb93SJoshua M. Clulow
116*1410cb93SJoshua M. Clulow if (nvlist_lookup_string(fnvl, FM_FMRI_FACILITY_NAME, &nn) != 0)
117*1410cb93SJoshua M. Clulow goto out;
118*1410cb93SJoshua M. Clulow
119*1410cb93SJoshua M. Clulow if (nvlist_lookup_string(fnvl, FM_FMRI_FACILITY_TYPE, &tt) != 0)
120*1410cb93SJoshua M. Clulow goto out;
121*1410cb93SJoshua M. Clulow
122*1410cb93SJoshua M. Clulow *facname = topo_hdl_strdup(hdl, nn);
123*1410cb93SJoshua M. Clulow *factype = topo_hdl_strdup(hdl, tt);
124*1410cb93SJoshua M. Clulow ret = 0;
125*1410cb93SJoshua M. Clulow
126*1410cb93SJoshua M. Clulow out:
127*1410cb93SJoshua M. Clulow nvlist_free(fmri);
128*1410cb93SJoshua M. Clulow return (ret);
129*1410cb93SJoshua M. Clulow }
130*1410cb93SJoshua M. Clulow
131*1410cb93SJoshua M. Clulow typedef struct dl_fault_walk_inner {
132*1410cb93SJoshua M. Clulow char *fwi_name;
133*1410cb93SJoshua M. Clulow uint32_t fwi_mode;
134*1410cb93SJoshua M. Clulow } dl_fault_walk_inner_t;
135*1410cb93SJoshua M. Clulow
136*1410cb93SJoshua M. Clulow static int
dl_fault_walk_inner(topo_hdl_t * thp,tnode_t * node,void * arg)137*1410cb93SJoshua M. Clulow dl_fault_walk_inner(topo_hdl_t *thp, tnode_t *node, void *arg)
138*1410cb93SJoshua M. Clulow {
139*1410cb93SJoshua M. Clulow dl_fault_walk_inner_t *fwi = arg;
140*1410cb93SJoshua M. Clulow char *facname = NULL, *factype = NULL;
141*1410cb93SJoshua M. Clulow int err;
142*1410cb93SJoshua M. Clulow
143*1410cb93SJoshua M. Clulow /*
144*1410cb93SJoshua M. Clulow * We're only interested in BAY children that are valid Facility Nodes.
145*1410cb93SJoshua M. Clulow */
146*1410cb93SJoshua M. Clulow if (topo_node_flags(node) != TOPO_NODE_FACILITY ||
147*1410cb93SJoshua M. Clulow get_facility_props(thp, node, &facname, &factype) != 0) {
148*1410cb93SJoshua M. Clulow goto out;
149*1410cb93SJoshua M. Clulow }
150*1410cb93SJoshua M. Clulow
151*1410cb93SJoshua M. Clulow if (strcmp(fwi->fwi_name, facname) != 0)
152*1410cb93SJoshua M. Clulow goto out;
153*1410cb93SJoshua M. Clulow
154*1410cb93SJoshua M. Clulow /*
155*1410cb93SJoshua M. Clulow * Attempt to set the LED mode appropriately. If this fails, give up
156*1410cb93SJoshua M. Clulow * and move on.
157*1410cb93SJoshua M. Clulow */
158*1410cb93SJoshua M. Clulow (void) topo_prop_set_uint32(node, TOPO_PGROUP_FACILITY, TOPO_LED_MODE,
159*1410cb93SJoshua M. Clulow TOPO_PROP_MUTABLE, fwi->fwi_mode, &err);
160*1410cb93SJoshua M. Clulow
161*1410cb93SJoshua M. Clulow out:
162*1410cb93SJoshua M. Clulow topo_hdl_strfree(thp, facname);
163*1410cb93SJoshua M. Clulow topo_hdl_strfree(thp, factype);
164*1410cb93SJoshua M. Clulow return (TOPO_WALK_NEXT);
165*1410cb93SJoshua M. Clulow }
166*1410cb93SJoshua M. Clulow
167*1410cb93SJoshua M. Clulow static int
dl_fault_walk_outer(topo_hdl_t * thp,tnode_t * node,void * arg)168*1410cb93SJoshua M. Clulow dl_fault_walk_outer(topo_hdl_t *thp, tnode_t *node, void *arg)
169*1410cb93SJoshua M. Clulow {
170*1410cb93SJoshua M. Clulow disk_lights_t *dl = arg;
171*1410cb93SJoshua M. Clulow dl_fault_walk_inner_t fwi;
172*1410cb93SJoshua M. Clulow tnode_t *pnode;
173*1410cb93SJoshua M. Clulow int err, has_fault;
174*1410cb93SJoshua M. Clulow nvlist_t *fmri = NULL;
175*1410cb93SJoshua M. Clulow
176*1410cb93SJoshua M. Clulow bzero(&fwi, sizeof (fwi));
177*1410cb93SJoshua M. Clulow
178*1410cb93SJoshua M. Clulow /*
179*1410cb93SJoshua M. Clulow * We are only looking for DISK nodes in the topology that have a parent
180*1410cb93SJoshua M. Clulow * BAY.
181*1410cb93SJoshua M. Clulow */
182*1410cb93SJoshua M. Clulow if (strcmp(DISK, topo_node_name(node)) != 0 ||
183*1410cb93SJoshua M. Clulow (pnode = topo_node_parent(node)) == NULL ||
184*1410cb93SJoshua M. Clulow strcmp(BAY, topo_node_name(pnode)) != 0) {
185*1410cb93SJoshua M. Clulow return (TOPO_WALK_NEXT);
186*1410cb93SJoshua M. Clulow }
187*1410cb93SJoshua M. Clulow
188*1410cb93SJoshua M. Clulow /*
189*1410cb93SJoshua M. Clulow * Check to see if the Resource this FMRI describes is Faulty:
190*1410cb93SJoshua M. Clulow */
191*1410cb93SJoshua M. Clulow if (topo_node_resource(node, &fmri, &err) != 0)
192*1410cb93SJoshua M. Clulow return (TOPO_WALK_NEXT);
193*1410cb93SJoshua M. Clulow has_fault = fmd_nvl_fmri_has_fault(dl->dl_fmd, fmri,
194*1410cb93SJoshua M. Clulow FMD_HAS_FAULT_RESOURCE, NULL);
195*1410cb93SJoshua M. Clulow nvlist_free(fmri);
196*1410cb93SJoshua M. Clulow
197*1410cb93SJoshua M. Clulow /*
198*1410cb93SJoshua M. Clulow * Walk the children of this BAY and flush out our fault status if
199*1410cb93SJoshua M. Clulow * we find an appropriate indicator node.
200*1410cb93SJoshua M. Clulow */
201*1410cb93SJoshua M. Clulow fwi.fwi_name = "fail";
202*1410cb93SJoshua M. Clulow fwi.fwi_mode = has_fault ? TOPO_LED_STATE_ON : TOPO_LED_STATE_OFF;
203*1410cb93SJoshua M. Clulow (void) topo_node_child_walk(thp, pnode, dl_fault_walk_inner, &fwi,
204*1410cb93SJoshua M. Clulow &err);
205*1410cb93SJoshua M. Clulow
206*1410cb93SJoshua M. Clulow return (TOPO_WALK_NEXT);
207*1410cb93SJoshua M. Clulow }
208*1410cb93SJoshua M. Clulow
209*1410cb93SJoshua M. Clulow /*
210*1410cb93SJoshua M. Clulow * Walk all of the topology nodes looking for DISKs that match the structure
211*1410cb93SJoshua M. Clulow * described in the overview. Once we find them, check their fault status
212*1410cb93SJoshua M. Clulow * and update their fault indiciator accordingly.
213*1410cb93SJoshua M. Clulow */
214*1410cb93SJoshua M. Clulow static void
dl_examine_topo(disk_lights_t * dl)215*1410cb93SJoshua M. Clulow dl_examine_topo(disk_lights_t *dl)
216*1410cb93SJoshua M. Clulow {
217*1410cb93SJoshua M. Clulow int err;
218*1410cb93SJoshua M. Clulow topo_hdl_t *thp = NULL;
219*1410cb93SJoshua M. Clulow topo_walk_t *twp = NULL;
220*1410cb93SJoshua M. Clulow
221*1410cb93SJoshua M. Clulow thp = fmd_hdl_topo_hold(dl->dl_fmd, TOPO_VERSION);
222*1410cb93SJoshua M. Clulow if ((twp = topo_walk_init(thp, FM_FMRI_SCHEME_HC, dl_fault_walk_outer,
223*1410cb93SJoshua M. Clulow dl, &err)) == NULL) {
224*1410cb93SJoshua M. Clulow fmd_hdl_error(dl->dl_fmd, "failed to get topology: %s\n",
225*1410cb93SJoshua M. Clulow topo_strerror(err));
226*1410cb93SJoshua M. Clulow goto out;
227*1410cb93SJoshua M. Clulow }
228*1410cb93SJoshua M. Clulow
229*1410cb93SJoshua M. Clulow if (topo_walk_step(twp, TOPO_WALK_CHILD) == TOPO_WALK_ERR) {
230*1410cb93SJoshua M. Clulow fmd_hdl_error(dl->dl_fmd, "failed to walk topology: %s\n",
231*1410cb93SJoshua M. Clulow topo_strerror(err));
232*1410cb93SJoshua M. Clulow goto out;
233*1410cb93SJoshua M. Clulow }
234*1410cb93SJoshua M. Clulow
235*1410cb93SJoshua M. Clulow out:
236*1410cb93SJoshua M. Clulow if (twp != NULL)
237*1410cb93SJoshua M. Clulow topo_walk_fini(twp);
238*1410cb93SJoshua M. Clulow if (thp != NULL)
239*1410cb93SJoshua M. Clulow fmd_hdl_topo_rele(dl->dl_fmd, thp);
240*1410cb93SJoshua M. Clulow }
241*1410cb93SJoshua M. Clulow
242*1410cb93SJoshua M. Clulow static void
dl_trigger_enum(disk_lights_t * dl)243*1410cb93SJoshua M. Clulow dl_trigger_enum(disk_lights_t *dl)
244*1410cb93SJoshua M. Clulow {
245*1410cb93SJoshua M. Clulow /*
246*1410cb93SJoshua M. Clulow * If we're already on the short-poll coalesce timer, then return
247*1410cb93SJoshua M. Clulow * immediately.
248*1410cb93SJoshua M. Clulow */
249*1410cb93SJoshua M. Clulow if (dl->dl_triggered == B_TRUE)
250*1410cb93SJoshua M. Clulow return;
251*1410cb93SJoshua M. Clulow dl->dl_triggered = B_TRUE;
252*1410cb93SJoshua M. Clulow
253*1410cb93SJoshua M. Clulow /*
254*1410cb93SJoshua M. Clulow * Replace existing poll timer with coalesce timer:
255*1410cb93SJoshua M. Clulow */
256*1410cb93SJoshua M. Clulow if (dl->dl_timer != 0)
257*1410cb93SJoshua M. Clulow fmd_timer_remove(dl->dl_fmd, dl->dl_timer);
258*1410cb93SJoshua M. Clulow dl->dl_timer = fmd_timer_install(dl->dl_fmd, NULL, NULL,
259*1410cb93SJoshua M. Clulow dl->dl_coalesce_interval);
260*1410cb93SJoshua M. Clulow }
261*1410cb93SJoshua M. Clulow
262*1410cb93SJoshua M. Clulow /*ARGSUSED*/
263*1410cb93SJoshua M. Clulow static void
disklights_timeout(fmd_hdl_t * hdl,id_t id,void * data)264*1410cb93SJoshua M. Clulow disklights_timeout(fmd_hdl_t *hdl, id_t id, void *data)
265*1410cb93SJoshua M. Clulow {
266*1410cb93SJoshua M. Clulow disk_lights_t *dl = fmd_hdl_getspecific(hdl);
267*1410cb93SJoshua M. Clulow
268*1410cb93SJoshua M. Clulow dl->dl_triggered = B_FALSE;
269*1410cb93SJoshua M. Clulow
270*1410cb93SJoshua M. Clulow dl_examine_topo(dl);
271*1410cb93SJoshua M. Clulow
272*1410cb93SJoshua M. Clulow /*
273*1410cb93SJoshua M. Clulow * Install the long-interval timer for the next poll.
274*1410cb93SJoshua M. Clulow */
275*1410cb93SJoshua M. Clulow dl->dl_timer = fmd_timer_install(hdl, NULL, NULL, dl->dl_poll_interval);
276*1410cb93SJoshua M. Clulow }
277*1410cb93SJoshua M. Clulow
278*1410cb93SJoshua M. Clulow /*ARGSUSED*/
279*1410cb93SJoshua M. Clulow static void
disklights_topo(fmd_hdl_t * hdl,topo_hdl_t * thp)280*1410cb93SJoshua M. Clulow disklights_topo(fmd_hdl_t *hdl, topo_hdl_t *thp)
281*1410cb93SJoshua M. Clulow {
282*1410cb93SJoshua M. Clulow disk_lights_t *dl = fmd_hdl_getspecific(hdl);
283*1410cb93SJoshua M. Clulow
284*1410cb93SJoshua M. Clulow dl_trigger_enum(dl);
285*1410cb93SJoshua M. Clulow }
286*1410cb93SJoshua M. Clulow
287*1410cb93SJoshua M. Clulow /*ARGSUSED*/
288*1410cb93SJoshua M. Clulow static void
disklights_recv(fmd_hdl_t * hdl,fmd_event_t * ep,nvlist_t * nvl,const char * class)289*1410cb93SJoshua M. Clulow disklights_recv(fmd_hdl_t *hdl, fmd_event_t *ep, nvlist_t *nvl,
290*1410cb93SJoshua M. Clulow const char *class)
291*1410cb93SJoshua M. Clulow {
292*1410cb93SJoshua M. Clulow disk_lights_t *dl = fmd_hdl_getspecific(hdl);
293*1410cb93SJoshua M. Clulow
294*1410cb93SJoshua M. Clulow dl_trigger_enum(dl);
295*1410cb93SJoshua M. Clulow }
296*1410cb93SJoshua M. Clulow
297*1410cb93SJoshua M. Clulow void
_fmd_init(fmd_hdl_t * hdl)298*1410cb93SJoshua M. Clulow _fmd_init(fmd_hdl_t *hdl)
299*1410cb93SJoshua M. Clulow {
300*1410cb93SJoshua M. Clulow disk_lights_t *dl;
301*1410cb93SJoshua M. Clulow
302*1410cb93SJoshua M. Clulow if (fmd_hdl_register(hdl, FMD_API_VERSION, &fmd_info) != 0)
303*1410cb93SJoshua M. Clulow return;
304*1410cb93SJoshua M. Clulow
305*1410cb93SJoshua M. Clulow dl = fmd_hdl_zalloc(hdl, sizeof (*dl), FMD_SLEEP);
306*1410cb93SJoshua M. Clulow fmd_hdl_setspecific(hdl, dl);
307*1410cb93SJoshua M. Clulow
308*1410cb93SJoshua M. Clulow /*
309*1410cb93SJoshua M. Clulow * Load Configuration:
310*1410cb93SJoshua M. Clulow */
311*1410cb93SJoshua M. Clulow dl->dl_fmd = hdl;
312*1410cb93SJoshua M. Clulow dl->dl_poll_interval = fmd_prop_get_int64(hdl, DL_PROP_POLL_INTERVAL);
313*1410cb93SJoshua M. Clulow dl->dl_coalesce_interval = fmd_prop_get_int64(hdl,
314*1410cb93SJoshua M. Clulow DL_PROP_COALESCE_INTERVAL);
315*1410cb93SJoshua M. Clulow
316*1410cb93SJoshua M. Clulow /*
317*1410cb93SJoshua M. Clulow * Schedule the initial enumeration:
318*1410cb93SJoshua M. Clulow */
319*1410cb93SJoshua M. Clulow dl_trigger_enum(dl);
320*1410cb93SJoshua M. Clulow }
321*1410cb93SJoshua M. Clulow
322*1410cb93SJoshua M. Clulow void
_fmd_fini(fmd_hdl_t * hdl)323*1410cb93SJoshua M. Clulow _fmd_fini(fmd_hdl_t *hdl)
324*1410cb93SJoshua M. Clulow {
325*1410cb93SJoshua M. Clulow disk_lights_t *dl = fmd_hdl_getspecific(hdl);
326*1410cb93SJoshua M. Clulow
327*1410cb93SJoshua M. Clulow fmd_hdl_free(hdl, dl, sizeof (*dl));
328*1410cb93SJoshua M. Clulow }
329