1184cd04cScth /*
2184cd04cScth * CDDL HEADER START
3184cd04cScth *
4184cd04cScth * The contents of this file are subject to the terms of the
5184cd04cScth * Common Development and Distribution License (the "License").
6184cd04cScth * You may not use this file except in compliance with the License.
7184cd04cScth *
8184cd04cScth * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9184cd04cScth * or http://www.opensolaris.org/os/licensing.
10184cd04cScth * See the License for the specific language governing permissions
11184cd04cScth * and limitations under the License.
12184cd04cScth *
13184cd04cScth * When distributing Covered Code, include this CDDL HEADER in each
14184cd04cScth * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15184cd04cScth * If applicable, add the following below this CDDL HEADER, with the
16184cd04cScth * fields enclosed by brackets "[]" replaced with your own identifying
17184cd04cScth * information: Portions Copyright [yyyy] [name of copyright owner]
18184cd04cScth *
19184cd04cScth * CDDL HEADER END
20184cd04cScth */
21184cd04cScth /*
22*81d9f076SRobert Johnston * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
23184cd04cScth * Use is subject to license terms.
24184cd04cScth */
25184cd04cScth
26184cd04cScth #include <ctype.h>
27184cd04cScth #include <libipmi.h>
28184cd04cScth #include <libnvpair.h>
29184cd04cScth #include <libuutil.h>
30184cd04cScth #include <limits.h>
31184cd04cScth #include <stddef.h>
32184cd04cScth #include <string.h>
33184cd04cScth
34184cd04cScth #include "diskmon_conf.h"
35184cd04cScth #include "dm_platform.h"
36184cd04cScth #include "util.h"
37184cd04cScth
38184cd04cScth /* For the purposes of disk capacity, a <X>B is 1000x, not 1024x */
39184cd04cScth #define ONE_KILOBYTE 1000.0
40184cd04cScth #define ONE_MEGABYTE (ONE_KILOBYTE * 1000)
41184cd04cScth #define ONE_GIGABYTE (ONE_MEGABYTE * 1000)
42184cd04cScth #define ONE_TERABYTE (ONE_GIGABYTE * 1000)
43184cd04cScth #define ONE_PETABYTE (ONE_TERABYTE * 1000)
44184cd04cScth
45184cd04cScth static ipmi_handle_t *g_ipmi_hdl;
46184cd04cScth
47184cd04cScth typedef enum {
48184cd04cScth IPMI_CACHE_SENSOR,
49184cd04cScth IPMI_CACHE_FRU
50184cd04cScth } ipmi_cache_type_t;
51184cd04cScth
52184cd04cScth typedef struct ipmi_cache_entry {
53184cd04cScth ipmi_cache_type_t ic_type;
54184cd04cScth uu_list_node_t ic_node;
55184cd04cScth union {
56184cd04cScth ipmi_set_sensor_reading_t ic_sensor;
57184cd04cScth ipmi_sunoem_fru_t ic_fru;
58184cd04cScth } ic_data;
59184cd04cScth } ipmi_cache_entry_t;
60184cd04cScth
61184cd04cScth static pthread_mutex_t g_ipmi_mtx = PTHREAD_MUTEX_INITIALIZER;
62184cd04cScth static uu_list_pool_t *g_ipmi_cache_pool;
63184cd04cScth static uu_list_t *g_ipmi_cache;
64184cd04cScth
65184cd04cScth /*
66184cd04cScth * The textual strings that are used in the actions may be one of the
67184cd04cScth * following forms:
68184cd04cScth *
69184cd04cScth * [1] `fru gid=<n> hdd=<m>'
70184cd04cScth * [2] `sensor id=<x> assert=<y> deassert=<z>'
71184cd04cScth *
72184cd04cScth * The generic parser will take a string and spit out the first token
73184cd04cScth * (e.g. `fru' or `sensor') and an nvlist that contains the key-value
74184cd04cScth * pairs in the rest of the string. The assumption is that there are
75184cd04cScth * no embedded spaces or tabs in the keys or values.
76184cd04cScth */
77184cd04cScth
78184cd04cScth static boolean_t
isnumber(const char * str)79184cd04cScth isnumber(const char *str)
80184cd04cScth {
81184cd04cScth boolean_t hex = B_FALSE;
82184cd04cScth int digits = 0;
83184cd04cScth
84184cd04cScth if (strncasecmp(str, "0x", 2) == 0) {
85184cd04cScth hex = B_TRUE;
86184cd04cScth str += 2;
87184cd04cScth } else if (*str == '-' || *str == '+') {
88184cd04cScth str++;
89184cd04cScth }
90184cd04cScth
91184cd04cScth while (*str != 0) {
92184cd04cScth if ((hex && !isxdigit(*str)) ||
93184cd04cScth (!hex && !isdigit(*str))) {
94184cd04cScth return (B_FALSE);
95184cd04cScth }
96184cd04cScth
97184cd04cScth str++;
98184cd04cScth digits++;
99184cd04cScth }
100184cd04cScth
101184cd04cScth return ((digits == 0) ? B_FALSE : B_TRUE);
102184cd04cScth }
103184cd04cScth
104184cd04cScth static void
tolowerString(char * str)105184cd04cScth tolowerString(char *str)
106184cd04cScth {
107184cd04cScth while (*str != 0) {
108184cd04cScth *str = tolower(*str);
109184cd04cScth str++;
110184cd04cScth }
111184cd04cScth }
112184cd04cScth
113184cd04cScth static boolean_t
parse_action_string(const char * actionString,char ** cmdp,nvlist_t ** propsp)114184cd04cScth parse_action_string(const char *actionString, char **cmdp, nvlist_t **propsp)
115184cd04cScth {
116184cd04cScth char *action;
117184cd04cScth char *tok, *lasts, *eq;
118184cd04cScth int actionlen;
119184cd04cScth boolean_t rv = B_TRUE;
120184cd04cScth
121184cd04cScth if (nvlist_alloc(propsp, NV_UNIQUE_NAME, 0) != 0)
122184cd04cScth return (B_FALSE);
123184cd04cScth
124184cd04cScth actionlen = strlen(actionString) + 1;
125184cd04cScth action = dstrdup(actionString);
126184cd04cScth
127184cd04cScth *cmdp = NULL;
128184cd04cScth
129184cd04cScth if ((tok = strtok_r(action, " \t", &lasts)) != NULL) {
130184cd04cScth
131184cd04cScth *cmdp = dstrdup(tok);
132184cd04cScth
133184cd04cScth while (rv && (tok = strtok_r(NULL, " \t", &lasts)) != NULL) {
134184cd04cScth
135184cd04cScth /* Look for a name=val construct */
136184cd04cScth if ((eq = strchr(tok, '=')) != NULL && eq[1] != 0) {
137184cd04cScth
138184cd04cScth *eq = 0;
139184cd04cScth eq++;
140184cd04cScth
141184cd04cScth /*
142184cd04cScth * Convert token to lowercase to preserve
143184cd04cScth * case-insensitivity, because nvlist doesn't
144184cd04cScth * do case-insensitive lookups
145184cd04cScth */
146184cd04cScth tolowerString(tok);
147184cd04cScth
148184cd04cScth if (isnumber(eq)) {
149184cd04cScth /* Integer property */
150184cd04cScth
151184cd04cScth if (nvlist_add_uint64(*propsp, tok,
152184cd04cScth strtoull(eq, NULL, 0)) != 0)
153184cd04cScth rv = B_FALSE;
154184cd04cScth } else {
155184cd04cScth /* String property */
156184cd04cScth
157184cd04cScth if (nvlist_add_string(*propsp, tok,
158184cd04cScth eq) != 0)
159184cd04cScth rv = B_FALSE;
160184cd04cScth }
161184cd04cScth } else if (eq == NULL) {
162184cd04cScth /* Boolean property */
163184cd04cScth if (nvlist_add_boolean(*propsp, tok) != 0)
164184cd04cScth rv = B_FALSE;
165184cd04cScth } else /* Parse error (`X=' is invalid) */
166184cd04cScth rv = B_FALSE;
167184cd04cScth }
168184cd04cScth } else
169184cd04cScth rv = B_FALSE;
170184cd04cScth
171184cd04cScth dfree(action, actionlen);
172184cd04cScth if (!rv) {
173184cd04cScth if (*cmdp) {
174184cd04cScth dstrfree(*cmdp);
175184cd04cScth *cmdp = NULL;
176184cd04cScth }
177184cd04cScth nvlist_free(*propsp);
178184cd04cScth *propsp = NULL;
179184cd04cScth }
180184cd04cScth return (rv);
181184cd04cScth }
182184cd04cScth
183184cd04cScth static int
platform_update_fru(nvlist_t * props,dm_fru_t * frup)184184cd04cScth platform_update_fru(nvlist_t *props, dm_fru_t *frup)
185184cd04cScth {
186184cd04cScth uint64_t gid, hdd;
187184cd04cScth ipmi_sunoem_fru_t fru;
188184cd04cScth char *buf;
189184cd04cScth ipmi_cache_entry_t *entry;
190184cd04cScth
191184cd04cScth if (nvlist_lookup_uint64(props, "gid", &gid) != 0 ||
192184cd04cScth nvlist_lookup_uint64(props, "hdd", &hdd) != 0) {
193184cd04cScth return (-1);
194184cd04cScth }
195184cd04cScth
196184cd04cScth fru.isf_type = (uint8_t)gid;
197184cd04cScth fru.isf_id = (uint8_t)hdd;
198184cd04cScth
199184cd04cScth buf = (char *)dzmalloc(sizeof (fru.isf_data.disk.isf_capacity) + 1);
200184cd04cScth
201184cd04cScth (void) memcpy(fru.isf_data.disk.isf_manufacturer, frup->manuf,
202184cd04cScth MIN(sizeof (fru.isf_data.disk.isf_manufacturer),
203184cd04cScth sizeof (frup->manuf)));
204184cd04cScth (void) memcpy(fru.isf_data.disk.isf_model, frup->model,
205184cd04cScth MIN(sizeof (fru.isf_data.disk.isf_model), sizeof (frup->model)));
206184cd04cScth (void) memcpy(fru.isf_data.disk.isf_serial, frup->serial,
207184cd04cScth MIN(sizeof (fru.isf_data.disk.isf_serial), sizeof (frup->serial)));
208184cd04cScth (void) memcpy(fru.isf_data.disk.isf_version, frup->rev,
209184cd04cScth MIN(sizeof (fru.isf_data.disk.isf_version), sizeof (frup->rev)));
210184cd04cScth /*
211184cd04cScth * Print the size of the disk to a temporary buffer whose size is
212184cd04cScth * 1 more than the size of the buffer in the ipmi request data
213184cd04cScth * structure, so we can get the full 8 characters (instead of 7 + NUL)
214184cd04cScth */
215184cd04cScth (void) snprintf(buf, sizeof (fru.isf_data.disk.isf_capacity) + 1,
216184cd04cScth "%.1f%s",
217184cd04cScth frup->size_in_bytes >= ONE_PETABYTE ?
218184cd04cScth (frup->size_in_bytes / ONE_PETABYTE) :
219184cd04cScth (frup->size_in_bytes >= ONE_TERABYTE ?
220184cd04cScth (frup->size_in_bytes / ONE_TERABYTE) :
221184cd04cScth (frup->size_in_bytes >= ONE_GIGABYTE ?
222184cd04cScth (frup->size_in_bytes / ONE_GIGABYTE) :
223184cd04cScth (frup->size_in_bytes >= ONE_MEGABYTE ?
224184cd04cScth (frup->size_in_bytes / ONE_MEGABYTE) :
225184cd04cScth (frup->size_in_bytes / ONE_KILOBYTE)))),
226184cd04cScth
227184cd04cScth frup->size_in_bytes >= ONE_PETABYTE ? "PB" :
228184cd04cScth (frup->size_in_bytes >= ONE_TERABYTE ? "TB" :
229184cd04cScth (frup->size_in_bytes >= ONE_GIGABYTE ? "GB" :
230184cd04cScth (frup->size_in_bytes >= ONE_MEGABYTE ? "MB" :
231184cd04cScth "KB"))));
232184cd04cScth (void) memcpy(fru.isf_data.disk.isf_capacity, buf,
233184cd04cScth sizeof (fru.isf_data.disk.isf_capacity));
234184cd04cScth
235184cd04cScth dfree(buf, sizeof (fru.isf_data.disk.isf_capacity) + 1);
236184cd04cScth
237184cd04cScth if (ipmi_sunoem_update_fru(g_ipmi_hdl, &fru) != 0)
238184cd04cScth return (-1);
239184cd04cScth
240184cd04cScth /* find a cache entry or create one if necessary */
241184cd04cScth for (entry = uu_list_first(g_ipmi_cache); entry != NULL;
242184cd04cScth entry = uu_list_next(g_ipmi_cache, entry)) {
243184cd04cScth if (entry->ic_type == IPMI_CACHE_FRU &&
244184cd04cScth entry->ic_data.ic_fru.isf_type == gid &&
245184cd04cScth entry->ic_data.ic_fru.isf_id == hdd)
246184cd04cScth break;
247184cd04cScth }
248184cd04cScth
249184cd04cScth if (entry == NULL) {
250184cd04cScth entry = dzmalloc(sizeof (ipmi_cache_entry_t));
251184cd04cScth entry->ic_type = IPMI_CACHE_FRU;
252184cd04cScth (void) uu_list_insert_before(g_ipmi_cache, NULL, entry);
253184cd04cScth }
254184cd04cScth
255184cd04cScth (void) memcpy(&entry->ic_data.ic_fru, &fru, sizeof (fru));
256184cd04cScth
257184cd04cScth return (0);
258184cd04cScth }
259184cd04cScth
260184cd04cScth static int
platform_set_sensor(nvlist_t * props)261184cd04cScth platform_set_sensor(nvlist_t *props)
262184cd04cScth {
263184cd04cScth uint64_t assertmask = 0, deassertmask = 0, sid;
264184cd04cScth boolean_t am_present, dam_present;
265184cd04cScth ipmi_set_sensor_reading_t sr, *sp;
266184cd04cScth ipmi_cache_entry_t *entry;
267184cd04cScth int ret;
268184cd04cScth
269184cd04cScth /* We need at least 2 properties: `sid' and (`amask' || `dmask'): */
270184cd04cScth am_present = nvlist_lookup_uint64(props, "amask", &assertmask) == 0;
271184cd04cScth dam_present = nvlist_lookup_uint64(props, "dmask", &deassertmask) == 0;
272184cd04cScth
273184cd04cScth if (nvlist_lookup_uint64(props, "sid", &sid) != 0 ||
274184cd04cScth (!am_present && !dam_present)) {
275184cd04cScth return (-1);
276184cd04cScth }
277184cd04cScth
278184cd04cScth if (sid > UINT8_MAX) {
279184cd04cScth log_warn("IPMI Plugin: Invalid sensor id `0x%llx'.\n",
280184cd04cScth (longlong_t)sid);
281184cd04cScth return (-1);
282184cd04cScth } else if (assertmask > UINT16_MAX) {
283184cd04cScth log_warn("IPMI Plugin: Invalid assertion mask `0x%llx'.\n",
284184cd04cScth (longlong_t)assertmask);
285184cd04cScth return (-1);
286184cd04cScth } else if (assertmask > UINT16_MAX) {
287184cd04cScth log_warn("IPMI Plugin: Invalid deassertion mask `0x%llx'.\n",
288184cd04cScth (longlong_t)deassertmask);
289184cd04cScth return (-1);
290184cd04cScth }
291184cd04cScth
292184cd04cScth (void) memset(&sr, '\0', sizeof (sr));
293184cd04cScth sr.iss_id = (uint8_t)sid;
294184cd04cScth if (am_present) {
295184cd04cScth sr.iss_assert_op = IPMI_SENSOR_OP_SET;
296184cd04cScth sr.iss_assert_state = (uint16_t)assertmask;
297184cd04cScth }
298184cd04cScth if (dam_present) {
299184cd04cScth sr.iss_deassrt_op = IPMI_SENSOR_OP_SET;
300184cd04cScth sr.iss_deassert_state = (uint16_t)deassertmask;
301184cd04cScth }
302184cd04cScth
303184cd04cScth ret = ipmi_set_sensor_reading(g_ipmi_hdl, &sr);
304184cd04cScth
305184cd04cScth /* find a cache entry or create one if necessary */
306184cd04cScth for (entry = uu_list_first(g_ipmi_cache); entry != NULL;
307184cd04cScth entry = uu_list_next(g_ipmi_cache, entry)) {
308184cd04cScth if (entry->ic_type == IPMI_CACHE_SENSOR &&
309184cd04cScth entry->ic_data.ic_sensor.iss_id == (uint8_t)sid)
310184cd04cScth break;
311184cd04cScth }
312184cd04cScth
313184cd04cScth if (entry == NULL) {
314184cd04cScth entry = dzmalloc(sizeof (ipmi_cache_entry_t));
315184cd04cScth entry->ic_type = IPMI_CACHE_SENSOR;
316184cd04cScth (void) uu_list_insert_before(g_ipmi_cache, NULL, entry);
317184cd04cScth entry->ic_data.ic_sensor.iss_id = (uint8_t)sid;
318184cd04cScth entry->ic_data.ic_sensor.iss_assert_op = IPMI_SENSOR_OP_SET;
319184cd04cScth entry->ic_data.ic_sensor.iss_deassrt_op = IPMI_SENSOR_OP_SET;
320184cd04cScth }
321184cd04cScth sp = &entry->ic_data.ic_sensor;
322184cd04cScth
323184cd04cScth if (am_present) {
324184cd04cScth sp->iss_assert_state |= assertmask;
325184cd04cScth sp->iss_deassert_state &= ~assertmask;
326184cd04cScth }
327184cd04cScth if (dam_present) {
328184cd04cScth sp->iss_deassert_state |= deassertmask;
329184cd04cScth sp->iss_assert_state &= ~deassertmask;
330184cd04cScth }
331184cd04cScth
332184cd04cScth return (ret);
333184cd04cScth }
334184cd04cScth
335184cd04cScth #define PROTOCOL_SEPARATOR ':'
336184cd04cScth
337184cd04cScth static char *
extract_protocol(const char * action)338184cd04cScth extract_protocol(const char *action)
339184cd04cScth {
340184cd04cScth char *s = strchr(action, PROTOCOL_SEPARATOR);
341184cd04cScth char *proto = NULL;
342184cd04cScth int len;
343184cd04cScth int i = 0;
344184cd04cScth
345184cd04cScth /* The protocol is the string before the separator, but in lower-case */
346184cd04cScth if (s) {
347184cd04cScth len = (uintptr_t)s - (uintptr_t)action;
348184cd04cScth proto = (char *)dmalloc(len + 1);
349184cd04cScth while (i < len) {
350184cd04cScth proto[i] = tolower(action[i]);
351184cd04cScth i++;
352184cd04cScth }
353184cd04cScth proto[len] = 0;
354184cd04cScth }
355184cd04cScth
356184cd04cScth return (proto);
357184cd04cScth }
358184cd04cScth
359184cd04cScth static char *
extract_action(const char * action)360184cd04cScth extract_action(const char *action)
361184cd04cScth {
362184cd04cScth /* The action is the string after the separator */
363184cd04cScth char *s = strchr(action, PROTOCOL_SEPARATOR);
364184cd04cScth
365184cd04cScth return (s ? (s + 1) : NULL);
366184cd04cScth }
367184cd04cScth
368184cd04cScth static int
do_action(const char * action,dm_fru_t * fru)369184cd04cScth do_action(const char *action, dm_fru_t *fru)
370184cd04cScth {
371184cd04cScth nvlist_t *props;
372184cd04cScth char *cmd;
373184cd04cScth int rv = -1;
374184cd04cScth char *protocol = extract_protocol(action);
375184cd04cScth char *actionp = extract_action(action);
376184cd04cScth
377184cd04cScth if (strcmp(protocol, "ipmi") != 0) {
378184cd04cScth log_err("unknown protocol '%s'\n", protocol);
379184cd04cScth dstrfree(protocol);
380184cd04cScth return (-1);
381184cd04cScth }
382184cd04cScth
383184cd04cScth dstrfree(protocol);
384184cd04cScth
385184cd04cScth (void) pthread_mutex_lock(&g_ipmi_mtx);
386184cd04cScth if (parse_action_string(actionp, &cmd, &props)) {
387184cd04cScth if (strcmp(cmd, "fru") == 0) {
388184cd04cScth rv = platform_update_fru(props, fru);
389184cd04cScth } else if (strcmp(cmd, "state") == 0) {
390184cd04cScth rv = platform_set_sensor(props);
391184cd04cScth } else {
392184cd04cScth log_err("unknown platform action '%s'\n", cmd);
393184cd04cScth }
394184cd04cScth dstrfree(cmd);
395184cd04cScth nvlist_free(props);
396184cd04cScth }
397184cd04cScth (void) pthread_mutex_unlock(&g_ipmi_mtx);
398184cd04cScth
399184cd04cScth return (rv);
400184cd04cScth }
401184cd04cScth
402184cd04cScth int
dm_platform_update_fru(const char * action,dm_fru_t * fru)403184cd04cScth dm_platform_update_fru(const char *action, dm_fru_t *fru)
404184cd04cScth {
405184cd04cScth return (do_action(action, fru));
406184cd04cScth }
407184cd04cScth
408184cd04cScth int
dm_platform_indicator_execute(const char * action)409184cd04cScth dm_platform_indicator_execute(const char *action)
410184cd04cScth {
411184cd04cScth return (do_action(action, NULL));
412184cd04cScth }
413184cd04cScth
414184cd04cScth int
dm_platform_resync(void)415184cd04cScth dm_platform_resync(void)
416184cd04cScth {
417184cd04cScth ipmi_cache_entry_t *entry;
418184cd04cScth int rv = 0;
419184cd04cScth
420184cd04cScth (void) pthread_mutex_lock(&g_ipmi_mtx);
421184cd04cScth
422184cd04cScth /*
423184cd04cScth * Called when the SP is reset, as the sensor/FRU state is not
424184cd04cScth * maintained across reboots. Note that we must update the FRU
425184cd04cScth * information first, as certain sensor states prevent this from
426184cd04cScth * working.
427184cd04cScth */
428184cd04cScth for (entry = uu_list_first(g_ipmi_cache); entry != NULL;
429184cd04cScth entry = uu_list_next(g_ipmi_cache, entry)) {
430184cd04cScth if (entry->ic_type == IPMI_CACHE_FRU)
431184cd04cScth rv |= ipmi_sunoem_update_fru(g_ipmi_hdl,
432184cd04cScth &entry->ic_data.ic_fru);
433184cd04cScth }
434184cd04cScth
435184cd04cScth for (entry = uu_list_first(g_ipmi_cache); entry != NULL;
436184cd04cScth entry = uu_list_next(g_ipmi_cache, entry)) {
437184cd04cScth if (entry->ic_type == IPMI_CACHE_SENSOR)
438184cd04cScth rv |= ipmi_set_sensor_reading(g_ipmi_hdl,
439184cd04cScth &entry->ic_data.ic_sensor);
440184cd04cScth }
441184cd04cScth
442184cd04cScth (void) pthread_mutex_unlock(&g_ipmi_mtx);
443184cd04cScth return (rv);
444184cd04cScth }
445184cd04cScth
446184cd04cScth int
dm_platform_init(void)447184cd04cScth dm_platform_init(void)
448184cd04cScth {
449184cd04cScth int err;
450184cd04cScth char *msg;
451184cd04cScth
452*81d9f076SRobert Johnston if ((g_ipmi_hdl = ipmi_open(&err, &msg, IPMI_TRANSPORT_BMC, NULL))
453*81d9f076SRobert Johnston == NULL) {
454184cd04cScth log_warn("Failed to load libipmi: %s\n", msg);
455184cd04cScth return (-1);
456184cd04cScth }
457184cd04cScth
458184cd04cScth if ((g_ipmi_cache_pool = uu_list_pool_create(
459184cd04cScth "ipmi_cache", sizeof (ipmi_cache_entry_t),
460184cd04cScth offsetof(ipmi_cache_entry_t, ic_node), NULL, 0)) == NULL)
461184cd04cScth return (-1);
462184cd04cScth
463184cd04cScth if ((g_ipmi_cache = uu_list_create(g_ipmi_cache_pool, NULL, 0))
464184cd04cScth == NULL)
465184cd04cScth return (-1);
466184cd04cScth
467184cd04cScth return (0);
468184cd04cScth }
469184cd04cScth
470184cd04cScth void
dm_platform_fini(void)471184cd04cScth dm_platform_fini(void)
472184cd04cScth {
473184cd04cScth if (g_ipmi_hdl)
474184cd04cScth ipmi_close(g_ipmi_hdl);
475184cd04cScth if (g_ipmi_cache) {
476184cd04cScth ipmi_cache_entry_t *entry;
477184cd04cScth
478184cd04cScth while ((entry = uu_list_first(g_ipmi_cache)) != NULL) {
479184cd04cScth uu_list_remove(g_ipmi_cache, entry);
480184cd04cScth dfree(entry, sizeof (*entry));
481184cd04cScth }
482184cd04cScth uu_list_destroy(g_ipmi_cache);
483184cd04cScth }
484184cd04cScth if (g_ipmi_cache_pool)
485184cd04cScth uu_list_pool_destroy(g_ipmi_cache_pool);
486184cd04cScth }
487