diff options
Diffstat (limited to 'usr/src')
-rw-r--r-- | usr/src/lib/libipmi/common/ipmi_fru.c | 38 |
1 files changed, 32 insertions, 6 deletions
diff --git a/usr/src/lib/libipmi/common/ipmi_fru.c b/usr/src/lib/libipmi/common/ipmi_fru.c index d652c23cd4..b429f91934 100644 --- a/usr/src/lib/libipmi/common/ipmi_fru.c +++ b/usr/src/lib/libipmi/common/ipmi_fru.c @@ -22,9 +22,9 @@ * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ - -#pragma ident "%Z%%M% %I% %E% SMI" - +/* + * Copyright (c) 2017, Joyent, Inc. + */ #include <libipmi.h> #include <string.h> @@ -36,6 +36,13 @@ */ #define BITX(u, h, l) (((u) >> (l)) & ((1LU << ((h) - (l) + 1LU)) - 1LU)) +/* + * The default and minimum size in bytes that will be used when reading + * the FRU inventory area. + */ +#define DEF_CHUNK_SZ 128 +#define MIN_CHUNK_SZ 16 + typedef struct ipmi_fru_read { uint8_t ifr_devid; @@ -52,7 +59,8 @@ int ipmi_fru_read(ipmi_handle_t *ihp, ipmi_sdr_fru_locator_t *fru_loc, char **buf) { ipmi_cmd_t cmd, *resp; - uint8_t count, devid; + int ierrno; + uint8_t count, devid, chunksz; uint16_t sz, offset = 0; ipmi_fru_read_t cmd_data_in; char *tmp; @@ -82,14 +90,15 @@ ipmi_fru_read(ipmi_handle_t *ihp, ipmi_sdr_fru_locator_t *fru_loc, char **buf) return (-1); } + chunksz = DEF_CHUNK_SZ; while (offset < sz) { cmd_data_in.ifr_devid = devid; cmd_data_in.ifr_offset_lsb = BITX(offset, 7, 0); cmd_data_in.ifr_offset_msb = BITX(offset, 15, 8); - if ((sz - offset) < 128) + if ((sz - offset) < chunksz) cmd_data_in.ifr_count = sz - offset; else - cmd_data_in.ifr_count = 128; + cmd_data_in.ifr_count = chunksz; cmd.ic_netfn = IPMI_NETFN_STORAGE; cmd.ic_cmd = IPMI_CMD_READ_FRU_DATA; @@ -97,7 +106,24 @@ ipmi_fru_read(ipmi_handle_t *ihp, ipmi_sdr_fru_locator_t *fru_loc, char **buf) cmd.ic_dlen = sizeof (ipmi_fru_read_t); cmd.ic_lun = 0; + /* + * The FRU area must be read in chunks as its total size will + * be larger that what would fit in a single message. The + * maximum size of a message can vary between platforms so + * if while attempting to read a chunk we receive an error code + * indicating that the requested chunk size is invalid, we will + * perform a reverse exponential backoff of the chunk size until + * either the read succeeds or we hit bottom, at which point + * we'll fail the operation. + */ if ((resp = ipmi_send(ihp, &cmd)) == NULL) { + ierrno = ipmi_errno(ihp); + if (chunksz > MIN_CHUNK_SZ && + (ierrno == EIPMI_DATA_LENGTH_EXCEEDED || + ierrno == EIPMI_INVALID_REQUEST)) { + chunksz = chunksz >> 1; + continue; + } free(tmp); return (-1); } |