The current IPMI KCS code is waiting 100us for all transitions (roughly

between each byte either sent or received). However, most transitions
actually complete in 2-3 microseconds.

By polling the status register with a delay of 4us with exponential
backoff, the performance of most IPMI operations is significantly
improved:
  - A BMC update on a Supermicro x9 or x11 motherboard goes from ~1 hour
    to ~6-8 minutes.
  - An ipmitool sensor list time improves by a factor of 4.

Testing showed no significant improvements on a modern server by using
a lower delay.

The changes should also generally reduce the total amount of CPU or
I/O bandwidth used for a given IPMI operation.

Submitted by:	Loic Prylli <lprylli@netflix.com>
Reviewed by:	jhb
MFC after:	2 weeks
Sponsored by:	Netflix
Differential Revision:	https://reviews.freebsd.org/D20527
This commit is contained in:
Jonathan T. Looney 2019-06-12 16:06:31 +00:00
parent 3a3ab50916
commit 1524298754
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=348996

View File

@ -48,55 +48,46 @@ __FBSDID("$FreeBSD$");
#include <dev/ipmi/ipmivars.h>
#endif
#define POLLING_DELAY_MIN 4 /* Waits are 2-3 usecs on typical systems */
#define POLLING_DELAY_MAX 256
static void kcs_clear_obf(struct ipmi_softc *, int);
static void kcs_error(struct ipmi_softc *);
static int kcs_wait_for_ibf(struct ipmi_softc *, int);
static int kcs_wait_for_obf(struct ipmi_softc *, int);
static int kcs_wait_for_ibf(struct ipmi_softc *, bool);
static int kcs_wait_for_obf(struct ipmi_softc *, bool);
static int
kcs_wait_for_ibf(struct ipmi_softc *sc, int state)
kcs_wait(struct ipmi_softc *sc, int value, int mask)
{
int status, start = ticks;
int delay_usec = POLLING_DELAY_MIN;
status = INB(sc, KCS_CTL_STS);
if (state == 0) {
/* WAIT FOR IBF = 0 */
while (ticks - start < MAX_TIMEOUT && status & KCS_STATUS_IBF) {
DELAY(100);
status = INB(sc, KCS_CTL_STS);
}
} else {
/* WAIT FOR IBF = 1 */
while (ticks - start < MAX_TIMEOUT &&
!(status & KCS_STATUS_IBF)) {
DELAY(100);
status = INB(sc, KCS_CTL_STS);
}
while (ticks - start < MAX_TIMEOUT && (status & mask) != value) {
/*
* The wait delay is increased exponentially to avoid putting
* significant load on I/O bus.
*/
DELAY(delay_usec);
status = INB(sc, KCS_CTL_STS);
if (delay_usec < POLLING_DELAY_MAX)
delay_usec *= 2;
}
return (status);
}
static int
kcs_wait_for_obf(struct ipmi_softc *sc, int state)
kcs_wait_for_ibf(struct ipmi_softc *sc, bool level)
{
int status, start = ticks;
status = INB(sc, KCS_CTL_STS);
if (state == 0) {
/* WAIT FOR OBF = 0 */
while (ticks - start < MAX_TIMEOUT && status & KCS_STATUS_OBF) {
DELAY(100);
status = INB(sc, KCS_CTL_STS);
}
} else {
/* WAIT FOR OBF = 1 */
while (ticks - start < MAX_TIMEOUT &&
!(status & KCS_STATUS_OBF)) {
DELAY(100);
status = INB(sc, KCS_CTL_STS);
}
}
return (status);
return (kcs_wait(sc, level ? KCS_STATUS_IBF : 0, KCS_STATUS_IBF));
}
static int
kcs_wait_for_obf(struct ipmi_softc *sc, bool level)
{
return (kcs_wait(sc, level ? KCS_STATUS_OBF : 0, KCS_STATUS_OBF));
}
static void