diff options
Diffstat (limited to 'drivers/char/ipmi')
| -rw-r--r-- | drivers/char/ipmi/Kconfig | 25 | ||||
| -rw-r--r-- | drivers/char/ipmi/Makefile | 6 | ||||
| -rw-r--r-- | drivers/char/ipmi/ipmi_bt_sm.c | 724 | ||||
| -rw-r--r-- | drivers/char/ipmi/ipmi_devintf.c | 129 | ||||
| -rw-r--r-- | drivers/char/ipmi/ipmi_kcs_sm.c | 182 | ||||
| -rw-r--r-- | drivers/char/ipmi/ipmi_msghandler.c | 3087 | ||||
| -rw-r--r-- | drivers/char/ipmi/ipmi_poweroff.c | 271 | ||||
| -rw-r--r-- | drivers/char/ipmi/ipmi_si_intf.c | 2499 | ||||
| -rw-r--r-- | drivers/char/ipmi/ipmi_si_sm.h | 89 | ||||
| -rw-r--r-- | drivers/char/ipmi/ipmi_smic_sm.c | 165 | ||||
| -rw-r--r-- | drivers/char/ipmi/ipmi_watchdog.c | 735 |
11 files changed, 5235 insertions, 2677 deletions
diff --git a/drivers/char/ipmi/Kconfig b/drivers/char/ipmi/Kconfig index a6dcb291815..db1c9b7adaa 100644 --- a/drivers/char/ipmi/Kconfig +++ b/drivers/char/ipmi/Kconfig @@ -2,9 +2,9 @@ # IPMI device configuration # -menu "IPMI" -config IPMI_HANDLER +menuconfig IPMI_HANDLER tristate 'IPMI top-level message handler' + depends on HAS_IOMEM help This enables the central IPMI message handler, required for IPMI to work. @@ -16,9 +16,10 @@ config IPMI_HANDLER If unsure, say N. +if IPMI_HANDLER + config IPMI_PANIC_EVENT bool 'Generate a panic event to all BMCs on a panic' - depends on IPMI_HANDLER help When a panic occurs, this will cause the IPMI message handler to generate an IPMI event describing the panic to each interface @@ -38,30 +39,38 @@ config IPMI_PANIC_STRING config IPMI_DEVICE_INTERFACE tristate 'Device interface for IPMI' - depends on IPMI_HANDLER help This provides an IOCTL interface to the IPMI message handler so userland processes may use IPMI. It supports poll() and select(). config IPMI_SI tristate 'IPMI System Interface handler' - depends on IPMI_HANDLER help Provides a driver for System Interfaces (KCS, SMIC, BT). Currently, only KCS and SMIC are supported. If you are using IPMI, you should probably say "y" here. +config IPMI_SI_PROBE_DEFAULTS + bool 'Probe for all possible IPMI system interfaces by default' + default n + depends on IPMI_SI + help + Modern systems will usually expose IPMI interfaces via a discoverable + firmware mechanism such as ACPI or DMI. Older systems do not, and so + the driver is forced to probe hardware manually. This may cause boot + delays. Say "n" here to disable this manual probing. IPMI will then + only be available on older systems if the "ipmi_si_intf.trydefaults=1" + boot argument is passed. + config IPMI_WATCHDOG tristate 'IPMI Watchdog Timer' - depends on IPMI_HANDLER help This enables the IPMI watchdog timer. config IPMI_POWEROFF tristate 'IPMI Poweroff' - depends on IPMI_HANDLER help This enables a function to power off the system with IPMI if the IPMI management controller is capable of this. -endmenu +endif # IPMI_HANDLER diff --git a/drivers/char/ipmi/Makefile b/drivers/char/ipmi/Makefile index 553f0a408ed..16a93648d54 100644 --- a/drivers/char/ipmi/Makefile +++ b/drivers/char/ipmi/Makefile @@ -2,14 +2,10 @@ # Makefile for the ipmi drivers. # -ipmi_si-objs := ipmi_si_intf.o ipmi_kcs_sm.o ipmi_smic_sm.o ipmi_bt_sm.o +ipmi_si-y := ipmi_si_intf.o ipmi_kcs_sm.o ipmi_smic_sm.o ipmi_bt_sm.o obj-$(CONFIG_IPMI_HANDLER) += ipmi_msghandler.o obj-$(CONFIG_IPMI_DEVICE_INTERFACE) += ipmi_devintf.o obj-$(CONFIG_IPMI_SI) += ipmi_si.o obj-$(CONFIG_IPMI_WATCHDOG) += ipmi_watchdog.o obj-$(CONFIG_IPMI_POWEROFF) += ipmi_poweroff.o - -ipmi_si.o: $(ipmi_si-objs) - $(LD) -r -o $@ $(ipmi_si-objs) - diff --git a/drivers/char/ipmi/ipmi_bt_sm.c b/drivers/char/ipmi/ipmi_bt_sm.c index 0030cd8e2e9..61e71616689 100644 --- a/drivers/char/ipmi/ipmi_bt_sm.c +++ b/drivers/char/ipmi/ipmi_bt_sm.c @@ -2,7 +2,7 @@ * ipmi_bt_sm.c * * The state machine for an Open IPMI BT sub-driver under ipmi_si.c, part - * of the driver architecture at http://sourceforge.net/project/openipmi + * of the driver architecture at http://sourceforge.net/projects/openipmi * * Author: Rocky Craig <first.last@hp.com> * @@ -33,52 +33,80 @@ #include <linux/ipmi_msgdefs.h> /* for completion codes */ #include "ipmi_si_sm.h" -static int bt_debug = 0x00; /* Production value 0, see following flags */ +#define BT_DEBUG_OFF 0 /* Used in production */ +#define BT_DEBUG_ENABLE 1 /* Generic messages */ +#define BT_DEBUG_MSG 2 /* Prints all request/response buffers */ +#define BT_DEBUG_STATES 4 /* Verbose look at state changes */ +/* + * BT_DEBUG_OFF must be zero to correspond to the default uninitialized + * value + */ + +static int bt_debug; /* 0 == BT_DEBUG_OFF */ -#define BT_DEBUG_ENABLE 1 -#define BT_DEBUG_MSG 2 -#define BT_DEBUG_STATES 4 module_param(bt_debug, int, 0644); MODULE_PARM_DESC(bt_debug, "debug bitmask, 1=enable, 2=messages, 4=states"); -/* Typical "Get BT Capabilities" values are 2-3 retries, 5-10 seconds, - and 64 byte buffers. However, one HP implementation wants 255 bytes of - buffer (with a documented message of 160 bytes) so go for the max. - Since the Open IPMI architecture is single-message oriented at this - stage, the queue depth of BT is of no concern. */ +/* + * Typical "Get BT Capabilities" values are 2-3 retries, 5-10 seconds, + * and 64 byte buffers. However, one HP implementation wants 255 bytes of + * buffer (with a documented message of 160 bytes) so go for the max. + * Since the Open IPMI architecture is single-message oriented at this + * stage, the queue depth of BT is of no concern. + */ + +#define BT_NORMAL_TIMEOUT 5 /* seconds */ +#define BT_NORMAL_RETRY_LIMIT 2 +#define BT_RESET_DELAY 6 /* seconds after warm reset */ -#define BT_NORMAL_TIMEOUT 5000000 /* seconds in microseconds */ -#define BT_RETRY_LIMIT 2 -#define BT_RESET_DELAY 6000000 /* 6 seconds after warm reset */ +/* + * States are written in chronological order and usually cover + * multiple rows of the state table discussion in the IPMI spec. + */ enum bt_states { - BT_STATE_IDLE, + BT_STATE_IDLE = 0, /* Order is critical in this list */ BT_STATE_XACTION_START, BT_STATE_WRITE_BYTES, - BT_STATE_WRITE_END, BT_STATE_WRITE_CONSUME, - BT_STATE_B2H_WAIT, - BT_STATE_READ_END, - BT_STATE_RESET1, /* These must come last */ + BT_STATE_READ_WAIT, + BT_STATE_CLEAR_B2H, + BT_STATE_READ_BYTES, + BT_STATE_RESET1, /* These must come last */ BT_STATE_RESET2, BT_STATE_RESET3, BT_STATE_RESTART, - BT_STATE_HOSED + BT_STATE_PRINTME, + BT_STATE_CAPABILITIES_BEGIN, + BT_STATE_CAPABILITIES_END, + BT_STATE_LONG_BUSY /* BT doesn't get hosed :-) */ }; +/* + * Macros seen at the end of state "case" blocks. They help with legibility + * and debugging. + */ + +#define BT_STATE_CHANGE(X, Y) { bt->state = X; return Y; } + +#define BT_SI_SM_RETURN(Y) { last_printed = BT_STATE_PRINTME; return Y; } + struct si_sm_data { enum bt_states state; - enum bt_states last_state; /* assist printing and resets */ unsigned char seq; /* BT sequence number */ struct si_sm_io *io; - unsigned char write_data[IPMI_MAX_MSG_LENGTH]; - int write_count; - unsigned char read_data[IPMI_MAX_MSG_LENGTH]; - int read_count; - int truncated; - long timeout; - unsigned int error_retries; /* end of "common" fields */ + unsigned char write_data[IPMI_MAX_MSG_LENGTH + 2]; /* +2 for memcpy */ + int write_count; + unsigned char read_data[IPMI_MAX_MSG_LENGTH + 2]; /* +2 for memcpy */ + int read_count; + int truncated; + long timeout; /* microseconds countdown */ + int error_retries; /* end of "common" fields */ int nonzero_status; /* hung BMCs stay all 0 */ + enum bt_states complete; /* to divert the state machine */ + int BT_CAP_outreqs; + long BT_CAP_req2rsp; + int BT_CAP_retries; /* Recommended retries */ }; #define BT_CLR_WR_PTR 0x01 /* See IPMI 1.5 table 11.6.4 */ @@ -90,11 +118,13 @@ struct si_sm_data { #define BT_H_BUSY 0x40 #define BT_B_BUSY 0x80 -/* Some bits are toggled on each write: write once to set it, once - more to clear it; writing a zero does nothing. To absolutely - clear it, check its state and write if set. This avoids the "get - current then use as mask" scheme to modify one bit. Note that the - variable "bt" is hardcoded into these macros. */ +/* + * Some bits are toggled on each write: write once to set it, once + * more to clear it; writing a zero does nothing. To absolutely + * clear it, check its state and write if set. This avoids the "get + * current then use as mask" scheme to modify one bit. Note that the + * variable "bt" is hardcoded into these macros. + */ #define BT_STATUS bt->io->inputb(bt->io, 0) #define BT_CONTROL(x) bt->io->outputb(bt->io, 0, x) @@ -105,131 +135,160 @@ struct si_sm_data { #define BT_INTMASK_R bt->io->inputb(bt->io, 2) #define BT_INTMASK_W(x) bt->io->outputb(bt->io, 2, x) -/* Convenience routines for debugging. These are not multi-open safe! - Note the macros have hardcoded variables in them. */ +/* + * Convenience routines for debugging. These are not multi-open safe! + * Note the macros have hardcoded variables in them. + */ static char *state2txt(unsigned char state) { switch (state) { - case BT_STATE_IDLE: return("IDLE"); - case BT_STATE_XACTION_START: return("XACTION"); - case BT_STATE_WRITE_BYTES: return("WR_BYTES"); - case BT_STATE_WRITE_END: return("WR_END"); - case BT_STATE_WRITE_CONSUME: return("WR_CONSUME"); - case BT_STATE_B2H_WAIT: return("B2H_WAIT"); - case BT_STATE_READ_END: return("RD_END"); - case BT_STATE_RESET1: return("RESET1"); - case BT_STATE_RESET2: return("RESET2"); - case BT_STATE_RESET3: return("RESET3"); - case BT_STATE_RESTART: return("RESTART"); - case BT_STATE_HOSED: return("HOSED"); + case BT_STATE_IDLE: return("IDLE"); + case BT_STATE_XACTION_START: return("XACTION"); + case BT_STATE_WRITE_BYTES: return("WR_BYTES"); + case BT_STATE_WRITE_CONSUME: return("WR_CONSUME"); + case BT_STATE_READ_WAIT: return("RD_WAIT"); + case BT_STATE_CLEAR_B2H: return("CLEAR_B2H"); + case BT_STATE_READ_BYTES: return("RD_BYTES"); + case BT_STATE_RESET1: return("RESET1"); + case BT_STATE_RESET2: return("RESET2"); + case BT_STATE_RESET3: return("RESET3"); + case BT_STATE_RESTART: return("RESTART"); + case BT_STATE_LONG_BUSY: return("LONG_BUSY"); + case BT_STATE_CAPABILITIES_BEGIN: return("CAP_BEGIN"); + case BT_STATE_CAPABILITIES_END: return("CAP_END"); } return("BAD STATE"); } #define STATE2TXT state2txt(bt->state) -static char *status2txt(unsigned char status, char *buf) +static char *status2txt(unsigned char status) { + /* + * This cannot be called by two threads at the same time and + * the buffer is always consumed immediately, so the static is + * safe to use. + */ + static char buf[40]; + strcpy(buf, "[ "); - if (status & BT_B_BUSY) strcat(buf, "B_BUSY "); - if (status & BT_H_BUSY) strcat(buf, "H_BUSY "); - if (status & BT_OEM0) strcat(buf, "OEM0 "); - if (status & BT_SMS_ATN) strcat(buf, "SMS "); - if (status & BT_B2H_ATN) strcat(buf, "B2H "); - if (status & BT_H2B_ATN) strcat(buf, "H2B "); + if (status & BT_B_BUSY) + strcat(buf, "B_BUSY "); + if (status & BT_H_BUSY) + strcat(buf, "H_BUSY "); + if (status & BT_OEM0) + strcat(buf, "OEM0 "); + if (status & BT_SMS_ATN) + strcat(buf, "SMS "); + if (status & BT_B2H_ATN) + strcat(buf, "B2H "); + if (status & BT_H2B_ATN) + strcat(buf, "H2B "); strcat(buf, "]"); return buf; } -#define STATUS2TXT(buf) status2txt(status, buf) +#define STATUS2TXT status2txt(status) + +/* called externally at insmod time, and internally on cleanup */ -/* This will be called from within this module on a hosed condition */ -#define FIRST_SEQ 0 static unsigned int bt_init_data(struct si_sm_data *bt, struct si_sm_io *io) { - bt->state = BT_STATE_IDLE; - bt->last_state = BT_STATE_IDLE; - bt->seq = FIRST_SEQ; - bt->io = io; - bt->write_count = 0; - bt->read_count = 0; - bt->error_retries = 0; - bt->nonzero_status = 0; - bt->truncated = 0; - bt->timeout = BT_NORMAL_TIMEOUT; + memset(bt, 0, sizeof(struct si_sm_data)); + if (bt->io != io) { + /* external: one-time only things */ + bt->io = io; + bt->seq = 0; + } + bt->state = BT_STATE_IDLE; /* start here */ + bt->complete = BT_STATE_IDLE; /* end here */ + bt->BT_CAP_req2rsp = BT_NORMAL_TIMEOUT * USEC_PER_SEC; + bt->BT_CAP_retries = BT_NORMAL_RETRY_LIMIT; + /* BT_CAP_outreqs == zero is a flag to read BT Capabilities */ return 3; /* We claim 3 bytes of space; ought to check SPMI table */ } +/* Jam a completion code (probably an error) into a response */ + +static void force_result(struct si_sm_data *bt, unsigned char completion_code) +{ + bt->read_data[0] = 4; /* # following bytes */ + bt->read_data[1] = bt->write_data[1] | 4; /* Odd NetFn/LUN */ + bt->read_data[2] = bt->write_data[2]; /* seq (ignored) */ + bt->read_data[3] = bt->write_data[3]; /* Command */ + bt->read_data[4] = completion_code; + bt->read_count = 5; +} + +/* The upper state machine starts here */ + static int bt_start_transaction(struct si_sm_data *bt, unsigned char *data, unsigned int size) { unsigned int i; - if ((size < 2) || (size > (IPMI_MAX_MSG_LENGTH - 2))) - return -1; + if (size < 2) + return IPMI_REQ_LEN_INVALID_ERR; + if (size > IPMI_MAX_MSG_LENGTH) + return IPMI_REQ_LEN_EXCEEDED_ERR; + + if (bt->state == BT_STATE_LONG_BUSY) + return IPMI_NODE_BUSY_ERR; - if ((bt->state != BT_STATE_IDLE) && (bt->state != BT_STATE_HOSED)) - return -2; + if (bt->state != BT_STATE_IDLE) + return IPMI_NOT_IN_MY_STATE_ERR; if (bt_debug & BT_DEBUG_MSG) { - printk(KERN_WARNING "+++++++++++++++++++++++++++++++++++++\n"); - printk(KERN_WARNING "BT: write seq=0x%02X:", bt->seq); + printk(KERN_WARNING "BT: +++++++++++++++++ New command\n"); + printk(KERN_WARNING "BT: NetFn/LUN CMD [%d data]:", size - 2); for (i = 0; i < size; i ++) - printk (" %02x", data[i]); + printk(" %02x", data[i]); printk("\n"); } bt->write_data[0] = size + 1; /* all data plus seq byte */ bt->write_data[1] = *data; /* NetFn/LUN */ - bt->write_data[2] = bt->seq; + bt->write_data[2] = bt->seq++; memcpy(bt->write_data + 3, data + 1, size - 1); bt->write_count = size + 2; - bt->error_retries = 0; bt->nonzero_status = 0; - bt->read_count = 0; bt->truncated = 0; bt->state = BT_STATE_XACTION_START; - bt->last_state = BT_STATE_IDLE; - bt->timeout = BT_NORMAL_TIMEOUT; + bt->timeout = bt->BT_CAP_req2rsp; + force_result(bt, IPMI_ERR_UNSPECIFIED); return 0; } -/* After the upper state machine has been told SI_SM_TRANSACTION_COMPLETE - it calls this. Strip out the length and seq bytes. */ +/* + * After the upper state machine has been told SI_SM_TRANSACTION_COMPLETE + * it calls this. Strip out the length and seq bytes. + */ static int bt_get_result(struct si_sm_data *bt, - unsigned char *data, - unsigned int length) + unsigned char *data, + unsigned int length) { int i, msg_len; msg_len = bt->read_count - 2; /* account for length & seq */ - /* Always NetFn, Cmd, cCode */ if (msg_len < 3 || msg_len > IPMI_MAX_MSG_LENGTH) { - printk(KERN_DEBUG "BT results: bad msg_len = %d\n", msg_len); - data[0] = bt->write_data[1] | 0x4; /* Kludge a response */ - data[1] = bt->write_data[3]; - data[2] = IPMI_ERR_UNSPECIFIED; + force_result(bt, IPMI_ERR_UNSPECIFIED); msg_len = 3; - } else { - data[0] = bt->read_data[1]; - data[1] = bt->read_data[3]; - if (length < msg_len) - bt->truncated = 1; - if (bt->truncated) { /* can be set in read_all_bytes() */ - data[2] = IPMI_ERR_MSG_TRUNCATED; - msg_len = 3; - } else - memcpy(data + 2, bt->read_data + 4, msg_len - 2); + } + data[0] = bt->read_data[1]; + data[1] = bt->read_data[3]; + if (length < msg_len || bt->truncated) { + data[2] = IPMI_ERR_MSG_TRUNCATED; + msg_len = 3; + } else + memcpy(data + 2, bt->read_data + 4, msg_len - 2); - if (bt_debug & BT_DEBUG_MSG) { - printk (KERN_WARNING "BT: res (raw)"); - for (i = 0; i < msg_len; i++) - printk(" %02x", data[i]); - printk ("\n"); - } + if (bt_debug & BT_DEBUG_MSG) { + printk(KERN_WARNING "BT: result %d bytes:", msg_len); + for (i = 0; i < msg_len; i++) + printk(" %02x", data[i]); + printk("\n"); } - bt->read_count = 0; /* paranoia */ return msg_len; } @@ -238,22 +297,42 @@ static int bt_get_result(struct si_sm_data *bt, static void reset_flags(struct si_sm_data *bt) { + if (bt_debug) + printk(KERN_WARNING "IPMI BT: flag reset %s\n", + status2txt(BT_STATUS)); if (BT_STATUS & BT_H_BUSY) - BT_CONTROL(BT_H_BUSY); - if (BT_STATUS & BT_B_BUSY) - BT_CONTROL(BT_B_BUSY); - BT_CONTROL(BT_CLR_WR_PTR); - BT_CONTROL(BT_SMS_ATN); - - if (BT_STATUS & BT_B2H_ATN) { - int i; - BT_CONTROL(BT_H_BUSY); - BT_CONTROL(BT_B2H_ATN); - BT_CONTROL(BT_CLR_RD_PTR); - for (i = 0; i < IPMI_MAX_MSG_LENGTH + 2; i++) - BMC2HOST; - BT_CONTROL(BT_H_BUSY); - } + BT_CONTROL(BT_H_BUSY); /* force clear */ + BT_CONTROL(BT_CLR_WR_PTR); /* always reset */ + BT_CONTROL(BT_SMS_ATN); /* always clear */ + BT_INTMASK_W(BT_BMC_HWRST); +} + +/* + * Get rid of an unwanted/stale response. This should only be needed for + * BMCs that support multiple outstanding requests. + */ + +static void drain_BMC2HOST(struct si_sm_data *bt) +{ + int i, size; + + if (!(BT_STATUS & BT_B2H_ATN)) /* Not signalling a response */ + return; + + BT_CONTROL(BT_H_BUSY); /* now set */ + BT_CONTROL(BT_B2H_ATN); /* always clear */ + BT_STATUS; /* pause */ + BT_CONTROL(BT_B2H_ATN); /* some BMCs are stubborn */ + BT_CONTROL(BT_CLR_RD_PTR); /* always reset */ + if (bt_debug) + printk(KERN_WARNING "IPMI BT: stale response %s; ", + status2txt(BT_STATUS)); + size = BMC2HOST; + for (i = 0; i < size ; i++) + BMC2HOST; + BT_CONTROL(BT_H_BUSY); /* now clear */ + if (bt_debug) + printk("drained %d bytes\n", size + 1); } static inline void write_all_bytes(struct si_sm_data *bt) @@ -261,201 +340,271 @@ static inline void write_all_bytes(struct si_sm_data *bt) int i; if (bt_debug & BT_DEBUG_MSG) { - printk(KERN_WARNING "BT: write %d bytes seq=0x%02X", + printk(KERN_WARNING "BT: write %d bytes seq=0x%02X", bt->write_count, bt->seq); for (i = 0; i < bt->write_count; i++) - printk (" %02x", bt->write_data[i]); - printk ("\n"); + printk(" %02x", bt->write_data[i]); + printk("\n"); } for (i = 0; i < bt->write_count; i++) - HOST2BMC(bt->write_data[i]); + HOST2BMC(bt->write_data[i]); } static inline int read_all_bytes(struct si_sm_data *bt) { - unsigned char i; + unsigned int i; + + /* + * length is "framing info", minimum = 4: NetFn, Seq, Cmd, cCode. + * Keep layout of first four bytes aligned with write_data[] + */ bt->read_data[0] = BMC2HOST; bt->read_count = bt->read_data[0]; - if (bt_debug & BT_DEBUG_MSG) - printk(KERN_WARNING "BT: read %d bytes:", bt->read_count); - /* minimum: length, NetFn, Seq, Cmd, cCode == 5 total, or 4 more - following the length byte. */ if (bt->read_count < 4 || bt->read_count >= IPMI_MAX_MSG_LENGTH) { if (bt_debug & BT_DEBUG_MSG) - printk("bad length %d\n", bt->read_count); + printk(KERN_WARNING "BT: bad raw rsp len=%d\n", + bt->read_count); bt->truncated = 1; return 1; /* let next XACTION START clean it up */ } for (i = 1; i <= bt->read_count; i++) - bt->read_data[i] = BMC2HOST; - bt->read_count++; /* account for the length byte */ + bt->read_data[i] = BMC2HOST; + bt->read_count++; /* Account internally for length byte */ if (bt_debug & BT_DEBUG_MSG) { - for (i = 0; i < bt->read_count; i++) - printk (" %02x", bt->read_data[i]); - printk ("\n"); + int max = bt->read_count; + + printk(KERN_WARNING "BT: got %d bytes seq=0x%02X", + max, bt->read_data[2]); + if (max > 16) + max = 16; + for (i = 0; i < max; i++) + printk(KERN_CONT " %02x", bt->read_data[i]); + printk(KERN_CONT "%s\n", bt->read_count == max ? "" : " ..."); } - if (bt->seq != bt->write_data[2]) /* idiot check */ - printk(KERN_DEBUG "BT: internal error: sequence mismatch\n"); - /* per the spec, the (NetFn, Seq, Cmd) tuples should match */ - if ((bt->read_data[3] == bt->write_data[3]) && /* Cmd */ - (bt->read_data[2] == bt->write_data[2]) && /* Sequence */ - ((bt->read_data[1] & 0xF8) == (bt->write_data[1] & 0xF8))) + /* per the spec, the (NetFn[1], Seq[2], Cmd[3]) tuples must match */ + if ((bt->read_data[3] == bt->write_data[3]) && + (bt->read_data[2] == bt->write_data[2]) && + ((bt->read_data[1] & 0xF8) == (bt->write_data[1] & 0xF8))) return 1; if (bt_debug & BT_DEBUG_MSG) - printk(KERN_WARNING "BT: bad packet: " + printk(KERN_WARNING "IPMI BT: bad packet: " "want 0x(%02X, %02X, %02X) got (%02X, %02X, %02X)\n", - bt->write_data[1], bt->write_data[2], bt->write_data[3], + bt->write_data[1] | 0x04, bt->write_data[2], bt->write_data[3], bt->read_data[1], bt->read_data[2], bt->read_data[3]); return 0; } -/* Modifies bt->state appropriately, need to get into the bt_event() switch */ +/* Restart if retries are left, or return an error completion code */ -static void error_recovery(struct si_sm_data *bt, char *reason) +static enum si_sm_result error_recovery(struct si_sm_data *bt, + unsigned char status, + unsigned char cCode) { - unsigned char status; - char buf[40]; /* For getting status */ + char *reason; - bt->timeout = BT_NORMAL_TIMEOUT; /* various places want to retry */ + bt->timeout = bt->BT_CAP_req2rsp; - status = BT_STATUS; - printk(KERN_DEBUG "BT: %s in %s %s\n", reason, STATE2TXT, - STATUS2TXT(buf)); + switch (cCode) { + case IPMI_TIMEOUT_ERR: + reason = "timeout"; + break; + default: + reason = "internal error"; + break; + } + + printk(KERN_WARNING "IPMI BT: %s in %s %s ", /* open-ended line */ + reason, STATE2TXT, STATUS2TXT); + /* + * Per the IPMI spec, retries are based on the sequence number + * known only to this module, so manage a restart here. + */ (bt->error_retries)++; - if (bt->error_retries > BT_RETRY_LIMIT) { - printk(KERN_DEBUG "retry limit (%d) exceeded\n", BT_RETRY_LIMIT); - bt->state = BT_STATE_HOSED; - if (!bt->nonzero_status) - printk(KERN_ERR "IPMI: BT stuck, try power cycle\n"); - else if (bt->error_retries <= BT_RETRY_LIMIT + 1) { - printk(KERN_DEBUG "IPMI: BT reset (takes 5 secs)\n"); - bt->state = BT_STATE_RESET1; - } - return; + if (bt->error_retries < bt->BT_CAP_retries) { + printk("%d retries left\n", + bt->BT_CAP_retries - bt->error_retries); + bt->state = BT_STATE_RESTART; + return SI_SM_CALL_WITHOUT_DELAY; } - /* Sometimes the BMC queues get in an "off-by-one" state...*/ - if ((bt->state == BT_STATE_B2H_WAIT) && (status & BT_B2H_ATN)) { - printk(KERN_DEBUG "retry B2H_WAIT\n"); - return; + printk(KERN_WARNING "failed %d retries, sending error response\n", + bt->BT_CAP_retries); + if (!bt->nonzero_status) + printk(KERN_ERR "IPMI BT: stuck, try power cycle\n"); + + /* this is most likely during insmod */ + else if (bt->seq <= (unsigned char)(bt->BT_CAP_retries & 0xFF)) { + printk(KERN_WARNING "IPMI: BT reset (takes 5 secs)\n"); + bt->state = BT_STATE_RESET1; + return SI_SM_CALL_WITHOUT_DELAY; } - printk(KERN_DEBUG "restart command\n"); - bt->state = BT_STATE_RESTART; + /* + * Concoct a useful error message, set up the next state, and + * be done with this sequence. + */ + + bt->state = BT_STATE_IDLE; + switch (cCode) { + case IPMI_TIMEOUT_ERR: + if (status & BT_B_BUSY) { + cCode = IPMI_NODE_BUSY_ERR; + bt->state = BT_STATE_LONG_BUSY; + } + break; + default: + break; + } + force_result(bt, cCode); + return SI_SM_TRANSACTION_COMPLETE; } -/* Check the status and (possibly) advance the BT state machine. The - default return is SI_SM_CALL_WITH_DELAY. */ +/* Check status and (usually) take action and change this state machine. */ static enum si_sm_result bt_event(struct si_sm_data *bt, long time) { - unsigned char status; - char buf[40]; /* For getting status */ + unsigned char status, BT_CAP[8]; + static enum bt_states last_printed = BT_STATE_PRINTME; int i; status = BT_STATUS; bt->nonzero_status |= status; - - if ((bt_debug & BT_DEBUG_STATES) && (bt->state != bt->last_state)) + if ((bt_debug & BT_DEBUG_STATES) && (bt->state != last_printed)) { printk(KERN_WARNING "BT: %s %s TO=%ld - %ld \n", STATE2TXT, - STATUS2TXT(buf), + STATUS2TXT, bt->timeout, time); - bt->last_state = bt->state; + last_printed = bt->state; + } + + /* + * Commands that time out may still (eventually) provide a response. + * This stale response will get in the way of a new response so remove + * it if possible (hopefully during IDLE). Even if it comes up later + * it will be rejected by its (now-forgotten) seq number. + */ - if (bt->state == BT_STATE_HOSED) - return SI_SM_HOSED; + if ((bt->state < BT_STATE_WRITE_BYTES) && (status & BT_B2H_ATN)) { + drain_BMC2HOST(bt); + BT_SI_SM_RETURN(SI_SM_CALL_WITH_DELAY); + } - if (bt->state != BT_STATE_IDLE) { /* do timeout test */ + if ((bt->state != BT_STATE_IDLE) && + (bt->state < BT_STATE_PRINTME)) { + /* check timeout */ bt->timeout -= time; - if ((bt->timeout < 0) && (bt->state < BT_STATE_RESET1)) { - error_recovery(bt, "timed out"); - return SI_SM_CALL_WITHOUT_DELAY; - } + if ((bt->timeout < 0) && (bt->state < BT_STATE_RESET1)) + return error_recovery(bt, + status, + IPMI_TIMEOUT_ERR); } switch (bt->state) { - case BT_STATE_IDLE: /* check for asynchronous messages */ + /* + * Idle state first checks for asynchronous messages from another + * channel, then does some opportunistic housekeeping. + */ + + case BT_STATE_IDLE: if (status & BT_SMS_ATN) { BT_CONTROL(BT_SMS_ATN); /* clear it */ return SI_SM_ATTN; } - return SI_SM_IDLE; - case BT_STATE_XACTION_START: - if (status & BT_H_BUSY) { + if (status & BT_H_BUSY) /* clear a leftover H_BUSY */ BT_CONTROL(BT_H_BUSY); - break; - } - if (status & BT_B2H_ATN) - break; - bt->state = BT_STATE_WRITE_BYTES; - return SI_SM_CALL_WITHOUT_DELAY; /* for logging */ - case BT_STATE_WRITE_BYTES: + /* Read BT capabilities if it hasn't been done yet */ + if (!bt->BT_CAP_outreqs) + BT_STATE_CHANGE(BT_STATE_CAPABILITIES_BEGIN, + SI_SM_CALL_WITHOUT_DELAY); + bt->timeout = bt->BT_CAP_req2rsp; + BT_SI_SM_RETURN(SI_SM_IDLE); + + case BT_STATE_XACTION_START: if (status & (BT_B_BUSY | BT_H2B_ATN)) - break; + BT_SI_SM_RETURN(SI_SM_CALL_WITH_DELAY); + if (BT_STATUS & BT_H_BUSY) + BT_CONTROL(BT_H_BUSY); /* force clear */ + BT_STATE_CHANGE(BT_STATE_WRITE_BYTES, + SI_SM_CALL_WITHOUT_DELAY); + + case BT_STATE_WRITE_BYTES: + if (status & BT_H_BUSY) + BT_CONTROL(BT_H_BUSY); /* clear */ BT_CONTROL(BT_CLR_WR_PTR); write_all_bytes(bt); - BT_CONTROL(BT_H2B_ATN); /* clears too fast to catch? */ - bt->state = BT_STATE_WRITE_CONSUME; - return SI_SM_CALL_WITHOUT_DELAY; /* it MIGHT sail through */ - - case BT_STATE_WRITE_CONSUME: /* BMCs usually blow right thru here */ - if (status & (BT_H2B_ATN | BT_B_BUSY)) - break; - bt->state = BT_STATE_B2H_WAIT; - /* fall through with status */ - - /* Stay in BT_STATE_B2H_WAIT until a packet matches. However, spinning - hard here, constantly reading status, seems to hold off the - generation of B2H_ATN so ALWAYS return CALL_WITH_DELAY. */ + BT_CONTROL(BT_H2B_ATN); /* can clear too fast to catch */ + BT_STATE_CHANGE(BT_STATE_WRITE_CONSUME, + SI_SM_CALL_WITHOUT_DELAY); - case BT_STATE_B2H_WAIT: - if (!(status & BT_B2H_ATN)) - break; + case BT_STATE_WRITE_CONSUME: + if (status & (BT_B_BUSY | BT_H2B_ATN)) + BT_SI_SM_RETURN(SI_SM_CALL_WITH_DELAY); + BT_STATE_CHANGE(BT_STATE_READ_WAIT, + SI_SM_CALL_WITHOUT_DELAY); + + /* Spinning hard can suppress B2H_ATN and force a timeout */ + + case BT_STATE_READ_WAIT: + if (!(status & BT_B2H_ATN)) + BT_SI_SM_RETURN(SI_SM_CALL_WITH_DELAY); + BT_CONTROL(BT_H_BUSY); /* set */ + + /* + * Uncached, ordered writes should just proceed serially but + * some BMCs don't clear B2H_ATN with one hit. Fast-path a + * workaround without too much penalty to the general case. + */ + + BT_CONTROL(BT_B2H_ATN); /* clear it to ACK the BMC */ + BT_STATE_CHANGE(BT_STATE_CLEAR_B2H, + SI_SM_CALL_WITHOUT_DELAY); + + case BT_STATE_CLEAR_B2H: + if (status & BT_B2H_ATN) { + /* keep hitting it */ + BT_CONTROL(BT_B2H_ATN); + BT_SI_SM_RETURN(SI_SM_CALL_WITH_DELAY); + } + BT_STATE_CHANGE(BT_STATE_READ_BYTES, + SI_SM_CALL_WITHOUT_DELAY); - /* Assume ordered, uncached writes: no need to wait */ + case BT_STATE_READ_BYTES: if (!(status & BT_H_BUSY)) - BT_CONTROL(BT_H_BUSY); /* set */ - BT_CONTROL(BT_B2H_ATN); /* clear it, ACK to the BMC */ - BT_CONTROL(BT_CLR_RD_PTR); /* reset the queue */ - i = read_all_bytes(bt); - BT_CONTROL(BT_H_BUSY); /* clear */ - if (!i) /* Try this state again */ - break; - bt->state = BT_STATE_READ_END; - return SI_SM_CALL_WITHOUT_DELAY; /* for logging */ - - case BT_STATE_READ_END: - - /* I could wait on BT_H_BUSY to go clear for a truly clean - exit. However, this is already done in XACTION_START - and the (possible) extra loop/status/possible wait affects - performance. So, as long as it works, just ignore H_BUSY */ - -#ifdef MAKE_THIS_TRUE_IF_NECESSARY - - if (status & BT_H_BUSY) - break; -#endif - bt->seq++; - bt->state = BT_STATE_IDLE; - return SI_SM_TRANSACTION_COMPLETE; + /* check in case of retry */ + BT_CONTROL(BT_H_BUSY); + BT_CONTROL(BT_CLR_RD_PTR); /* start of BMC2HOST buffer */ + i = read_all_bytes(bt); /* true == packet seq match */ + BT_CONTROL(BT_H_BUSY); /* NOW clear */ + if (!i) /* Not my message */ + BT_STATE_CHANGE(BT_STATE_READ_WAIT, + SI_SM_CALL_WITHOUT_DELAY); + bt->state = bt->complete; + return bt->state == BT_STATE_IDLE ? /* where to next? */ + SI_SM_TRANSACTION_COMPLETE : /* normal */ + SI_SM_CALL_WITHOUT_DELAY; /* Startup magic */ + + case BT_STATE_LONG_BUSY: /* For example: after FW update */ + if (!(status & BT_B_BUSY)) { + reset_flags(bt); /* next state is now IDLE */ + bt_init_data(bt, bt->io); + } + return SI_SM_CALL_WITH_DELAY; /* No repeat printing */ case BT_STATE_RESET1: - reset_flags(bt); - bt->timeout = BT_RESET_DELAY; - bt->state = BT_STATE_RESET2; - break; + reset_flags(bt); + drain_BMC2HOST(bt); + BT_STATE_CHANGE(BT_STATE_RESET2, + SI_SM_CALL_WITH_DELAY); case BT_STATE_RESET2: /* Send a soft reset */ BT_CONTROL(BT_CLR_WR_PTR); @@ -464,40 +613,74 @@ static enum si_sm_result bt_event(struct si_sm_data *bt, long time) HOST2BMC(42); /* Sequence number */ HOST2BMC(3); /* Cmd == Soft reset */ BT_CONTROL(BT_H2B_ATN); - bt->state = BT_STATE_RESET3; - break; + bt->timeout = BT_RESET_DELAY * USEC_PER_SEC; + BT_STATE_CHANGE(BT_STATE_RESET3, + SI_SM_CALL_WITH_DELAY); - case BT_STATE_RESET3: + case BT_STATE_RESET3: /* Hold off everything for a bit */ if (bt->timeout > 0) - return SI_SM_CALL_WITH_DELAY; - bt->state = BT_STATE_RESTART; /* printk in debug modes */ - break; + return SI_SM_CALL_WITH_DELAY; + drain_BMC2HOST(bt); + BT_STATE_CHANGE(BT_STATE_RESTART, + SI_SM_CALL_WITH_DELAY); - case BT_STATE_RESTART: /* don't reset retries! */ - reset_flags(bt); - bt->write_data[2] = ++bt->seq; + case BT_STATE_RESTART: /* don't reset retries or seq! */ bt->read_count = 0; bt->nonzero_status = 0; - bt->timeout = BT_NORMAL_TIMEOUT; - bt->state = BT_STATE_XACTION_START; - break; - - default: /* HOSED is supposed to be caught much earlier */ - error_recovery(bt, "internal logic error"); - break; - } - return SI_SM_CALL_WITH_DELAY; + bt->timeout = bt->BT_CAP_req2rsp; + BT_STATE_CHANGE(BT_STATE_XACTION_START, + SI_SM_CALL_WITH_DELAY); + + /* + * Get BT Capabilities, using timing of upper level state machine. + * Set outreqs to prevent infinite loop on timeout. + */ + case BT_STATE_CAPABILITIES_BEGIN: + bt->BT_CAP_outreqs = 1; + { + unsigned char GetBT_CAP[] = { 0x18, 0x36 }; + bt->state = BT_STATE_IDLE; + bt_start_transaction(bt, GetBT_CAP, sizeof(GetBT_CAP)); + } + bt->complete = BT_STATE_CAPABILITIES_END; + BT_STATE_CHANGE(BT_STATE_XACTION_START, + SI_SM_CALL_WITH_DELAY); + + case BT_STATE_CAPABILITIES_END: + i = bt_get_result(bt, BT_CAP, sizeof(BT_CAP)); + bt_init_data(bt, bt->io); + if ((i == 8) && !BT_CAP[2]) { + bt->BT_CAP_outreqs = BT_CAP[3]; + bt->BT_CAP_req2rsp = BT_CAP[6] * USEC_PER_SEC; + bt->BT_CAP_retries = BT_CAP[7]; + } else + printk(KERN_WARNING "IPMI BT: using default values\n"); + if (!bt->BT_CAP_outreqs) + bt->BT_CAP_outreqs = 1; + printk(KERN_WARNING "IPMI BT: req2rsp=%ld secs retries=%d\n", + bt->BT_CAP_req2rsp / USEC_PER_SEC, bt->BT_CAP_retries); + bt->timeout = bt->BT_CAP_req2rsp; + return SI_SM_CALL_WITHOUT_DELAY; + + default: /* should never occur */ + return error_recovery(bt, + status, + IPMI_ERR_UNSPECIFIED); + } + return SI_SM_CALL_WITH_DELAY; } static int bt_detect(struct si_sm_data *bt) { - /* It's impossible for the BT status and interrupt registers to be - all 1's, (assuming a properly functioning, self-initialized BMC) - but that's what you get from reading a bogus address, so we - test that first. The calling routine uses negative logic. */ + /* + * It's impossible for the BT status and interrupt registers to be + * all 1's, (assuming a properly functioning, self-initialized BMC) + * but that's what you get from reading a bogus address, so we + * test that first. The calling routine uses negative logic. + */ if ((BT_STATUS == 0xFF) && (BT_INTMASK_R == 0xFF)) - return 1; + return 1; reset_flags(bt); return 0; } @@ -511,13 +694,12 @@ static int bt_size(void) return sizeof(struct si_sm_data); } -struct si_sm_handlers bt_smi_handlers = -{ - .init_data = bt_init_data, - .start_transaction = bt_start_transaction, - .get_result = bt_get_result, - .event = bt_event, - .detect = bt_detect, - .cleanup = bt_cleanup, - .size = bt_size, +struct si_sm_handlers bt_smi_handlers = { + .init_data = bt_init_data, + .start_transaction = bt_start_transaction, + .get_result = bt_get_result, + .event = bt_event, + .detect = bt_detect, + .cleanup = bt_cleanup, + .size = bt_size, }; diff --git a/drivers/char/ipmi/ipmi_devintf.c b/drivers/char/ipmi/ipmi_devintf.c index 68d7c61a864..ec318bf434a 100644 --- a/drivers/char/ipmi/ipmi_devintf.c +++ b/drivers/char/ipmi/ipmi_devintf.c @@ -34,9 +34,8 @@ #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/errno.h> -#include <asm/system.h> -#include <linux/sched.h> #include <linux/poll.h> +#include <linux/sched.h> #include <linux/spinlock.h> #include <linux/slab.h> #include <linux/ipmi.h> @@ -58,6 +57,7 @@ struct ipmi_file_private unsigned int default_retry_time_ms; }; +static DEFINE_MUTEX(ipmi_mutex); static void file_receive_handler(struct ipmi_recv_msg *msg, void *handler_data) { @@ -101,7 +101,9 @@ static int ipmi_fasync(int fd, struct file *file, int on) struct ipmi_file_private *priv = file->private_data; int result; + mutex_lock(&ipmi_mutex); /* could race against open() otherwise */ result = fasync_helper(fd, file, on, &priv->fasync_queue); + mutex_unlock(&ipmi_mutex); return (result); } @@ -122,6 +124,7 @@ static int ipmi_open(struct inode *inode, struct file *file) if (!priv) return -ENOMEM; + mutex_lock(&ipmi_mutex); priv->file = file; rv = ipmi_create_user(if_num, @@ -130,7 +133,7 @@ static int ipmi_open(struct inode *inode, struct file *file) &(priv->user)); if (rv) { kfree(priv); - return rv; + goto out; } file->private_data = priv; @@ -145,7 +148,9 @@ static int ipmi_open(struct inode *inode, struct file *file) priv->default_retries = -1; priv->default_retry_time_ms = 0; - return 0; +out: + mutex_unlock(&ipmi_mutex); + return rv; } static int ipmi_release(struct inode *inode, struct file *file) @@ -157,8 +162,6 @@ static int ipmi_release(struct inode *inode, struct file *file) if (rv) return rv; - ipmi_fasync (-1, file, 0); - /* FIXME - free the messages in the list. */ kfree(priv); @@ -224,8 +227,7 @@ static int handle_send_req(ipmi_user_t user, return rv; } -static int ipmi_ioctl(struct inode *inode, - struct file *file, +static int ipmi_ioctl(struct file *file, unsigned int cmd, unsigned long data) { @@ -377,7 +379,8 @@ static int ipmi_ioctl(struct inode *inode, break; } - rv = ipmi_register_for_cmd(priv->user, val.netfn, val.cmd); + rv = ipmi_register_for_cmd(priv->user, val.netfn, val.cmd, + IPMI_CHAN_ALL); break; } @@ -390,7 +393,36 @@ static int ipmi_ioctl(struct inode *inode, break; } - rv = ipmi_unregister_for_cmd(priv->user, val.netfn, val.cmd); + rv = ipmi_unregister_for_cmd(priv->user, val.netfn, val.cmd, + IPMI_CHAN_ALL); + break; + } + + case IPMICTL_REGISTER_FOR_CMD_CHANS: + { + struct ipmi_cmdspec_chans val; + + if (copy_from_user(&val, arg, sizeof(val))) { + rv = -EFAULT; + break; + } + + rv = ipmi_register_for_cmd(priv->user, val.netfn, val.cmd, + val.chans); + break; + } + + case IPMICTL_UNREGISTER_FOR_CMD_CHANS: + { + struct ipmi_cmdspec_chans val; + + if (copy_from_user(&val, arg, sizeof(val))) { + rv = -EFAULT; + break; + } + + rv = ipmi_unregister_for_cmd(priv->user, val.netfn, val.cmd, + val.chans); break; } @@ -566,11 +598,53 @@ static int ipmi_ioctl(struct inode *inode, rv = 0; break; } + + case IPMICTL_GET_MAINTENANCE_MODE_CMD: + { + int mode; + + mode = ipmi_get_maintenance_mode(priv->user); + if (copy_to_user(arg, &mode, sizeof(mode))) { + rv = -EFAULT; + break; + } + rv = 0; + break; + } + + case IPMICTL_SET_MAINTENANCE_MODE_CMD: + { + int mode; + + if (copy_from_user(&mode, arg, sizeof(mode))) { + rv = -EFAULT; + break; + } + rv = ipmi_set_maintenance_mode(priv->user, mode); + break; + } } return rv; } +/* + * Note: it doesn't make sense to take the BKL here but + * not in compat_ipmi_ioctl. -arnd + */ +static long ipmi_unlocked_ioctl(struct file *file, + unsigned int cmd, + unsigned long data) +{ + int ret; + + mutex_lock(&ipmi_mutex); + ret = ipmi_ioctl(file, cmd, data); + mutex_unlock(&ipmi_mutex); + + return ret; +} + #ifdef CONFIG_COMPAT /* @@ -736,6 +810,7 @@ static long compat_ipmi_ioctl(struct file *filep, unsigned int cmd, struct ipmi_recv __user *precv64; struct ipmi_recv recv64; + memset(&recv64, 0, sizeof(recv64)); if (get_compat_ipmi_recv(&recv64, compat_ptr(arg))) return -EFAULT; @@ -743,7 +818,7 @@ static long compat_ipmi_ioctl(struct file *filep, unsigned int cmd, if (copy_to_user(precv64, &recv64, sizeof(recv64))) return -EFAULT; - rc = ipmi_ioctl(filep->f_dentry->d_inode, filep, + rc = ipmi_ioctl(filep, ((cmd == COMPAT_IPMICTL_RECEIVE_MSG) ? IPMICTL_RECEIVE_MSG : IPMICTL_RECEIVE_MSG_TRUNC), @@ -760,26 +835,39 @@ static long compat_ipmi_ioctl(struct file *filep, unsigned int cmd, return rc; } default: - return ipmi_ioctl(filep->f_dentry->d_inode, filep, cmd, arg); + return ipmi_ioctl(filep, cmd, arg); } } + +static long unlocked_compat_ipmi_ioctl(struct file *filep, unsigned int cmd, + unsigned long arg) +{ + int ret; + + mutex_lock(&ipmi_mutex); + ret = compat_ipmi_ioctl(filep, cmd, arg); + mutex_unlock(&ipmi_mutex); + + return ret; +} #endif static const struct file_operations ipmi_fops = { .owner = THIS_MODULE, - .ioctl = ipmi_ioctl, + .unlocked_ioctl = ipmi_unlocked_ioctl, #ifdef CONFIG_COMPAT - .compat_ioctl = compat_ipmi_ioctl, + .compat_ioctl = unlocked_compat_ipmi_ioctl, #endif .open = ipmi_open, .release = ipmi_release, .fasync = ipmi_fasync, .poll = ipmi_poll, + .llseek = noop_llseek, }; #define DEVICE_NAME "ipmidev" -static int ipmi_major = 0; +static int ipmi_major; module_param(ipmi_major, int, 0); MODULE_PARM_DESC(ipmi_major, "Sets the major number of the IPMI device. By" " default, or if you set it to zero, it will choose the next" @@ -811,7 +899,7 @@ static void ipmi_new_smi(int if_num, struct device *device) entry->dev = dev; mutex_lock(®_list_mutex); - class_device_create(ipmi_class, NULL, dev, device, "ipmi%d", if_num); + device_create(ipmi_class, device, dev, NULL, "ipmi%d", if_num); list_add(&entry->link, ®_list); mutex_unlock(®_list_mutex); } @@ -829,7 +917,7 @@ static void ipmi_smi_gone(int if_num) break; } } - class_device_destroy(ipmi_class, dev); + device_destroy(ipmi_class, dev); mutex_unlock(®_list_mutex); } @@ -840,7 +928,7 @@ static struct ipmi_smi_watcher smi_watcher = .smi_gone = ipmi_smi_gone, }; -static __init int init_ipmi_devintf(void) +static int __init init_ipmi_devintf(void) { int rv; @@ -878,13 +966,13 @@ static __init int init_ipmi_devintf(void) } module_init(init_ipmi_devintf); -static __exit void cleanup_ipmi(void) +static void __exit cleanup_ipmi(void) { struct ipmi_reg_list *entry, *entry2; mutex_lock(®_list_mutex); list_for_each_entry_safe(entry, entry2, ®_list, link) { list_del(&entry->link); - class_device_destroy(ipmi_class, entry->dev); + device_destroy(ipmi_class, entry->dev); kfree(entry); } mutex_unlock(®_list_mutex); @@ -897,3 +985,4 @@ module_exit(cleanup_ipmi); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Corey Minyard <minyard@mvista.com>"); MODULE_DESCRIPTION("Linux device interface for the IPMI message handler."); +MODULE_ALIAS("platform:ipmi_si"); diff --git a/drivers/char/ipmi/ipmi_kcs_sm.c b/drivers/char/ipmi/ipmi_kcs_sm.c index 2062675f9e9..8c25f596808 100644 --- a/drivers/char/ipmi/ipmi_kcs_sm.c +++ b/drivers/char/ipmi/ipmi_kcs_sm.c @@ -60,50 +60,70 @@ MODULE_PARM_DESC(kcs_debug, "debug bitmask, 1=enable, 2=messages, 4=states"); /* The states the KCS driver may be in. */ enum kcs_states { - KCS_IDLE, /* The KCS interface is currently - doing nothing. */ - KCS_START_OP, /* We are starting an operation. The - data is in the output buffer, but - nothing has been done to the - interface yet. This was added to - the state machine in the spec to - wait for the initial IBF. */ - KCS_WAIT_WRITE_START, /* We have written a write cmd to the - interface. */ - KCS_WAIT_WRITE, /* We are writing bytes to the - interface. */ - KCS_WAIT_WRITE_END, /* We have written the write end cmd - to the interface, and still need to - write the last byte. */ - KCS_WAIT_READ, /* We are waiting to read data from - the interface. */ - KCS_ERROR0, /* State to transition to the error - handler, this was added to the - state machine in the spec to be - sure IBF was there. */ - KCS_ERROR1, /* First stage error handler, wait for - the interface to respond. */ - KCS_ERROR2, /* The abort cmd has been written, - wait for the interface to - respond. */ - KCS_ERROR3, /* We wrote some data to the - interface, wait for it to switch to - read mode. */ - KCS_HOSED /* The hardware failed to follow the - state machine. */ + /* The KCS interface is currently doing nothing. */ + KCS_IDLE, + + /* + * We are starting an operation. The data is in the output + * buffer, but nothing has been done to the interface yet. This + * was added to the state machine in the spec to wait for the + * initial IBF. + */ + KCS_START_OP, + + /* We have written a write cmd to the interface. */ + KCS_WAIT_WRITE_START, + + /* We are writing bytes to the interface. */ + KCS_WAIT_WRITE, + + /* + * We have written the write end cmd to the interface, and + * still need to write the last byte. + */ + KCS_WAIT_WRITE_END, + + /* We are waiting to read data from the interface. */ + KCS_WAIT_READ, + + /* + * State to transition to the error handler, this was added to + * the state machine in the spec to be sure IBF was there. + */ + KCS_ERROR0, + + /* + * First stage error handler, wait for the interface to + * respond. + */ + KCS_ERROR1, + + /* + * The abort cmd has been written, wait for the interface to + * respond. + */ + KCS_ERROR2, + + /* + * We wrote some data to the interface, wait for it to switch + * to read mode. + */ + KCS_ERROR3, + + /* The hardware failed to follow the state machine. */ + KCS_HOSED }; -#define MAX_KCS_READ_SIZE 80 -#define MAX_KCS_WRITE_SIZE 80 +#define MAX_KCS_READ_SIZE IPMI_MAX_MSG_LENGTH +#define MAX_KCS_WRITE_SIZE IPMI_MAX_MSG_LENGTH /* Timeouts in microseconds. */ -#define IBF_RETRY_TIMEOUT 1000000 -#define OBF_RETRY_TIMEOUT 1000000 +#define IBF_RETRY_TIMEOUT (5*USEC_PER_SEC) +#define OBF_RETRY_TIMEOUT (5*USEC_PER_SEC) #define MAX_ERROR_RETRIES 10 #define ERROR0_OBF_WAIT_JIFFIES (2*HZ) -struct si_sm_data -{ +struct si_sm_data { enum kcs_states state; struct si_sm_io *io; unsigned char write_data[MAX_KCS_WRITE_SIZE]; @@ -187,7 +207,8 @@ static inline void start_error_recovery(struct si_sm_data *kcs, char *reason) (kcs->error_retries)++; if (kcs->error_retries > MAX_ERROR_RETRIES) { if (kcs_debug & KCS_DEBUG_ENABLE) - printk(KERN_DEBUG "ipmi_kcs_sm: kcs hosed: %s\n", reason); + printk(KERN_DEBUG "ipmi_kcs_sm: kcs hosed: %s\n", + reason); kcs->state = KCS_HOSED; } else { kcs->error0_timeout = jiffies + ERROR0_OBF_WAIT_JIFFIES; @@ -230,8 +251,9 @@ static inline int check_obf(struct si_sm_data *kcs, unsigned char status, if (!GET_STATUS_OBF(status)) { kcs->obf_timeout -= time; if (kcs->obf_timeout < 0) { - start_error_recovery(kcs, "OBF not ready in time"); - return 1; + kcs->obf_timeout = OBF_RETRY_TIMEOUT; + start_error_recovery(kcs, "OBF not ready in time"); + return 1; } return 0; } @@ -261,18 +283,19 @@ static int start_kcs_transaction(struct si_sm_data *kcs, unsigned char *data, { unsigned int i; - if ((size < 2) || (size > MAX_KCS_WRITE_SIZE)) { - return -1; - } - if ((kcs->state != KCS_IDLE) && (kcs->state != KCS_HOSED)) { - return -2; - } + if (size < 2) + return IPMI_REQ_LEN_INVALID_ERR; + if (size > MAX_KCS_WRITE_SIZE) + return IPMI_REQ_LEN_EXCEEDED_ERR; + + if ((kcs->state != KCS_IDLE) && (kcs->state != KCS_HOSED)) + return IPMI_NOT_IN_MY_STATE_ERR; + if (kcs_debug & KCS_DEBUG_MSG) { printk(KERN_DEBUG "start_kcs_transaction -"); - for (i = 0; i < size; i ++) { + for (i = 0; i < size; i++) printk(" %02x", (unsigned char) (data [i])); - } - printk ("\n"); + printk("\n"); } kcs->error_retries = 0; memcpy(kcs->write_data, data, size); @@ -303,9 +326,11 @@ static int get_kcs_result(struct si_sm_data *kcs, unsigned char *data, kcs->read_pos = 3; } if (kcs->truncated) { - /* Report a truncated error. We might overwrite - another error, but that's too bad, the user needs - to know it was truncated. */ + /* + * Report a truncated error. We might overwrite + * another error, but that's too bad, the user needs + * to know it was truncated. + */ data[2] = IPMI_ERR_MSG_TRUNCATED; kcs->truncated = 0; } @@ -313,9 +338,11 @@ static int get_kcs_result(struct si_sm_data *kcs, unsigned char *data, return kcs->read_pos; } -/* This implements the state machine defined in the IPMI manual, see - that for details on how this works. Divide that flowchart into - sections delimited by "Wait for IBF" and this will become clear. */ +/* + * This implements the state machine defined in the IPMI manual, see + * that for details on how this works. Divide that flowchart into + * sections delimited by "Wait for IBF" and this will become clear. + */ static enum si_sm_result kcs_event(struct si_sm_data *kcs, long time) { unsigned char status; @@ -344,7 +371,7 @@ static enum si_sm_result kcs_event(struct si_sm_data *kcs, long time) return SI_SM_IDLE; case KCS_START_OP: - if (state != KCS_IDLE) { + if (state != KCS_IDLE_STATE) { start_error_recovery(kcs, "State machine not idle at start"); break; @@ -386,11 +413,12 @@ static enum si_sm_result kcs_event(struct si_sm_data *kcs, long time) write_next_byte(kcs); } break; - + case KCS_WAIT_WRITE_END: if (state != KCS_WRITE_STATE) { start_error_recovery(kcs, - "Not in write state for write end"); + "Not in write state" + " for write end"); break; } clear_obf(kcs, status); @@ -411,13 +439,15 @@ static enum si_sm_result kcs_event(struct si_sm_data *kcs, long time) return SI_SM_CALL_WITH_DELAY; read_next_byte(kcs); } else { - /* We don't implement this exactly like the state - machine in the spec. Some broken hardware - does not write the final dummy byte to the - read register. Thus obf will never go high - here. We just go straight to idle, and we - handle clearing out obf in idle state if it - happens to come in. */ + /* + * We don't implement this exactly like the state + * machine in the spec. Some broken hardware + * does not write the final dummy byte to the + * read register. Thus obf will never go high + * here. We just go straight to idle, and we + * handle clearing out obf in idle state if it + * happens to come in. + */ clear_obf(kcs, status); kcs->orig_write_count = 0; kcs->state = KCS_IDLE; @@ -428,7 +458,8 @@ static enum si_sm_result kcs_event(struct si_sm_data *kcs, long time) case KCS_ERROR0: clear_obf(kcs, status); status = read_status(kcs); - if (GET_STATUS_OBF(status)) /* controller isn't responding */ + if (GET_STATUS_OBF(status)) + /* controller isn't responding */ if (time_before(jiffies, kcs->error0_timeout)) return SI_SM_CALL_WITH_TICK_DELAY; write_cmd(kcs, KCS_GET_STATUS_ABORT); @@ -440,7 +471,7 @@ static enum si_sm_result kcs_event(struct si_sm_data *kcs, long time) write_data(kcs, 0); kcs->state = KCS_ERROR2; break; - + case KCS_ERROR2: if (state != KCS_READ_STATE) { start_error_recovery(kcs, @@ -454,7 +485,7 @@ static enum si_sm_result kcs_event(struct si_sm_data *kcs, long time) write_data(kcs, KCS_READ_BYTE); kcs->state = KCS_ERROR3; break; - + case KCS_ERROR3: if (state != KCS_IDLE_STATE) { start_error_recovery(kcs, @@ -473,7 +504,7 @@ static enum si_sm_result kcs_event(struct si_sm_data *kcs, long time) return SI_SM_TRANSACTION_COMPLETE; } break; - + case KCS_HOSED: break; } @@ -493,10 +524,12 @@ static int kcs_size(void) static int kcs_detect(struct si_sm_data *kcs) { - /* It's impossible for the KCS status register to be all 1's, - (assuming a properly functioning, self-initialized BMC) - but that's what you get from reading a bogus address, so we - test that first. */ + /* + * It's impossible for the KCS status register to be all 1's, + * (assuming a properly functioning, self-initialized BMC) + * but that's what you get from reading a bogus address, so we + * test that first. + */ if (read_status(kcs) == 0xff) return 1; @@ -507,8 +540,7 @@ static void kcs_cleanup(struct si_sm_data *kcs) { } -struct si_sm_handlers kcs_smi_handlers = -{ +struct si_sm_handlers kcs_smi_handlers = { .init_data = init_kcs_data, .start_transaction = start_kcs_transaction, .get_result = get_kcs_result, diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c index 843d34c8627..e6db9381b2c 100644 --- a/drivers/char/ipmi/ipmi_msghandler.c +++ b/drivers/char/ipmi/ipmi_msghandler.c @@ -33,9 +33,9 @@ #include <linux/module.h> #include <linux/errno.h> -#include <asm/system.h> -#include <linux/sched.h> #include <linux/poll.h> +#include <linux/sched.h> +#include <linux/seq_file.h> #include <linux/spinlock.h> #include <linux/mutex.h> #include <linux/slab.h> @@ -45,36 +45,57 @@ #include <linux/init.h> #include <linux/proc_fs.h> #include <linux/rcupdate.h> +#include <linux/interrupt.h> #define PFX "IPMI message handler: " -#define IPMI_DRIVER_VERSION "39.0" +#define IPMI_DRIVER_VERSION "39.2" static struct ipmi_recv_msg *ipmi_alloc_recv_msg(void); static int ipmi_init_msghandler(void); +static void smi_recv_tasklet(unsigned long); +static void handle_new_recv_msgs(ipmi_smi_t intf); +static void need_waiter(ipmi_smi_t intf); -static int initialized = 0; +static int initialized; #ifdef CONFIG_PROC_FS -static struct proc_dir_entry *proc_ipmi_root = NULL; +static struct proc_dir_entry *proc_ipmi_root; #endif /* CONFIG_PROC_FS */ +/* Remain in auto-maintenance mode for this amount of time (in ms). */ +#define IPMI_MAINTENANCE_MODE_TIMEOUT 30000 + #define MAX_EVENTS_IN_QUEUE 25 -/* Don't let a message sit in a queue forever, always time it with at lest - the max message timer. This is in milliseconds. */ +/* + * Don't let a message sit in a queue forever, always time it with at lest + * the max message timer. This is in milliseconds. + */ #define MAX_MSG_TIMEOUT 60000 +/* Call every ~1000 ms. */ +#define IPMI_TIMEOUT_TIME 1000 + +/* How many jiffies does it take to get to the timeout time. */ +#define IPMI_TIMEOUT_JIFFIES ((IPMI_TIMEOUT_TIME * HZ) / 1000) + +/* + * Request events from the queue every second (this is the number of + * IPMI_TIMEOUT_TIMES between event requests). Hopefully, in the + * future, IPMI will add a way to know immediately if an event is in + * the queue and this silliness can go away. + */ +#define IPMI_REQUEST_EV_TIME (1000 / (IPMI_TIMEOUT_TIME)) /* * The main "user" data structure. */ -struct ipmi_user -{ +struct ipmi_user { struct list_head link; - /* Set to "0" when the user is destroyed. */ - int valid; + /* Set to false when the user is destroyed. */ + bool valid; struct kref refcount; @@ -86,16 +107,16 @@ struct ipmi_user ipmi_smi_t intf; /* Does this interface receive IPMI events? */ - int gets_events; + bool gets_events; }; -struct cmd_rcvr -{ +struct cmd_rcvr { struct list_head link; ipmi_user_t user; unsigned char netfn; unsigned char cmd; + unsigned int chans; /* * This is used to form a linked lised during mass deletion. @@ -103,12 +124,12 @@ struct cmd_rcvr * or change any data until the RCU period completes. So we * use this next variable during mass deletion so we can have * a list and don't have to wait and restart the search on - * every individual deletion of a command. */ + * every individual deletion of a command. + */ struct cmd_rcvr *next; }; -struct seq_table -{ +struct seq_table { unsigned int inuse : 1; unsigned int broadcast : 1; @@ -116,53 +137,60 @@ struct seq_table unsigned long orig_timeout; unsigned int retries_left; - /* To verify on an incoming send message response that this is - the message that the response is for, we keep a sequence id - and increment it every time we send a message. */ + /* + * To verify on an incoming send message response that this is + * the message that the response is for, we keep a sequence id + * and increment it every time we send a message. + */ long seqid; - /* This is held so we can properly respond to the message on a - timeout, and it is used to hold the temporary data for - retransmission, too. */ + /* + * This is held so we can properly respond to the message on a + * timeout, and it is used to hold the temporary data for + * retransmission, too. + */ struct ipmi_recv_msg *recv_msg; }; -/* Store the information in a msgid (long) to allow us to find a - sequence table entry from the msgid. */ +/* + * Store the information in a msgid (long) to allow us to find a + * sequence table entry from the msgid. + */ #define STORE_SEQ_IN_MSGID(seq, seqid) (((seq&0xff)<<26) | (seqid&0x3ffffff)) #define GET_SEQ_FROM_MSGID(msgid, seq, seqid) \ do { \ seq = ((msgid >> 26) & 0x3f); \ seqid = (msgid & 0x3fffff); \ - } while (0) + } while (0) #define NEXT_SEQID(seqid) (((seqid) + 1) & 0x3fffff) -struct ipmi_channel -{ +struct ipmi_channel { unsigned char medium; unsigned char protocol; - /* My slave address. This is initialized to IPMI_BMC_SLAVE_ADDR, - but may be changed by the user. */ + /* + * My slave address. This is initialized to IPMI_BMC_SLAVE_ADDR, + * but may be changed by the user. + */ unsigned char address; - /* My LUN. This should generally stay the SMS LUN, but just in - case... */ + /* + * My LUN. This should generally stay the SMS LUN, but just in + * case... + */ unsigned char lun; }; #ifdef CONFIG_PROC_FS -struct ipmi_proc_entry -{ +struct ipmi_proc_entry { char *name; struct ipmi_proc_entry *next; }; #endif -struct bmc_device -{ +struct bmc_device { struct platform_device *dev; struct ipmi_device_id id; unsigned char guid[16]; @@ -183,80 +211,224 @@ struct bmc_device struct device_attribute aux_firmware_rev_attr; }; +/* + * Various statistics for IPMI, these index stats[] in the ipmi_smi + * structure. + */ +enum ipmi_stat_indexes { + /* Commands we got from the user that were invalid. */ + IPMI_STAT_sent_invalid_commands = 0, + + /* Commands we sent to the MC. */ + IPMI_STAT_sent_local_commands, + + /* Responses from the MC that were delivered to a user. */ + IPMI_STAT_handled_local_responses, + + /* Responses from the MC that were not delivered to a user. */ + IPMI_STAT_unhandled_local_responses, + + /* Commands we sent out to the IPMB bus. */ + IPMI_STAT_sent_ipmb_commands, + + /* Commands sent on the IPMB that had errors on the SEND CMD */ + IPMI_STAT_sent_ipmb_command_errs, + + /* Each retransmit increments this count. */ + IPMI_STAT_retransmitted_ipmb_commands, + + /* + * When a message times out (runs out of retransmits) this is + * incremented. + */ + IPMI_STAT_timed_out_ipmb_commands, + + /* + * This is like above, but for broadcasts. Broadcasts are + * *not* included in the above count (they are expected to + * time out). + */ + IPMI_STAT_timed_out_ipmb_broadcasts, + + /* Responses I have sent to the IPMB bus. */ + IPMI_STAT_sent_ipmb_responses, + + /* The response was delivered to the user. */ + IPMI_STAT_handled_ipmb_responses, + + /* The response had invalid data in it. */ + IPMI_STAT_invalid_ipmb_responses, + + /* The response didn't have anyone waiting for it. */ + IPMI_STAT_unhandled_ipmb_responses, + + /* Commands we sent out to the IPMB bus. */ + IPMI_STAT_sent_lan_commands, + + /* Commands sent on the IPMB that had errors on the SEND CMD */ + IPMI_STAT_sent_lan_command_errs, + + /* Each retransmit increments this count. */ + IPMI_STAT_retransmitted_lan_commands, + + /* + * When a message times out (runs out of retransmits) this is + * incremented. + */ + IPMI_STAT_timed_out_lan_commands, + + /* Responses I have sent to the IPMB bus. */ + IPMI_STAT_sent_lan_responses, + + /* The response was delivered to the user. */ + IPMI_STAT_handled_lan_responses, + + /* The response had invalid data in it. */ + IPMI_STAT_invalid_lan_responses, + + /* The response didn't have anyone waiting for it. */ + IPMI_STAT_unhandled_lan_responses, + + /* The command was delivered to the user. */ + IPMI_STAT_handled_commands, + + /* The command had invalid data in it. */ + IPMI_STAT_invalid_commands, + + /* The command didn't have anyone waiting for it. */ + IPMI_STAT_unhandled_commands, + + /* Invalid data in an event. */ + IPMI_STAT_invalid_events, + + /* Events that were received with the proper format. */ + IPMI_STAT_events, + + /* Retransmissions on IPMB that failed. */ + IPMI_STAT_dropped_rexmit_ipmb_commands, + + /* Retransmissions on LAN that failed. */ + IPMI_STAT_dropped_rexmit_lan_commands, + + /* This *must* remain last, add new values above this. */ + IPMI_NUM_STATS +}; + + #define IPMI_IPMB_NUM_SEQ 64 #define IPMI_MAX_CHANNELS 16 -struct ipmi_smi -{ +struct ipmi_smi { /* What interface number are we? */ int intf_num; struct kref refcount; - /* The list of upper layers that are using me. seq_lock - * protects this. */ + /* Used for a list of interfaces. */ + struct list_head link; + + /* + * The list of upper layers that are using me. seq_lock + * protects this. + */ struct list_head users; + /* Information to supply to users. */ + unsigned char ipmi_version_major; + unsigned char ipmi_version_minor; + /* Used for wake ups at startup. */ wait_queue_head_t waitq; struct bmc_device *bmc; char *my_dev_name; + char *sysfs_name; - /* This is the lower-layer's sender routine. */ + /* + * This is the lower-layer's sender routine. Note that you + * must either be holding the ipmi_interfaces_mutex or be in + * an umpreemptible region to use this. You must fetch the + * value into a local variable and make sure it is not NULL. + */ struct ipmi_smi_handlers *handlers; void *send_info; #ifdef CONFIG_PROC_FS - /* A list of proc entries for this interface. This does not - need a lock, only one thread creates it and only one thread - destroys it. */ - spinlock_t proc_entry_lock; + /* A list of proc entries for this interface. */ + struct mutex proc_entry_lock; struct ipmi_proc_entry *proc_entries; #endif /* Driver-model device for the system interface. */ struct device *si_dev; - /* A table of sequence numbers for this interface. We use the - sequence numbers for IPMB messages that go out of the - interface to match them up with their responses. A routine - is called periodically to time the items in this list. */ + /* + * A table of sequence numbers for this interface. We use the + * sequence numbers for IPMB messages that go out of the + * interface to match them up with their responses. A routine + * is called periodically to time the items in this list. + */ spinlock_t seq_lock; struct seq_table seq_table[IPMI_IPMB_NUM_SEQ]; int curr_seq; - /* Messages that were delayed for some reason (out of memory, - for instance), will go in here to be processed later in a - periodic timer interrupt. */ + /* + * Messages queued for delivery. If delivery fails (out of memory + * for instance), They will stay in here to be processed later in a + * periodic timer interrupt. The tasklet is for handling received + * messages directly from the handler. + */ spinlock_t waiting_msgs_lock; struct list_head waiting_msgs; + atomic_t watchdog_pretimeouts_to_deliver; + struct tasklet_struct recv_tasklet; - /* The list of command receivers that are registered for commands - on this interface. */ + /* + * The list of command receivers that are registered for commands + * on this interface. + */ struct mutex cmd_rcvrs_mutex; struct list_head cmd_rcvrs; - /* Events that were queues because no one was there to receive - them. */ + /* + * Events that were queues because no one was there to receive + * them. + */ spinlock_t events_lock; /* For dealing with event stuff. */ struct list_head waiting_events; unsigned int waiting_events_count; /* How many events in queue? */ + char delivering_events; + char event_msg_printed; + atomic_t event_waiters; + unsigned int ticks_to_req_ev; + int last_needs_timer; - /* The event receiver for my BMC, only really used at panic - shutdown as a place to store this. */ + /* + * The event receiver for my BMC, only really used at panic + * shutdown as a place to store this. + */ unsigned char event_receiver; unsigned char event_receiver_lun; unsigned char local_sel_device; unsigned char local_event_generator; - /* A cheap hack, if this is non-null and a message to an - interface comes in with a NULL user, call this routine with - it. Note that the message will still be freed by the - caller. This only works on the system interface. */ + /* For handling of maintenance mode. */ + int maintenance_mode; + bool maintenance_mode_enable; + int auto_maintenance_timeout; + spinlock_t maintenance_mode_lock; /* Used in a timer... */ + + /* + * A cheap hack, if this is non-null and a message to an + * interface comes in with a NULL user, call this routine with + * it. Note that the message will still be freed by the + * caller. This only works on the system interface. + */ void (*null_user_handler)(ipmi_smi_t intf, struct ipmi_recv_msg *msg); - /* When we are scanning the channels for an SMI, this will - tell which channel we are scanning. */ + /* + * When we are scanning the channels for an SMI, this will + * tell which channel we are scanning. + */ int curr_channel; /* Channel information */ @@ -266,104 +438,56 @@ struct ipmi_smi struct proc_dir_entry *proc_dir; char proc_dir_name[10]; - spinlock_t counter_lock; /* For making counters atomic. */ - - /* Commands we got that were invalid. */ - unsigned int sent_invalid_commands; - - /* Commands we sent to the MC. */ - unsigned int sent_local_commands; - /* Responses from the MC that were delivered to a user. */ - unsigned int handled_local_responses; - /* Responses from the MC that were not delivered to a user. */ - unsigned int unhandled_local_responses; - - /* Commands we sent out to the IPMB bus. */ - unsigned int sent_ipmb_commands; - /* Commands sent on the IPMB that had errors on the SEND CMD */ - unsigned int sent_ipmb_command_errs; - /* Each retransmit increments this count. */ - unsigned int retransmitted_ipmb_commands; - /* When a message times out (runs out of retransmits) this is - incremented. */ - unsigned int timed_out_ipmb_commands; - - /* This is like above, but for broadcasts. Broadcasts are - *not* included in the above count (they are expected to - time out). */ - unsigned int timed_out_ipmb_broadcasts; - - /* Responses I have sent to the IPMB bus. */ - unsigned int sent_ipmb_responses; - - /* The response was delivered to the user. */ - unsigned int handled_ipmb_responses; - /* The response had invalid data in it. */ - unsigned int invalid_ipmb_responses; - /* The response didn't have anyone waiting for it. */ - unsigned int unhandled_ipmb_responses; - - /* Commands we sent out to the IPMB bus. */ - unsigned int sent_lan_commands; - /* Commands sent on the IPMB that had errors on the SEND CMD */ - unsigned int sent_lan_command_errs; - /* Each retransmit increments this count. */ - unsigned int retransmitted_lan_commands; - /* When a message times out (runs out of retransmits) this is - incremented. */ - unsigned int timed_out_lan_commands; - - /* Responses I have sent to the IPMB bus. */ - unsigned int sent_lan_responses; - - /* The response was delivered to the user. */ - unsigned int handled_lan_responses; - /* The response had invalid data in it. */ - unsigned int invalid_lan_responses; - /* The response didn't have anyone waiting for it. */ - unsigned int unhandled_lan_responses; - - /* The command was delivered to the user. */ - unsigned int handled_commands; - /* The command had invalid data in it. */ - unsigned int invalid_commands; - /* The command didn't have anyone waiting for it. */ - unsigned int unhandled_commands; + atomic_t stats[IPMI_NUM_STATS]; - /* Invalid data in an event. */ - unsigned int invalid_events; - /* Events that were received with the proper format. */ - unsigned int events; + /* + * run_to_completion duplicate of smb_info, smi_info + * and ipmi_serial_info structures. Used to decrease numbers of + * parameters passed by "low" level IPMI code. + */ + int run_to_completion; }; #define to_si_intf_from_dev(device) container_of(device, struct ipmi_smi, dev) -/* Used to mark an interface entry that cannot be used but is not a - * free entry, either, primarily used at creation and deletion time so - * a slot doesn't get reused too quickly. */ -#define IPMI_INVALID_INTERFACE_ENTRY ((ipmi_smi_t) ((long) 1)) -#define IPMI_INVALID_INTERFACE(i) (((i) == NULL) \ - || (i == IPMI_INVALID_INTERFACE_ENTRY)) - /** * The driver model view of the IPMI messaging driver. */ -static struct device_driver ipmidriver = { - .name = "ipmi", - .bus = &platform_bus_type +static struct platform_driver ipmidriver = { + .driver = { + .name = "ipmi", + .bus = &platform_bus_type + } }; static DEFINE_MUTEX(ipmidriver_mutex); -#define MAX_IPMI_INTERFACES 4 -static ipmi_smi_t ipmi_interfaces[MAX_IPMI_INTERFACES]; +static LIST_HEAD(ipmi_interfaces); +static DEFINE_MUTEX(ipmi_interfaces_mutex); + +/* + * List of watchers that want to know when smi's are added and deleted. + */ +static LIST_HEAD(smi_watchers); +static DEFINE_MUTEX(smi_watchers_mutex); + +#define ipmi_inc_stat(intf, stat) \ + atomic_inc(&(intf)->stats[IPMI_STAT_ ## stat]) +#define ipmi_get_stat(intf, stat) \ + ((unsigned int) atomic_read(&(intf)->stats[IPMI_STAT_ ## stat])) -/* Directly protects the ipmi_interfaces data structure. */ -static DEFINE_SPINLOCK(interfaces_lock); +static int is_lan_addr(struct ipmi_addr *addr) +{ + return addr->addr_type == IPMI_LAN_ADDR_TYPE; +} -/* List of watchers that want to know when smi's are added and - deleted. */ -static struct list_head smi_watchers = LIST_HEAD_INIT(smi_watchers); -static DECLARE_RWSEM(smi_watchers_sem); +static int is_ipmb_addr(struct ipmi_addr *addr) +{ + return addr->addr_type == IPMI_IPMB_ADDR_TYPE; +} +static int is_ipmb_bcast_addr(struct ipmi_addr *addr) +{ + return addr->addr_type == IPMI_IPMB_BROADCAST_ADDR_TYPE; +} static void free_recv_msg_list(struct list_head *q) { @@ -375,32 +499,43 @@ static void free_recv_msg_list(struct list_head *q) } } +static void free_smi_msg_list(struct list_head *q) +{ + struct ipmi_smi_msg *msg, *msg2; + + list_for_each_entry_safe(msg, msg2, q, link) { + list_del(&msg->link); + ipmi_free_smi_msg(msg); + } +} + static void clean_up_interface_data(ipmi_smi_t intf) { int i; struct cmd_rcvr *rcvr, *rcvr2; struct list_head list; - free_recv_msg_list(&intf->waiting_msgs); + tasklet_kill(&intf->recv_tasklet); + + free_smi_msg_list(&intf->waiting_msgs); free_recv_msg_list(&intf->waiting_events); - /* Wholesale remove all the entries from the list in the - * interface and wait for RCU to know that none are in use. */ + /* + * Wholesale remove all the entries from the list in the + * interface and wait for RCU to know that none are in use. + */ mutex_lock(&intf->cmd_rcvrs_mutex); - list_add_rcu(&list, &intf->cmd_rcvrs); - list_del_rcu(&intf->cmd_rcvrs); + INIT_LIST_HEAD(&list); + list_splice_init_rcu(&intf->cmd_rcvrs, &list, synchronize_rcu); mutex_unlock(&intf->cmd_rcvrs_mutex); - synchronize_rcu(); list_for_each_entry_safe(rcvr, rcvr2, &list, link) kfree(rcvr); for (i = 0; i < IPMI_IPMB_NUM_SEQ; i++) { if ((intf->seq_table[i].inuse) - && (intf->seq_table[i].recv_msg)) - { + && (intf->seq_table[i].recv_msg)) ipmi_free_recv_msg(intf->seq_table[i].recv_msg); - } } } @@ -412,48 +547,86 @@ static void intf_free(struct kref *ref) kfree(intf); } +struct watcher_entry { + int intf_num; + ipmi_smi_t intf; + struct list_head link; +}; + int ipmi_smi_watcher_register(struct ipmi_smi_watcher *watcher) { - int i; - unsigned long flags; + ipmi_smi_t intf; + LIST_HEAD(to_deliver); + struct watcher_entry *e, *e2; + + mutex_lock(&smi_watchers_mutex); + + mutex_lock(&ipmi_interfaces_mutex); - down_write(&smi_watchers_sem); - list_add(&(watcher->link), &smi_watchers); - up_write(&smi_watchers_sem); - spin_lock_irqsave(&interfaces_lock, flags); - for (i = 0; i < MAX_IPMI_INTERFACES; i++) { - ipmi_smi_t intf = ipmi_interfaces[i]; - if (IPMI_INVALID_INTERFACE(intf)) + /* Build a list of things to deliver. */ + list_for_each_entry(intf, &ipmi_interfaces, link) { + if (intf->intf_num == -1) continue; - spin_unlock_irqrestore(&interfaces_lock, flags); - watcher->new_smi(i, intf->si_dev); - spin_lock_irqsave(&interfaces_lock, flags); + e = kmalloc(sizeof(*e), GFP_KERNEL); + if (!e) + goto out_err; + kref_get(&intf->refcount); + e->intf = intf; + e->intf_num = intf->intf_num; + list_add_tail(&e->link, &to_deliver); + } + + /* We will succeed, so add it to the list. */ + list_add(&watcher->link, &smi_watchers); + + mutex_unlock(&ipmi_interfaces_mutex); + + list_for_each_entry_safe(e, e2, &to_deliver, link) { + list_del(&e->link); + watcher->new_smi(e->intf_num, e->intf->si_dev); + kref_put(&e->intf->refcount, intf_free); + kfree(e); } - spin_unlock_irqrestore(&interfaces_lock, flags); + + mutex_unlock(&smi_watchers_mutex); + return 0; + + out_err: + mutex_unlock(&ipmi_interfaces_mutex); + mutex_unlock(&smi_watchers_mutex); + list_for_each_entry_safe(e, e2, &to_deliver, link) { + list_del(&e->link); + kref_put(&e->intf->refcount, intf_free); + kfree(e); + } + return -ENOMEM; } +EXPORT_SYMBOL(ipmi_smi_watcher_register); int ipmi_smi_watcher_unregister(struct ipmi_smi_watcher *watcher) { - down_write(&smi_watchers_sem); + mutex_lock(&smi_watchers_mutex); list_del(&(watcher->link)); - up_write(&smi_watchers_sem); + mutex_unlock(&smi_watchers_mutex); return 0; } +EXPORT_SYMBOL(ipmi_smi_watcher_unregister); +/* + * Must be called with smi_watchers_mutex held. + */ static void call_smi_watchers(int i, struct device *dev) { struct ipmi_smi_watcher *w; - down_read(&smi_watchers_sem); list_for_each_entry(w, &smi_watchers, link) { if (try_module_get(w->owner)) { w->new_smi(i, dev); module_put(w->owner); } } - up_read(&smi_watchers_sem); } static int @@ -473,9 +646,7 @@ ipmi_addr_equal(struct ipmi_addr *addr1, struct ipmi_addr *addr2) return (smi_addr1->lun == smi_addr2->lun); } - if ((addr1->addr_type == IPMI_IPMB_ADDR_TYPE) - || (addr1->addr_type == IPMI_IPMB_BROADCAST_ADDR_TYPE)) - { + if (is_ipmb_addr(addr1) || is_ipmb_bcast_addr(addr1)) { struct ipmi_ipmb_addr *ipmb_addr1 = (struct ipmi_ipmb_addr *) addr1; struct ipmi_ipmb_addr *ipmb_addr2 @@ -485,7 +656,7 @@ ipmi_addr_equal(struct ipmi_addr *addr1, struct ipmi_addr *addr2) && (ipmb_addr1->lun == ipmb_addr2->lun)); } - if (addr1->addr_type == IPMI_LAN_ADDR_TYPE) { + if (is_lan_addr(addr1)) { struct ipmi_lan_addr *lan_addr1 = (struct ipmi_lan_addr *) addr1; struct ipmi_lan_addr *lan_addr2 @@ -503,9 +674,8 @@ ipmi_addr_equal(struct ipmi_addr *addr1, struct ipmi_addr *addr2) int ipmi_validate_addr(struct ipmi_addr *addr, int len) { - if (len < sizeof(struct ipmi_system_interface_addr)) { + if (len < sizeof(struct ipmi_system_interface_addr)) return -EINVAL; - } if (addr->addr_type == IPMI_SYSTEM_INTERFACE_ADDR_TYPE) { if (addr->channel != IPMI_BMC_CHANNEL) @@ -518,24 +688,21 @@ int ipmi_validate_addr(struct ipmi_addr *addr, int len) || (addr->channel < 0)) return -EINVAL; - if ((addr->addr_type == IPMI_IPMB_ADDR_TYPE) - || (addr->addr_type == IPMI_IPMB_BROADCAST_ADDR_TYPE)) - { - if (len < sizeof(struct ipmi_ipmb_addr)) { + if (is_ipmb_addr(addr) || is_ipmb_bcast_addr(addr)) { + if (len < sizeof(struct ipmi_ipmb_addr)) return -EINVAL; - } return 0; } - if (addr->addr_type == IPMI_LAN_ADDR_TYPE) { - if (len < sizeof(struct ipmi_lan_addr)) { + if (is_lan_addr(addr)) { + if (len < sizeof(struct ipmi_lan_addr)) return -EINVAL; - } return 0; } return -EINVAL; } +EXPORT_SYMBOL(ipmi_validate_addr); unsigned int ipmi_addr_length(int addr_type) { @@ -543,34 +710,28 @@ unsigned int ipmi_addr_length(int addr_type) return sizeof(struct ipmi_system_interface_addr); if ((addr_type == IPMI_IPMB_ADDR_TYPE) - || (addr_type == IPMI_IPMB_BROADCAST_ADDR_TYPE)) - { + || (addr_type == IPMI_IPMB_BROADCAST_ADDR_TYPE)) return sizeof(struct ipmi_ipmb_addr); - } if (addr_type == IPMI_LAN_ADDR_TYPE) return sizeof(struct ipmi_lan_addr); return 0; } +EXPORT_SYMBOL(ipmi_addr_length); static void deliver_response(struct ipmi_recv_msg *msg) { if (!msg->user) { ipmi_smi_t intf = msg->user_msg_data; - unsigned long flags; /* Special handling for NULL users. */ if (intf->null_user_handler) { intf->null_user_handler(intf, msg); - spin_lock_irqsave(&intf->counter_lock, flags); - intf->handled_local_responses++; - spin_unlock_irqrestore(&intf->counter_lock, flags); + ipmi_inc_stat(intf, handled_local_responses); } else { /* No handler, so give up. */ - spin_lock_irqsave(&intf->counter_lock, flags); - intf->unhandled_local_responses++; - spin_unlock_irqrestore(&intf->counter_lock, flags); + ipmi_inc_stat(intf, unhandled_local_responses); } ipmi_free_recv_msg(msg); } else { @@ -579,9 +740,22 @@ static void deliver_response(struct ipmi_recv_msg *msg) } } -/* Find the next sequence number not being used and add the given - message with the given timeout to the sequence table. This must be - called with the interface's seq_lock held. */ +static void +deliver_err_response(struct ipmi_recv_msg *msg, int err) +{ + msg->recv_type = IPMI_RESPONSE_RECV_TYPE; + msg->msg_data[0] = err; + msg->msg.netfn |= 1; /* Convert to a response. */ + msg->msg.data_len = 1; + msg->msg.data = msg->msg_data; + deliver_response(msg); +} + +/* + * Find the next sequence number not being used and add the given + * message with the given timeout to the sequence table. This must be + * called with the interface's seq_lock held. + */ static int intf_next_seq(ipmi_smi_t intf, struct ipmi_recv_msg *recv_msg, unsigned long timeout, @@ -593,10 +767,8 @@ static int intf_next_seq(ipmi_smi_t intf, int rv = 0; unsigned int i; - for (i = intf->curr_seq; - (i+1)%IPMI_IPMB_NUM_SEQ != intf->curr_seq; - i = (i+1)%IPMI_IPMB_NUM_SEQ) - { + for (i = intf->curr_seq; (i+1)%IPMI_IPMB_NUM_SEQ != intf->curr_seq; + i = (i+1)%IPMI_IPMB_NUM_SEQ) { if (!intf->seq_table[i].inuse) break; } @@ -604,8 +776,10 @@ static int intf_next_seq(ipmi_smi_t intf, if (!intf->seq_table[i].inuse) { intf->seq_table[i].recv_msg = recv_msg; - /* Start with the maximum timeout, when the send response - comes in we will start the real timer. */ + /* + * Start with the maximum timeout, when the send response + * comes in we will start the real timer. + */ intf->seq_table[i].timeout = MAX_MSG_TIMEOUT; intf->seq_table[i].orig_timeout = timeout; intf->seq_table[i].retries_left = retries; @@ -615,18 +789,21 @@ static int intf_next_seq(ipmi_smi_t intf, *seq = i; *seqid = intf->seq_table[i].seqid; intf->curr_seq = (i+1)%IPMI_IPMB_NUM_SEQ; + need_waiter(intf); } else { rv = -EAGAIN; } - + return rv; } -/* Return the receive message for the given sequence number and - release the sequence number so it can be reused. Some other data - is passed in to be sure the message matches up correctly (to help - guard against message coming in after their timeout and the - sequence number being reused). */ +/* + * Return the receive message for the given sequence number and + * release the sequence number so it can be reused. Some other data + * is passed in to be sure the message matches up correctly (to help + * guard against message coming in after their timeout and the + * sequence number being reused). + */ static int intf_find_seq(ipmi_smi_t intf, unsigned char seq, short channel, @@ -645,11 +822,9 @@ static int intf_find_seq(ipmi_smi_t intf, if (intf->seq_table[seq].inuse) { struct ipmi_recv_msg *msg = intf->seq_table[seq].recv_msg; - if ((msg->addr.channel == channel) - && (msg->msg.cmd == cmd) - && (msg->msg.netfn == netfn) - && (ipmi_addr_equal(addr, &(msg->addr)))) - { + if ((msg->addr.channel == channel) && (msg->msg.cmd == cmd) + && (msg->msg.netfn == netfn) + && (ipmi_addr_equal(addr, &(msg->addr)))) { *recv_msg = msg; intf->seq_table[seq].inuse = 0; rv = 0; @@ -674,11 +849,12 @@ static int intf_start_seq_timer(ipmi_smi_t intf, GET_SEQ_FROM_MSGID(msgid, seq, seqid); spin_lock_irqsave(&(intf->seq_lock), flags); - /* We do this verification because the user can be deleted - while a message is outstanding. */ + /* + * We do this verification because the user can be deleted + * while a message is outstanding. + */ if ((intf->seq_table[seq].inuse) - && (intf->seq_table[seq].seqid == seqid)) - { + && (intf->seq_table[seq].seqid == seqid)) { struct seq_table *ent = &(intf->seq_table[seq]); ent->timeout = ent->orig_timeout; rv = 0; @@ -703,11 +879,12 @@ static int intf_err_seq(ipmi_smi_t intf, GET_SEQ_FROM_MSGID(msgid, seq, seqid); spin_lock_irqsave(&(intf->seq_lock), flags); - /* We do this verification because the user can be deleted - while a message is outstanding. */ + /* + * We do this verification because the user can be deleted + * while a message is outstanding. + */ if ((intf->seq_table[seq].inuse) - && (intf->seq_table[seq].seqid == seqid)) - { + && (intf->seq_table[seq].seqid == seqid)) { struct seq_table *ent = &(intf->seq_table[seq]); ent->inuse = 0; @@ -716,14 +893,8 @@ static int intf_err_seq(ipmi_smi_t intf, } spin_unlock_irqrestore(&(intf->seq_lock), flags); - if (msg) { - msg->recv_type = IPMI_RESPONSE_RECV_TYPE; - msg->msg_data[0] = err; - msg->msg.netfn |= 1; /* Convert to a response. */ - msg->msg.data_len = 1; - msg->msg.data = msg->msg_data; - deliver_response(msg); - } + if (msg) + deliver_err_response(msg, err); return rv; } @@ -739,24 +910,30 @@ int ipmi_create_user(unsigned int if_num, int rv = 0; ipmi_smi_t intf; - /* There is no module usecount here, because it's not - required. Since this can only be used by and called from - other modules, they will implicitly use this module, and - thus this can't be removed unless the other modules are - removed. */ + /* + * There is no module usecount here, because it's not + * required. Since this can only be used by and called from + * other modules, they will implicitly use this module, and + * thus this can't be removed unless the other modules are + * removed. + */ if (handler == NULL) return -EINVAL; - /* Make sure the driver is actually initialized, this handles - problems with initialization order. */ + /* + * Make sure the driver is actually initialized, this handles + * problems with initialization order. + */ if (!initialized) { rv = ipmi_init_msghandler(); if (rv) return rv; - /* The init code doesn't return an error if it was turned - off, but it won't initialize. Check that. */ + /* + * The init code doesn't return an error if it was turned + * off, but it won't initialize. Check that. + */ if (!initialized) return -ENODEV; } @@ -765,23 +942,24 @@ int ipmi_create_user(unsigned int if_num, if (!new_user) return -ENOMEM; - spin_lock_irqsave(&interfaces_lock, flags); - intf = ipmi_interfaces[if_num]; - if ((if_num >= MAX_IPMI_INTERFACES) || IPMI_INVALID_INTERFACE(intf)) { - spin_unlock_irqrestore(&interfaces_lock, flags); - rv = -EINVAL; - goto out_kfree; + mutex_lock(&ipmi_interfaces_mutex); + list_for_each_entry_rcu(intf, &ipmi_interfaces, link) { + if (intf->intf_num == if_num) + goto found; } + /* Not found, return an error */ + rv = -EINVAL; + goto out_kfree; + found: /* Note that each existing user holds a refcount to the interface. */ kref_get(&intf->refcount); - spin_unlock_irqrestore(&interfaces_lock, flags); kref_init(&new_user->refcount); new_user->handler = handler; new_user->handler_data = handler_data; new_user->intf = intf; - new_user->gets_events = 0; + new_user->gets_events = false; if (!try_module_get(intf->handlers->owner)) { rv = -ENODEV; @@ -796,19 +974,59 @@ int ipmi_create_user(unsigned int if_num, } } - new_user->valid = 1; + /* + * Hold the lock so intf->handlers is guaranteed to be good + * until now + */ + mutex_unlock(&ipmi_interfaces_mutex); + + new_user->valid = true; spin_lock_irqsave(&intf->seq_lock, flags); list_add_rcu(&new_user->link, &intf->users); spin_unlock_irqrestore(&intf->seq_lock, flags); + if (handler->ipmi_watchdog_pretimeout) { + /* User wants pretimeouts, so make sure to watch for them. */ + if (atomic_inc_return(&intf->event_waiters) == 1) + need_waiter(intf); + } *user = new_user; return 0; out_kref: kref_put(&intf->refcount, intf_free); out_kfree: + mutex_unlock(&ipmi_interfaces_mutex); kfree(new_user); return rv; } +EXPORT_SYMBOL(ipmi_create_user); + +int ipmi_get_smi_info(int if_num, struct ipmi_smi_info *data) +{ + int rv = 0; + ipmi_smi_t intf; + struct ipmi_smi_handlers *handlers; + + mutex_lock(&ipmi_interfaces_mutex); + list_for_each_entry_rcu(intf, &ipmi_interfaces, link) { + if (intf->intf_num == if_num) + goto found; + } + /* Not found, return an error */ + rv = -EINVAL; + mutex_unlock(&ipmi_interfaces_mutex); + return rv; + +found: + handlers = intf->handlers; + rv = -ENOSYS; + if (handlers->get_smi_info) + rv = handlers->get_smi_info(intf->send_info, data); + mutex_unlock(&ipmi_interfaces_mutex); + + return rv; +} +EXPORT_SYMBOL(ipmi_get_smi_info); static void free_user(struct kref *ref) { @@ -824,7 +1042,13 @@ int ipmi_destroy_user(ipmi_user_t user) struct cmd_rcvr *rcvr; struct cmd_rcvr *rcvrs = NULL; - user->valid = 0; + user->valid = false; + + if (user->handler->ipmi_watchdog_pretimeout) + atomic_dec(&intf->event_waiters); + + if (user->gets_events) + atomic_dec(&intf->event_waiters); /* Remove the user from the interface's sequence table. */ spin_lock_irqsave(&intf->seq_lock, flags); @@ -832,9 +1056,9 @@ int ipmi_destroy_user(ipmi_user_t user) for (i = 0; i < IPMI_IPMB_NUM_SEQ; i++) { if (intf->seq_table[i].inuse - && (intf->seq_table[i].recv_msg->user == user)) - { + && (intf->seq_table[i].recv_msg->user == user)) { intf->seq_table[i].inuse = 0; + ipmi_free_recv_msg(intf->seq_table[i].recv_msg); } } spin_unlock_irqrestore(&intf->seq_lock, flags); @@ -861,9 +1085,13 @@ int ipmi_destroy_user(ipmi_user_t user) kfree(rcvr); } - module_put(intf->handlers->owner); - if (intf->handlers->dec_usecount) - intf->handlers->dec_usecount(intf->send_info); + mutex_lock(&ipmi_interfaces_mutex); + if (intf->handlers) { + module_put(intf->handlers->owner); + if (intf->handlers->dec_usecount) + intf->handlers->dec_usecount(intf->send_info); + } + mutex_unlock(&ipmi_interfaces_mutex); kref_put(&intf->refcount, intf_free); @@ -871,14 +1099,16 @@ int ipmi_destroy_user(ipmi_user_t user) return 0; } +EXPORT_SYMBOL(ipmi_destroy_user); void ipmi_get_version(ipmi_user_t user, unsigned char *major, unsigned char *minor) { - *major = ipmi_version_major(&user->intf->bmc->id); - *minor = ipmi_version_minor(&user->intf->bmc->id); + *major = user->intf->ipmi_version_major; + *minor = user->intf->ipmi_version_minor; } +EXPORT_SYMBOL(ipmi_get_version); int ipmi_set_my_address(ipmi_user_t user, unsigned int channel, @@ -889,6 +1119,7 @@ int ipmi_set_my_address(ipmi_user_t user, user->intf->channels[channel].address = address; return 0; } +EXPORT_SYMBOL(ipmi_set_my_address); int ipmi_get_my_address(ipmi_user_t user, unsigned int channel, @@ -899,6 +1130,7 @@ int ipmi_get_my_address(ipmi_user_t user, *address = user->intf->channels[channel].address; return 0; } +EXPORT_SYMBOL(ipmi_get_my_address); int ipmi_set_my_LUN(ipmi_user_t user, unsigned int channel, @@ -909,6 +1141,7 @@ int ipmi_set_my_LUN(ipmi_user_t user, user->intf->channels[channel].lun = LUN & 0x3; return 0; } +EXPORT_SYMBOL(ipmi_set_my_LUN); int ipmi_get_my_LUN(ipmi_user_t user, unsigned int channel, @@ -919,8 +1152,66 @@ int ipmi_get_my_LUN(ipmi_user_t user, *address = user->intf->channels[channel].lun; return 0; } +EXPORT_SYMBOL(ipmi_get_my_LUN); + +int ipmi_get_maintenance_mode(ipmi_user_t user) +{ + int mode; + unsigned long flags; + + spin_lock_irqsave(&user->intf->maintenance_mode_lock, flags); + mode = user->intf->maintenance_mode; + spin_unlock_irqrestore(&user->intf->maintenance_mode_lock, flags); + + return mode; +} +EXPORT_SYMBOL(ipmi_get_maintenance_mode); + +static void maintenance_mode_update(ipmi_smi_t intf) +{ + if (intf->handlers->set_maintenance_mode) + intf->handlers->set_maintenance_mode( + intf->send_info, intf->maintenance_mode_enable); +} + +int ipmi_set_maintenance_mode(ipmi_user_t user, int mode) +{ + int rv = 0; + unsigned long flags; + ipmi_smi_t intf = user->intf; + + spin_lock_irqsave(&intf->maintenance_mode_lock, flags); + if (intf->maintenance_mode != mode) { + switch (mode) { + case IPMI_MAINTENANCE_MODE_AUTO: + intf->maintenance_mode_enable + = (intf->auto_maintenance_timeout > 0); + break; + + case IPMI_MAINTENANCE_MODE_OFF: + intf->maintenance_mode_enable = false; + break; + + case IPMI_MAINTENANCE_MODE_ON: + intf->maintenance_mode_enable = true; + break; + + default: + rv = -EINVAL; + goto out_unlock; + } + intf->maintenance_mode = mode; + + maintenance_mode_update(intf); + } + out_unlock: + spin_unlock_irqrestore(&intf->maintenance_mode_lock, flags); + + return rv; +} +EXPORT_SYMBOL(ipmi_set_maintenance_mode); -int ipmi_set_gets_events(ipmi_user_t user, int val) +int ipmi_set_gets_events(ipmi_user_t user, bool val) { unsigned long flags; ipmi_smi_t intf = user->intf; @@ -930,47 +1221,93 @@ int ipmi_set_gets_events(ipmi_user_t user, int val) INIT_LIST_HEAD(&msgs); spin_lock_irqsave(&intf->events_lock, flags); + if (user->gets_events == val) + goto out; + user->gets_events = val; if (val) { - /* Deliver any queued events. */ + if (atomic_inc_return(&intf->event_waiters) == 1) + need_waiter(intf); + } else { + atomic_dec(&intf->event_waiters); + } + + if (intf->delivering_events) + /* + * Another thread is delivering events for this, so + * let it handle any new events. + */ + goto out; + + /* Deliver any queued events. */ + while (user->gets_events && !list_empty(&intf->waiting_events)) { list_for_each_entry_safe(msg, msg2, &intf->waiting_events, link) list_move_tail(&msg->link, &msgs); intf->waiting_events_count = 0; - } + if (intf->event_msg_printed) { + printk(KERN_WARNING PFX "Event queue no longer" + " full\n"); + intf->event_msg_printed = 0; + } - /* Hold the events lock while doing this to preserve order. */ - list_for_each_entry_safe(msg, msg2, &msgs, link) { - msg->user = user; - kref_get(&user->refcount); - deliver_response(msg); + intf->delivering_events = 1; + spin_unlock_irqrestore(&intf->events_lock, flags); + + list_for_each_entry_safe(msg, msg2, &msgs, link) { + msg->user = user; + kref_get(&user->refcount); + deliver_response(msg); + } + + spin_lock_irqsave(&intf->events_lock, flags); + intf->delivering_events = 0; } + out: spin_unlock_irqrestore(&intf->events_lock, flags); return 0; } +EXPORT_SYMBOL(ipmi_set_gets_events); static struct cmd_rcvr *find_cmd_rcvr(ipmi_smi_t intf, unsigned char netfn, - unsigned char cmd) + unsigned char cmd, + unsigned char chan) { struct cmd_rcvr *rcvr; list_for_each_entry_rcu(rcvr, &intf->cmd_rcvrs, link) { - if ((rcvr->netfn == netfn) && (rcvr->cmd == cmd)) + if ((rcvr->netfn == netfn) && (rcvr->cmd == cmd) + && (rcvr->chans & (1 << chan))) return rcvr; } return NULL; } +static int is_cmd_rcvr_exclusive(ipmi_smi_t intf, + unsigned char netfn, + unsigned char cmd, + unsigned int chans) +{ + struct cmd_rcvr *rcvr; + + list_for_each_entry_rcu(rcvr, &intf->cmd_rcvrs, link) { + if ((rcvr->netfn == netfn) && (rcvr->cmd == cmd) + && (rcvr->chans & chans)) + return 0; + } + return 1; +} + int ipmi_register_for_cmd(ipmi_user_t user, unsigned char netfn, - unsigned char cmd) + unsigned char cmd, + unsigned int chans) { ipmi_smi_t intf = user->intf; struct cmd_rcvr *rcvr; - struct cmd_rcvr *entry; int rv = 0; @@ -979,16 +1316,19 @@ int ipmi_register_for_cmd(ipmi_user_t user, return -ENOMEM; rcvr->cmd = cmd; rcvr->netfn = netfn; + rcvr->chans = chans; rcvr->user = user; mutex_lock(&intf->cmd_rcvrs_mutex); /* Make sure the command/netfn is not already registered. */ - entry = find_cmd_rcvr(intf, netfn, cmd); - if (entry) { + if (!is_cmd_rcvr_exclusive(intf, netfn, cmd, chans)) { rv = -EBUSY; goto out_unlock; } + if (atomic_inc_return(&intf->event_waiters) == 1) + need_waiter(intf); + list_add_rcu(&rcvr->link, &intf->cmd_rcvrs); out_unlock: @@ -998,40 +1338,52 @@ int ipmi_register_for_cmd(ipmi_user_t user, return rv; } +EXPORT_SYMBOL(ipmi_register_for_cmd); int ipmi_unregister_for_cmd(ipmi_user_t user, unsigned char netfn, - unsigned char cmd) + unsigned char cmd, + unsigned int chans) { ipmi_smi_t intf = user->intf; struct cmd_rcvr *rcvr; + struct cmd_rcvr *rcvrs = NULL; + int i, rv = -ENOENT; mutex_lock(&intf->cmd_rcvrs_mutex); - /* Make sure the command/netfn is not already registered. */ - rcvr = find_cmd_rcvr(intf, netfn, cmd); - if ((rcvr) && (rcvr->user == user)) { - list_del_rcu(&rcvr->link); - mutex_unlock(&intf->cmd_rcvrs_mutex); - synchronize_rcu(); + for (i = 0; i < IPMI_NUM_CHANNELS; i++) { + if (((1 << i) & chans) == 0) + continue; + rcvr = find_cmd_rcvr(intf, netfn, cmd, i); + if (rcvr == NULL) + continue; + if (rcvr->user == user) { + rv = 0; + rcvr->chans &= ~chans; + if (rcvr->chans == 0) { + list_del_rcu(&rcvr->link); + rcvr->next = rcvrs; + rcvrs = rcvr; + } + } + } + mutex_unlock(&intf->cmd_rcvrs_mutex); + synchronize_rcu(); + while (rcvrs) { + atomic_dec(&intf->event_waiters); + rcvr = rcvrs; + rcvrs = rcvr->next; kfree(rcvr); - return 0; - } else { - mutex_unlock(&intf->cmd_rcvrs_mutex); - return -ENOENT; } + return rv; } - -void ipmi_user_set_run_to_completion(ipmi_user_t user, int val) -{ - ipmi_smi_t intf = user->intf; - intf->handlers->set_run_to_completion(intf->send_info, val); -} +EXPORT_SYMBOL(ipmi_unregister_for_cmd); static unsigned char ipmb_checksum(unsigned char *data, int size) { unsigned char csum = 0; - + for (; size > 0; size--, data++) csum += *data; @@ -1073,8 +1425,10 @@ static inline void format_ipmb_msg(struct ipmi_smi_msg *smi_msg, = ipmb_checksum(&(smi_msg->data[i+6]), smi_msg->data_size-6); - /* Add on the checksum size and the offset from the - broadcast. */ + /* + * Add on the checksum size and the offset from the + * broadcast. + */ smi_msg->data_size += 1 + i; smi_msg->msgid = msgid; @@ -1110,17 +1464,21 @@ static inline void format_lan_msg(struct ipmi_smi_msg *smi_msg, = ipmb_checksum(&(smi_msg->data[7]), smi_msg->data_size-7); - /* Add on the checksum size and the offset from the - broadcast. */ + /* + * Add on the checksum size and the offset from the + * broadcast. + */ smi_msg->data_size += 1; smi_msg->msgid = msgid; } -/* Separate from ipmi_request so that the user does not have to be - supplied in certain circumstances (mainly at panic time). If - messages are supplied, they will be freed, even if an error - occurs. */ +/* + * Separate from ipmi_request so that the user does not have to be + * supplied in certain circumstances (mainly at panic time). If + * messages are supplied, they will be freed, even if an error + * occurs. + */ static int i_ipmi_request(ipmi_user_t user, ipmi_smi_t intf, struct ipmi_addr *addr, @@ -1135,25 +1493,25 @@ static int i_ipmi_request(ipmi_user_t user, int retries, unsigned int retry_time_ms) { - int rv = 0; - struct ipmi_smi_msg *smi_msg; - struct ipmi_recv_msg *recv_msg; - unsigned long flags; + int rv = 0; + struct ipmi_smi_msg *smi_msg; + struct ipmi_recv_msg *recv_msg; + unsigned long flags; + struct ipmi_smi_handlers *handlers; - if (supplied_recv) { + if (supplied_recv) recv_msg = supplied_recv; - } else { + else { recv_msg = ipmi_alloc_recv_msg(); - if (recv_msg == NULL) { + if (recv_msg == NULL) return -ENOMEM; - } } recv_msg->user_msg_data = user_msg_data; - if (supplied_smi) { + if (supplied_smi) smi_msg = (struct ipmi_smi_msg *) supplied_smi; - } else { + else { smi_msg = ipmi_alloc_smi_msg(); if (smi_msg == NULL) { ipmi_free_recv_msg(recv_msg); @@ -1161,12 +1519,21 @@ static int i_ipmi_request(ipmi_user_t user, } } + rcu_read_lock(); + handlers = intf->handlers; + if (!handlers) { + rv = -ENODEV; + goto out_err; + } + recv_msg->user = user; if (user) kref_get(&user->refcount); recv_msg->msgid = msgid; - /* Store the message to send in the receive message so timeout - responses can get the proper response data. */ + /* + * Store the message to send in the receive message so timeout + * responses can get the proper response data. + */ recv_msg->msg = *msg; if (addr->addr_type == IPMI_SYSTEM_INTERFACE_ADDR_TYPE) { @@ -1180,9 +1547,7 @@ static int i_ipmi_request(ipmi_user_t user, smi_addr = (struct ipmi_system_interface_addr *) addr; if (smi_addr->lun > 3) { - spin_lock_irqsave(&intf->counter_lock, flags); - intf->sent_invalid_commands++; - spin_unlock_irqrestore(&intf->counter_lock, flags); + ipmi_inc_stat(intf, sent_invalid_commands); rv = -EINVAL; goto out_err; } @@ -1192,21 +1557,34 @@ static int i_ipmi_request(ipmi_user_t user, if ((msg->netfn == IPMI_NETFN_APP_REQUEST) && ((msg->cmd == IPMI_SEND_MSG_CMD) || (msg->cmd == IPMI_GET_MSG_CMD) - || (msg->cmd == IPMI_READ_EVENT_MSG_BUFFER_CMD))) - { - /* We don't let the user do these, since we manage - the sequence numbers. */ - spin_lock_irqsave(&intf->counter_lock, flags); - intf->sent_invalid_commands++; - spin_unlock_irqrestore(&intf->counter_lock, flags); + || (msg->cmd == IPMI_READ_EVENT_MSG_BUFFER_CMD))) { + /* + * We don't let the user do these, since we manage + * the sequence numbers. + */ + ipmi_inc_stat(intf, sent_invalid_commands); rv = -EINVAL; goto out_err; } + if (((msg->netfn == IPMI_NETFN_APP_REQUEST) + && ((msg->cmd == IPMI_COLD_RESET_CMD) + || (msg->cmd == IPMI_WARM_RESET_CMD))) + || (msg->netfn == IPMI_NETFN_FIRMWARE_REQUEST)) { + spin_lock_irqsave(&intf->maintenance_mode_lock, flags); + intf->auto_maintenance_timeout + = IPMI_MAINTENANCE_MODE_TIMEOUT; + if (!intf->maintenance_mode + && !intf->maintenance_mode_enable) { + intf->maintenance_mode_enable = true; + maintenance_mode_update(intf); + } + spin_unlock_irqrestore(&intf->maintenance_mode_lock, + flags); + } + if ((msg->data_len + 2) > IPMI_MAX_MSG_LENGTH) { - spin_lock_irqsave(&intf->counter_lock, flags); - intf->sent_invalid_commands++; - spin_unlock_irqrestore(&intf->counter_lock, flags); + ipmi_inc_stat(intf, sent_invalid_commands); rv = -EMSGSIZE; goto out_err; } @@ -1218,31 +1596,22 @@ static int i_ipmi_request(ipmi_user_t user, if (msg->data_len > 0) memcpy(&(smi_msg->data[2]), msg->data, msg->data_len); smi_msg->data_size = msg->data_len + 2; - spin_lock_irqsave(&intf->counter_lock, flags); - intf->sent_local_commands++; - spin_unlock_irqrestore(&intf->counter_lock, flags); - } else if ((addr->addr_type == IPMI_IPMB_ADDR_TYPE) - || (addr->addr_type == IPMI_IPMB_BROADCAST_ADDR_TYPE)) - { + ipmi_inc_stat(intf, sent_local_commands); + } else if (is_ipmb_addr(addr) || is_ipmb_bcast_addr(addr)) { struct ipmi_ipmb_addr *ipmb_addr; unsigned char ipmb_seq; long seqid; int broadcast = 0; if (addr->channel >= IPMI_MAX_CHANNELS) { - spin_lock_irqsave(&intf->counter_lock, flags); - intf->sent_invalid_commands++; - spin_unlock_irqrestore(&intf->counter_lock, flags); + ipmi_inc_stat(intf, sent_invalid_commands); rv = -EINVAL; goto out_err; } if (intf->channels[addr->channel].medium - != IPMI_CHANNEL_MEDIUM_IPMB) - { - spin_lock_irqsave(&intf->counter_lock, flags); - intf->sent_invalid_commands++; - spin_unlock_irqrestore(&intf->counter_lock, flags); + != IPMI_CHANNEL_MEDIUM_IPMB) { + ipmi_inc_stat(intf, sent_invalid_commands); rv = -EINVAL; goto out_err; } @@ -1254,9 +1623,11 @@ static int i_ipmi_request(ipmi_user_t user, retries = 4; } if (addr->addr_type == IPMI_IPMB_BROADCAST_ADDR_TYPE) { - /* Broadcasts add a zero at the beginning of the - message, but otherwise is the same as an IPMB - address. */ + /* + * Broadcasts add a zero at the beginning of the + * message, but otherwise is the same as an IPMB + * address. + */ addr->addr_type = IPMI_IPMB_ADDR_TYPE; broadcast = 1; } @@ -1266,21 +1637,19 @@ static int i_ipmi_request(ipmi_user_t user, if (retry_time_ms == 0) retry_time_ms = 1000; - /* 9 for the header and 1 for the checksum, plus - possibly one for the broadcast. */ + /* + * 9 for the header and 1 for the checksum, plus + * possibly one for the broadcast. + */ if ((msg->data_len + 10 + broadcast) > IPMI_MAX_MSG_LENGTH) { - spin_lock_irqsave(&intf->counter_lock, flags); - intf->sent_invalid_commands++; - spin_unlock_irqrestore(&intf->counter_lock, flags); + ipmi_inc_stat(intf, sent_invalid_commands); rv = -EMSGSIZE; goto out_err; } ipmb_addr = (struct ipmi_ipmb_addr *) addr; if (ipmb_addr->lun > 3) { - spin_lock_irqsave(&intf->counter_lock, flags); - intf->sent_invalid_commands++; - spin_unlock_irqrestore(&intf->counter_lock, flags); + ipmi_inc_stat(intf, sent_invalid_commands); rv = -EINVAL; goto out_err; } @@ -1288,29 +1657,29 @@ static int i_ipmi_request(ipmi_user_t user, memcpy(&recv_msg->addr, ipmb_addr, sizeof(*ipmb_addr)); if (recv_msg->msg.netfn & 0x1) { - /* It's a response, so use the user's sequence - from msgid. */ - spin_lock_irqsave(&intf->counter_lock, flags); - intf->sent_ipmb_responses++; - spin_unlock_irqrestore(&intf->counter_lock, flags); + /* + * It's a response, so use the user's sequence + * from msgid. + */ + ipmi_inc_stat(intf, sent_ipmb_responses); format_ipmb_msg(smi_msg, msg, ipmb_addr, msgid, msgid, broadcast, source_address, source_lun); - /* Save the receive message so we can use it - to deliver the response. */ + /* + * Save the receive message so we can use it + * to deliver the response. + */ smi_msg->user_data = recv_msg; } else { /* It's a command, so get a sequence for it. */ spin_lock_irqsave(&(intf->seq_lock), flags); - spin_lock(&intf->counter_lock); - intf->sent_ipmb_commands++; - spin_unlock(&intf->counter_lock); - - /* Create a sequence number with a 1 second - timeout and 4 retries. */ + /* + * Create a sequence number with a 1 second + * timeout and 4 retries. + */ rv = intf_next_seq(intf, recv_msg, retry_time_ms, @@ -1319,57 +1688,62 @@ static int i_ipmi_request(ipmi_user_t user, &ipmb_seq, &seqid); if (rv) { - /* We have used up all the sequence numbers, - probably, so abort. */ + /* + * We have used up all the sequence numbers, + * probably, so abort. + */ spin_unlock_irqrestore(&(intf->seq_lock), flags); goto out_err; } - /* Store the sequence number in the message, - so that when the send message response - comes back we can start the timer. */ + ipmi_inc_stat(intf, sent_ipmb_commands); + + /* + * Store the sequence number in the message, + * so that when the send message response + * comes back we can start the timer. + */ format_ipmb_msg(smi_msg, msg, ipmb_addr, STORE_SEQ_IN_MSGID(ipmb_seq, seqid), ipmb_seq, broadcast, source_address, source_lun); - /* Copy the message into the recv message data, so we - can retransmit it later if necessary. */ + /* + * Copy the message into the recv message data, so we + * can retransmit it later if necessary. + */ memcpy(recv_msg->msg_data, smi_msg->data, smi_msg->data_size); recv_msg->msg.data = recv_msg->msg_data; recv_msg->msg.data_len = smi_msg->data_size; - /* We don't unlock until here, because we need - to copy the completed message into the - recv_msg before we release the lock. - Otherwise, race conditions may bite us. I - know that's pretty paranoid, but I prefer - to be correct. */ + /* + * We don't unlock until here, because we need + * to copy the completed message into the + * recv_msg before we release the lock. + * Otherwise, race conditions may bite us. I + * know that's pretty paranoid, but I prefer + * to be correct. + */ spin_unlock_irqrestore(&(intf->seq_lock), flags); } - } else if (addr->addr_type == IPMI_LAN_ADDR_TYPE) { + } else if (is_lan_addr(addr)) { struct ipmi_lan_addr *lan_addr; unsigned char ipmb_seq; long seqid; if (addr->channel >= IPMI_MAX_CHANNELS) { - spin_lock_irqsave(&intf->counter_lock, flags); - intf->sent_invalid_commands++; - spin_unlock_irqrestore(&intf->counter_lock, flags); + ipmi_inc_stat(intf, sent_invalid_commands); rv = -EINVAL; goto out_err; } if ((intf->channels[addr->channel].medium - != IPMI_CHANNEL_MEDIUM_8023LAN) + != IPMI_CHANNEL_MEDIUM_8023LAN) && (intf->channels[addr->channel].medium - != IPMI_CHANNEL_MEDIUM_ASYNC)) - { - spin_lock_irqsave(&intf->counter_lock, flags); - intf->sent_invalid_commands++; - spin_unlock_irqrestore(&intf->counter_lock, flags); + != IPMI_CHANNEL_MEDIUM_ASYNC)) { + ipmi_inc_stat(intf, sent_invalid_commands); rv = -EINVAL; goto out_err; } @@ -1382,18 +1756,14 @@ static int i_ipmi_request(ipmi_user_t user, /* 11 for the header and 1 for the checksum. */ if ((msg->data_len + 12) > IPMI_MAX_MSG_LENGTH) { - spin_lock_irqsave(&intf->counter_lock, flags); - intf->sent_invalid_commands++; - spin_unlock_irqrestore(&intf->counter_lock, flags); + ipmi_inc_stat(intf, sent_invalid_commands); rv = -EMSGSIZE; goto out_err; } lan_addr = (struct ipmi_lan_addr *) addr; if (lan_addr->lun > 3) { - spin_lock_irqsave(&intf->counter_lock, flags); - intf->sent_invalid_commands++; - spin_unlock_irqrestore(&intf->counter_lock, flags); + ipmi_inc_stat(intf, sent_invalid_commands); rv = -EINVAL; goto out_err; } @@ -1401,28 +1771,28 @@ static int i_ipmi_request(ipmi_user_t user, memcpy(&recv_msg->addr, lan_addr, sizeof(*lan_addr)); if (recv_msg->msg.netfn & 0x1) { - /* It's a response, so use the user's sequence - from msgid. */ - spin_lock_irqsave(&intf->counter_lock, flags); - intf->sent_lan_responses++; - spin_unlock_irqrestore(&intf->counter_lock, flags); + /* + * It's a response, so use the user's sequence + * from msgid. + */ + ipmi_inc_stat(intf, sent_lan_responses); format_lan_msg(smi_msg, msg, lan_addr, msgid, msgid, source_lun); - /* Save the receive message so we can use it - to deliver the response. */ + /* + * Save the receive message so we can use it + * to deliver the response. + */ smi_msg->user_data = recv_msg; } else { /* It's a command, so get a sequence for it. */ spin_lock_irqsave(&(intf->seq_lock), flags); - spin_lock(&intf->counter_lock); - intf->sent_lan_commands++; - spin_unlock(&intf->counter_lock); - - /* Create a sequence number with a 1 second - timeout and 4 retries. */ + /* + * Create a sequence number with a 1 second + * timeout and 4 retries. + */ rv = intf_next_seq(intf, recv_msg, retry_time_ms, @@ -1431,40 +1801,48 @@ static int i_ipmi_request(ipmi_user_t user, &ipmb_seq, &seqid); if (rv) { - /* We have used up all the sequence numbers, - probably, so abort. */ + /* + * We have used up all the sequence numbers, + * probably, so abort. + */ spin_unlock_irqrestore(&(intf->seq_lock), flags); goto out_err; } - /* Store the sequence number in the message, - so that when the send message response - comes back we can start the timer. */ + ipmi_inc_stat(intf, sent_lan_commands); + + /* + * Store the sequence number in the message, + * so that when the send message response + * comes back we can start the timer. + */ format_lan_msg(smi_msg, msg, lan_addr, STORE_SEQ_IN_MSGID(ipmb_seq, seqid), ipmb_seq, source_lun); - /* Copy the message into the recv message data, so we - can retransmit it later if necessary. */ + /* + * Copy the message into the recv message data, so we + * can retransmit it later if necessary. + */ memcpy(recv_msg->msg_data, smi_msg->data, smi_msg->data_size); recv_msg->msg.data = recv_msg->msg_data; recv_msg->msg.data_len = smi_msg->data_size; - /* We don't unlock until here, because we need - to copy the completed message into the - recv_msg before we release the lock. - Otherwise, race conditions may bite us. I - know that's pretty paranoid, but I prefer - to be correct. */ + /* + * We don't unlock until here, because we need + * to copy the completed message into the + * recv_msg before we release the lock. + * Otherwise, race conditions may bite us. I + * know that's pretty paranoid, but I prefer + * to be correct. + */ spin_unlock_irqrestore(&(intf->seq_lock), flags); } } else { /* Unknown address type. */ - spin_lock_irqsave(&intf->counter_lock, flags); - intf->sent_invalid_commands++; - spin_unlock_irqrestore(&intf->counter_lock, flags); + ipmi_inc_stat(intf, sent_invalid_commands); rv = -EINVAL; goto out_err; } @@ -1477,11 +1855,14 @@ static int i_ipmi_request(ipmi_user_t user, printk("\n"); } #endif - intf->handlers->sender(intf->send_info, smi_msg, priority); + + handlers->sender(intf->send_info, smi_msg, priority); + rcu_read_unlock(); return 0; out_err: + rcu_read_unlock(); ipmi_free_smi_msg(smi_msg); ipmi_free_recv_msg(recv_msg); return rv; @@ -1508,7 +1889,7 @@ int ipmi_request_settime(ipmi_user_t user, int retries, unsigned int retry_time_ms) { - unsigned char saddr, lun; + unsigned char saddr = 0, lun = 0; int rv; if (!user) @@ -1529,6 +1910,7 @@ int ipmi_request_settime(ipmi_user_t user, retries, retry_time_ms); } +EXPORT_SYMBOL(ipmi_request_settime); int ipmi_request_supply_msgs(ipmi_user_t user, struct ipmi_addr *addr, @@ -1539,7 +1921,7 @@ int ipmi_request_supply_msgs(ipmi_user_t user, struct ipmi_recv_msg *supplied_recv, int priority) { - unsigned char saddr, lun; + unsigned char saddr = 0, lun = 0; int rv; if (!user) @@ -1560,99 +1942,132 @@ int ipmi_request_supply_msgs(ipmi_user_t user, lun, -1, 0); } +EXPORT_SYMBOL(ipmi_request_supply_msgs); -static int ipmb_file_read_proc(char *page, char **start, off_t off, - int count, int *eof, void *data) +#ifdef CONFIG_PROC_FS +static int smi_ipmb_proc_show(struct seq_file *m, void *v) { - char *out = (char *) page; - ipmi_smi_t intf = data; + ipmi_smi_t intf = m->private; int i; - int rv = 0; - for (i = 0; i < IPMI_MAX_CHANNELS; i++) - rv += sprintf(out+rv, "%x ", intf->channels[i].address); - out[rv-1] = '\n'; /* Replace the final space with a newline */ - out[rv] = '\0'; - rv++; - return rv; + seq_printf(m, "%x", intf->channels[0].address); + for (i = 1; i < IPMI_MAX_CHANNELS; i++) + seq_printf(m, " %x", intf->channels[i].address); + return seq_putc(m, '\n'); } -static int version_file_read_proc(char *page, char **start, off_t off, - int count, int *eof, void *data) +static int smi_ipmb_proc_open(struct inode *inode, struct file *file) { - char *out = (char *) page; - ipmi_smi_t intf = data; + return single_open(file, smi_ipmb_proc_show, PDE_DATA(inode)); +} + +static const struct file_operations smi_ipmb_proc_ops = { + .open = smi_ipmb_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; - return sprintf(out, "%d.%d\n", +static int smi_version_proc_show(struct seq_file *m, void *v) +{ + ipmi_smi_t intf = m->private; + + return seq_printf(m, "%u.%u\n", ipmi_version_major(&intf->bmc->id), ipmi_version_minor(&intf->bmc->id)); } -static int stat_file_read_proc(char *page, char **start, off_t off, - int count, int *eof, void *data) -{ - char *out = (char *) page; - ipmi_smi_t intf = data; - - out += sprintf(out, "sent_invalid_commands: %d\n", - intf->sent_invalid_commands); - out += sprintf(out, "sent_local_commands: %d\n", - intf->sent_local_commands); - out += sprintf(out, "handled_local_responses: %d\n", - intf->handled_local_responses); - out += sprintf(out, "unhandled_local_responses: %d\n", - intf->unhandled_local_responses); - out += sprintf(out, "sent_ipmb_commands: %d\n", - intf->sent_ipmb_commands); - out += sprintf(out, "sent_ipmb_command_errs: %d\n", - intf->sent_ipmb_command_errs); - out += sprintf(out, "retransmitted_ipmb_commands: %d\n", - intf->retransmitted_ipmb_commands); - out += sprintf(out, "timed_out_ipmb_commands: %d\n", - intf->timed_out_ipmb_commands); - out += sprintf(out, "timed_out_ipmb_broadcasts: %d\n", - intf->timed_out_ipmb_broadcasts); - out += sprintf(out, "sent_ipmb_responses: %d\n", - intf->sent_ipmb_responses); - out += sprintf(out, "handled_ipmb_responses: %d\n", - intf->handled_ipmb_responses); - out += sprintf(out, "invalid_ipmb_responses: %d\n", - intf->invalid_ipmb_responses); - out += sprintf(out, "unhandled_ipmb_responses: %d\n", - intf->unhandled_ipmb_responses); - out += sprintf(out, "sent_lan_commands: %d\n", - intf->sent_lan_commands); - out += sprintf(out, "sent_lan_command_errs: %d\n", - intf->sent_lan_command_errs); - out += sprintf(out, "retransmitted_lan_commands: %d\n", - intf->retransmitted_lan_commands); - out += sprintf(out, "timed_out_lan_commands: %d\n", - intf->timed_out_lan_commands); - out += sprintf(out, "sent_lan_responses: %d\n", - intf->sent_lan_responses); - out += sprintf(out, "handled_lan_responses: %d\n", - intf->handled_lan_responses); - out += sprintf(out, "invalid_lan_responses: %d\n", - intf->invalid_lan_responses); - out += sprintf(out, "unhandled_lan_responses: %d\n", - intf->unhandled_lan_responses); - out += sprintf(out, "handled_commands: %d\n", - intf->handled_commands); - out += sprintf(out, "invalid_commands: %d\n", - intf->invalid_commands); - out += sprintf(out, "unhandled_commands: %d\n", - intf->unhandled_commands); - out += sprintf(out, "invalid_events: %d\n", - intf->invalid_events); - out += sprintf(out, "events: %d\n", - intf->events); - - return (out - ((char *) page)); +static int smi_version_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, smi_version_proc_show, PDE_DATA(inode)); +} + +static const struct file_operations smi_version_proc_ops = { + .open = smi_version_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int smi_stats_proc_show(struct seq_file *m, void *v) +{ + ipmi_smi_t intf = m->private; + + seq_printf(m, "sent_invalid_commands: %u\n", + ipmi_get_stat(intf, sent_invalid_commands)); + seq_printf(m, "sent_local_commands: %u\n", + ipmi_get_stat(intf, sent_local_commands)); + seq_printf(m, "handled_local_responses: %u\n", + ipmi_get_stat(intf, handled_local_responses)); + seq_printf(m, "unhandled_local_responses: %u\n", + ipmi_get_stat(intf, unhandled_local_responses)); + seq_printf(m, "sent_ipmb_commands: %u\n", + ipmi_get_stat(intf, sent_ipmb_commands)); + seq_printf(m, "sent_ipmb_command_errs: %u\n", + ipmi_get_stat(intf, sent_ipmb_command_errs)); + seq_printf(m, "retransmitted_ipmb_commands: %u\n", + ipmi_get_stat(intf, retransmitted_ipmb_commands)); + seq_printf(m, "timed_out_ipmb_commands: %u\n", + ipmi_get_stat(intf, timed_out_ipmb_commands)); + seq_printf(m, "timed_out_ipmb_broadcasts: %u\n", + ipmi_get_stat(intf, timed_out_ipmb_broadcasts)); + seq_printf(m, "sent_ipmb_responses: %u\n", + ipmi_get_stat(intf, sent_ipmb_responses)); + seq_printf(m, "handled_ipmb_responses: %u\n", + ipmi_get_stat(intf, handled_ipmb_responses)); + seq_printf(m, "invalid_ipmb_responses: %u\n", + ipmi_get_stat(intf, invalid_ipmb_responses)); + seq_printf(m, "unhandled_ipmb_responses: %u\n", + ipmi_get_stat(intf, unhandled_ipmb_responses)); + seq_printf(m, "sent_lan_commands: %u\n", + ipmi_get_stat(intf, sent_lan_commands)); + seq_printf(m, "sent_lan_command_errs: %u\n", + ipmi_get_stat(intf, sent_lan_command_errs)); + seq_printf(m, "retransmitted_lan_commands: %u\n", + ipmi_get_stat(intf, retransmitted_lan_commands)); + seq_printf(m, "timed_out_lan_commands: %u\n", + ipmi_get_stat(intf, timed_out_lan_commands)); + seq_printf(m, "sent_lan_responses: %u\n", + ipmi_get_stat(intf, sent_lan_responses)); + seq_printf(m, "handled_lan_responses: %u\n", + ipmi_get_stat(intf, handled_lan_responses)); + seq_printf(m, "invalid_lan_responses: %u\n", + ipmi_get_stat(intf, invalid_lan_responses)); + seq_printf(m, "unhandled_lan_responses: %u\n", + ipmi_get_stat(intf, unhandled_lan_responses)); + seq_printf(m, "handled_commands: %u\n", + ipmi_get_stat(intf, handled_commands)); + seq_printf(m, "invalid_commands: %u\n", + ipmi_get_stat(intf, invalid_commands)); + seq_printf(m, "unhandled_commands: %u\n", + ipmi_get_stat(intf, unhandled_commands)); + seq_printf(m, "invalid_events: %u\n", + ipmi_get_stat(intf, invalid_events)); + seq_printf(m, "events: %u\n", + ipmi_get_stat(intf, events)); + seq_printf(m, "failed rexmit LAN msgs: %u\n", + ipmi_get_stat(intf, dropped_rexmit_lan_commands)); + seq_printf(m, "failed rexmit IPMB msgs: %u\n", + ipmi_get_stat(intf, dropped_rexmit_ipmb_commands)); + return 0; +} + +static int smi_stats_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, smi_stats_proc_show, PDE_DATA(inode)); } +static const struct file_operations smi_stats_proc_ops = { + .open = smi_stats_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; +#endif /* CONFIG_PROC_FS */ + int ipmi_smi_add_proc_entry(ipmi_smi_t smi, char *name, - read_proc_t *read_proc, write_proc_t *write_proc, - void *data, struct module *owner) + const struct file_operations *proc_ops, + void *data) { int rv = 0; #ifdef CONFIG_PROC_FS @@ -1663,35 +2078,29 @@ int ipmi_smi_add_proc_entry(ipmi_smi_t smi, char *name, entry = kmalloc(sizeof(*entry), GFP_KERNEL); if (!entry) return -ENOMEM; - entry->name = kmalloc(strlen(name)+1, GFP_KERNEL); + entry->name = kstrdup(name, GFP_KERNEL); if (!entry->name) { kfree(entry); return -ENOMEM; } - strcpy(entry->name, name); - file = create_proc_entry(name, 0, smi->proc_dir); + file = proc_create_data(name, 0, smi->proc_dir, proc_ops, data); if (!file) { kfree(entry->name); kfree(entry); rv = -ENOMEM; } else { - file->nlink = 1; - file->data = data; - file->read_proc = read_proc; - file->write_proc = write_proc; - file->owner = owner; - - spin_lock(&smi->proc_entry_lock); + mutex_lock(&smi->proc_entry_lock); /* Stick it on the list. */ entry->next = smi->proc_entries; smi->proc_entries = entry; - spin_unlock(&smi->proc_entry_lock); + mutex_unlock(&smi->proc_entry_lock); } #endif /* CONFIG_PROC_FS */ return rv; } +EXPORT_SYMBOL(ipmi_smi_add_proc_entry); static int add_proc_entries(ipmi_smi_t smi, int num) { @@ -1702,24 +2111,21 @@ static int add_proc_entries(ipmi_smi_t smi, int num) smi->proc_dir = proc_mkdir(smi->proc_dir_name, proc_ipmi_root); if (!smi->proc_dir) rv = -ENOMEM; - else { - smi->proc_dir->owner = THIS_MODULE; - } if (rv == 0) rv = ipmi_smi_add_proc_entry(smi, "stats", - stat_file_read_proc, NULL, - smi, THIS_MODULE); + &smi_stats_proc_ops, + smi); if (rv == 0) rv = ipmi_smi_add_proc_entry(smi, "ipmb", - ipmb_file_read_proc, NULL, - smi, THIS_MODULE); + &smi_ipmb_proc_ops, + smi); if (rv == 0) rv = ipmi_smi_add_proc_entry(smi, "version", - version_file_read_proc, NULL, - smi, THIS_MODULE); + &smi_version_proc_ops, + smi); #endif /* CONFIG_PROC_FS */ return rv; @@ -1730,7 +2136,7 @@ static void remove_proc_entries(ipmi_smi_t smi) #ifdef CONFIG_PROC_FS struct ipmi_proc_entry *entry; - spin_lock(&smi->proc_entry_lock); + mutex_lock(&smi->proc_entry_lock); while (smi->proc_entries) { entry = smi->proc_entries; smi->proc_entries = entry->next; @@ -1739,7 +2145,7 @@ static void remove_proc_entries(ipmi_smi_t smi) kfree(entry->name); kfree(entry); } - spin_unlock(&smi->proc_entry_lock); + mutex_unlock(&smi->proc_entry_lock); remove_proc_entry(smi->proc_dir_name, proc_ipmi_root); #endif /* CONFIG_PROC_FS */ } @@ -1774,13 +2180,12 @@ static int __find_bmc_prod_dev_id(struct device *dev, void *data) struct bmc_device *bmc = dev_get_drvdata(dev); return (bmc->id.product_id == id->product_id - && bmc->id.product_id == id->product_id && bmc->id.device_id == id->device_id); } static struct bmc_device *ipmi_find_bmc_prod_dev_id( struct device_driver *drv, - unsigned char product_id, unsigned char device_id) + unsigned int product_id, unsigned char device_id) { struct prod_dev_id id = { .product_id = product_id, @@ -1811,7 +2216,7 @@ static ssize_t provides_dev_sdrs_show(struct device *dev, struct bmc_device *bmc = dev_get_drvdata(dev); return snprintf(buf, 10, "%u\n", - bmc->id.device_revision && 0x80 >> 7); + (bmc->id.device_revision & 0x80) >> 7); } static ssize_t revision_show(struct device *dev, struct device_attribute *attr, @@ -1820,7 +2225,7 @@ static ssize_t revision_show(struct device *dev, struct device_attribute *attr, struct bmc_device *bmc = dev_get_drvdata(dev); return snprintf(buf, 20, "%u\n", - bmc->id.device_revision && 0x0F); + bmc->id.device_revision & 0x0F); } static ssize_t firmware_rev_show(struct device *dev, @@ -1895,12 +2300,10 @@ static ssize_t guid_show(struct device *dev, struct device_attribute *attr, (long long) bmc->guid[8]); } -static void -cleanup_bmc_device(struct kref *ref) +static void remove_files(struct bmc_device *bmc) { - struct bmc_device *bmc; - - bmc = container_of(ref, struct bmc_device, refcount); + if (!bmc->dev) + return; device_remove_file(&bmc->dev->dev, &bmc->device_id_attr); @@ -1918,12 +2321,23 @@ cleanup_bmc_device(struct kref *ref) &bmc->manufacturer_id_attr); device_remove_file(&bmc->dev->dev, &bmc->product_id_attr); + if (bmc->id.aux_firmware_revision_set) device_remove_file(&bmc->dev->dev, &bmc->aux_firmware_rev_attr); if (bmc->guid_set) device_remove_file(&bmc->dev->dev, &bmc->guid_attr); +} + +static void +cleanup_bmc_device(struct kref *ref) +{ + struct bmc_device *bmc; + + bmc = container_of(ref, struct bmc_device, refcount); + + remove_files(bmc); platform_device_unregister(bmc->dev); kfree(bmc); } @@ -1932,7 +2346,11 @@ static void ipmi_bmc_unregister(ipmi_smi_t intf) { struct bmc_device *bmc = intf->bmc; - sysfs_remove_link(&intf->si_dev->kobj, "bmc"); + if (intf->sysfs_name) { + sysfs_remove_link(&intf->si_dev->kobj, intf->sysfs_name); + kfree(intf->sysfs_name); + intf->sysfs_name = NULL; + } if (intf->my_dev_name) { sysfs_remove_link(&bmc->dev->dev.kobj, intf->my_dev_name); kfree(intf->my_dev_name); @@ -1941,10 +2359,145 @@ static void ipmi_bmc_unregister(ipmi_smi_t intf) mutex_lock(&ipmidriver_mutex); kref_put(&bmc->refcount, cleanup_bmc_device); + intf->bmc = NULL; mutex_unlock(&ipmidriver_mutex); } -static int ipmi_bmc_register(ipmi_smi_t intf) +static int create_files(struct bmc_device *bmc) +{ + int err; + + bmc->device_id_attr.attr.name = "device_id"; + bmc->device_id_attr.attr.mode = S_IRUGO; + bmc->device_id_attr.show = device_id_show; + sysfs_attr_init(&bmc->device_id_attr.attr); + + bmc->provides_dev_sdrs_attr.attr.name = "provides_device_sdrs"; + bmc->provides_dev_sdrs_attr.attr.mode = S_IRUGO; + bmc->provides_dev_sdrs_attr.show = provides_dev_sdrs_show; + sysfs_attr_init(&bmc->provides_dev_sdrs_attr.attr); + + bmc->revision_attr.attr.name = "revision"; + bmc->revision_attr.attr.mode = S_IRUGO; + bmc->revision_attr.show = revision_show; + sysfs_attr_init(&bmc->revision_attr.attr); + + bmc->firmware_rev_attr.attr.name = "firmware_revision"; + bmc->firmware_rev_attr.attr.mode = S_IRUGO; + bmc->firmware_rev_attr.show = firmware_rev_show; + sysfs_attr_init(&bmc->firmware_rev_attr.attr); + + bmc->version_attr.attr.name = "ipmi_version"; + bmc->version_attr.attr.mode = S_IRUGO; + bmc->version_attr.show = ipmi_version_show; + sysfs_attr_init(&bmc->version_attr.attr); + + bmc->add_dev_support_attr.attr.name = "additional_device_support"; + bmc->add_dev_support_attr.attr.mode = S_IRUGO; + bmc->add_dev_support_attr.show = add_dev_support_show; + sysfs_attr_init(&bmc->add_dev_support_attr.attr); + + bmc->manufacturer_id_attr.attr.name = "manufacturer_id"; + bmc->manufacturer_id_attr.attr.mode = S_IRUGO; + bmc->manufacturer_id_attr.show = manufacturer_id_show; + sysfs_attr_init(&bmc->manufacturer_id_attr.attr); + + bmc->product_id_attr.attr.name = "product_id"; + bmc->product_id_attr.attr.mode = S_IRUGO; + bmc->product_id_attr.show = product_id_show; + sysfs_attr_init(&bmc->product_id_attr.attr); + + bmc->guid_attr.attr.name = "guid"; + bmc->guid_attr.attr.mode = S_IRUGO; + bmc->guid_attr.show = guid_show; + sysfs_attr_init(&bmc->guid_attr.attr); + + bmc->aux_firmware_rev_attr.attr.name = "aux_firmware_revision"; + bmc->aux_firmware_rev_attr.attr.mode = S_IRUGO; + bmc->aux_firmware_rev_attr.show = aux_firmware_rev_show; + sysfs_attr_init(&bmc->aux_firmware_rev_attr.attr); + + err = device_create_file(&bmc->dev->dev, + &bmc->device_id_attr); + if (err) + goto out; + err = device_create_file(&bmc->dev->dev, + &bmc->provides_dev_sdrs_attr); + if (err) + goto out_devid; + err = device_create_file(&bmc->dev->dev, + &bmc->revision_attr); + if (err) + goto out_sdrs; + err = device_create_file(&bmc->dev->dev, + &bmc->firmware_rev_attr); + if (err) + goto out_rev; + err = device_create_file(&bmc->dev->dev, + &bmc->version_attr); + if (err) + goto out_firm; + err = device_create_file(&bmc->dev->dev, + &bmc->add_dev_support_attr); + if (err) + goto out_version; + err = device_create_file(&bmc->dev->dev, + &bmc->manufacturer_id_attr); + if (err) + goto out_add_dev; + err = device_create_file(&bmc->dev->dev, + &bmc->product_id_attr); + if (err) + goto out_manu; + if (bmc->id.aux_firmware_revision_set) { + err = device_create_file(&bmc->dev->dev, + &bmc->aux_firmware_rev_attr); + if (err) + goto out_prod_id; + } + if (bmc->guid_set) { + err = device_create_file(&bmc->dev->dev, + &bmc->guid_attr); + if (err) + goto out_aux_firm; + } + + return 0; + +out_aux_firm: + if (bmc->id.aux_firmware_revision_set) + device_remove_file(&bmc->dev->dev, + &bmc->aux_firmware_rev_attr); +out_prod_id: + device_remove_file(&bmc->dev->dev, + &bmc->product_id_attr); +out_manu: + device_remove_file(&bmc->dev->dev, + &bmc->manufacturer_id_attr); +out_add_dev: + device_remove_file(&bmc->dev->dev, + &bmc->add_dev_support_attr); +out_version: + device_remove_file(&bmc->dev->dev, + &bmc->version_attr); +out_firm: + device_remove_file(&bmc->dev->dev, + &bmc->firmware_rev_attr); +out_rev: + device_remove_file(&bmc->dev->dev, + &bmc->revision_attr); +out_sdrs: + device_remove_file(&bmc->dev->dev, + &bmc->provides_dev_sdrs_attr); +out_devid: + device_remove_file(&bmc->dev->dev, + &bmc->device_id_attr); +out: + return err; +} + +static int ipmi_bmc_register(ipmi_smi_t intf, int ifnum, + const char *sysfs_name) { int rv; struct bmc_device *bmc = intf->bmc; @@ -1959,9 +2512,9 @@ static int ipmi_bmc_register(ipmi_smi_t intf) * representing the interfaced BMC already */ if (bmc->guid_set) - old_bmc = ipmi_find_bmc_guid(&ipmidriver, bmc->guid); + old_bmc = ipmi_find_bmc_guid(&ipmidriver.driver, bmc->guid); else - old_bmc = ipmi_find_bmc_prod_dev_id(&ipmidriver, + old_bmc = ipmi_find_bmc_prod_dev_id(&ipmidriver.driver, bmc->id.product_id, bmc->id.device_id); @@ -1984,140 +2537,121 @@ static int ipmi_bmc_register(ipmi_smi_t intf) bmc->id.product_id, bmc->id.device_id); } else { - bmc->dev = platform_device_alloc("ipmi_bmc", - bmc->id.device_id); + char name[14]; + unsigned char orig_dev_id = bmc->id.device_id; + int warn_printed = 0; + + snprintf(name, sizeof(name), + "ipmi_bmc.%4.4x", bmc->id.product_id); + + while (ipmi_find_bmc_prod_dev_id(&ipmidriver.driver, + bmc->id.product_id, + bmc->id.device_id)) { + if (!warn_printed) { + printk(KERN_WARNING PFX + "This machine has two different BMCs" + " with the same product id and device" + " id. This is an error in the" + " firmware, but incrementing the" + " device id to work around the problem." + " Prod ID = 0x%x, Dev ID = 0x%x\n", + bmc->id.product_id, bmc->id.device_id); + warn_printed = 1; + } + bmc->id.device_id++; /* Wraps at 255 */ + if (bmc->id.device_id == orig_dev_id) { + printk(KERN_ERR PFX + "Out of device ids!\n"); + break; + } + } + + bmc->dev = platform_device_alloc(name, bmc->id.device_id); if (!bmc->dev) { + mutex_unlock(&ipmidriver_mutex); printk(KERN_ERR "ipmi_msghandler:" " Unable to allocate platform device\n"); return -ENOMEM; } - bmc->dev->dev.driver = &ipmidriver; + bmc->dev->dev.driver = &ipmidriver.driver; dev_set_drvdata(&bmc->dev->dev, bmc); kref_init(&bmc->refcount); - rv = platform_device_register(bmc->dev); + rv = platform_device_add(bmc->dev); mutex_unlock(&ipmidriver_mutex); if (rv) { + platform_device_put(bmc->dev); + bmc->dev = NULL; printk(KERN_ERR "ipmi_msghandler:" " Unable to register bmc device: %d\n", rv); - /* Don't go to out_err, you can only do that if - the device is registered already. */ + /* + * Don't go to out_err, you can only do that if + * the device is registered already. + */ return rv; } - bmc->device_id_attr.attr.name = "device_id"; - bmc->device_id_attr.attr.owner = THIS_MODULE; - bmc->device_id_attr.attr.mode = S_IRUGO; - bmc->device_id_attr.show = device_id_show; - - bmc->provides_dev_sdrs_attr.attr.name = "provides_device_sdrs"; - bmc->provides_dev_sdrs_attr.attr.owner = THIS_MODULE; - bmc->provides_dev_sdrs_attr.attr.mode = S_IRUGO; - bmc->provides_dev_sdrs_attr.show = provides_dev_sdrs_show; - - - bmc->revision_attr.attr.name = "revision"; - bmc->revision_attr.attr.owner = THIS_MODULE; - bmc->revision_attr.attr.mode = S_IRUGO; - bmc->revision_attr.show = revision_show; - - bmc->firmware_rev_attr.attr.name = "firmware_revision"; - bmc->firmware_rev_attr.attr.owner = THIS_MODULE; - bmc->firmware_rev_attr.attr.mode = S_IRUGO; - bmc->firmware_rev_attr.show = firmware_rev_show; - - bmc->version_attr.attr.name = "ipmi_version"; - bmc->version_attr.attr.owner = THIS_MODULE; - bmc->version_attr.attr.mode = S_IRUGO; - bmc->version_attr.show = ipmi_version_show; - - bmc->add_dev_support_attr.attr.name - = "additional_device_support"; - bmc->add_dev_support_attr.attr.owner = THIS_MODULE; - bmc->add_dev_support_attr.attr.mode = S_IRUGO; - bmc->add_dev_support_attr.show = add_dev_support_show; - - bmc->manufacturer_id_attr.attr.name = "manufacturer_id"; - bmc->manufacturer_id_attr.attr.owner = THIS_MODULE; - bmc->manufacturer_id_attr.attr.mode = S_IRUGO; - bmc->manufacturer_id_attr.show = manufacturer_id_show; - - bmc->product_id_attr.attr.name = "product_id"; - bmc->product_id_attr.attr.owner = THIS_MODULE; - bmc->product_id_attr.attr.mode = S_IRUGO; - bmc->product_id_attr.show = product_id_show; - - bmc->guid_attr.attr.name = "guid"; - bmc->guid_attr.attr.owner = THIS_MODULE; - bmc->guid_attr.attr.mode = S_IRUGO; - bmc->guid_attr.show = guid_show; - - bmc->aux_firmware_rev_attr.attr.name = "aux_firmware_revision"; - bmc->aux_firmware_rev_attr.attr.owner = THIS_MODULE; - bmc->aux_firmware_rev_attr.attr.mode = S_IRUGO; - bmc->aux_firmware_rev_attr.show = aux_firmware_rev_show; - - device_create_file(&bmc->dev->dev, - &bmc->device_id_attr); - device_create_file(&bmc->dev->dev, - &bmc->provides_dev_sdrs_attr); - device_create_file(&bmc->dev->dev, - &bmc->revision_attr); - device_create_file(&bmc->dev->dev, - &bmc->firmware_rev_attr); - device_create_file(&bmc->dev->dev, - &bmc->version_attr); - device_create_file(&bmc->dev->dev, - &bmc->add_dev_support_attr); - device_create_file(&bmc->dev->dev, - &bmc->manufacturer_id_attr); - device_create_file(&bmc->dev->dev, - &bmc->product_id_attr); - if (bmc->id.aux_firmware_revision_set) - device_create_file(&bmc->dev->dev, - &bmc->aux_firmware_rev_attr); - if (bmc->guid_set) - device_create_file(&bmc->dev->dev, - &bmc->guid_attr); + rv = create_files(bmc); + if (rv) { + mutex_lock(&ipmidriver_mutex); + platform_device_unregister(bmc->dev); + mutex_unlock(&ipmidriver_mutex); - printk(KERN_INFO - "ipmi: Found new BMC (man_id: 0x%6.6x, " - " prod_id: 0x%4.4x, dev_id: 0x%2.2x)\n", - bmc->id.manufacturer_id, - bmc->id.product_id, - bmc->id.device_id); + return rv; + } + + dev_info(intf->si_dev, "Found new BMC (man_id: 0x%6.6x, " + "prod_id: 0x%4.4x, dev_id: 0x%2.2x)\n", + bmc->id.manufacturer_id, + bmc->id.product_id, + bmc->id.device_id); } /* * create symlink from system interface device to bmc device * and back. */ + intf->sysfs_name = kstrdup(sysfs_name, GFP_KERNEL); + if (!intf->sysfs_name) { + rv = -ENOMEM; + printk(KERN_ERR + "ipmi_msghandler: allocate link to BMC: %d\n", + rv); + goto out_err; + } + rv = sysfs_create_link(&intf->si_dev->kobj, - &bmc->dev->dev.kobj, "bmc"); + &bmc->dev->dev.kobj, intf->sysfs_name); if (rv) { + kfree(intf->sysfs_name); + intf->sysfs_name = NULL; printk(KERN_ERR "ipmi_msghandler: Unable to create bmc symlink: %d\n", rv); goto out_err; } - size = snprintf(dummy, 0, "ipmi%d", intf->intf_num); + size = snprintf(dummy, 0, "ipmi%d", ifnum); intf->my_dev_name = kmalloc(size+1, GFP_KERNEL); if (!intf->my_dev_name) { + kfree(intf->sysfs_name); + intf->sysfs_name = NULL; rv = -ENOMEM; printk(KERN_ERR "ipmi_msghandler: allocate link from BMC: %d\n", rv); goto out_err; } - snprintf(intf->my_dev_name, size+1, "ipmi%d", intf->intf_num); + snprintf(intf->my_dev_name, size+1, "ipmi%d", ifnum); rv = sysfs_create_link(&bmc->dev->dev.kobj, &intf->si_dev->kobj, intf->my_dev_name); if (rv) { + kfree(intf->sysfs_name); + intf->sysfs_name = NULL; kfree(intf->my_dev_name); intf->my_dev_name = NULL; printk(KERN_ERR @@ -2246,17 +2780,18 @@ channel_handler(ipmi_smi_t intf, struct ipmi_recv_msg *msg) if ((msg->addr.addr_type == IPMI_SYSTEM_INTERFACE_ADDR_TYPE) && (msg->msg.netfn == IPMI_NETFN_APP_RESPONSE) - && (msg->msg.cmd == IPMI_GET_CHANNEL_INFO_CMD)) - { + && (msg->msg.cmd == IPMI_GET_CHANNEL_INFO_CMD)) { /* It's the one we want */ if (msg->msg.data[0] != 0) { /* Got an error from the channel, just go on. */ if (msg->msg.data[0] == IPMI_INVALID_COMMAND_ERR) { - /* If the MC does not support this - command, that is legal. We just - assume it has one IPMB at channel - zero. */ + /* + * If the MC does not support this + * command, that is legal. We just + * assume it has one IPMB at channel + * zero. + */ intf->channels[0].medium = IPMI_CHANNEL_MEDIUM_IPMB; intf->channels[0].protocol @@ -2277,7 +2812,7 @@ channel_handler(ipmi_smi_t intf, struct ipmi_recv_msg *msg) intf->channels[chan].medium = msg->msg.data[2] & 0x7f; intf->channels[chan].protocol = msg->msg.data[3] & 0x1f; - next_channel: + next_channel: intf->curr_channel++; if (intf->curr_channel >= IPMI_MAX_CHANNELS) wake_up(&intf->waitq); @@ -2298,44 +2833,62 @@ channel_handler(ipmi_smi_t intf, struct ipmi_recv_msg *msg) return; } +static void ipmi_poll(ipmi_smi_t intf) +{ + if (intf->handlers->poll) + intf->handlers->poll(intf->send_info); + /* In case something came in */ + handle_new_recv_msgs(intf); +} + +void ipmi_poll_interface(ipmi_user_t user) +{ + ipmi_poll(user->intf); +} +EXPORT_SYMBOL(ipmi_poll_interface); + int ipmi_register_smi(struct ipmi_smi_handlers *handlers, void *send_info, struct ipmi_device_id *device_id, struct device *si_dev, + const char *sysfs_name, unsigned char slave_addr) { int i, j; int rv; ipmi_smi_t intf; - unsigned long flags; - int version_major; - int version_minor; - - version_major = ipmi_version_major(device_id); - version_minor = ipmi_version_minor(device_id); + ipmi_smi_t tintf; + struct list_head *link; - /* Make sure the driver is actually initialized, this handles - problems with initialization order. */ + /* + * Make sure the driver is actually initialized, this handles + * problems with initialization order. + */ if (!initialized) { rv = ipmi_init_msghandler(); if (rv) return rv; - /* The init code doesn't return an error if it was turned - off, but it won't initialize. Check that. */ + /* + * The init code doesn't return an error if it was turned + * off, but it won't initialize. Check that. + */ if (!initialized) return -ENODEV; } - intf = kmalloc(sizeof(*intf), GFP_KERNEL); + intf = kzalloc(sizeof(*intf), GFP_KERNEL); if (!intf) return -ENOMEM; - memset(intf, 0, sizeof(*intf)); + + intf->ipmi_version_major = ipmi_version_major(device_id); + intf->ipmi_version_minor = ipmi_version_minor(device_id); + intf->bmc = kzalloc(sizeof(*intf->bmc), GFP_KERNEL); if (!intf->bmc) { kfree(intf); return -ENOMEM; } - intf->intf_num = -1; + intf->intf_num = -1; /* Mark it invalid for now. */ kref_init(&intf->refcount); intf->bmc->id = *device_id; intf->si_dev = si_dev; @@ -2355,34 +2908,45 @@ int ipmi_register_smi(struct ipmi_smi_handlers *handlers, } intf->curr_seq = 0; #ifdef CONFIG_PROC_FS - spin_lock_init(&intf->proc_entry_lock); + mutex_init(&intf->proc_entry_lock); #endif spin_lock_init(&intf->waiting_msgs_lock); INIT_LIST_HEAD(&intf->waiting_msgs); + tasklet_init(&intf->recv_tasklet, + smi_recv_tasklet, + (unsigned long) intf); + atomic_set(&intf->watchdog_pretimeouts_to_deliver, 0); spin_lock_init(&intf->events_lock); + atomic_set(&intf->event_waiters, 0); + intf->ticks_to_req_ev = IPMI_REQUEST_EV_TIME; INIT_LIST_HEAD(&intf->waiting_events); intf->waiting_events_count = 0; mutex_init(&intf->cmd_rcvrs_mutex); + spin_lock_init(&intf->maintenance_mode_lock); INIT_LIST_HEAD(&intf->cmd_rcvrs); init_waitqueue_head(&intf->waitq); + for (i = 0; i < IPMI_NUM_STATS; i++) + atomic_set(&intf->stats[i], 0); - spin_lock_init(&intf->counter_lock); intf->proc_dir = NULL; - rv = -ENOMEM; - spin_lock_irqsave(&interfaces_lock, flags); - for (i = 0; i < MAX_IPMI_INTERFACES; i++) { - if (ipmi_interfaces[i] == NULL) { - intf->intf_num = i; - /* Reserve the entry till we are done. */ - ipmi_interfaces[i] = IPMI_INVALID_INTERFACE_ENTRY; - rv = 0; + mutex_lock(&smi_watchers_mutex); + mutex_lock(&ipmi_interfaces_mutex); + /* Look for a hole in the numbers. */ + i = 0; + link = &ipmi_interfaces; + list_for_each_entry_rcu(tintf, &ipmi_interfaces, link) { + if (tintf->intf_num != i) { + link = &tintf->link; break; } + i++; } - spin_unlock_irqrestore(&interfaces_lock, flags); - if (rv) - goto out; + /* Add the new interface in numeric order. */ + if (i == 0) + list_add_rcu(&intf->link, &ipmi_interfaces); + else + list_add_tail_rcu(&intf->link, link); rv = handlers->start_processing(send_info, intf); if (rv) @@ -2390,11 +2954,13 @@ int ipmi_register_smi(struct ipmi_smi_handlers *handlers, get_guid(intf); - if ((version_major > 1) - || ((version_major == 1) && (version_minor >= 5))) - { - /* Start scanning the channels to see what is - available. */ + if ((intf->ipmi_version_major > 1) + || ((intf->ipmi_version_major == 1) + && (intf->ipmi_version_minor >= 5))) { + /* + * Start scanning the channels to see what is + * available. + */ intf->null_user_handler = channel_handler; intf->curr_channel = 0; rv = send_channel_info_cmd(intf, 0); @@ -2409,89 +2975,101 @@ int ipmi_register_smi(struct ipmi_smi_handlers *handlers, /* Assume a single IPMB channel at zero. */ intf->channels[0].medium = IPMI_CHANNEL_MEDIUM_IPMB; intf->channels[0].protocol = IPMI_CHANNEL_PROTOCOL_IPMB; + intf->curr_channel = IPMI_MAX_CHANNELS; } if (rv == 0) rv = add_proc_entries(intf, i); - rv = ipmi_bmc_register(intf); + rv = ipmi_bmc_register(intf, i, sysfs_name); out: if (rv) { if (intf->proc_dir) remove_proc_entries(intf); + intf->handlers = NULL; + list_del_rcu(&intf->link); + mutex_unlock(&ipmi_interfaces_mutex); + mutex_unlock(&smi_watchers_mutex); + synchronize_rcu(); kref_put(&intf->refcount, intf_free); - if (i < MAX_IPMI_INTERFACES) { - spin_lock_irqsave(&interfaces_lock, flags); - ipmi_interfaces[i] = NULL; - spin_unlock_irqrestore(&interfaces_lock, flags); - } } else { - spin_lock_irqsave(&interfaces_lock, flags); - ipmi_interfaces[i] = intf; - spin_unlock_irqrestore(&interfaces_lock, flags); + /* + * Keep memory order straight for RCU readers. Make + * sure everything else is committed to memory before + * setting intf_num to mark the interface valid. + */ + smp_wmb(); + intf->intf_num = i; + mutex_unlock(&ipmi_interfaces_mutex); + /* After this point the interface is legal to use. */ call_smi_watchers(i, intf->si_dev); + mutex_unlock(&smi_watchers_mutex); } return rv; } +EXPORT_SYMBOL(ipmi_register_smi); + +static void cleanup_smi_msgs(ipmi_smi_t intf) +{ + int i; + struct seq_table *ent; + + /* No need for locks, the interface is down. */ + for (i = 0; i < IPMI_IPMB_NUM_SEQ; i++) { + ent = &(intf->seq_table[i]); + if (!ent->inuse) + continue; + deliver_err_response(ent->recv_msg, IPMI_ERR_UNSPECIFIED); + } +} int ipmi_unregister_smi(ipmi_smi_t intf) { - int i; struct ipmi_smi_watcher *w; - unsigned long flags; + int intf_num = intf->intf_num; ipmi_bmc_unregister(intf); - spin_lock_irqsave(&interfaces_lock, flags); - for (i = 0; i < MAX_IPMI_INTERFACES; i++) { - if (ipmi_interfaces[i] == intf) { - /* Set the interface number reserved until we - * are done. */ - ipmi_interfaces[i] = IPMI_INVALID_INTERFACE_ENTRY; - intf->intf_num = -1; - break; - } - } - spin_unlock_irqrestore(&interfaces_lock,flags); + mutex_lock(&smi_watchers_mutex); + mutex_lock(&ipmi_interfaces_mutex); + intf->intf_num = -1; + intf->handlers = NULL; + list_del_rcu(&intf->link); + mutex_unlock(&ipmi_interfaces_mutex); + synchronize_rcu(); - if (i == MAX_IPMI_INTERFACES) - return -ENODEV; + cleanup_smi_msgs(intf); remove_proc_entries(intf); - /* Call all the watcher interfaces to tell them that - an interface is gone. */ - down_read(&smi_watchers_sem); + /* + * Call all the watcher interfaces to tell them that + * an interface is gone. + */ list_for_each_entry(w, &smi_watchers, link) - w->smi_gone(i); - up_read(&smi_watchers_sem); - - /* Allow the entry to be reused now. */ - spin_lock_irqsave(&interfaces_lock, flags); - ipmi_interfaces[i] = NULL; - spin_unlock_irqrestore(&interfaces_lock,flags); + w->smi_gone(intf_num); + mutex_unlock(&smi_watchers_mutex); kref_put(&intf->refcount, intf_free); return 0; } +EXPORT_SYMBOL(ipmi_unregister_smi); static int handle_ipmb_get_msg_rsp(ipmi_smi_t intf, struct ipmi_smi_msg *msg) { struct ipmi_ipmb_addr ipmb_addr; struct ipmi_recv_msg *recv_msg; - unsigned long flags; - - /* This is 11, not 10, because the response must contain a - * completion code. */ + /* + * This is 11, not 10, because the response must contain a + * completion code. + */ if (msg->rsp_size < 11) { /* Message not big enough, just ignore it. */ - spin_lock_irqsave(&intf->counter_lock, flags); - intf->invalid_ipmb_responses++; - spin_unlock_irqrestore(&intf->counter_lock, flags); + ipmi_inc_stat(intf, invalid_ipmb_responses); return 0; } @@ -2505,37 +3083,38 @@ static int handle_ipmb_get_msg_rsp(ipmi_smi_t intf, ipmb_addr.channel = msg->rsp[3] & 0x0f; ipmb_addr.lun = msg->rsp[7] & 3; - /* It's a response from a remote entity. Look up the sequence - number and handle the response. */ + /* + * It's a response from a remote entity. Look up the sequence + * number and handle the response. + */ if (intf_find_seq(intf, msg->rsp[7] >> 2, msg->rsp[3] & 0x0f, msg->rsp[8], (msg->rsp[4] >> 2) & (~1), (struct ipmi_addr *) &(ipmb_addr), - &recv_msg)) - { - /* We were unable to find the sequence number, - so just nuke the message. */ - spin_lock_irqsave(&intf->counter_lock, flags); - intf->unhandled_ipmb_responses++; - spin_unlock_irqrestore(&intf->counter_lock, flags); + &recv_msg)) { + /* + * We were unable to find the sequence number, + * so just nuke the message. + */ + ipmi_inc_stat(intf, unhandled_ipmb_responses); return 0; } memcpy(recv_msg->msg_data, &(msg->rsp[9]), msg->rsp_size - 9); - /* THe other fields matched, so no need to set them, except - for netfn, which needs to be the response that was - returned, not the request value. */ + /* + * The other fields matched, so no need to set them, except + * for netfn, which needs to be the response that was + * returned, not the request value. + */ recv_msg->msg.netfn = msg->rsp[4] >> 2; recv_msg->msg.data = recv_msg->msg_data; recv_msg->msg.data_len = msg->rsp_size - 10; recv_msg->recv_type = IPMI_RESPONSE_RECV_TYPE; - spin_lock_irqsave(&intf->counter_lock, flags); - intf->handled_ipmb_responses++; - spin_unlock_irqrestore(&intf->counter_lock, flags); + ipmi_inc_stat(intf, handled_ipmb_responses); deliver_response(recv_msg); return 0; @@ -2548,16 +3127,15 @@ static int handle_ipmb_get_msg_cmd(ipmi_smi_t intf, int rv = 0; unsigned char netfn; unsigned char cmd; + unsigned char chan; ipmi_user_t user = NULL; struct ipmi_ipmb_addr *ipmb_addr; struct ipmi_recv_msg *recv_msg; - unsigned long flags; + struct ipmi_smi_handlers *handlers; if (msg->rsp_size < 10) { /* Message not big enough, just ignore it. */ - spin_lock_irqsave(&intf->counter_lock, flags); - intf->invalid_commands++; - spin_unlock_irqrestore(&intf->counter_lock, flags); + ipmi_inc_stat(intf, invalid_commands); return 0; } @@ -2568,9 +3146,10 @@ static int handle_ipmb_get_msg_cmd(ipmi_smi_t intf, netfn = msg->rsp[4] >> 2; cmd = msg->rsp[8]; + chan = msg->rsp[3] & 0xf; rcu_read_lock(); - rcvr = find_cmd_rcvr(intf, netfn, cmd); + rcvr = find_cmd_rcvr(intf, netfn, cmd, chan); if (rcvr) { user = rcvr->user; kref_get(&user->refcount); @@ -2580,19 +3159,17 @@ static int handle_ipmb_get_msg_cmd(ipmi_smi_t intf, if (user == NULL) { /* We didn't find a user, deliver an error response. */ - spin_lock_irqsave(&intf->counter_lock, flags); - intf->unhandled_commands++; - spin_unlock_irqrestore(&intf->counter_lock, flags); + ipmi_inc_stat(intf, unhandled_commands); msg->data[0] = (IPMI_NETFN_APP_REQUEST << 2); msg->data[1] = IPMI_SEND_MSG_CMD; msg->data[2] = msg->rsp[3]; msg->data[3] = msg->rsp[6]; - msg->data[4] = ((netfn + 1) << 2) | (msg->rsp[7] & 0x3); + msg->data[4] = ((netfn + 1) << 2) | (msg->rsp[7] & 0x3); msg->data[5] = ipmb_checksum(&(msg->data[3]), 2); msg->data[6] = intf->channels[msg->rsp[3] & 0xf].address; - /* rqseq/lun */ - msg->data[7] = (msg->rsp[7] & 0xfc) | (msg->rsp[4] & 0x3); + /* rqseq/lun */ + msg->data[7] = (msg->rsp[7] & 0xfc) | (msg->rsp[4] & 0x3); msg->data[8] = msg->rsp[8]; /* cmd */ msg->data[9] = IPMI_INVALID_CMD_COMPLETION_CODE; msg->data[10] = ipmb_checksum(&(msg->data[6]), 4); @@ -2607,21 +3184,29 @@ static int handle_ipmb_get_msg_cmd(ipmi_smi_t intf, printk("\n"); } #endif - intf->handlers->sender(intf->send_info, msg, 0); - - rv = -1; /* We used the message, so return the value that - causes it to not be freed or queued. */ + rcu_read_lock(); + handlers = intf->handlers; + if (handlers) { + handlers->sender(intf->send_info, msg, 0); + /* + * We used the message, so return the value + * that causes it to not be freed or + * queued. + */ + rv = -1; + } + rcu_read_unlock(); } else { /* Deliver the message to the user. */ - spin_lock_irqsave(&intf->counter_lock, flags); - intf->handled_commands++; - spin_unlock_irqrestore(&intf->counter_lock, flags); + ipmi_inc_stat(intf, handled_commands); recv_msg = ipmi_alloc_recv_msg(); if (!recv_msg) { - /* We couldn't allocate memory for the - message, so requeue it for handling - later. */ + /* + * We couldn't allocate memory for the + * message, so requeue it for handling + * later. + */ rv = 1; kref_put(&user->refcount, free_user); } else { @@ -2632,8 +3217,10 @@ static int handle_ipmb_get_msg_cmd(ipmi_smi_t intf, ipmb_addr->lun = msg->rsp[7] & 3; ipmb_addr->channel = msg->rsp[3] & 0xf; - /* Extract the rest of the message information - from the IPMB header.*/ + /* + * Extract the rest of the message information + * from the IPMB header. + */ recv_msg->user = user; recv_msg->recv_type = IPMI_CMD_RECV_TYPE; recv_msg->msgid = msg->rsp[7] >> 2; @@ -2641,8 +3228,10 @@ static int handle_ipmb_get_msg_cmd(ipmi_smi_t intf, recv_msg->msg.cmd = msg->rsp[8]; recv_msg->msg.data = recv_msg->msg_data; - /* We chop off 10, not 9 bytes because the checksum - at the end also needs to be removed. */ + /* + * We chop off 10, not 9 bytes because the checksum + * at the end also needs to be removed. + */ recv_msg->msg.data_len = msg->rsp_size - 10; memcpy(recv_msg->msg_data, &(msg->rsp[9]), @@ -2659,16 +3248,15 @@ static int handle_lan_get_msg_rsp(ipmi_smi_t intf, { struct ipmi_lan_addr lan_addr; struct ipmi_recv_msg *recv_msg; - unsigned long flags; - /* This is 13, not 12, because the response must contain a - * completion code. */ + /* + * This is 13, not 12, because the response must contain a + * completion code. + */ if (msg->rsp_size < 13) { /* Message not big enough, just ignore it. */ - spin_lock_irqsave(&intf->counter_lock, flags); - intf->invalid_lan_responses++; - spin_unlock_irqrestore(&intf->counter_lock, flags); + ipmi_inc_stat(intf, invalid_lan_responses); return 0; } @@ -2685,37 +3273,38 @@ static int handle_lan_get_msg_rsp(ipmi_smi_t intf, lan_addr.privilege = msg->rsp[3] >> 4; lan_addr.lun = msg->rsp[9] & 3; - /* It's a response from a remote entity. Look up the sequence - number and handle the response. */ + /* + * It's a response from a remote entity. Look up the sequence + * number and handle the response. + */ if (intf_find_seq(intf, msg->rsp[9] >> 2, msg->rsp[3] & 0x0f, msg->rsp[10], (msg->rsp[6] >> 2) & (~1), (struct ipmi_addr *) &(lan_addr), - &recv_msg)) - { - /* We were unable to find the sequence number, - so just nuke the message. */ - spin_lock_irqsave(&intf->counter_lock, flags); - intf->unhandled_lan_responses++; - spin_unlock_irqrestore(&intf->counter_lock, flags); + &recv_msg)) { + /* + * We were unable to find the sequence number, + * so just nuke the message. + */ + ipmi_inc_stat(intf, unhandled_lan_responses); return 0; } memcpy(recv_msg->msg_data, &(msg->rsp[11]), msg->rsp_size - 11); - /* The other fields matched, so no need to set them, except - for netfn, which needs to be the response that was - returned, not the request value. */ + /* + * The other fields matched, so no need to set them, except + * for netfn, which needs to be the response that was + * returned, not the request value. + */ recv_msg->msg.netfn = msg->rsp[6] >> 2; recv_msg->msg.data = recv_msg->msg_data; recv_msg->msg.data_len = msg->rsp_size - 12; recv_msg->recv_type = IPMI_RESPONSE_RECV_TYPE; - spin_lock_irqsave(&intf->counter_lock, flags); - intf->handled_lan_responses++; - spin_unlock_irqrestore(&intf->counter_lock, flags); + ipmi_inc_stat(intf, handled_lan_responses); deliver_response(recv_msg); return 0; @@ -2728,16 +3317,14 @@ static int handle_lan_get_msg_cmd(ipmi_smi_t intf, int rv = 0; unsigned char netfn; unsigned char cmd; + unsigned char chan; ipmi_user_t user = NULL; struct ipmi_lan_addr *lan_addr; struct ipmi_recv_msg *recv_msg; - unsigned long flags; if (msg->rsp_size < 12) { /* Message not big enough, just ignore it. */ - spin_lock_irqsave(&intf->counter_lock, flags); - intf->invalid_commands++; - spin_unlock_irqrestore(&intf->counter_lock, flags); + ipmi_inc_stat(intf, invalid_commands); return 0; } @@ -2748,9 +3335,10 @@ static int handle_lan_get_msg_cmd(ipmi_smi_t intf, netfn = msg->rsp[6] >> 2; cmd = msg->rsp[10]; + chan = msg->rsp[3] & 0xf; rcu_read_lock(); - rcvr = find_cmd_rcvr(intf, netfn, cmd); + rcvr = find_cmd_rcvr(intf, netfn, cmd, chan); if (rcvr) { user = rcvr->user; kref_get(&user->refcount); @@ -2760,23 +3348,23 @@ static int handle_lan_get_msg_cmd(ipmi_smi_t intf, if (user == NULL) { /* We didn't find a user, just give up. */ - spin_lock_irqsave(&intf->counter_lock, flags); - intf->unhandled_commands++; - spin_unlock_irqrestore(&intf->counter_lock, flags); + ipmi_inc_stat(intf, unhandled_commands); - rv = 0; /* Don't do anything with these messages, just - allow them to be freed. */ + /* + * Don't do anything with these messages, just allow + * them to be freed. + */ + rv = 0; } else { /* Deliver the message to the user. */ - spin_lock_irqsave(&intf->counter_lock, flags); - intf->handled_commands++; - spin_unlock_irqrestore(&intf->counter_lock, flags); + ipmi_inc_stat(intf, handled_commands); recv_msg = ipmi_alloc_recv_msg(); if (!recv_msg) { - /* We couldn't allocate memory for the - message, so requeue it for handling - later. */ + /* + * We couldn't allocate memory for the + * message, so requeue it for handling later. + */ rv = 1; kref_put(&user->refcount, free_user); } else { @@ -2790,8 +3378,10 @@ static int handle_lan_get_msg_cmd(ipmi_smi_t intf, lan_addr->channel = msg->rsp[3] & 0xf; lan_addr->privilege = msg->rsp[3] >> 4; - /* Extract the rest of the message information - from the IPMB header.*/ + /* + * Extract the rest of the message information + * from the IPMB header. + */ recv_msg->user = user; recv_msg->recv_type = IPMI_CMD_RECV_TYPE; recv_msg->msgid = msg->rsp[9] >> 2; @@ -2799,8 +3389,10 @@ static int handle_lan_get_msg_cmd(ipmi_smi_t intf, recv_msg->msg.cmd = msg->rsp[10]; recv_msg->msg.data = recv_msg->msg_data; - /* We chop off 12, not 11 bytes because the checksum - at the end also needs to be removed. */ + /* + * We chop off 12, not 11 bytes because the checksum + * at the end also needs to be removed. + */ recv_msg->msg.data_len = msg->rsp_size - 12; memcpy(recv_msg->msg_data, &(msg->rsp[11]), @@ -2812,11 +3404,119 @@ static int handle_lan_get_msg_cmd(ipmi_smi_t intf, return rv; } +/* + * This routine will handle "Get Message" command responses with + * channels that use an OEM Medium. The message format belongs to + * the OEM. See IPMI 2.0 specification, Chapter 6 and + * Chapter 22, sections 22.6 and 22.24 for more details. + */ +static int handle_oem_get_msg_cmd(ipmi_smi_t intf, + struct ipmi_smi_msg *msg) +{ + struct cmd_rcvr *rcvr; + int rv = 0; + unsigned char netfn; + unsigned char cmd; + unsigned char chan; + ipmi_user_t user = NULL; + struct ipmi_system_interface_addr *smi_addr; + struct ipmi_recv_msg *recv_msg; + + /* + * We expect the OEM SW to perform error checking + * so we just do some basic sanity checks + */ + if (msg->rsp_size < 4) { + /* Message not big enough, just ignore it. */ + ipmi_inc_stat(intf, invalid_commands); + return 0; + } + + if (msg->rsp[2] != 0) { + /* An error getting the response, just ignore it. */ + return 0; + } + + /* + * This is an OEM Message so the OEM needs to know how + * handle the message. We do no interpretation. + */ + netfn = msg->rsp[0] >> 2; + cmd = msg->rsp[1]; + chan = msg->rsp[3] & 0xf; + + rcu_read_lock(); + rcvr = find_cmd_rcvr(intf, netfn, cmd, chan); + if (rcvr) { + user = rcvr->user; + kref_get(&user->refcount); + } else + user = NULL; + rcu_read_unlock(); + + if (user == NULL) { + /* We didn't find a user, just give up. */ + ipmi_inc_stat(intf, unhandled_commands); + + /* + * Don't do anything with these messages, just allow + * them to be freed. + */ + + rv = 0; + } else { + /* Deliver the message to the user. */ + ipmi_inc_stat(intf, handled_commands); + + recv_msg = ipmi_alloc_recv_msg(); + if (!recv_msg) { + /* + * We couldn't allocate memory for the + * message, so requeue it for handling + * later. + */ + rv = 1; + kref_put(&user->refcount, free_user); + } else { + /* + * OEM Messages are expected to be delivered via + * the system interface to SMS software. We might + * need to visit this again depending on OEM + * requirements + */ + smi_addr = ((struct ipmi_system_interface_addr *) + &(recv_msg->addr)); + smi_addr->addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; + smi_addr->channel = IPMI_BMC_CHANNEL; + smi_addr->lun = msg->rsp[0] & 3; + + recv_msg->user = user; + recv_msg->user_msg_data = NULL; + recv_msg->recv_type = IPMI_OEM_RECV_TYPE; + recv_msg->msg.netfn = msg->rsp[0] >> 2; + recv_msg->msg.cmd = msg->rsp[1]; + recv_msg->msg.data = recv_msg->msg_data; + + /* + * The message starts at byte 4 which follows the + * the Channel Byte in the "GET MESSAGE" command + */ + recv_msg->msg.data_len = msg->rsp_size - 4; + memcpy(recv_msg->msg_data, + &(msg->rsp[4]), + msg->rsp_size - 4); + deliver_response(recv_msg); + } + } + + return rv; +} + static void copy_event_into_recv_msg(struct ipmi_recv_msg *recv_msg, struct ipmi_smi_msg *msg) { struct ipmi_system_interface_addr *smi_addr; - + recv_msg->msgid = 0; smi_addr = (struct ipmi_system_interface_addr *) &(recv_msg->addr); smi_addr->addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; @@ -2842,9 +3542,7 @@ static int handle_read_event_rsp(ipmi_smi_t intf, if (msg->rsp_size < 19) { /* Message is too small to be an IPMB event. */ - spin_lock_irqsave(&intf->counter_lock, flags); - intf->invalid_events++; - spin_unlock_irqrestore(&intf->counter_lock, flags); + ipmi_inc_stat(intf, invalid_events); return 0; } @@ -2857,12 +3555,12 @@ static int handle_read_event_rsp(ipmi_smi_t intf, spin_lock_irqsave(&intf->events_lock, flags); - spin_lock(&intf->counter_lock); - intf->events++; - spin_unlock(&intf->counter_lock); + ipmi_inc_stat(intf, events); - /* Allocate and fill in one message for every user that is getting - events. */ + /* + * Allocate and fill in one message for every user that is + * getting events. + */ rcu_read_lock(); list_for_each_entry_rcu(user, &intf->users, link) { if (!user->gets_events) @@ -2876,9 +3574,11 @@ static int handle_read_event_rsp(ipmi_smi_t intf, list_del(&recv_msg->link); ipmi_free_recv_msg(recv_msg); } - /* We couldn't allocate memory for the - message, so requeue it for handling - later. */ + /* + * We couldn't allocate memory for the + * message, so requeue it for handling + * later. + */ rv = 1; goto out; } @@ -2899,13 +3599,17 @@ static int handle_read_event_rsp(ipmi_smi_t intf, deliver_response(recv_msg); } } else if (intf->waiting_events_count < MAX_EVENTS_IN_QUEUE) { - /* No one to receive the message, put it in queue if there's - not already too many things in the queue. */ + /* + * No one to receive the message, put it in queue if there's + * not already too many things in the queue. + */ recv_msg = ipmi_alloc_recv_msg(); if (!recv_msg) { - /* We couldn't allocate memory for the - message, so requeue it for handling - later. */ + /* + * We couldn't allocate memory for the + * message, so requeue it for handling + * later. + */ rv = 1; goto out; } @@ -2913,11 +3617,14 @@ static int handle_read_event_rsp(ipmi_smi_t intf, copy_event_into_recv_msg(recv_msg, msg); list_add_tail(&(recv_msg->link), &(intf->waiting_events)); intf->waiting_events_count++; - } else { - /* There's too many things in the queue, discard this - message. */ - printk(KERN_WARNING PFX "Event queue full, discarding an" - " incoming event\n"); + } else if (!intf->event_msg_printed) { + /* + * There's too many things in the queue, discard this + * message. + */ + printk(KERN_WARNING PFX "Event queue full, discarding" + " incoming events\n"); + intf->event_msg_printed = 1; } out: @@ -2930,16 +3637,15 @@ static int handle_bmc_rsp(ipmi_smi_t intf, struct ipmi_smi_msg *msg) { struct ipmi_recv_msg *recv_msg; - unsigned long flags; struct ipmi_user *user; recv_msg = (struct ipmi_recv_msg *) msg->user_data; - if (recv_msg == NULL) - { - printk(KERN_WARNING"IPMI message received with no owner. This\n" - "could be because of a malformed message, or\n" - "because of a hardware error. Contact your\n" - "hardware vender for assistance\n"); + if (recv_msg == NULL) { + printk(KERN_WARNING + "IPMI message received with no owner. This\n" + "could be because of a malformed message, or\n" + "because of a hardware error. Contact your\n" + "hardware vender for assistance\n"); return 0; } @@ -2947,16 +3653,12 @@ static int handle_bmc_rsp(ipmi_smi_t intf, /* Make sure the user still exists. */ if (user && !user->valid) { /* The user for the message went away, so give up. */ - spin_lock_irqsave(&intf->counter_lock, flags); - intf->unhandled_local_responses++; - spin_unlock_irqrestore(&intf->counter_lock, flags); + ipmi_inc_stat(intf, unhandled_local_responses); ipmi_free_recv_msg(recv_msg); } else { struct ipmi_system_interface_addr *smi_addr; - spin_lock_irqsave(&intf->counter_lock, flags); - intf->handled_local_responses++; - spin_unlock_irqrestore(&intf->counter_lock, flags); + ipmi_inc_stat(intf, handled_local_responses); recv_msg->recv_type = IPMI_RESPONSE_RECV_TYPE; recv_msg->msgid = msg->msgid; smi_addr = ((struct ipmi_system_interface_addr *) @@ -2977,10 +3679,12 @@ static int handle_bmc_rsp(ipmi_smi_t intf, return 0; } -/* Handle a new message. Return 1 if the message should be requeued, - 0 if the message should be freed, or -1 if the message should not - be freed or requeued. */ -static int handle_new_recv_msg(ipmi_smi_t intf, +/* + * Handle a received message. Return 1 if the message should be requeued, + * 0 if the message should be freed, or -1 if the message should not + * be freed or requeued. + */ +static int handle_one_recv_msg(ipmi_smi_t intf, struct ipmi_smi_msg *msg) { int requeue; @@ -3004,10 +3708,12 @@ static int handle_new_recv_msg(ipmi_smi_t intf, msg->rsp[1] = msg->data[1]; msg->rsp[2] = IPMI_ERR_UNSPECIFIED; msg->rsp_size = 3; - } else if (((msg->rsp[0] >> 2) != ((msg->data[0] >> 2) | 1))/* Netfn */ - || (msg->rsp[1] != msg->data[1])) /* Command */ - { - /* The response is not even marginally correct. */ + } else if (((msg->rsp[0] >> 2) != ((msg->data[0] >> 2) | 1)) + || (msg->rsp[1] != msg->data[1])) { + /* + * The NetFN and Command in the response is not even + * marginally correct. + */ printk(KERN_WARNING PFX "BMC returned incorrect response," " expected netfn %x cmd %x, got netfn %x cmd %x\n", (msg->data[0] >> 2) | 1, msg->data[1], @@ -3022,10 +3728,11 @@ static int handle_new_recv_msg(ipmi_smi_t intf, if ((msg->rsp[0] == ((IPMI_NETFN_APP_REQUEST|1) << 2)) && (msg->rsp[1] == IPMI_SEND_MSG_CMD) - && (msg->user_data != NULL)) - { - /* It's a response to a response we sent. For this we - deliver a send message response to the user. */ + && (msg->user_data != NULL)) { + /* + * It's a response to a response we sent. For this we + * deliver a send message response to the user. + */ struct ipmi_recv_msg *recv_msg = msg->user_data; requeue = 0; @@ -3051,8 +3758,7 @@ static int handle_new_recv_msg(ipmi_smi_t intf, recv_msg->msg_data[0] = msg->rsp[2]; deliver_response(recv_msg); } else if ((msg->rsp[0] == ((IPMI_NETFN_APP_REQUEST|1) << 2)) - && (msg->rsp[1] == IPMI_GET_MSG_CMD)) - { + && (msg->rsp[1] == IPMI_GET_MSG_CMD)) { /* It's from the receive queue. */ chan = msg->rsp[3] & 0xf; if (chan >= IPMI_MAX_CHANNELS) { @@ -3061,15 +3767,30 @@ static int handle_new_recv_msg(ipmi_smi_t intf, goto out; } + /* + * We need to make sure the channels have been initialized. + * The channel_handler routine will set the "curr_channel" + * equal to or greater than IPMI_MAX_CHANNELS when all the + * channels for this interface have been initialized. + */ + if (intf->curr_channel < IPMI_MAX_CHANNELS) { + requeue = 0; /* Throw the message away */ + goto out; + } + switch (intf->channels[chan].medium) { case IPMI_CHANNEL_MEDIUM_IPMB: if (msg->rsp[4] & 0x04) { - /* It's a response, so find the - requesting message and send it up. */ + /* + * It's a response, so find the + * requesting message and send it up. + */ requeue = handle_ipmb_get_msg_rsp(intf, msg); } else { - /* It's a command to the SMS from some other - entity. Handle that. */ + /* + * It's a command to the SMS from some other + * entity. Handle that. + */ requeue = handle_ipmb_get_msg_cmd(intf, msg); } break; @@ -3077,26 +3798,40 @@ static int handle_new_recv_msg(ipmi_smi_t intf, case IPMI_CHANNEL_MEDIUM_8023LAN: case IPMI_CHANNEL_MEDIUM_ASYNC: if (msg->rsp[6] & 0x04) { - /* It's a response, so find the - requesting message and send it up. */ + /* + * It's a response, so find the + * requesting message and send it up. + */ requeue = handle_lan_get_msg_rsp(intf, msg); } else { - /* It's a command to the SMS from some other - entity. Handle that. */ + /* + * It's a command to the SMS from some other + * entity. Handle that. + */ requeue = handle_lan_get_msg_cmd(intf, msg); } break; default: - /* We don't handle the channel type, so just - * free the message. */ - requeue = 0; + /* Check for OEM Channels. Clients had better + register for these commands. */ + if ((intf->channels[chan].medium + >= IPMI_CHANNEL_MEDIUM_OEM_MIN) + && (intf->channels[chan].medium + <= IPMI_CHANNEL_MEDIUM_OEM_MAX)) { + requeue = handle_oem_get_msg_cmd(intf, msg); + } else { + /* + * We don't handle the channel type, so just + * free the message. + */ + requeue = 0; + } } } else if ((msg->rsp[0] == ((IPMI_NETFN_APP_REQUEST|1) << 2)) - && (msg->rsp[1] == IPMI_READ_EVENT_MSG_BUFFER_CMD)) - { - /* It's an asyncronous event. */ + && (msg->rsp[1] == IPMI_READ_EVENT_MSG_BUFFER_CMD)) { + /* It's an asynchronous event. */ requeue = handle_read_event_rsp(intf, msg); } else { /* It's a response from the local BMC. */ @@ -3107,105 +3842,141 @@ static int handle_new_recv_msg(ipmi_smi_t intf, return requeue; } +/* + * If there are messages in the queue or pretimeouts, handle them. + */ +static void handle_new_recv_msgs(ipmi_smi_t intf) +{ + struct ipmi_smi_msg *smi_msg; + unsigned long flags = 0; + int rv; + int run_to_completion = intf->run_to_completion; + + /* See if any waiting messages need to be processed. */ + if (!run_to_completion) + spin_lock_irqsave(&intf->waiting_msgs_lock, flags); + while (!list_empty(&intf->waiting_msgs)) { + smi_msg = list_entry(intf->waiting_msgs.next, + struct ipmi_smi_msg, link); + list_del(&smi_msg->link); + if (!run_to_completion) + spin_unlock_irqrestore(&intf->waiting_msgs_lock, flags); + rv = handle_one_recv_msg(intf, smi_msg); + if (!run_to_completion) + spin_lock_irqsave(&intf->waiting_msgs_lock, flags); + if (rv == 0) { + /* Message handled */ + ipmi_free_smi_msg(smi_msg); + } else if (rv < 0) { + /* Fatal error on the message, del but don't free. */ + } else { + /* + * To preserve message order, quit if we + * can't handle a message. + */ + list_add(&smi_msg->link, &intf->waiting_msgs); + break; + } + } + if (!run_to_completion) + spin_unlock_irqrestore(&intf->waiting_msgs_lock, flags); + + /* + * If the pretimout count is non-zero, decrement one from it and + * deliver pretimeouts to all the users. + */ + if (atomic_add_unless(&intf->watchdog_pretimeouts_to_deliver, -1, 0)) { + ipmi_user_t user; + + rcu_read_lock(); + list_for_each_entry_rcu(user, &intf->users, link) { + if (user->handler->ipmi_watchdog_pretimeout) + user->handler->ipmi_watchdog_pretimeout( + user->handler_data); + } + rcu_read_unlock(); + } +} + +static void smi_recv_tasklet(unsigned long val) +{ + handle_new_recv_msgs((ipmi_smi_t) val); +} + /* Handle a new message from the lower layer. */ void ipmi_smi_msg_received(ipmi_smi_t intf, struct ipmi_smi_msg *msg) { - unsigned long flags; - int rv; + unsigned long flags = 0; /* keep us warning-free. */ + int run_to_completion; if ((msg->data_size >= 2) && (msg->data[0] == (IPMI_NETFN_APP_REQUEST << 2)) && (msg->data[1] == IPMI_SEND_MSG_CMD) - && (msg->user_data == NULL)) - { - /* This is the local response to a command send, start - the timer for these. The user_data will not be - NULL if this is a response send, and we will let - response sends just go through. */ - - /* Check for errors, if we get certain errors (ones - that mean basically we can try again later), we - ignore them and start the timer. Otherwise we - report the error immediately. */ + && (msg->user_data == NULL)) { + /* + * This is the local response to a command send, start + * the timer for these. The user_data will not be + * NULL if this is a response send, and we will let + * response sends just go through. + */ + + /* + * Check for errors, if we get certain errors (ones + * that mean basically we can try again later), we + * ignore them and start the timer. Otherwise we + * report the error immediately. + */ if ((msg->rsp_size >= 3) && (msg->rsp[2] != 0) && (msg->rsp[2] != IPMI_NODE_BUSY_ERR) - && (msg->rsp[2] != IPMI_LOST_ARBITRATION_ERR)) - { + && (msg->rsp[2] != IPMI_LOST_ARBITRATION_ERR) + && (msg->rsp[2] != IPMI_BUS_ERR) + && (msg->rsp[2] != IPMI_NAK_ON_WRITE_ERR)) { int chan = msg->rsp[3] & 0xf; /* Got an error sending the message, handle it. */ - spin_lock_irqsave(&intf->counter_lock, flags); if (chan >= IPMI_MAX_CHANNELS) ; /* This shouldn't happen */ else if ((intf->channels[chan].medium == IPMI_CHANNEL_MEDIUM_8023LAN) || (intf->channels[chan].medium == IPMI_CHANNEL_MEDIUM_ASYNC)) - intf->sent_lan_command_errs++; + ipmi_inc_stat(intf, sent_lan_command_errs); else - intf->sent_ipmb_command_errs++; - spin_unlock_irqrestore(&intf->counter_lock, flags); + ipmi_inc_stat(intf, sent_ipmb_command_errs); intf_err_seq(intf, msg->msgid, msg->rsp[2]); - } else { + } else /* The message was sent, start the timer. */ intf_start_seq_timer(intf, msg->msgid); - } ipmi_free_smi_msg(msg); goto out; } - /* To preserve message order, if the list is not empty, we - tack this message onto the end of the list. */ - spin_lock_irqsave(&intf->waiting_msgs_lock, flags); - if (!list_empty(&intf->waiting_msgs)) { - list_add_tail(&msg->link, &intf->waiting_msgs); - spin_unlock_irqrestore(&intf->waiting_msgs_lock, flags); - goto out; - } - spin_unlock_irqrestore(&intf->waiting_msgs_lock, flags); - - rv = handle_new_recv_msg(intf, msg); - if (rv > 0) { - /* Could not handle the message now, just add it to a - list to handle later. */ + /* + * To preserve message order, if the list is not empty, we + * tack this message onto the end of the list. + */ + run_to_completion = intf->run_to_completion; + if (!run_to_completion) spin_lock_irqsave(&intf->waiting_msgs_lock, flags); - list_add_tail(&msg->link, &intf->waiting_msgs); + list_add_tail(&msg->link, &intf->waiting_msgs); + if (!run_to_completion) spin_unlock_irqrestore(&intf->waiting_msgs_lock, flags); - } else if (rv == 0) { - ipmi_free_smi_msg(msg); - } + tasklet_schedule(&intf->recv_tasklet); out: return; } +EXPORT_SYMBOL(ipmi_smi_msg_received); void ipmi_smi_watchdog_pretimeout(ipmi_smi_t intf) { - ipmi_user_t user; - - rcu_read_lock(); - list_for_each_entry_rcu(user, &intf->users, link) { - if (!user->handler->ipmi_watchdog_pretimeout) - continue; - - user->handler->ipmi_watchdog_pretimeout(user->handler_data); - } - rcu_read_unlock(); -} - -static void -handle_msg_timeout(struct ipmi_recv_msg *msg) -{ - msg->recv_type = IPMI_RESPONSE_RECV_TYPE; - msg->msg_data[0] = IPMI_TIMEOUT_COMPLETION_CODE; - msg->msg.netfn |= 1; /* Convert to a response. */ - msg->msg.data_len = 1; - msg->msg.data = msg->msg_data; - deliver_response(msg); + atomic_set(&intf->watchdog_pretimeouts_to_deliver, 1); + tasklet_schedule(&intf->recv_tasklet); } +EXPORT_SYMBOL(ipmi_smi_watchdog_pretimeout); static struct ipmi_smi_msg * smi_from_recv_msg(ipmi_smi_t intf, struct ipmi_recv_msg *recv_msg, @@ -3213,14 +3984,16 @@ smi_from_recv_msg(ipmi_smi_t intf, struct ipmi_recv_msg *recv_msg, { struct ipmi_smi_msg *smi_msg = ipmi_alloc_smi_msg(); if (!smi_msg) - /* If we can't allocate the message, then just return, we - get 4 retries, so this should be ok. */ + /* + * If we can't allocate the message, then just return, we + * get 4 retries, so this should be ok. + */ return NULL; memcpy(smi_msg->data, recv_msg->msg.data, recv_msg->msg.data_len); smi_msg->data_size = recv_msg->msg.data_len; smi_msg->msgid = STORE_SEQ_IN_MSGID(seq, seqid); - + #ifdef DEBUG_MSGING { int m; @@ -3235,164 +4008,198 @@ smi_from_recv_msg(ipmi_smi_t intf, struct ipmi_recv_msg *recv_msg, static void check_msg_timeout(ipmi_smi_t intf, struct seq_table *ent, struct list_head *timeouts, long timeout_period, - int slot, unsigned long *flags) + int slot, unsigned long *flags, + unsigned int *waiting_msgs) { - struct ipmi_recv_msg *msg; + struct ipmi_recv_msg *msg; + struct ipmi_smi_handlers *handlers; + + if (intf->intf_num == -1) + return; if (!ent->inuse) return; ent->timeout -= timeout_period; - if (ent->timeout > 0) + if (ent->timeout > 0) { + (*waiting_msgs)++; return; + } if (ent->retries_left == 0) { /* The message has used all its retries. */ ent->inuse = 0; msg = ent->recv_msg; list_add_tail(&msg->link, timeouts); - spin_lock(&intf->counter_lock); if (ent->broadcast) - intf->timed_out_ipmb_broadcasts++; - else if (ent->recv_msg->addr.addr_type == IPMI_LAN_ADDR_TYPE) - intf->timed_out_lan_commands++; + ipmi_inc_stat(intf, timed_out_ipmb_broadcasts); + else if (is_lan_addr(&ent->recv_msg->addr)) + ipmi_inc_stat(intf, timed_out_lan_commands); else - intf->timed_out_ipmb_commands++; - spin_unlock(&intf->counter_lock); + ipmi_inc_stat(intf, timed_out_ipmb_commands); } else { struct ipmi_smi_msg *smi_msg; /* More retries, send again. */ - /* Start with the max timer, set to normal - timer after the message is sent. */ + (*waiting_msgs)++; + + /* + * Start with the max timer, set to normal timer after + * the message is sent. + */ ent->timeout = MAX_MSG_TIMEOUT; ent->retries_left--; - spin_lock(&intf->counter_lock); - if (ent->recv_msg->addr.addr_type == IPMI_LAN_ADDR_TYPE) - intf->retransmitted_lan_commands++; - else - intf->retransmitted_ipmb_commands++; - spin_unlock(&intf->counter_lock); - smi_msg = smi_from_recv_msg(intf, ent->recv_msg, slot, ent->seqid); - if (!smi_msg) + if (!smi_msg) { + if (is_lan_addr(&ent->recv_msg->addr)) + ipmi_inc_stat(intf, + dropped_rexmit_lan_commands); + else + ipmi_inc_stat(intf, + dropped_rexmit_ipmb_commands); return; + } spin_unlock_irqrestore(&intf->seq_lock, *flags); - /* Send the new message. We send with a zero - * priority. It timed out, I doubt time is - * that critical now, and high priority - * messages are really only for messages to the - * local MC, which don't get resent. */ - intf->handlers->sender(intf->send_info, - smi_msg, 0); + + /* + * Send the new message. We send with a zero + * priority. It timed out, I doubt time is that + * critical now, and high priority messages are really + * only for messages to the local MC, which don't get + * resent. + */ + handlers = intf->handlers; + if (handlers) { + if (is_lan_addr(&ent->recv_msg->addr)) + ipmi_inc_stat(intf, + retransmitted_lan_commands); + else + ipmi_inc_stat(intf, + retransmitted_ipmb_commands); + + intf->handlers->sender(intf->send_info, + smi_msg, 0); + } else + ipmi_free_smi_msg(smi_msg); + spin_lock_irqsave(&intf->seq_lock, *flags); } } -static void ipmi_timeout_handler(long timeout_period) +static unsigned int ipmi_timeout_handler(ipmi_smi_t intf, long timeout_period) { - ipmi_smi_t intf; struct list_head timeouts; struct ipmi_recv_msg *msg, *msg2; - struct ipmi_smi_msg *smi_msg, *smi_msg2; unsigned long flags; - int i, j; + int i; + unsigned int waiting_msgs = 0; + /* + * Go through the seq table and find any messages that + * have timed out, putting them in the timeouts + * list. + */ INIT_LIST_HEAD(&timeouts); + spin_lock_irqsave(&intf->seq_lock, flags); + for (i = 0; i < IPMI_IPMB_NUM_SEQ; i++) + check_msg_timeout(intf, &(intf->seq_table[i]), + &timeouts, timeout_period, i, + &flags, &waiting_msgs); + spin_unlock_irqrestore(&intf->seq_lock, flags); - spin_lock(&interfaces_lock); - for (i = 0; i < MAX_IPMI_INTERFACES; i++) { - intf = ipmi_interfaces[i]; - if (IPMI_INVALID_INTERFACE(intf)) - continue; - kref_get(&intf->refcount); - spin_unlock(&interfaces_lock); + list_for_each_entry_safe(msg, msg2, &timeouts, link) + deliver_err_response(msg, IPMI_TIMEOUT_COMPLETION_CODE); - /* See if any waiting messages need to be processed. */ - spin_lock_irqsave(&intf->waiting_msgs_lock, flags); - list_for_each_entry_safe(smi_msg, smi_msg2, - &intf->waiting_msgs, link) { - if (!handle_new_recv_msg(intf, smi_msg)) { - list_del(&smi_msg->link); - ipmi_free_smi_msg(smi_msg); - } else { - /* To preserve message order, quit if we - can't handle a message. */ - break; + /* + * Maintenance mode handling. Check the timeout + * optimistically before we claim the lock. It may + * mean a timeout gets missed occasionally, but that + * only means the timeout gets extended by one period + * in that case. No big deal, and it avoids the lock + * most of the time. + */ + if (intf->auto_maintenance_timeout > 0) { + spin_lock_irqsave(&intf->maintenance_mode_lock, flags); + if (intf->auto_maintenance_timeout > 0) { + intf->auto_maintenance_timeout + -= timeout_period; + if (!intf->maintenance_mode + && (intf->auto_maintenance_timeout <= 0)) { + intf->maintenance_mode_enable = false; + maintenance_mode_update(intf); } } - spin_unlock_irqrestore(&intf->waiting_msgs_lock, flags); - - /* Go through the seq table and find any messages that - have timed out, putting them in the timeouts - list. */ - spin_lock_irqsave(&intf->seq_lock, flags); - for (j = 0; j < IPMI_IPMB_NUM_SEQ; j++) - check_msg_timeout(intf, &(intf->seq_table[j]), - &timeouts, timeout_period, j, - &flags); - spin_unlock_irqrestore(&intf->seq_lock, flags); + spin_unlock_irqrestore(&intf->maintenance_mode_lock, + flags); + } - list_for_each_entry_safe(msg, msg2, &timeouts, link) - handle_msg_timeout(msg); + tasklet_schedule(&intf->recv_tasklet); - kref_put(&intf->refcount, intf_free); - spin_lock(&interfaces_lock); - } - spin_unlock(&interfaces_lock); + return waiting_msgs; } -static void ipmi_request_event(void) +static void ipmi_request_event(ipmi_smi_t intf) { - ipmi_smi_t intf; - int i; + struct ipmi_smi_handlers *handlers; - spin_lock(&interfaces_lock); - for (i = 0; i < MAX_IPMI_INTERFACES; i++) { - intf = ipmi_interfaces[i]; - if (IPMI_INVALID_INTERFACE(intf)) - continue; + /* No event requests when in maintenance mode. */ + if (intf->maintenance_mode_enable) + return; - intf->handlers->request_events(intf->send_info); - } - spin_unlock(&interfaces_lock); + handlers = intf->handlers; + if (handlers) + handlers->request_events(intf->send_info); } static struct timer_list ipmi_timer; -/* Call every ~100 ms. */ -#define IPMI_TIMEOUT_TIME 100 - -/* How many jiffies does it take to get to the timeout time. */ -#define IPMI_TIMEOUT_JIFFIES ((IPMI_TIMEOUT_TIME * HZ) / 1000) - -/* Request events from the queue every second (this is the number of - IPMI_TIMEOUT_TIMES between event requests). Hopefully, in the - future, IPMI will add a way to know immediately if an event is in - the queue and this silliness can go away. */ -#define IPMI_REQUEST_EV_TIME (1000 / (IPMI_TIMEOUT_TIME)) - static atomic_t stop_operation; -static unsigned int ticks_to_req_ev = IPMI_REQUEST_EV_TIME; static void ipmi_timeout(unsigned long data) { + ipmi_smi_t intf; + int nt = 0; + if (atomic_read(&stop_operation)) return; - ticks_to_req_ev--; - if (ticks_to_req_ev == 0) { - ipmi_request_event(); - ticks_to_req_ev = IPMI_REQUEST_EV_TIME; - } + rcu_read_lock(); + list_for_each_entry_rcu(intf, &ipmi_interfaces, link) { + int lnt = 0; + + if (atomic_read(&intf->event_waiters)) { + intf->ticks_to_req_ev--; + if (intf->ticks_to_req_ev == 0) { + ipmi_request_event(intf); + intf->ticks_to_req_ev = IPMI_REQUEST_EV_TIME; + } + lnt++; + } - ipmi_timeout_handler(IPMI_TIMEOUT_TIME); + lnt += ipmi_timeout_handler(intf, IPMI_TIMEOUT_TIME); - mod_timer(&ipmi_timer, jiffies + IPMI_TIMEOUT_JIFFIES); + lnt = !!lnt; + if (lnt != intf->last_needs_timer && + intf->handlers->set_need_watch) + intf->handlers->set_need_watch(intf->send_info, lnt); + intf->last_needs_timer = lnt; + + nt += lnt; + } + rcu_read_unlock(); + + if (nt) + mod_timer(&ipmi_timer, jiffies + IPMI_TIMEOUT_JIFFIES); } +static void need_waiter(ipmi_smi_t intf) +{ + /* Racy, but worst case we start the timer twice. */ + if (!timer_pending(&ipmi_timer)) + mod_timer(&ipmi_timer, jiffies + IPMI_TIMEOUT_JIFFIES); +} static atomic_t smi_msg_inuse_count = ATOMIC_INIT(0); static atomic_t recv_msg_inuse_count = ATOMIC_INIT(0); @@ -3415,6 +4222,7 @@ struct ipmi_smi_msg *ipmi_alloc_smi_msg(void) } return rv; } +EXPORT_SYMBOL(ipmi_alloc_smi_msg); static void free_recv_msg(struct ipmi_recv_msg *msg) { @@ -3422,7 +4230,7 @@ static void free_recv_msg(struct ipmi_recv_msg *msg) kfree(msg); } -struct ipmi_recv_msg *ipmi_alloc_recv_msg(void) +static struct ipmi_recv_msg *ipmi_alloc_recv_msg(void) { struct ipmi_recv_msg *rv; @@ -3441,15 +4249,52 @@ void ipmi_free_recv_msg(struct ipmi_recv_msg *msg) kref_put(&msg->user->refcount, free_user); msg->done(msg); } +EXPORT_SYMBOL(ipmi_free_recv_msg); #ifdef CONFIG_IPMI_PANIC_EVENT +static atomic_t panic_done_count = ATOMIC_INIT(0); + static void dummy_smi_done_handler(struct ipmi_smi_msg *msg) { + atomic_dec(&panic_done_count); } static void dummy_recv_done_handler(struct ipmi_recv_msg *msg) { + atomic_dec(&panic_done_count); +} + +/* + * Inside a panic, send a message and wait for a response. + */ +static void ipmi_panic_request_and_wait(ipmi_smi_t intf, + struct ipmi_addr *addr, + struct kernel_ipmi_msg *msg) +{ + struct ipmi_smi_msg smi_msg; + struct ipmi_recv_msg recv_msg; + int rv; + + smi_msg.done = dummy_smi_done_handler; + recv_msg.done = dummy_recv_done_handler; + atomic_add(2, &panic_done_count); + rv = i_ipmi_request(NULL, + intf, + addr, + 0, + msg, + intf, + &smi_msg, + &recv_msg, + 0, + intf->channels[0].address, + intf->channels[0].lun, + 0, 1); /* Don't retry, and don't wait. */ + if (rv) + atomic_sub(2, &panic_done_count); + while (atomic_read(&panic_done_count) != 0) + ipmi_poll(intf); } #ifdef CONFIG_IPMI_PANIC_STRING @@ -3458,8 +4303,7 @@ static void event_receiver_fetcher(ipmi_smi_t intf, struct ipmi_recv_msg *msg) if ((msg->addr.addr_type == IPMI_SYSTEM_INTERFACE_ADDR_TYPE) && (msg->msg.netfn == IPMI_NETFN_SENSOR_EVENT_RESPONSE) && (msg->msg.cmd == IPMI_GET_EVENT_RECEIVER_CMD) - && (msg->msg.data[0] == IPMI_CC_NO_ERROR)) - { + && (msg->msg.data[0] == IPMI_CC_NO_ERROR)) { /* A get event receiver command, save it. */ intf->event_receiver = msg->msg.data[1]; intf->event_receiver_lun = msg->msg.data[2] & 0x3; @@ -3471,10 +4315,11 @@ static void device_id_fetcher(ipmi_smi_t intf, struct ipmi_recv_msg *msg) if ((msg->addr.addr_type == IPMI_SYSTEM_INTERFACE_ADDR_TYPE) && (msg->msg.netfn == IPMI_NETFN_APP_RESPONSE) && (msg->msg.cmd == IPMI_GET_DEVICE_ID_CMD) - && (msg->msg.data[0] == IPMI_CC_NO_ERROR)) - { - /* A get device id command, save if we are an event - receiver or generator. */ + && (msg->msg.data[0] == IPMI_CC_NO_ERROR)) { + /* + * A get device id command, save if we are an event + * receiver or generator. + */ intf->local_sel_device = (msg->msg.data[6] >> 2) & 1; intf->local_event_generator = (msg->msg.data[6] >> 5) & 1; } @@ -3486,11 +4331,8 @@ static void send_panic_events(char *str) struct kernel_ipmi_msg msg; ipmi_smi_t intf; unsigned char data[16]; - int i; struct ipmi_system_interface_addr *si; struct ipmi_addr addr; - struct ipmi_smi_msg smi_msg; - struct ipmi_recv_msg recv_msg; si = (struct ipmi_system_interface_addr *) &addr; si->addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; @@ -3508,59 +4350,61 @@ static void send_panic_events(char *str) data[4] = 0x6f; /* Sensor specific, IPMI table 36-1 */ data[5] = 0xa1; /* Runtime stop OEM bytes 2 & 3. */ - /* Put a few breadcrumbs in. Hopefully later we can add more things - to make the panic events more useful. */ + /* + * Put a few breadcrumbs in. Hopefully later we can add more things + * to make the panic events more useful. + */ if (str) { data[3] = str[0]; data[6] = str[1]; data[7] = str[2]; } - smi_msg.done = dummy_smi_done_handler; - recv_msg.done = dummy_recv_done_handler; - /* For every registered interface, send the event. */ - for (i = 0; i < MAX_IPMI_INTERFACES; i++) { - intf = ipmi_interfaces[i]; - if (IPMI_INVALID_INTERFACE(intf)) + list_for_each_entry_rcu(intf, &ipmi_interfaces, link) { + if (!intf->handlers) + /* Interface is not ready. */ continue; + intf->run_to_completion = 1; /* Send the event announcing the panic. */ intf->handlers->set_run_to_completion(intf->send_info, 1); - i_ipmi_request(NULL, - intf, - &addr, - 0, - &msg, - intf, - &smi_msg, - &recv_msg, - 0, - intf->channels[0].address, - intf->channels[0].lun, - 0, 1); /* Don't retry, and don't wait. */ + ipmi_panic_request_and_wait(intf, &addr, &msg); } #ifdef CONFIG_IPMI_PANIC_STRING - /* On every interface, dump a bunch of OEM event holding the - string. */ - if (!str) + /* + * On every interface, dump a bunch of OEM event holding the + * string. + */ + if (!str) return; - for (i = 0; i < MAX_IPMI_INTERFACES; i++) { + /* For every registered interface, send the event. */ + list_for_each_entry_rcu(intf, &ipmi_interfaces, link) { char *p = str; struct ipmi_ipmb_addr *ipmb; int j; - intf = ipmi_interfaces[i]; - if (IPMI_INVALID_INTERFACE(intf)) + if (intf->intf_num == -1) + /* Interface was not ready yet. */ continue; - /* First job here is to figure out where to send the - OEM events. There's no way in IPMI to send OEM - events using an event send command, so we have to - find the SEL to put them in and stick them in - there. */ + /* + * intf_num is used as an marker to tell if the + * interface is valid. Thus we need a read barrier to + * make sure data fetched before checking intf_num + * won't be used. + */ + smp_rmb(); + + /* + * First job here is to figure out where to send the + * OEM events. There's no way in IPMI to send OEM + * events using an event send command, so we have to + * find the SEL to put them in and stick them in + * there. + */ /* Get capabilities from the get device id. */ intf->local_sel_device = 0; @@ -3573,18 +4417,7 @@ static void send_panic_events(char *str) msg.data = NULL; msg.data_len = 0; intf->null_user_handler = device_id_fetcher; - i_ipmi_request(NULL, - intf, - &addr, - 0, - &msg, - intf, - &smi_msg, - &recv_msg, - 0, - intf->channels[0].address, - intf->channels[0].lun, - 0, 1); /* Don't retry, and don't wait. */ + ipmi_panic_request_and_wait(intf, &addr, &msg); if (intf->local_event_generator) { /* Request the event receiver from the local MC. */ @@ -3593,39 +4426,33 @@ static void send_panic_events(char *str) msg.data = NULL; msg.data_len = 0; intf->null_user_handler = event_receiver_fetcher; - i_ipmi_request(NULL, - intf, - &addr, - 0, - &msg, - intf, - &smi_msg, - &recv_msg, - 0, - intf->channels[0].address, - intf->channels[0].lun, - 0, 1); /* no retry, and no wait. */ + ipmi_panic_request_and_wait(intf, &addr, &msg); } intf->null_user_handler = NULL; - /* Validate the event receiver. The low bit must not - be 1 (it must be a valid IPMB address), it cannot - be zero, and it must not be my address. */ - if (((intf->event_receiver & 1) == 0) + /* + * Validate the event receiver. The low bit must not + * be 1 (it must be a valid IPMB address), it cannot + * be zero, and it must not be my address. + */ + if (((intf->event_receiver & 1) == 0) && (intf->event_receiver != 0) - && (intf->event_receiver != intf->channels[0].address)) - { - /* The event receiver is valid, send an IPMB - message. */ + && (intf->event_receiver != intf->channels[0].address)) { + /* + * The event receiver is valid, send an IPMB + * message. + */ ipmb = (struct ipmi_ipmb_addr *) &addr; ipmb->addr_type = IPMI_IPMB_ADDR_TYPE; ipmb->channel = 0; /* FIXME - is this right? */ ipmb->lun = intf->event_receiver_lun; ipmb->slave_addr = intf->event_receiver; } else if (intf->local_sel_device) { - /* The event receiver was not valid (or was - me), but I am an SEL device, just dump it - in my SEL. */ + /* + * The event receiver was not valid (or was + * me), but I am an SEL device, just dump it + * in my SEL. + */ si = (struct ipmi_system_interface_addr *) &addr; si->addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; si->channel = IPMI_BMC_CHANNEL; @@ -3633,7 +4460,6 @@ static void send_panic_events(char *str) } else continue; /* No where to send the event. */ - msg.netfn = IPMI_NETFN_STORAGE_REQUEST; /* Storage. */ msg.cmd = IPMI_ADD_SEL_ENTRY_CMD; msg.data = data; @@ -3650,36 +4476,26 @@ static void send_panic_events(char *str) data[2] = 0xf0; /* OEM event without timestamp. */ data[3] = intf->channels[0].address; data[4] = j++; /* sequence # */ - /* Always give 11 bytes, so strncpy will fill - it with zeroes for me. */ + /* + * Always give 11 bytes, so strncpy will fill + * it with zeroes for me. + */ strncpy(data+5, p, 11); p += size; - i_ipmi_request(NULL, - intf, - &addr, - 0, - &msg, - intf, - &smi_msg, - &recv_msg, - 0, - intf->channels[0].address, - intf->channels[0].lun, - 0, 1); /* no retry, and no wait. */ + ipmi_panic_request_and_wait(intf, &addr, &msg); } - } + } #endif /* CONFIG_IPMI_PANIC_STRING */ } #endif /* CONFIG_IPMI_PANIC_EVENT */ -static int has_panicked = 0; +static int has_panicked; static int panic_event(struct notifier_block *this, unsigned long event, - void *ptr) + void *ptr) { - int i; ipmi_smi_t intf; if (has_panicked) @@ -3687,11 +4503,12 @@ static int panic_event(struct notifier_block *this, has_panicked = 1; /* For every registered interface, set it to run to completion. */ - for (i = 0; i < MAX_IPMI_INTERFACES; i++) { - intf = ipmi_interfaces[i]; - if (IPMI_INVALID_INTERFACE(intf)) + list_for_each_entry_rcu(intf, &ipmi_interfaces, link) { + if (!intf->handlers) + /* Interface is not ready. */ continue; + intf->run_to_completion = 1; intf->handlers->set_run_to_completion(intf->send_info, 1); } @@ -3710,13 +4527,12 @@ static struct notifier_block panic_block = { static int ipmi_init_msghandler(void) { - int i; int rv; if (initialized) return 0; - rv = driver_register(&ipmidriver); + rv = driver_register(&ipmidriver.driver); if (rv) { printk(KERN_ERR PFX "Could not register IPMI driver\n"); return rv; @@ -3725,9 +4541,6 @@ static int ipmi_init_msghandler(void) printk(KERN_INFO "ipmi message handler version " IPMI_DRIVER_VERSION "\n"); - for (i = 0; i < MAX_IPMI_INTERFACES; i++) - ipmi_interfaces[i] = NULL; - #ifdef CONFIG_PROC_FS proc_ipmi_root = proc_mkdir("ipmi", NULL); if (!proc_ipmi_root) { @@ -3735,7 +4548,6 @@ static int ipmi_init_msghandler(void) return -ENOMEM; } - proc_ipmi_root->owner = THIS_MODULE; #endif /* CONFIG_PROC_FS */ setup_timer(&ipmi_timer, ipmi_timeout, 0); @@ -3748,13 +4560,13 @@ static int ipmi_init_msghandler(void) return 0; } -static __init int ipmi_init_msghandler_mod(void) +static int __init ipmi_init_msghandler_mod(void) { ipmi_init_msghandler(); return 0; } -static __exit void cleanup_ipmi(void) +static void __exit cleanup_ipmi(void) { int count; @@ -3763,19 +4575,24 @@ static __exit void cleanup_ipmi(void) atomic_notifier_chain_unregister(&panic_notifier_list, &panic_block); - /* This can't be called if any interfaces exist, so no worry about - shutting down the interfaces. */ + /* + * This can't be called if any interfaces exist, so no worry + * about shutting down the interfaces. + */ - /* Tell the timer to stop, then wait for it to stop. This avoids - problems with race conditions removing the timer here. */ + /* + * Tell the timer to stop, then wait for it to stop. This + * avoids problems with race conditions removing the timer + * here. + */ atomic_inc(&stop_operation); del_timer_sync(&ipmi_timer); #ifdef CONFIG_PROC_FS - remove_proc_entry(proc_ipmi_root->name, &proc_root); + proc_remove(proc_ipmi_root); #endif /* CONFIG_PROC_FS */ - driver_unregister(&ipmidriver); + driver_unregister(&ipmidriver.driver); initialized = 0; @@ -3794,30 +4611,6 @@ module_exit(cleanup_ipmi); module_init(ipmi_init_msghandler_mod); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Corey Minyard <minyard@mvista.com>"); -MODULE_DESCRIPTION("Incoming and outgoing message routing for an IPMI interface."); +MODULE_DESCRIPTION("Incoming and outgoing message routing for an IPMI" + " interface."); MODULE_VERSION(IPMI_DRIVER_VERSION); - -EXPORT_SYMBOL(ipmi_create_user); -EXPORT_SYMBOL(ipmi_destroy_user); -EXPORT_SYMBOL(ipmi_get_version); -EXPORT_SYMBOL(ipmi_request_settime); -EXPORT_SYMBOL(ipmi_request_supply_msgs); -EXPORT_SYMBOL(ipmi_register_smi); -EXPORT_SYMBOL(ipmi_unregister_smi); -EXPORT_SYMBOL(ipmi_register_for_cmd); -EXPORT_SYMBOL(ipmi_unregister_for_cmd); -EXPORT_SYMBOL(ipmi_smi_msg_received); -EXPORT_SYMBOL(ipmi_smi_watchdog_pretimeout); -EXPORT_SYMBOL(ipmi_alloc_smi_msg); -EXPORT_SYMBOL(ipmi_addr_length); -EXPORT_SYMBOL(ipmi_validate_addr); -EXPORT_SYMBOL(ipmi_set_gets_events); -EXPORT_SYMBOL(ipmi_smi_watcher_register); -EXPORT_SYMBOL(ipmi_smi_watcher_unregister); -EXPORT_SYMBOL(ipmi_set_my_address); -EXPORT_SYMBOL(ipmi_get_my_address); -EXPORT_SYMBOL(ipmi_set_my_LUN); -EXPORT_SYMBOL(ipmi_get_my_LUN); -EXPORT_SYMBOL(ipmi_smi_add_proc_entry); -EXPORT_SYMBOL(ipmi_user_set_run_to_completion); -EXPORT_SYMBOL(ipmi_free_recv_msg); diff --git a/drivers/char/ipmi/ipmi_poweroff.c b/drivers/char/ipmi/ipmi_poweroff.c index 8d941db8345..9f2e3be2c5b 100644 --- a/drivers/char/ipmi/ipmi_poweroff.c +++ b/drivers/char/ipmi/ipmi_poweroff.c @@ -43,6 +43,9 @@ #define PFX "IPMI poweroff: " +static void ipmi_po_smi_gone(int if_num); +static void ipmi_po_new_smi(int if_num, struct device *device); + /* Definitions for controlling power off (if the system supports it). It * conveniently matches the IPMI chassis control values. */ #define IPMI_CHASSIS_POWER_DOWN 0 /* power down, the default. */ @@ -51,9 +54,43 @@ /* the IPMI data command */ static int poweroff_powercycle; +/* Which interface to use, -1 means the first we see. */ +static int ifnum_to_use = -1; + +/* Our local state. */ +static int ready; +static ipmi_user_t ipmi_user; +static int ipmi_ifnum; +static void (*specific_poweroff_func)(ipmi_user_t user); + +/* Holds the old poweroff function so we can restore it on removal. */ +static void (*old_poweroff_func)(void); + +static int set_param_ifnum(const char *val, struct kernel_param *kp) +{ + int rv = param_set_int(val, kp); + if (rv) + return rv; + if ((ifnum_to_use < 0) || (ifnum_to_use == ipmi_ifnum)) + return 0; + + ipmi_po_smi_gone(ipmi_ifnum); + ipmi_po_new_smi(ifnum_to_use, NULL); + return 0; +} + +module_param_call(ifnum_to_use, set_param_ifnum, param_get_int, + &ifnum_to_use, 0644); +MODULE_PARM_DESC(ifnum_to_use, "The interface number to use for the watchdog " + "timer. Setting to -1 defaults to the first registered " + "interface"); + /* parameter definition to allow user to flag power cycle */ module_param(poweroff_powercycle, int, 0644); -MODULE_PARM_DESC(poweroff_powercycle, " Set to non-zero to enable power cycle instead of power down. Power cycle is contingent on hardware support, otherwise it defaults back to power down."); +MODULE_PARM_DESC(poweroff_powercycle, + " Set to non-zero to enable power cycle instead of power" + " down. Power cycle is contingent on hardware support," + " otherwise it defaults back to power down."); /* Stuff from the get device id command. */ static unsigned int mfg_id; @@ -61,28 +98,31 @@ static unsigned int prod_id; static unsigned char capabilities; static unsigned char ipmi_version; -/* We use our own messages for this operation, we don't let the system - allocate them, since we may be in a panic situation. The whole - thing is single-threaded, anyway, so multiple messages are not - required. */ +/* + * We use our own messages for this operation, we don't let the system + * allocate them, since we may be in a panic situation. The whole + * thing is single-threaded, anyway, so multiple messages are not + * required. + */ +static atomic_t dummy_count = ATOMIC_INIT(0); static void dummy_smi_free(struct ipmi_smi_msg *msg) { + atomic_dec(&dummy_count); } static void dummy_recv_free(struct ipmi_recv_msg *msg) { + atomic_dec(&dummy_count); } -static struct ipmi_smi_msg halt_smi_msg = -{ +static struct ipmi_smi_msg halt_smi_msg = { .done = dummy_smi_free }; -static struct ipmi_recv_msg halt_recv_msg = -{ +static struct ipmi_recv_msg halt_recv_msg = { .done = dummy_recv_free }; /* - * Code to send a message and wait for the reponse. + * Code to send a message and wait for the response. */ static void receive_handler(struct ipmi_recv_msg *recv_msg, void *handler_data) @@ -93,8 +133,7 @@ static void receive_handler(struct ipmi_recv_msg *recv_msg, void *handler_data) complete(comp); } -static struct ipmi_user_hndl ipmi_poweroff_handler = -{ +static struct ipmi_user_hndl ipmi_poweroff_handler = { .ipmi_recv_hndl = receive_handler }; @@ -118,17 +157,28 @@ static int ipmi_request_wait_for_response(ipmi_user_t user, return halt_recv_msg.msg.data[0]; } -/* We are in run-to-completion mode, no completion is desired. */ +/* Wait for message to complete, spinning. */ static int ipmi_request_in_rc_mode(ipmi_user_t user, struct ipmi_addr *addr, struct kernel_ipmi_msg *send_msg) { int rv; + atomic_set(&dummy_count, 2); rv = ipmi_request_supply_msgs(user, addr, 0, send_msg, NULL, &halt_smi_msg, &halt_recv_msg, 0); - if (rv) + if (rv) { + atomic_set(&dummy_count, 0); return rv; + } + + /* + * Spin until our message is done. + */ + while (atomic_read(&dummy_count) > 0) { + ipmi_poll_interface(user); + cpu_relax(); + } return halt_recv_msg.msg.data[0]; } @@ -142,19 +192,55 @@ static int ipmi_request_in_rc_mode(ipmi_user_t user, #define IPMI_ATCA_GET_ADDR_INFO_CMD 0x01 #define IPMI_PICMG_ID 0 -static int ipmi_atca_detect (ipmi_user_t user) +#define IPMI_NETFN_OEM 0x2e +#define IPMI_ATCA_PPS_GRACEFUL_RESTART 0x11 +#define IPMI_ATCA_PPS_IANA "\x00\x40\x0A" +#define IPMI_MOTOROLA_MANUFACTURER_ID 0x0000A1 +#define IPMI_MOTOROLA_PPS_IPMC_PRODUCT_ID 0x0051 + +static void (*atca_oem_poweroff_hook)(ipmi_user_t user); + +static void pps_poweroff_atca(ipmi_user_t user) +{ + struct ipmi_system_interface_addr smi_addr; + struct kernel_ipmi_msg send_msg; + int rv; + /* + * Configure IPMI address for local access + */ + smi_addr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; + smi_addr.channel = IPMI_BMC_CHANNEL; + smi_addr.lun = 0; + + printk(KERN_INFO PFX "PPS powerdown hook used"); + + send_msg.netfn = IPMI_NETFN_OEM; + send_msg.cmd = IPMI_ATCA_PPS_GRACEFUL_RESTART; + send_msg.data = IPMI_ATCA_PPS_IANA; + send_msg.data_len = 3; + rv = ipmi_request_in_rc_mode(user, + (struct ipmi_addr *) &smi_addr, + &send_msg); + if (rv && rv != IPMI_UNKNOWN_ERR_COMPLETION_CODE) { + printk(KERN_ERR PFX "Unable to send ATCA ," + " IPMI error 0x%x\n", rv); + } + return; +} + +static int ipmi_atca_detect(ipmi_user_t user) { struct ipmi_system_interface_addr smi_addr; struct kernel_ipmi_msg send_msg; int rv; unsigned char data[1]; - /* - * Configure IPMI address for local access - */ - smi_addr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; - smi_addr.channel = IPMI_BMC_CHANNEL; - smi_addr.lun = 0; + /* + * Configure IPMI address for local access + */ + smi_addr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; + smi_addr.channel = IPMI_BMC_CHANNEL; + smi_addr.lun = 0; /* * Use get address info to check and see if we are ATCA @@ -167,22 +253,31 @@ static int ipmi_atca_detect (ipmi_user_t user) rv = ipmi_request_wait_for_response(user, (struct ipmi_addr *) &smi_addr, &send_msg); + + printk(KERN_INFO PFX "ATCA Detect mfg 0x%X prod 0x%X\n", + mfg_id, prod_id); + if ((mfg_id == IPMI_MOTOROLA_MANUFACTURER_ID) + && (prod_id == IPMI_MOTOROLA_PPS_IPMC_PRODUCT_ID)) { + printk(KERN_INFO PFX + "Installing Pigeon Point Systems Poweroff Hook\n"); + atca_oem_poweroff_hook = pps_poweroff_atca; + } return !rv; } -static void ipmi_poweroff_atca (ipmi_user_t user) +static void ipmi_poweroff_atca(ipmi_user_t user) { struct ipmi_system_interface_addr smi_addr; struct kernel_ipmi_msg send_msg; int rv; unsigned char data[4]; - /* - * Configure IPMI address for local access - */ - smi_addr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; - smi_addr.channel = IPMI_BMC_CHANNEL; - smi_addr.lun = 0; + /* + * Configure IPMI address for local access + */ + smi_addr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; + smi_addr.channel = IPMI_BMC_CHANNEL; + smi_addr.lun = 0; printk(KERN_INFO PFX "Powering down via ATCA power command\n"); @@ -196,16 +291,24 @@ static void ipmi_poweroff_atca (ipmi_user_t user) data[2] = 0; /* Power Level */ data[3] = 0; /* Don't change saved presets */ send_msg.data = data; - send_msg.data_len = sizeof (data); + send_msg.data_len = sizeof(data); rv = ipmi_request_in_rc_mode(user, (struct ipmi_addr *) &smi_addr, &send_msg); - if (rv) { + /* + * At this point, the system may be shutting down, and most + * serial drivers (if used) will have interrupts turned off + * it may be better to ignore IPMI_UNKNOWN_ERR_COMPLETION_CODE + * return code + */ + if (rv && rv != IPMI_UNKNOWN_ERR_COMPLETION_CODE) { printk(KERN_ERR PFX "Unable to send ATCA powerdown message," " IPMI error 0x%x\n", rv); goto out; } + if (atca_oem_poweroff_hook) + atca_oem_poweroff_hook(user); out: return; } @@ -226,13 +329,13 @@ static void ipmi_poweroff_atca (ipmi_user_t user) #define IPMI_CPI1_PRODUCT_ID 0x000157 #define IPMI_CPI1_MANUFACTURER_ID 0x0108 -static int ipmi_cpi1_detect (ipmi_user_t user) +static int ipmi_cpi1_detect(ipmi_user_t user) { return ((mfg_id == IPMI_CPI1_MANUFACTURER_ID) && (prod_id == IPMI_CPI1_PRODUCT_ID)); } -static void ipmi_poweroff_cpi1 (ipmi_user_t user) +static void ipmi_poweroff_cpi1(ipmi_user_t user) { struct ipmi_system_interface_addr smi_addr; struct ipmi_ipmb_addr ipmb_addr; @@ -244,12 +347,12 @@ static void ipmi_poweroff_cpi1 (ipmi_user_t user) unsigned char aer_addr; unsigned char aer_lun; - /* - * Configure IPMI address for local access - */ - smi_addr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; - smi_addr.channel = IPMI_BMC_CHANNEL; - smi_addr.lun = 0; + /* + * Configure IPMI address for local access + */ + smi_addr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; + smi_addr.channel = IPMI_BMC_CHANNEL; + smi_addr.lun = 0; printk(KERN_INFO PFX "Powering down via CPI1 power command\n"); @@ -341,7 +444,7 @@ static void ipmi_poweroff_cpi1 (ipmi_user_t user) */ #define DELL_IANA_MFR_ID {0xA2, 0x02, 0x00} -static int ipmi_dell_chassis_detect (ipmi_user_t user) +static int ipmi_dell_chassis_detect(ipmi_user_t user) { const char ipmi_version_major = ipmi_version & 0xF; const char ipmi_version_minor = (ipmi_version >> 4) & 0xF; @@ -360,25 +463,25 @@ static int ipmi_dell_chassis_detect (ipmi_user_t user) #define IPMI_NETFN_CHASSIS_REQUEST 0 #define IPMI_CHASSIS_CONTROL_CMD 0x02 -static int ipmi_chassis_detect (ipmi_user_t user) +static int ipmi_chassis_detect(ipmi_user_t user) { /* Chassis support, use it. */ return (capabilities & 0x80); } -static void ipmi_poweroff_chassis (ipmi_user_t user) +static void ipmi_poweroff_chassis(ipmi_user_t user) { struct ipmi_system_interface_addr smi_addr; struct kernel_ipmi_msg send_msg; int rv; unsigned char data[1]; - /* - * Configure IPMI address for local access - */ - smi_addr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; - smi_addr.channel = IPMI_BMC_CHANNEL; - smi_addr.lun = 0; + /* + * Configure IPMI address for local access + */ + smi_addr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; + smi_addr.channel = IPMI_BMC_CHANNEL; + smi_addr.lun = 0; powercyclefailed: printk(KERN_INFO PFX "Powering %s via IPMI chassis control command\n", @@ -440,25 +543,14 @@ static struct poweroff_function poweroff_functions[] = { / sizeof(struct poweroff_function)) -/* Our local state. */ -static int ready = 0; -static ipmi_user_t ipmi_user; -static void (*specific_poweroff_func)(ipmi_user_t user) = NULL; - -/* Holds the old poweroff function so we can restore it on removal. */ -static void (*old_poweroff_func)(void); - - /* Called on a powerdown request. */ -static void ipmi_poweroff_function (void) +static void ipmi_poweroff_function(void) { if (!ready) return; /* Use run-to-completion mode, since interrupts may be off. */ - ipmi_user_set_run_to_completion(ipmi_user, 1); specific_poweroff_func(ipmi_user); - ipmi_user_set_run_to_completion(ipmi_user, 0); } /* Wait for an IPMI interface to be installed, the first one installed @@ -473,6 +565,9 @@ static void ipmi_po_new_smi(int if_num, struct device *device) if (ready) return; + if ((ifnum_to_use >= 0) && (ifnum_to_use != if_num)) + return; + rv = ipmi_create_user(if_num, &ipmi_poweroff_handler, NULL, &ipmi_user); if (rv) { @@ -481,13 +576,15 @@ static void ipmi_po_new_smi(int if_num, struct device *device) return; } - /* - * Do a get device ide and store some results, since this is + ipmi_ifnum = if_num; + + /* + * Do a get device ide and store some results, since this is * used by several functions. - */ - smi_addr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; - smi_addr.channel = IPMI_BMC_CHANNEL; - smi_addr.lun = 0; + */ + smi_addr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; + smi_addr.channel = IPMI_BMC_CHANNEL; + smi_addr.lun = 0; send_msg.netfn = IPMI_NETFN_APP_REQUEST; send_msg.cmd = IPMI_GET_DEVICE_ID_CMD; @@ -541,13 +638,18 @@ static void ipmi_po_new_smi(int if_num, struct device *device) static void ipmi_po_smi_gone(int if_num) { - /* This can never be called, because once poweroff driver is - registered, the interface can't go away until the power - driver is unregistered. */ + if (!ready) + return; + + if (ipmi_ifnum != if_num) + return; + + ready = 0; + ipmi_destroy_user(ipmi_user); + pm_power_off = old_poweroff_func; } -static struct ipmi_smi_watcher smi_watcher = -{ +static struct ipmi_smi_watcher smi_watcher = { .owner = THIS_MODULE, .new_smi = ipmi_po_new_smi, .smi_gone = ipmi_po_smi_gone @@ -557,27 +659,24 @@ static struct ipmi_smi_watcher smi_watcher = #ifdef CONFIG_PROC_FS #include <linux/sysctl.h> -static ctl_table ipmi_table[] = { - { .ctl_name = DEV_IPMI_POWEROFF_POWERCYCLE, - .procname = "poweroff_powercycle", +static struct ctl_table ipmi_table[] = { + { .procname = "poweroff_powercycle", .data = &poweroff_powercycle, .maxlen = sizeof(poweroff_powercycle), .mode = 0644, - .proc_handler = &proc_dointvec }, + .proc_handler = proc_dointvec }, { } }; -static ctl_table ipmi_dir_table[] = { - { .ctl_name = DEV_IPMI, - .procname = "ipmi", +static struct ctl_table ipmi_dir_table[] = { + { .procname = "ipmi", .mode = 0555, .child = ipmi_table }, { } }; -static ctl_table ipmi_root_table[] = { - { .ctl_name = CTL_DEV, - .procname = "dev", +static struct ctl_table ipmi_root_table[] = { + { .procname = "dev", .mode = 0555, .child = ipmi_dir_table }, { } @@ -589,18 +688,18 @@ static struct ctl_table_header *ipmi_table_header; /* * Startup and shutdown functions. */ -static int ipmi_poweroff_init (void) +static int __init ipmi_poweroff_init(void) { int rv; - printk ("Copyright (C) 2004 MontaVista Software -" - " IPMI Powerdown via sys_reboot.\n"); + printk(KERN_INFO "Copyright (C) 2004 MontaVista Software -" + " IPMI Powerdown via sys_reboot.\n"); if (poweroff_powercycle) printk(KERN_INFO PFX "Power cycle is enabled.\n"); #ifdef CONFIG_PROC_FS - ipmi_table_header = register_sysctl_table(ipmi_root_table, 1); + ipmi_table_header = register_sysctl_table(ipmi_root_table); if (!ipmi_table_header) { printk(KERN_ERR PFX "Unable to register powercycle sysctl\n"); rv = -ENOMEM; @@ -616,14 +715,14 @@ static int ipmi_poweroff_init (void) printk(KERN_ERR PFX "Unable to register SMI watcher: %d\n", rv); goto out_err; } -#endif out_err: +#endif return rv; } #ifdef MODULE -static __exit void ipmi_poweroff_cleanup(void) +static void __exit ipmi_poweroff_cleanup(void) { int rv; diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c index abca98beac1..5d665680ae3 100644 --- a/drivers/char/ipmi/ipmi_si_intf.c +++ b/drivers/char/ipmi/ipmi_si_intf.c @@ -9,6 +9,7 @@ * source@mvista.com * * Copyright 2002 MontaVista Software Inc. + * Copyright 2006 IBM Corp., Christian Krafft <krafft@de.ibm.com> * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -40,8 +41,8 @@ #include <linux/module.h> #include <linux/moduleparam.h> -#include <asm/system.h> #include <linux/sched.h> +#include <linux/seq_file.h> #include <linux/timer.h> #include <linux/errno.h> #include <linux/spinlock.h> @@ -56,11 +57,25 @@ #include <asm/irq.h> #include <linux/interrupt.h> #include <linux/rcupdate.h> +#include <linux/ipmi.h> #include <linux/ipmi_smi.h> #include <asm/io.h> #include "ipmi_si_sm.h" -#include <linux/init.h> #include <linux/dmi.h> +#include <linux/string.h> +#include <linux/ctype.h> +#include <linux/pnp.h> +#include <linux/of_device.h> +#include <linux/of_platform.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> + +#ifdef CONFIG_PARISC +#include <asm/hardware.h> /* for register_parisc_driver() stuff */ +#include <asm/parisc-device.h> +#endif + +#define PFX "ipmi_si: " /* Measure times between events in the driver. */ #undef DEBUG_TIMING @@ -70,7 +85,7 @@ #define SI_USEC_PER_JIFFY (1000000/HZ) #define SI_TIMEOUT_JIFFIES (SI_TIMEOUT_TIME_USEC/SI_USEC_PER_JIFFY) #define SI_SHORT_TIMEOUT_USEC 250 /* .25ms when the SM request a - short timeout */ + short timeout */ enum si_intf_state { SI_NORMAL, @@ -80,7 +95,9 @@ enum si_intf_state { SI_CLEARING_FLAGS_THEN_SET_IRQ, SI_GETTING_MESSAGES, SI_ENABLE_INTERRUPTS1, - SI_ENABLE_INTERRUPTS2 + SI_ENABLE_INTERRUPTS2, + SI_DISABLE_INTERRUPTS1, + SI_DISABLE_INTERRUPTS2 /* FIXME - add watchdog stuff. */ }; @@ -92,51 +109,102 @@ enum si_intf_state { enum si_type { SI_KCS, SI_SMIC, SI_BT }; -static char *si_to_str[] = { "KCS", "SMIC", "BT" }; +static char *si_to_str[] = { "kcs", "smic", "bt" }; + +static char *ipmi_addr_src_to_str[] = { NULL, "hotmod", "hardcoded", "SPMI", + "ACPI", "SMBIOS", "PCI", + "device-tree", "default" }; #define DEVICE_NAME "ipmi_si" -static struct device_driver ipmi_driver = -{ - .name = DEVICE_NAME, - .bus = &platform_bus_type +static struct platform_driver ipmi_driver; + +/* + * Indexes into stats[] in smi_info below. + */ +enum si_stat_indexes { + /* + * Number of times the driver requested a timer while an operation + * was in progress. + */ + SI_STAT_short_timeouts = 0, + + /* + * Number of times the driver requested a timer while nothing was in + * progress. + */ + SI_STAT_long_timeouts, + + /* Number of times the interface was idle while being polled. */ + SI_STAT_idles, + + /* Number of interrupts the driver handled. */ + SI_STAT_interrupts, + + /* Number of time the driver got an ATTN from the hardware. */ + SI_STAT_attentions, + + /* Number of times the driver requested flags from the hardware. */ + SI_STAT_flag_fetches, + + /* Number of times the hardware didn't follow the state machine. */ + SI_STAT_hosed_count, + + /* Number of completed messages. */ + SI_STAT_complete_transactions, + + /* Number of IPMI events received from the hardware. */ + SI_STAT_events, + + /* Number of watchdog pretimeouts. */ + SI_STAT_watchdog_pretimeouts, + + /* Number of asynchronous messages received. */ + SI_STAT_incoming_messages, + + + /* This *must* remain last, add new values above this. */ + SI_NUM_STATS }; -struct smi_info -{ +struct smi_info { int intf_num; ipmi_smi_t intf; struct si_sm_data *si_sm; struct si_sm_handlers *handlers; enum si_type si_type; spinlock_t si_lock; - spinlock_t msg_lock; struct list_head xmit_msgs; struct list_head hp_xmit_msgs; struct ipmi_smi_msg *curr_msg; enum si_intf_state si_state; - /* Used to handle the various types of I/O that can occur with - IPMI */ + /* + * Used to handle the various types of I/O that can occur with + * IPMI + */ struct si_sm_io io; int (*io_setup)(struct smi_info *info); void (*io_cleanup)(struct smi_info *info); int (*irq_setup)(struct smi_info *info); void (*irq_cleanup)(struct smi_info *info); unsigned int io_size; - char *addr_source; /* ACPI, PCI, SMBIOS, hardcode, default. */ + enum ipmi_addr_src addr_source; /* ACPI, PCI, SMBIOS, hardcode, etc. */ void (*addr_source_cleanup)(struct smi_info *info); void *addr_source_data; - /* Per-OEM handler, called from handle_flags(). - Returns 1 when handle_flags() needs to be re-run - or 0 indicating it set si_state itself. - */ + /* + * Per-OEM handler, called from handle_flags(). Returns 1 + * when handle_flags() needs to be re-run or 0 indicating it + * set si_state itself. + */ int (*oem_data_avail_handler)(struct smi_info *smi_info); - /* Flags from the last GET_MSG_FLAGS command, used when an ATTN - is set to hold the flags until we are done handling everything - from the flags. */ + /* + * Flags from the last GET_MSG_FLAGS command, used when an ATTN + * is set to hold the flags until we are done handling everything + * from the flags. + */ #define RECEIVE_MSG_AVAIL 0x01 #define EVENT_MSG_BUFFER_FULL 0x02 #define WDT_PRE_TIMEOUT_INT 0x08 @@ -144,25 +212,34 @@ struct smi_info #define OEM1_DATA_AVAIL 0x40 #define OEM2_DATA_AVAIL 0x80 #define OEM_DATA_AVAIL (OEM0_DATA_AVAIL | \ - OEM1_DATA_AVAIL | \ - OEM2_DATA_AVAIL) + OEM1_DATA_AVAIL | \ + OEM2_DATA_AVAIL) unsigned char msg_flags; - /* If set to true, this will request events the next time the - state machine is idle. */ + /* Does the BMC have an event buffer? */ + bool has_event_buffer; + + /* + * If set to true, this will request events the next time the + * state machine is idle. + */ atomic_t req_events; - /* If true, run the state machine to completion on every send - call. Generally used after a panic to make sure stuff goes - out. */ - int run_to_completion; + /* + * If true, run the state machine to completion on every send + * call. Generally used after a panic to make sure stuff goes + * out. + */ + bool run_to_completion; /* The I/O port of an SI interface. */ int port; - /* The space between start addresses of the two ports. For - instance, if the first port is 0xca2 and the spacing is 4, then - the second port is 0xca6. */ + /* + * The space between start addresses of the two ports. For + * instance, if the first port is 0xca2 and the spacing is 4, then + * the second port is 0xca6. + */ unsigned int spacing; /* zero if no irq; */ @@ -171,17 +248,25 @@ struct smi_info /* The timer for this si. */ struct timer_list si_timer; + /* This flag is set, if the timer is running (timer_pending() isn't enough) */ + bool timer_running; + /* The time (in jiffies) the last timeout occurred at. */ unsigned long last_timeout_jiffies; /* Used to gracefully stop the timer without race conditions. */ atomic_t stop_operation; - /* The driver will disable interrupts when it gets into a - situation where it cannot handle messages due to lack of - memory. Once that situation clears up, it will re-enable - interrupts. */ - int interrupt_disabled; + /* Are we waiting for the events, pretimeouts, received msgs? */ + atomic_t need_watch; + + /* + * The driver will disable interrupts when it gets into a + * situation where it cannot handle messages due to lack of + * memory. Once that situation clears up, it will re-enable + * interrupts. + */ + bool interrupt_disabled; /* From the get device id response... */ struct ipmi_device_id device_id; @@ -190,37 +275,55 @@ struct smi_info struct device *dev; struct platform_device *pdev; - /* True if we allocated the device, false if it came from - * someplace else (like PCI). */ - int dev_registered; + /* + * True if we allocated the device, false if it came from + * someplace else (like PCI). + */ + bool dev_registered; /* Slave address, could be reported from DMI. */ unsigned char slave_addr; /* Counters and things for the proc filesystem. */ - spinlock_t count_lock; - unsigned long short_timeouts; - unsigned long long_timeouts; - unsigned long timeout_restarts; - unsigned long idles; - unsigned long interrupts; - unsigned long attentions; - unsigned long flag_fetches; - unsigned long hosed_count; - unsigned long complete_transactions; - unsigned long events; - unsigned long watchdog_pretimeouts; - unsigned long incoming_messages; - - struct task_struct *thread; + atomic_t stats[SI_NUM_STATS]; + + struct task_struct *thread; struct list_head link; + union ipmi_smi_info_union addr_info; }; +#define smi_inc_stat(smi, stat) \ + atomic_inc(&(smi)->stats[SI_STAT_ ## stat]) +#define smi_get_stat(smi, stat) \ + ((unsigned int) atomic_read(&(smi)->stats[SI_STAT_ ## stat])) + +#define SI_MAX_PARMS 4 + +static int force_kipmid[SI_MAX_PARMS]; +static int num_force_kipmid; +#ifdef CONFIG_PCI +static bool pci_registered; +#endif +#ifdef CONFIG_ACPI +static bool pnp_registered; +#endif +#ifdef CONFIG_PARISC +static bool parisc_registered; +#endif + +static unsigned int kipmid_max_busy_us[SI_MAX_PARMS]; +static int num_max_busy_us; + +static bool unload_when_empty = true; + +static int add_smi(struct smi_info *smi); static int try_smi_init(struct smi_info *smi); +static void cleanup_one_si(struct smi_info *to_clean); +static void cleanup_ipmi_si(void); static ATOMIC_NOTIFIER_HEAD(xaction_notifier_list); -static int register_xaction_notifier(struct notifier_block * nb) +static int register_xaction_notifier(struct notifier_block *nb) { return atomic_notifier_chain_register(&xaction_notifier_list, nb); } @@ -228,21 +331,22 @@ static int register_xaction_notifier(struct notifier_block * nb) static void deliver_recv_msg(struct smi_info *smi_info, struct ipmi_smi_msg *msg) { - /* Deliver the message to the upper layer with the lock - released. */ - spin_unlock(&(smi_info->si_lock)); + /* Deliver the message to the upper layer. */ ipmi_smi_msg_received(smi_info->intf, msg); - spin_lock(&(smi_info->si_lock)); } -static void return_hosed_msg(struct smi_info *smi_info) +static void return_hosed_msg(struct smi_info *smi_info, int cCode) { struct ipmi_smi_msg *msg = smi_info->curr_msg; - /* Make it a reponse */ + if (cCode < 0 || cCode > IPMI_ERR_UNSPECIFIED) + cCode = IPMI_ERR_UNSPECIFIED; + /* else use it as is */ + + /* Make it a response */ msg->rsp[0] = msg->data[0] | 4; msg->rsp[1] = msg->data[1]; - msg->rsp[2] = 0xFF; /* Unknown error. */ + msg->rsp[2] = cCode; msg->rsp_size = 3; smi_info->curr_msg = NULL; @@ -257,10 +361,6 @@ static enum si_sm_result start_next_msg(struct smi_info *smi_info) struct timeval t; #endif - /* No need to save flags, we aleady have interrupts off and we - already hold the SMI lock. */ - spin_lock(&(smi_info->msg_lock)); - /* Pick the high priority queue first. */ if (!list_empty(&(smi_info->hp_xmit_msgs))) { entry = smi_info->hp_xmit_msgs.next; @@ -280,7 +380,7 @@ static enum si_sm_result start_next_msg(struct smi_info *smi_info) link); #ifdef DEBUG_TIMING do_gettimeofday(&t); - printk("**Start2: %d.%9.9d\n", t.tv_sec, t.tv_usec); + printk(KERN_DEBUG "**Start2: %d.%9.9d\n", t.tv_sec, t.tv_usec); #endif err = atomic_notifier_call_chain(&xaction_notifier_list, 0, smi_info); @@ -292,15 +392,12 @@ static enum si_sm_result start_next_msg(struct smi_info *smi_info) smi_info->si_sm, smi_info->curr_msg->data, smi_info->curr_msg->data_size); - if (err) { - return_hosed_msg(smi_info); - } + if (err) + return_hosed_msg(smi_info, err); rv = SI_SM_CALL_WITHOUT_DELAY; } - out: - spin_unlock(&(smi_info->msg_lock)); - + out: return rv; } @@ -308,8 +405,10 @@ static void start_enable_irq(struct smi_info *smi_info) { unsigned char msg[2]; - /* If we are enabling interrupts, we have to tell the - BMC to use them. */ + /* + * If we are enabling interrupts, we have to tell the + * BMC to use them. + */ msg[0] = (IPMI_NETFN_APP_REQUEST << 2); msg[1] = IPMI_GET_BMC_GLOBAL_ENABLES_CMD; @@ -317,6 +416,17 @@ static void start_enable_irq(struct smi_info *smi_info) smi_info->si_state = SI_ENABLE_INTERRUPTS1; } +static void start_disable_irq(struct smi_info *smi_info) +{ + unsigned char msg[2]; + + msg[0] = (IPMI_NETFN_APP_REQUEST << 2); + msg[1] = IPMI_GET_BMC_GLOBAL_ENABLES_CMD; + + smi_info->handlers->start_transaction(smi_info->si_sm, msg, 2); + smi_info->si_state = SI_DISABLE_INTERRUPTS1; +} + static void start_clear_flags(struct smi_info *smi_info) { unsigned char msg[3]; @@ -330,23 +440,34 @@ static void start_clear_flags(struct smi_info *smi_info) smi_info->si_state = SI_CLEARING_FLAGS; } -/* When we have a situtaion where we run out of memory and cannot - allocate messages, we just leave them in the BMC and run the system - polled until we can allocate some memory. Once we have some - memory, we will re-enable the interrupt. */ +static void smi_mod_timer(struct smi_info *smi_info, unsigned long new_val) +{ + smi_info->last_timeout_jiffies = jiffies; + mod_timer(&smi_info->si_timer, new_val); + smi_info->timer_running = true; +} + +/* + * When we have a situtaion where we run out of memory and cannot + * allocate messages, we just leave them in the BMC and run the system + * polled until we can allocate some memory. Once we have some + * memory, we will re-enable the interrupt. + */ static inline void disable_si_irq(struct smi_info *smi_info) { if ((smi_info->irq) && (!smi_info->interrupt_disabled)) { - disable_irq_nosync(smi_info->irq); - smi_info->interrupt_disabled = 1; + start_disable_irq(smi_info); + smi_info->interrupt_disabled = true; + if (!atomic_read(&smi_info->stop_operation)) + smi_mod_timer(smi_info, jiffies + SI_TIMEOUT_JIFFIES); } } static inline void enable_si_irq(struct smi_info *smi_info) { if ((smi_info->irq) && (smi_info->interrupt_disabled)) { - enable_irq(smi_info->irq); - smi_info->interrupt_disabled = 0; + start_enable_irq(smi_info); + smi_info->interrupt_disabled = false; } } @@ -355,15 +476,11 @@ static void handle_flags(struct smi_info *smi_info) retry: if (smi_info->msg_flags & WDT_PRE_TIMEOUT_INT) { /* Watchdog pre-timeout */ - spin_lock(&smi_info->count_lock); - smi_info->watchdog_pretimeouts++; - spin_unlock(&smi_info->count_lock); + smi_inc_stat(smi_info, watchdog_pretimeouts); start_clear_flags(smi_info); smi_info->msg_flags &= ~WDT_PRE_TIMEOUT_INT; - spin_unlock(&(smi_info->si_lock)); ipmi_smi_watchdog_pretimeout(smi_info->intf); - spin_lock(&(smi_info->si_lock)); } else if (smi_info->msg_flags & RECEIVE_MSG_AVAIL) { /* Messages available. */ smi_info->curr_msg = ipmi_alloc_smi_msg(); @@ -403,12 +520,11 @@ static void handle_flags(struct smi_info *smi_info) smi_info->curr_msg->data_size); smi_info->si_state = SI_GETTING_EVENTS; } else if (smi_info->msg_flags & OEM_DATA_AVAIL && - smi_info->oem_data_avail_handler) { + smi_info->oem_data_avail_handler) { if (smi_info->oem_data_avail_handler(smi_info)) goto retry; - } else { + } else smi_info->si_state = SI_NORMAL; - } } static void handle_transaction_done(struct smi_info *smi_info) @@ -418,7 +534,7 @@ static void handle_transaction_done(struct smi_info *smi_info) struct timeval t; do_gettimeofday(&t); - printk("**Done: %d.%9.9d\n", t.tv_sec, t.tv_usec); + printk(KERN_DEBUG "**Done: %d.%9.9d\n", t.tv_sec, t.tv_usec); #endif switch (smi_info->si_state) { case SI_NORMAL: @@ -431,9 +547,11 @@ static void handle_transaction_done(struct smi_info *smi_info) smi_info->curr_msg->rsp, IPMI_MAX_MSG_LENGTH); - /* Do this here becase deliver_recv_msg() releases the - lock, and a new message can be put in during the - time the lock is released. */ + /* + * Do this here becase deliver_recv_msg() releases the + * lock, and a new message can be put in during the + * time the lock is released. + */ msg = smi_info->curr_msg; smi_info->curr_msg = NULL; deliver_recv_msg(smi_info, msg); @@ -447,12 +565,13 @@ static void handle_transaction_done(struct smi_info *smi_info) /* We got the flags from the SMI, now handle them. */ len = smi_info->handlers->get_result(smi_info->si_sm, msg, 4); if (msg[2] != 0) { - /* Error fetching flags, just give up for - now. */ + /* Error fetching flags, just give up for now. */ smi_info->si_state = SI_NORMAL; } else if (len < 4) { - /* Hmm, no flags. That's technically illegal, but - don't use uninitialized data. */ + /* + * Hmm, no flags. That's technically illegal, but + * don't use uninitialized data. + */ smi_info->si_state = SI_NORMAL; } else { smi_info->msg_flags = msg[3]; @@ -470,9 +589,8 @@ static void handle_transaction_done(struct smi_info *smi_info) smi_info->handlers->get_result(smi_info->si_sm, msg, 3); if (msg[2] != 0) { /* Error clearing flags */ - printk(KERN_WARNING - "ipmi_si: Error clearing flags: %2.2x\n", - msg[2]); + dev_warn(smi_info->dev, + "Error clearing flags: %2.2x\n", msg[2]); } if (smi_info->si_state == SI_CLEARING_FLAGS_THEN_SET_IRQ) start_enable_irq(smi_info); @@ -489,9 +607,11 @@ static void handle_transaction_done(struct smi_info *smi_info) smi_info->curr_msg->rsp, IPMI_MAX_MSG_LENGTH); - /* Do this here becase deliver_recv_msg() releases the - lock, and a new message can be put in during the - time the lock is released. */ + /* + * Do this here becase deliver_recv_msg() releases the + * lock, and a new message can be put in during the + * time the lock is released. + */ msg = smi_info->curr_msg; smi_info->curr_msg = NULL; if (msg->rsp[2] != 0) { @@ -502,14 +622,14 @@ static void handle_transaction_done(struct smi_info *smi_info) smi_info->msg_flags &= ~EVENT_MSG_BUFFER_FULL; handle_flags(smi_info); } else { - spin_lock(&smi_info->count_lock); - smi_info->events++; - spin_unlock(&smi_info->count_lock); - - /* Do this before we deliver the message - because delivering the message releases the - lock and something else can mess with the - state. */ + smi_inc_stat(smi_info, events); + + /* + * Do this before we deliver the message + * because delivering the message releases the + * lock and something else can mess with the + * state. + */ handle_flags(smi_info); deliver_recv_msg(smi_info, msg); @@ -525,9 +645,11 @@ static void handle_transaction_done(struct smi_info *smi_info) smi_info->curr_msg->rsp, IPMI_MAX_MSG_LENGTH); - /* Do this here becase deliver_recv_msg() releases the - lock, and a new message can be put in during the - time the lock is released. */ + /* + * Do this here becase deliver_recv_msg() releases the + * lock, and a new message can be put in during the + * time the lock is released. + */ msg = smi_info->curr_msg; smi_info->curr_msg = NULL; if (msg->rsp[2] != 0) { @@ -538,14 +660,14 @@ static void handle_transaction_done(struct smi_info *smi_info) smi_info->msg_flags &= ~RECEIVE_MSG_AVAIL; handle_flags(smi_info); } else { - spin_lock(&smi_info->count_lock); - smi_info->incoming_messages++; - spin_unlock(&smi_info->count_lock); - - /* Do this before we deliver the message - because delivering the message releases the - lock and something else can mess with the - state. */ + smi_inc_stat(smi_info, incoming_messages); + + /* + * Do this before we deliver the message + * because delivering the message releases the + * lock and something else can mess with the + * state. + */ handle_flags(smi_info); deliver_recv_msg(smi_info, msg); @@ -560,14 +682,17 @@ static void handle_transaction_done(struct smi_info *smi_info) /* We got the flags from the SMI, now handle them. */ smi_info->handlers->get_result(smi_info->si_sm, msg, 4); if (msg[2] != 0) { - printk(KERN_WARNING - "ipmi_si: Could not enable interrupts" - ", failed get, using polled mode.\n"); + dev_warn(smi_info->dev, + "Couldn't get irq info: %x.\n", msg[2]); + dev_warn(smi_info->dev, + "Maybe ok, but ipmi might run very slowly.\n"); smi_info->si_state = SI_NORMAL; } else { msg[0] = (IPMI_NETFN_APP_REQUEST << 2); msg[1] = IPMI_SET_BMC_GLOBAL_ENABLES_CMD; - msg[2] = msg[3] | 1; /* enable msg queue int */ + msg[2] = (msg[3] | + IPMI_BMC_RCV_MSG_INTR | + IPMI_BMC_EVT_MSG_INTR); smi_info->handlers->start_transaction( smi_info->si_sm, msg, 3); smi_info->si_state = SI_ENABLE_INTERRUPTS2; @@ -582,9 +707,48 @@ static void handle_transaction_done(struct smi_info *smi_info) /* We got the flags from the SMI, now handle them. */ smi_info->handlers->get_result(smi_info->si_sm, msg, 4); if (msg[2] != 0) { - printk(KERN_WARNING - "ipmi_si: Could not enable interrupts" - ", failed set, using polled mode.\n"); + dev_warn(smi_info->dev, + "Couldn't set irq info: %x.\n", msg[2]); + dev_warn(smi_info->dev, + "Maybe ok, but ipmi might run very slowly.\n"); + } else + smi_info->interrupt_disabled = false; + smi_info->si_state = SI_NORMAL; + break; + } + + case SI_DISABLE_INTERRUPTS1: + { + unsigned char msg[4]; + + /* We got the flags from the SMI, now handle them. */ + smi_info->handlers->get_result(smi_info->si_sm, msg, 4); + if (msg[2] != 0) { + dev_warn(smi_info->dev, "Could not disable interrupts" + ", failed get.\n"); + smi_info->si_state = SI_NORMAL; + } else { + msg[0] = (IPMI_NETFN_APP_REQUEST << 2); + msg[1] = IPMI_SET_BMC_GLOBAL_ENABLES_CMD; + msg[2] = (msg[3] & + ~(IPMI_BMC_RCV_MSG_INTR | + IPMI_BMC_EVT_MSG_INTR)); + smi_info->handlers->start_transaction( + smi_info->si_sm, msg, 3); + smi_info->si_state = SI_DISABLE_INTERRUPTS2; + } + break; + } + + case SI_DISABLE_INTERRUPTS2: + { + unsigned char msg[4]; + + /* We got the flags from the SMI, now handle them. */ + smi_info->handlers->get_result(smi_info->si_sm, msg, 4); + if (msg[2] != 0) { + dev_warn(smi_info->dev, "Could not disable interrupts" + ", failed set.\n"); } smi_info->si_state = SI_NORMAL; break; @@ -592,68 +756,70 @@ static void handle_transaction_done(struct smi_info *smi_info) } } -/* Called on timeouts and events. Timeouts should pass the elapsed - time, interrupts should pass in zero. */ +/* + * Called on timeouts and events. Timeouts should pass the elapsed + * time, interrupts should pass in zero. Must be called with + * si_lock held and interrupts disabled. + */ static enum si_sm_result smi_event_handler(struct smi_info *smi_info, int time) { enum si_sm_result si_sm_result; restart: - /* There used to be a loop here that waited a little while - (around 25us) before giving up. That turned out to be - pointless, the minimum delays I was seeing were in the 300us - range, which is far too long to wait in an interrupt. So - we just run until the state machine tells us something - happened or it needs a delay. */ + /* + * There used to be a loop here that waited a little while + * (around 25us) before giving up. That turned out to be + * pointless, the minimum delays I was seeing were in the 300us + * range, which is far too long to wait in an interrupt. So + * we just run until the state machine tells us something + * happened or it needs a delay. + */ si_sm_result = smi_info->handlers->event(smi_info->si_sm, time); time = 0; while (si_sm_result == SI_SM_CALL_WITHOUT_DELAY) - { si_sm_result = smi_info->handlers->event(smi_info->si_sm, 0); - } - if (si_sm_result == SI_SM_TRANSACTION_COMPLETE) - { - spin_lock(&smi_info->count_lock); - smi_info->complete_transactions++; - spin_unlock(&smi_info->count_lock); + if (si_sm_result == SI_SM_TRANSACTION_COMPLETE) { + smi_inc_stat(smi_info, complete_transactions); handle_transaction_done(smi_info); si_sm_result = smi_info->handlers->event(smi_info->si_sm, 0); - } - else if (si_sm_result == SI_SM_HOSED) - { - spin_lock(&smi_info->count_lock); - smi_info->hosed_count++; - spin_unlock(&smi_info->count_lock); + } else if (si_sm_result == SI_SM_HOSED) { + smi_inc_stat(smi_info, hosed_count); - /* Do the before return_hosed_msg, because that - releases the lock. */ + /* + * Do the before return_hosed_msg, because that + * releases the lock. + */ smi_info->si_state = SI_NORMAL; if (smi_info->curr_msg != NULL) { - /* If we were handling a user message, format - a response to send to the upper layer to - tell it about the error. */ - return_hosed_msg(smi_info); + /* + * If we were handling a user message, format + * a response to send to the upper layer to + * tell it about the error. + */ + return_hosed_msg(smi_info, IPMI_ERR_UNSPECIFIED); } si_sm_result = smi_info->handlers->event(smi_info->si_sm, 0); } - /* We prefer handling attn over new messages. */ - if (si_sm_result == SI_SM_ATTN) - { + /* + * We prefer handling attn over new messages. But don't do + * this if there is not yet an upper layer to handle anything. + */ + if (likely(smi_info->intf) && si_sm_result == SI_SM_ATTN) { unsigned char msg[2]; - spin_lock(&smi_info->count_lock); - smi_info->attentions++; - spin_unlock(&smi_info->count_lock); + smi_inc_stat(smi_info, attentions); - /* Got a attn, send down a get message flags to see - what's causing it. It would be better to handle - this in the upper layer, but due to the way - interrupts work with the SMI, that's not really - possible. */ + /* + * Got a attn, send down a get message flags to see + * what's causing it. It would be better to handle + * this in the upper layer, but due to the way + * interrupts work with the SMI, that's not really + * possible. + */ msg[0] = (IPMI_NETFN_APP_REQUEST << 2); msg[1] = IPMI_GET_MSG_FLAGS_CMD; @@ -665,39 +831,53 @@ static enum si_sm_result smi_event_handler(struct smi_info *smi_info, /* If we are currently idle, try to start the next message. */ if (si_sm_result == SI_SM_IDLE) { - spin_lock(&smi_info->count_lock); - smi_info->idles++; - spin_unlock(&smi_info->count_lock); + smi_inc_stat(smi_info, idles); si_sm_result = start_next_msg(smi_info); if (si_sm_result != SI_SM_IDLE) goto restart; - } + } if ((si_sm_result == SI_SM_IDLE) - && (atomic_read(&smi_info->req_events))) - { - /* We are idle and the upper layer requested that I fetch - events, so do so. */ - unsigned char msg[2]; + && (atomic_read(&smi_info->req_events))) { + /* + * We are idle and the upper layer requested that I fetch + * events, so do so. + */ + atomic_set(&smi_info->req_events, 0); - spin_lock(&smi_info->count_lock); - smi_info->flag_fetches++; - spin_unlock(&smi_info->count_lock); + smi_info->curr_msg = ipmi_alloc_smi_msg(); + if (!smi_info->curr_msg) + goto out; - atomic_set(&smi_info->req_events, 0); - msg[0] = (IPMI_NETFN_APP_REQUEST << 2); - msg[1] = IPMI_GET_MSG_FLAGS_CMD; + smi_info->curr_msg->data[0] = (IPMI_NETFN_APP_REQUEST << 2); + smi_info->curr_msg->data[1] = IPMI_READ_EVENT_MSG_BUFFER_CMD; + smi_info->curr_msg->data_size = 2; smi_info->handlers->start_transaction( - smi_info->si_sm, msg, 2); - smi_info->si_state = SI_GETTING_FLAGS; + smi_info->si_sm, + smi_info->curr_msg->data, + smi_info->curr_msg->data_size); + smi_info->si_state = SI_GETTING_EVENTS; goto restart; } - + out: return si_sm_result; } +static void check_start_timer_thread(struct smi_info *smi_info) +{ + if (smi_info->si_state == SI_NORMAL && smi_info->curr_msg == NULL) { + smi_mod_timer(smi_info, jiffies + SI_TIMEOUT_JIFFIES); + + if (smi_info->thread) + wake_up_process(smi_info->thread); + + start_next_msg(smi_info); + smi_event_handler(smi_info, 0); + } +} + static void sender(void *send_info, struct ipmi_smi_msg *msg, int priority) @@ -709,56 +889,56 @@ static void sender(void *send_info, struct timeval t; #endif - spin_lock_irqsave(&(smi_info->msg_lock), flags); + if (atomic_read(&smi_info->stop_operation)) { + msg->rsp[0] = msg->data[0] | 4; + msg->rsp[1] = msg->data[1]; + msg->rsp[2] = IPMI_ERR_UNSPECIFIED; + msg->rsp_size = 3; + deliver_recv_msg(smi_info, msg); + return; + } + #ifdef DEBUG_TIMING do_gettimeofday(&t); printk("**Enqueue: %d.%9.9d\n", t.tv_sec, t.tv_usec); #endif if (smi_info->run_to_completion) { - /* If we are running to completion, then throw it in - the list and run transactions until everything is - clear. Priority doesn't matter here. */ + /* + * If we are running to completion, then throw it in + * the list and run transactions until everything is + * clear. Priority doesn't matter here. + */ + + /* + * Run to completion means we are single-threaded, no + * need for locks. + */ list_add_tail(&(msg->link), &(smi_info->xmit_msgs)); - /* We have to release the msg lock and claim the smi - lock in this case, because of race conditions. */ - spin_unlock_irqrestore(&(smi_info->msg_lock), flags); - - spin_lock_irqsave(&(smi_info->si_lock), flags); result = smi_event_handler(smi_info, 0); while (result != SI_SM_IDLE) { udelay(SI_SHORT_TIMEOUT_USEC); result = smi_event_handler(smi_info, SI_SHORT_TIMEOUT_USEC); } - spin_unlock_irqrestore(&(smi_info->si_lock), flags); return; - } else { - if (priority > 0) { - list_add_tail(&(msg->link), &(smi_info->hp_xmit_msgs)); - } else { - list_add_tail(&(msg->link), &(smi_info->xmit_msgs)); - } } - spin_unlock_irqrestore(&(smi_info->msg_lock), flags); - spin_lock_irqsave(&(smi_info->si_lock), flags); - if ((smi_info->si_state == SI_NORMAL) - && (smi_info->curr_msg == NULL)) - { - start_next_msg(smi_info); - } - spin_unlock_irqrestore(&(smi_info->si_lock), flags); + spin_lock_irqsave(&smi_info->si_lock, flags); + if (priority > 0) + list_add_tail(&msg->link, &smi_info->hp_xmit_msgs); + else + list_add_tail(&msg->link, &smi_info->xmit_msgs); + + check_start_timer_thread(smi_info); + spin_unlock_irqrestore(&smi_info->si_lock, flags); } -static void set_run_to_completion(void *send_info, int i_run_to_completion) +static void set_run_to_completion(void *send_info, bool i_run_to_completion) { struct smi_info *smi_info = send_info; enum si_sm_result result; - unsigned long flags; - - spin_lock_irqsave(&(smi_info->si_lock), flags); smi_info->run_to_completion = i_run_to_completion; if (i_run_to_completion) { @@ -769,27 +949,97 @@ static void set_run_to_completion(void *send_info, int i_run_to_completion) SI_SHORT_TIMEOUT_USEC); } } +} - spin_unlock_irqrestore(&(smi_info->si_lock), flags); +/* + * Use -1 in the nsec value of the busy waiting timespec to tell that + * we are spinning in kipmid looking for something and not delaying + * between checks + */ +static inline void ipmi_si_set_not_busy(struct timespec *ts) +{ + ts->tv_nsec = -1; +} +static inline int ipmi_si_is_busy(struct timespec *ts) +{ + return ts->tv_nsec != -1; } +static int ipmi_thread_busy_wait(enum si_sm_result smi_result, + const struct smi_info *smi_info, + struct timespec *busy_until) +{ + unsigned int max_busy_us = 0; + + if (smi_info->intf_num < num_max_busy_us) + max_busy_us = kipmid_max_busy_us[smi_info->intf_num]; + if (max_busy_us == 0 || smi_result != SI_SM_CALL_WITH_DELAY) + ipmi_si_set_not_busy(busy_until); + else if (!ipmi_si_is_busy(busy_until)) { + getnstimeofday(busy_until); + timespec_add_ns(busy_until, max_busy_us*NSEC_PER_USEC); + } else { + struct timespec now; + getnstimeofday(&now); + if (unlikely(timespec_compare(&now, busy_until) > 0)) { + ipmi_si_set_not_busy(busy_until); + return 0; + } + } + return 1; +} + + +/* + * A busy-waiting loop for speeding up IPMI operation. + * + * Lousy hardware makes this hard. This is only enabled for systems + * that are not BT and do not have interrupts. It starts spinning + * when an operation is complete or until max_busy tells it to stop + * (if that is enabled). See the paragraph on kimid_max_busy_us in + * Documentation/IPMI.txt for details. + */ static int ipmi_thread(void *data) { struct smi_info *smi_info = data; unsigned long flags; enum si_sm_result smi_result; + struct timespec busy_until; - set_user_nice(current, 19); + ipmi_si_set_not_busy(&busy_until); + set_user_nice(current, MAX_NICE); while (!kthread_should_stop()) { + int busy_wait; + spin_lock_irqsave(&(smi_info->si_lock), flags); smi_result = smi_event_handler(smi_info, 0); + + /* + * If the driver is doing something, there is a possible + * race with the timer. If the timer handler see idle, + * and the thread here sees something else, the timer + * handler won't restart the timer even though it is + * required. So start it here if necessary. + */ + if (smi_result != SI_SM_IDLE && !smi_info->timer_running) + smi_mod_timer(smi_info, jiffies + SI_TIMEOUT_JIFFIES); + spin_unlock_irqrestore(&(smi_info->si_lock), flags); - if (smi_result == SI_SM_CALL_WITHOUT_DELAY) { - /* do nothing */ - } - else if (smi_result == SI_SM_CALL_WITH_DELAY) + busy_wait = ipmi_thread_busy_wait(smi_result, smi_info, + &busy_until); + if (smi_result == SI_SM_CALL_WITHOUT_DELAY) + ; /* do nothing */ + else if (smi_result == SI_SM_CALL_WITH_DELAY && busy_wait) schedule(); - else + else if (smi_result == SI_SM_IDLE) { + if (atomic_read(&smi_info->need_watch)) { + schedule_timeout_interruptible(100); + } else { + /* Wait to be woken up when we are needed. */ + __set_current_state(TASK_INTERRUPTIBLE); + schedule(); + } + } else schedule_timeout_interruptible(1); } return 0; @@ -799,18 +1049,44 @@ static int ipmi_thread(void *data) static void poll(void *send_info) { struct smi_info *smi_info = send_info; + unsigned long flags = 0; + bool run_to_completion = smi_info->run_to_completion; - smi_event_handler(smi_info, 0); + /* + * Make sure there is some delay in the poll loop so we can + * drive time forward and timeout things. + */ + udelay(10); + if (!run_to_completion) + spin_lock_irqsave(&smi_info->si_lock, flags); + smi_event_handler(smi_info, 10); + if (!run_to_completion) + spin_unlock_irqrestore(&smi_info->si_lock, flags); } static void request_events(void *send_info) { struct smi_info *smi_info = send_info; + if (atomic_read(&smi_info->stop_operation) || + !smi_info->has_event_buffer) + return; + atomic_set(&smi_info->req_events, 1); } -static int initialized = 0; +static void set_need_watch(void *send_info, bool enable) +{ + struct smi_info *smi_info = send_info; + unsigned long flags; + + atomic_set(&smi_info->need_watch, enable); + spin_lock_irqsave(&smi_info->si_lock, flags); + check_start_timer_thread(smi_info); + spin_unlock_irqrestore(&smi_info->si_lock, flags); +} + +static int initialized; static void smi_timeout(unsigned long data) { @@ -819,55 +1095,49 @@ static void smi_timeout(unsigned long data) unsigned long flags; unsigned long jiffies_now; long time_diff; + long timeout; #ifdef DEBUG_TIMING struct timeval t; #endif - if (atomic_read(&smi_info->stop_operation)) - return; - spin_lock_irqsave(&(smi_info->si_lock), flags); #ifdef DEBUG_TIMING do_gettimeofday(&t); - printk("**Timer: %d.%9.9d\n", t.tv_sec, t.tv_usec); + printk(KERN_DEBUG "**Timer: %d.%9.9d\n", t.tv_sec, t.tv_usec); #endif jiffies_now = jiffies; time_diff = (((long)jiffies_now - (long)smi_info->last_timeout_jiffies) * SI_USEC_PER_JIFFY); smi_result = smi_event_handler(smi_info, time_diff); - spin_unlock_irqrestore(&(smi_info->si_lock), flags); - - smi_info->last_timeout_jiffies = jiffies_now; - if ((smi_info->irq) && (!smi_info->interrupt_disabled)) { /* Running with interrupts, only do long timeouts. */ - smi_info->si_timer.expires = jiffies + SI_TIMEOUT_JIFFIES; - spin_lock_irqsave(&smi_info->count_lock, flags); - smi_info->long_timeouts++; - spin_unlock_irqrestore(&smi_info->count_lock, flags); - goto do_add_timer; + timeout = jiffies + SI_TIMEOUT_JIFFIES; + smi_inc_stat(smi_info, long_timeouts); + goto do_mod_timer; } - /* If the state machine asks for a short delay, then shorten - the timer timeout. */ + /* + * If the state machine asks for a short delay, then shorten + * the timer timeout. + */ if (smi_result == SI_SM_CALL_WITH_DELAY) { - spin_lock_irqsave(&smi_info->count_lock, flags); - smi_info->short_timeouts++; - spin_unlock_irqrestore(&smi_info->count_lock, flags); - smi_info->si_timer.expires = jiffies + 1; + smi_inc_stat(smi_info, short_timeouts); + timeout = jiffies + 1; } else { - spin_lock_irqsave(&smi_info->count_lock, flags); - smi_info->long_timeouts++; - spin_unlock_irqrestore(&smi_info->count_lock, flags); - smi_info->si_timer.expires = jiffies + SI_TIMEOUT_JIFFIES; + smi_inc_stat(smi_info, long_timeouts); + timeout = jiffies + SI_TIMEOUT_JIFFIES; } - do_add_timer: - add_timer(&(smi_info->si_timer)); + do_mod_timer: + if (smi_result != SI_SM_IDLE) + smi_mod_timer(smi_info, timeout); + else + smi_info->timer_running = false; + spin_unlock_irqrestore(&(smi_info->si_lock), flags); } -static irqreturn_t si_irq_handler(int irq, void *data, struct pt_regs *regs) +static irqreturn_t si_irq_handler(int irq, void *data) { struct smi_info *smi_info = data; unsigned long flags; @@ -877,53 +1147,63 @@ static irqreturn_t si_irq_handler(int irq, void *data, struct pt_regs *regs) spin_lock_irqsave(&(smi_info->si_lock), flags); - spin_lock(&smi_info->count_lock); - smi_info->interrupts++; - spin_unlock(&smi_info->count_lock); - - if (atomic_read(&smi_info->stop_operation)) - goto out; + smi_inc_stat(smi_info, interrupts); #ifdef DEBUG_TIMING do_gettimeofday(&t); - printk("**Interrupt: %d.%9.9d\n", t.tv_sec, t.tv_usec); + printk(KERN_DEBUG "**Interrupt: %d.%9.9d\n", t.tv_sec, t.tv_usec); #endif smi_event_handler(smi_info, 0); - out: spin_unlock_irqrestore(&(smi_info->si_lock), flags); return IRQ_HANDLED; } -static irqreturn_t si_bt_irq_handler(int irq, void *data, struct pt_regs *regs) +static irqreturn_t si_bt_irq_handler(int irq, void *data) { struct smi_info *smi_info = data; /* We need to clear the IRQ flag for the BT interface. */ smi_info->io.outputb(&smi_info->io, IPMI_BT_INTMASK_REG, IPMI_BT_INTMASK_CLEAR_IRQ_BIT | IPMI_BT_INTMASK_ENABLE_IRQ_BIT); - return si_irq_handler(irq, data, regs); + return si_irq_handler(irq, data); } static int smi_start_processing(void *send_info, ipmi_smi_t intf) { struct smi_info *new_smi = send_info; + int enable = 0; new_smi->intf = intf; + /* Try to claim any interrupts. */ + if (new_smi->irq_setup) + new_smi->irq_setup(new_smi); + /* Set up the timer that drives the interface. */ setup_timer(&new_smi->si_timer, smi_timeout, (long)new_smi); - new_smi->last_timeout_jiffies = jiffies; - mod_timer(&new_smi->si_timer, jiffies + SI_TIMEOUT_JIFFIES); + smi_mod_timer(new_smi, jiffies + SI_TIMEOUT_JIFFIES); - if (new_smi->si_type != SI_BT) { + /* + * Check if the user forcefully enabled the daemon. + */ + if (new_smi->intf_num < num_force_kipmid) + enable = force_kipmid[new_smi->intf_num]; + /* + * The BT interface is efficient enough to not need a thread, + * and there is no need for a thread if we have interrupts. + */ + else if ((new_smi->si_type != SI_BT) && (!new_smi->irq)) + enable = 1; + + if (enable) { new_smi->thread = kthread_run(ipmi_thread, new_smi, "kipmi%d", new_smi->intf_num); if (IS_ERR(new_smi->thread)) { - printk(KERN_NOTICE "ipmi_si_intf: Could not start" - " kernel thread due to error %ld, only using" - " timers to drive the interface\n", - PTR_ERR(new_smi->thread)); + dev_notice(new_smi->dev, "Could not start" + " kernel thread due to error %ld, only using" + " timers to drive the interface\n", + PTR_ERR(new_smi->thread)); new_smi->thread = NULL; } } @@ -931,46 +1211,109 @@ static int smi_start_processing(void *send_info, return 0; } -static struct ipmi_smi_handlers handlers = +static int get_smi_info(void *send_info, struct ipmi_smi_info *data) { + struct smi_info *smi = send_info; + + data->addr_src = smi->addr_source; + data->dev = smi->dev; + data->addr_info = smi->addr_info; + get_device(smi->dev); + + return 0; +} + +static void set_maintenance_mode(void *send_info, bool enable) +{ + struct smi_info *smi_info = send_info; + + if (!enable) + atomic_set(&smi_info->req_events, 0); +} + +static struct ipmi_smi_handlers handlers = { .owner = THIS_MODULE, .start_processing = smi_start_processing, + .get_smi_info = get_smi_info, .sender = sender, .request_events = request_events, + .set_need_watch = set_need_watch, + .set_maintenance_mode = set_maintenance_mode, .set_run_to_completion = set_run_to_completion, .poll = poll, }; -/* There can be 4 IO ports passed in (with or without IRQs), 4 addresses, - a default IO port, and 1 ACPI/SPMI address. That sets SI_MAX_DRIVERS */ +/* + * There can be 4 IO ports passed in (with or without IRQs), 4 addresses, + * a default IO port, and 1 ACPI/SPMI address. That sets SI_MAX_DRIVERS. + */ -#define SI_MAX_PARMS 4 static LIST_HEAD(smi_infos); static DEFINE_MUTEX(smi_infos_lock); static int smi_num; /* Used to sequence the SMIs */ #define DEFAULT_REGSPACING 1 +#define DEFAULT_REGSIZE 1 -static int si_trydefaults = 1; +#ifdef CONFIG_ACPI +static bool si_tryacpi = 1; +#endif +#ifdef CONFIG_DMI +static bool si_trydmi = 1; +#endif +static bool si_tryplatform = 1; +#ifdef CONFIG_PCI +static bool si_trypci = 1; +#endif +static bool si_trydefaults = IS_ENABLED(CONFIG_IPMI_SI_PROBE_DEFAULTS); static char *si_type[SI_MAX_PARMS]; #define MAX_SI_TYPE_STR 30 static char si_type_str[MAX_SI_TYPE_STR]; static unsigned long addrs[SI_MAX_PARMS]; -static int num_addrs; +static unsigned int num_addrs; static unsigned int ports[SI_MAX_PARMS]; -static int num_ports; +static unsigned int num_ports; static int irqs[SI_MAX_PARMS]; -static int num_irqs; +static unsigned int num_irqs; static int regspacings[SI_MAX_PARMS]; -static int num_regspacings = 0; +static unsigned int num_regspacings; static int regsizes[SI_MAX_PARMS]; -static int num_regsizes = 0; +static unsigned int num_regsizes; static int regshifts[SI_MAX_PARMS]; -static int num_regshifts = 0; -static int slave_addrs[SI_MAX_PARMS]; -static int num_slave_addrs = 0; +static unsigned int num_regshifts; +static int slave_addrs[SI_MAX_PARMS]; /* Leaving 0 chooses the default value */ +static unsigned int num_slave_addrs; +#define IPMI_IO_ADDR_SPACE 0 +#define IPMI_MEM_ADDR_SPACE 1 +static char *addr_space_to_str[] = { "i/o", "mem" }; + +static int hotmod_handler(const char *val, struct kernel_param *kp); +module_param_call(hotmod, hotmod_handler, NULL, NULL, 0200); +MODULE_PARM_DESC(hotmod, "Add and remove interfaces. See" + " Documentation/IPMI.txt in the kernel sources for the" + " gory details."); + +#ifdef CONFIG_ACPI +module_param_named(tryacpi, si_tryacpi, bool, 0); +MODULE_PARM_DESC(tryacpi, "Setting this to zero will disable the" + " default scan of the interfaces identified via ACPI"); +#endif +#ifdef CONFIG_DMI +module_param_named(trydmi, si_trydmi, bool, 0); +MODULE_PARM_DESC(trydmi, "Setting this to zero will disable the" + " default scan of the interfaces identified via DMI"); +#endif +module_param_named(tryplatform, si_tryplatform, bool, 0); +MODULE_PARM_DESC(tryacpi, "Setting this to zero will disable the" + " default scan of the interfaces identified via platform" + " interfaces like openfirmware"); +#ifdef CONFIG_PCI +module_param_named(trypci, si_trypci, bool, 0); +MODULE_PARM_DESC(tryacpi, "Setting this to zero will disable the" + " default scan of the interfaces identified via pci"); +#endif module_param_named(trydefaults, si_trydefaults, bool, 0); MODULE_PARM_DESC(trydefaults, "Setting this to 'false' will disable the" " default scan of the KCS and SMIC interface at the standard" @@ -980,12 +1323,12 @@ MODULE_PARM_DESC(type, "Defines the type of each interface, each" " interface separated by commas. The types are 'kcs'," " 'smic', and 'bt'. For example si_type=kcs,bt will set" " the first interface to kcs and the second to bt"); -module_param_array(addrs, long, &num_addrs, 0); +module_param_array(addrs, ulong, &num_addrs, 0); MODULE_PARM_DESC(addrs, "Sets the memory address of each interface, the" " addresses separated by commas. Only use if an interface" " is in memory. Otherwise, set it to zero or leave" " it blank."); -module_param_array(ports, int, &num_ports, 0); +module_param_array(ports, uint, &num_ports, 0); MODULE_PARM_DESC(ports, "Sets the port address of each interface, the" " addresses separated by commas. Only use if an interface" " is a port. Otherwise, set it to zero or leave" @@ -1017,12 +1360,21 @@ MODULE_PARM_DESC(slave_addrs, "Set the default IPMB slave address for" " the controller. Normally this is 0x20, but can be" " overridden by this parm. This is an array indexed" " by interface number."); +module_param_array(force_kipmid, int, &num_force_kipmid, 0); +MODULE_PARM_DESC(force_kipmid, "Force the kipmi daemon to be enabled (1) or" + " disabled(0). Normally the IPMI driver auto-detects" + " this, but the value may be overridden by this parm."); +module_param(unload_when_empty, bool, 0); +MODULE_PARM_DESC(unload_when_empty, "Unload the module if no interfaces are" + " specified or found, default is 1. Setting to 0" + " is useful for hot add of devices using hotmod."); +module_param_array(kipmid_max_busy_us, uint, &num_max_busy_us, 0644); +MODULE_PARM_DESC(kipmid_max_busy_us, + "Max time (in microseconds) to busy-wait for IPMI data before" + " sleeping. 0 (default) means to wait forever. Set to 100-500" + " if kipmid is using up a lot of CPU time."); -#define IPMI_IO_ADDR_SPACE 0 -#define IPMI_MEM_ADDR_SPACE 1 -static char *addr_space_to_str[] = { "I/O", "memory" }; - static void std_irq_cleanup(struct smi_info *info) { if (info->si_type == SI_BT) @@ -1041,7 +1393,7 @@ static int std_irq_setup(struct smi_info *info) if (info->si_type == SI_BT) { rv = request_irq(info->irq, si_bt_irq_handler, - IRQF_DISABLED, + IRQF_SHARED, DEVICE_NAME, info); if (!rv) @@ -1051,18 +1403,17 @@ static int std_irq_setup(struct smi_info *info) } else rv = request_irq(info->irq, si_irq_handler, - IRQF_DISABLED, + IRQF_SHARED, DEVICE_NAME, info); if (rv) { - printk(KERN_WARNING - "ipmi_si: %s unable to claim interrupt %d," - " running polled\n", - DEVICE_NAME, info->irq); + dev_warn(info->dev, "%s unable to claim interrupt %d," + " running polled\n", + DEVICE_NAME, info->irq); info->irq = 0; } else { info->irq_cleanup = std_irq_cleanup; - printk(" Using irq %d\n", info->irq); + dev_info(info->dev, "Using irq %d\n", info->irq); } return rv; @@ -1119,10 +1470,9 @@ static void port_cleanup(struct smi_info *info) int idx; if (addr) { - for (idx = 0; idx < info->io_size; idx++) { + for (idx = 0; idx < info->io_size; idx++) release_region(addr + idx * info->io.regspacing, info->io.regsize); - } } } @@ -1136,8 +1486,10 @@ static int port_setup(struct smi_info *info) info->io_cleanup = port_cleanup; - /* Figure out the actual inb/inw/inl/etc routine to use based - upon the register size. */ + /* + * Figure out the actual inb/inw/inl/etc routine to use based + * upon the register size. + */ switch (info->io.regsize) { case 1: info->io.inputb = port_inb; @@ -1152,17 +1504,18 @@ static int port_setup(struct smi_info *info) info->io.outputb = port_outl; break; default: - printk("ipmi_si: Invalid register size: %d\n", - info->io.regsize); + dev_warn(info->dev, "Invalid register size: %d\n", + info->io.regsize); return -EINVAL; } - /* Some BIOSes reserve disjoint I/O regions in their ACPI + /* + * Some BIOSes reserve disjoint I/O regions in their ACPI * tables. This causes problems when trying to register the * entire I/O region. Therefore we must register each I/O * port separately. */ - for (idx = 0; idx < info->io_size; idx++) { + for (idx = 0; idx < info->io_size; idx++) { if (request_region(addr + idx * info->io.regspacing, info->io.regsize, DEVICE_NAME) == NULL) { /* Undo allocations */ @@ -1190,7 +1543,7 @@ static void intf_mem_outb(struct si_sm_io *io, unsigned int offset, static unsigned char intf_mem_inw(struct si_sm_io *io, unsigned int offset) { return (readw((io->addr)+(offset * io->regspacing)) >> io->regshift) - && 0xff; + & 0xff; } static void intf_mem_outw(struct si_sm_io *io, unsigned int offset, @@ -1202,7 +1555,7 @@ static void intf_mem_outw(struct si_sm_io *io, unsigned int offset, static unsigned char intf_mem_inl(struct si_sm_io *io, unsigned int offset) { return (readl((io->addr)+(offset * io->regspacing)) >> io->regshift) - && 0xff; + & 0xff; } static void intf_mem_outl(struct si_sm_io *io, unsigned int offset, @@ -1215,7 +1568,7 @@ static void intf_mem_outl(struct si_sm_io *io, unsigned int offset, static unsigned char mem_inq(struct si_sm_io *io, unsigned int offset) { return (readq((io->addr)+(offset * io->regspacing)) >> io->regshift) - && 0xff; + & 0xff; } static void mem_outq(struct si_sm_io *io, unsigned int offset, @@ -1250,8 +1603,10 @@ static int mem_setup(struct smi_info *info) info->io_cleanup = mem_cleanup; - /* Figure out the actual readb/readw/readl/etc routine to use based - upon the register size. */ + /* + * Figure out the actual readb/readw/readl/etc routine to use based + * upon the register size. + */ switch (info->io.regsize) { case 1: info->io.inputb = intf_mem_inb; @@ -1272,16 +1627,18 @@ static int mem_setup(struct smi_info *info) break; #endif default: - printk("ipmi_si: Invalid register size: %d\n", - info->io.regsize); + dev_warn(info->dev, "Invalid register size: %d\n", + info->io.regsize); return -EINVAL; } - /* Calculate the total amount of memory to claim. This is an + /* + * Calculate the total amount of memory to claim. This is an * unusual looking calculation, but it avoids claiming any * more memory than it has to. It will claim everything * between the first address to the end of the last full - * register. */ + * register. + */ mapsize = ((info->io_size * info->io.regspacing) - (info->io.regspacing - info->io.regsize)); @@ -1296,9 +1653,272 @@ static int mem_setup(struct smi_info *info) return 0; } +/* + * Parms come in as <op1>[:op2[:op3...]]. ops are: + * add|remove,kcs|bt|smic,mem|i/o,<address>[,<opt1>[,<opt2>[,...]]] + * Options are: + * rsp=<regspacing> + * rsi=<regsize> + * rsh=<regshift> + * irq=<irq> + * ipmb=<ipmb addr> + */ +enum hotmod_op { HM_ADD, HM_REMOVE }; +struct hotmod_vals { + char *name; + int val; +}; +static struct hotmod_vals hotmod_ops[] = { + { "add", HM_ADD }, + { "remove", HM_REMOVE }, + { NULL } +}; +static struct hotmod_vals hotmod_si[] = { + { "kcs", SI_KCS }, + { "smic", SI_SMIC }, + { "bt", SI_BT }, + { NULL } +}; +static struct hotmod_vals hotmod_as[] = { + { "mem", IPMI_MEM_ADDR_SPACE }, + { "i/o", IPMI_IO_ADDR_SPACE }, + { NULL } +}; -static __devinit void hardcode_find_bmc(void) +static int parse_str(struct hotmod_vals *v, int *val, char *name, char **curr) { + char *s; + int i; + + s = strchr(*curr, ','); + if (!s) { + printk(KERN_WARNING PFX "No hotmod %s given.\n", name); + return -EINVAL; + } + *s = '\0'; + s++; + for (i = 0; hotmod_ops[i].name; i++) { + if (strcmp(*curr, v[i].name) == 0) { + *val = v[i].val; + *curr = s; + return 0; + } + } + + printk(KERN_WARNING PFX "Invalid hotmod %s '%s'\n", name, *curr); + return -EINVAL; +} + +static int check_hotmod_int_op(const char *curr, const char *option, + const char *name, int *val) +{ + char *n; + + if (strcmp(curr, name) == 0) { + if (!option) { + printk(KERN_WARNING PFX + "No option given for '%s'\n", + curr); + return -EINVAL; + } + *val = simple_strtoul(option, &n, 0); + if ((*n != '\0') || (*option == '\0')) { + printk(KERN_WARNING PFX + "Bad option given for '%s'\n", + curr); + return -EINVAL; + } + return 1; + } + return 0; +} + +static struct smi_info *smi_info_alloc(void) +{ + struct smi_info *info = kzalloc(sizeof(*info), GFP_KERNEL); + + if (info) + spin_lock_init(&info->si_lock); + return info; +} + +static int hotmod_handler(const char *val, struct kernel_param *kp) +{ + char *str = kstrdup(val, GFP_KERNEL); + int rv; + char *next, *curr, *s, *n, *o; + enum hotmod_op op; + enum si_type si_type; + int addr_space; + unsigned long addr; + int regspacing; + int regsize; + int regshift; + int irq; + int ipmb; + int ival; + int len; + struct smi_info *info; + + if (!str) + return -ENOMEM; + + /* Kill any trailing spaces, as we can get a "\n" from echo. */ + len = strlen(str); + ival = len - 1; + while ((ival >= 0) && isspace(str[ival])) { + str[ival] = '\0'; + ival--; + } + + for (curr = str; curr; curr = next) { + regspacing = 1; + regsize = 1; + regshift = 0; + irq = 0; + ipmb = 0; /* Choose the default if not specified */ + + next = strchr(curr, ':'); + if (next) { + *next = '\0'; + next++; + } + + rv = parse_str(hotmod_ops, &ival, "operation", &curr); + if (rv) + break; + op = ival; + + rv = parse_str(hotmod_si, &ival, "interface type", &curr); + if (rv) + break; + si_type = ival; + + rv = parse_str(hotmod_as, &addr_space, "address space", &curr); + if (rv) + break; + + s = strchr(curr, ','); + if (s) { + *s = '\0'; + s++; + } + addr = simple_strtoul(curr, &n, 0); + if ((*n != '\0') || (*curr == '\0')) { + printk(KERN_WARNING PFX "Invalid hotmod address" + " '%s'\n", curr); + break; + } + + while (s) { + curr = s; + s = strchr(curr, ','); + if (s) { + *s = '\0'; + s++; + } + o = strchr(curr, '='); + if (o) { + *o = '\0'; + o++; + } + rv = check_hotmod_int_op(curr, o, "rsp", ®spacing); + if (rv < 0) + goto out; + else if (rv) + continue; + rv = check_hotmod_int_op(curr, o, "rsi", ®size); + if (rv < 0) + goto out; + else if (rv) + continue; + rv = check_hotmod_int_op(curr, o, "rsh", ®shift); + if (rv < 0) + goto out; + else if (rv) + continue; + rv = check_hotmod_int_op(curr, o, "irq", &irq); + if (rv < 0) + goto out; + else if (rv) + continue; + rv = check_hotmod_int_op(curr, o, "ipmb", &ipmb); + if (rv < 0) + goto out; + else if (rv) + continue; + + rv = -EINVAL; + printk(KERN_WARNING PFX + "Invalid hotmod option '%s'\n", + curr); + goto out; + } + + if (op == HM_ADD) { + info = smi_info_alloc(); + if (!info) { + rv = -ENOMEM; + goto out; + } + + info->addr_source = SI_HOTMOD; + info->si_type = si_type; + info->io.addr_data = addr; + info->io.addr_type = addr_space; + if (addr_space == IPMI_MEM_ADDR_SPACE) + info->io_setup = mem_setup; + else + info->io_setup = port_setup; + + info->io.addr = NULL; + info->io.regspacing = regspacing; + if (!info->io.regspacing) + info->io.regspacing = DEFAULT_REGSPACING; + info->io.regsize = regsize; + if (!info->io.regsize) + info->io.regsize = DEFAULT_REGSPACING; + info->io.regshift = regshift; + info->irq = irq; + if (info->irq) + info->irq_setup = std_irq_setup; + info->slave_addr = ipmb; + + rv = add_smi(info); + if (rv) { + kfree(info); + goto out; + } + rv = try_smi_init(info); + if (rv) { + cleanup_one_si(info); + goto out; + } + } else { + /* remove */ + struct smi_info *e, *tmp_e; + + mutex_lock(&smi_infos_lock); + list_for_each_entry_safe(e, tmp_e, &smi_infos, link) { + if (e->io.addr_type != addr_space) + continue; + if (e->si_type != si_type) + continue; + if (e->io.addr_data == addr) + cleanup_one_si(e); + } + mutex_unlock(&smi_infos_lock); + } + } + rv = len; + out: + kfree(str); + return rv; +} + +static int hardcode_find_bmc(void) +{ + int ret = -ENODEV; int i; struct smi_info *info; @@ -1306,11 +1926,12 @@ static __devinit void hardcode_find_bmc(void) if (!ports[i] && !addrs[i]) continue; - info = kzalloc(sizeof(*info), GFP_KERNEL); + info = smi_info_alloc(); if (!info) - return; + return -ENOMEM; - info->addr_source = "hardcoded"; + info->addr_source = SI_HARDCODED; + printk(KERN_INFO PFX "probing via hardcoded address\n"); if (!si_type[i] || strcmp(si_type[i], "kcs") == 0) { info->si_type = SI_KCS; @@ -1319,8 +1940,7 @@ static __devinit void hardcode_find_bmc(void) } else if (strcmp(si_type[i], "bt") == 0) { info->si_type = SI_BT; } else { - printk(KERN_WARNING - "ipmi_si: Interface type specified " + printk(KERN_WARNING PFX "Interface type specified " "for interface %d, was invalid: %s\n", i, si_type[i]); kfree(info); @@ -1338,11 +1958,9 @@ static __devinit void hardcode_find_bmc(void) info->io.addr_data = addrs[i]; info->io.addr_type = IPMI_MEM_ADDR_SPACE; } else { - printk(KERN_WARNING - "ipmi_si: Interface type specified " - "for interface %d, " - "but port and address were not set or " - "set to zero.\n", i); + printk(KERN_WARNING PFX "Interface type specified " + "for interface %d, but port and address were " + "not set or set to zero.\n", i); kfree(info); continue; } @@ -1358,22 +1976,33 @@ static __devinit void hardcode_find_bmc(void) info->irq = irqs[i]; if (info->irq) info->irq_setup = std_irq_setup; + info->slave_addr = slave_addrs[i]; - try_smi_init(info); + if (!add_smi(info)) { + if (try_smi_init(info)) + cleanup_one_si(info); + ret = 0; + } else { + kfree(info); + } } + return ret; } #ifdef CONFIG_ACPI #include <linux/acpi.h> -/* Once we get an ACPI failure, we don't try any more, because we go - through the tables sequentially. Once we don't find a table, there - are no more. */ -static int acpi_failure = 0; +/* + * Once we get an ACPI failure, we don't try any more, because we go + * through the tables sequentially. Once we don't find a table, there + * are no more. + */ +static int acpi_failure; /* For GPE-type interrupts. */ -static u32 ipmi_acpi_gpe(void *context) +static u32 ipmi_acpi_gpe(acpi_handle gpe_device, + u32 gpe_number, void *context) { struct smi_info *smi_info = context; unsigned long flags; @@ -1383,19 +2012,13 @@ static u32 ipmi_acpi_gpe(void *context) spin_lock_irqsave(&(smi_info->si_lock), flags); - spin_lock(&smi_info->count_lock); - smi_info->interrupts++; - spin_unlock(&smi_info->count_lock); - - if (atomic_read(&smi_info->stop_operation)) - goto out; + smi_inc_stat(smi_info, interrupts); #ifdef DEBUG_TIMING do_gettimeofday(&t); printk("**ACPI_GPE: %d.%9.9d\n", t.tv_sec, t.tv_usec); #endif smi_event_handler(smi_info, 0); - out: spin_unlock_irqrestore(&(smi_info->si_lock), flags); return ACPI_INTERRUPT_HANDLED; @@ -1423,22 +2046,20 @@ static int acpi_gpe_irq_setup(struct smi_info *info) &ipmi_acpi_gpe, info); if (status != AE_OK) { - printk(KERN_WARNING - "ipmi_si: %s unable to claim ACPI GPE %d," - " running polled\n", - DEVICE_NAME, info->irq); + dev_warn(info->dev, "%s unable to claim ACPI GPE %d," + " running polled\n", DEVICE_NAME, info->irq); info->irq = 0; return -EINVAL; } else { info->irq_cleanup = acpi_gpe_irq_cleanup; - printk(" Using ACPI GPE %d\n", info->irq); + dev_info(info->dev, "Using ACPI GPE %d\n", info->irq); return 0; } } /* * Defined at - * http://h21007.www2.hp.com/dspp/files/unprotected/devresource/Docs/TechPapers/IA64/hpspmi.pdf + * http://h21007.www2.hp.com/portal/download/files/unprot/hpspmi.pdf */ struct SPMITable { s8 Signature[4]; @@ -1460,14 +2081,18 @@ struct SPMITable { */ u8 InterruptType; - /* If bit 0 of InterruptType is set, then this is the SCI - interrupt in the GPEx_STS register. */ + /* + * If bit 0 of InterruptType is set, then this is the SCI + * interrupt in the GPEx_STS register. + */ u8 GPE; s16 Reserved; - /* If bit 1 of InterruptType is set, then this is the I/O - APIC/SAPIC interrupt. */ + /* + * If bit 1 of InterruptType is set, then this is the I/O + * APIC/SAPIC interrupt. + */ u32 GlobalSystemInterrupt; /* The actual register address. */ @@ -1478,33 +2103,27 @@ struct SPMITable { s8 spmi_id[1]; /* A '\0' terminated array starts here. */ }; -static __devinit int try_init_acpi(struct SPMITable *spmi) +static int try_init_spmi(struct SPMITable *spmi) { struct smi_info *info; - char *io_type; - u8 addr_space; + int rv; if (spmi->IPMIlegacy != 1) { - printk(KERN_INFO "IPMI: Bad SPMI legacy %d\n", spmi->IPMIlegacy); - return -ENODEV; + printk(KERN_INFO PFX "Bad SPMI legacy %d\n", spmi->IPMIlegacy); + return -ENODEV; } - if (spmi->addr.address_space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) - addr_space = IPMI_MEM_ADDR_SPACE; - else - addr_space = IPMI_IO_ADDR_SPACE; - - info = kzalloc(sizeof(*info), GFP_KERNEL); + info = smi_info_alloc(); if (!info) { - printk(KERN_ERR "ipmi_si: Could not allocate SI data (3)\n"); + printk(KERN_ERR PFX "Could not allocate SI data (3)\n"); return -ENOMEM; } - info->addr_source = "ACPI"; + info->addr_source = SI_SPMI; + printk(KERN_INFO PFX "probing via SPMI\n"); /* Figure out the interface type. */ - switch (spmi->InterfaceType) - { + switch (spmi->InterfaceType) { case 1: /* KCS */ info->si_type = SI_KCS; break; @@ -1515,8 +2134,8 @@ static __devinit int try_init_acpi(struct SPMITable *spmi) info->si_type = SI_BT; break; default: - printk(KERN_INFO "ipmi_si: Unknown ACPI/SPMI SI type %d\n", - spmi->InterfaceType); + printk(KERN_INFO PFX "Unknown ACPI/SPMI SI type %d\n", + spmi->InterfaceType); kfree(info); return -EIO; } @@ -1535,36 +2154,41 @@ static __devinit int try_init_acpi(struct SPMITable *spmi) info->irq_setup = NULL; } - if (spmi->addr.register_bit_width) { + if (spmi->addr.bit_width) { /* A (hopefully) properly formed register bit width. */ - info->io.regspacing = spmi->addr.register_bit_width / 8; + info->io.regspacing = spmi->addr.bit_width / 8; } else { info->io.regspacing = DEFAULT_REGSPACING; } info->io.regsize = info->io.regspacing; - info->io.regshift = spmi->addr.register_bit_offset; + info->io.regshift = spmi->addr.bit_offset; - if (spmi->addr.address_space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) { - io_type = "memory"; + if (spmi->addr.space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) { info->io_setup = mem_setup; - info->io.addr_type = IPMI_IO_ADDR_SPACE; - } else if (spmi->addr.address_space_id == ACPI_ADR_SPACE_SYSTEM_IO) { - io_type = "I/O"; - info->io_setup = port_setup; info->io.addr_type = IPMI_MEM_ADDR_SPACE; + } else if (spmi->addr.space_id == ACPI_ADR_SPACE_SYSTEM_IO) { + info->io_setup = port_setup; + info->io.addr_type = IPMI_IO_ADDR_SPACE; } else { kfree(info); - printk("ipmi_si: Unknown ACPI I/O Address type\n"); + printk(KERN_WARNING PFX "Unknown ACPI I/O Address type\n"); return -EIO; } info->io.addr_data = spmi->addr.address; - try_smi_init(info); + pr_info("ipmi_si: SPMI: %s %#lx regsize %d spacing %d irq %d\n", + (info->io.addr_type == IPMI_IO_ADDR_SPACE) ? "io" : "mem", + info->io.addr_data, info->io.regsize, info->io.regspacing, + info->irq); - return 0; + rv = add_smi(info); + if (rv) + kfree(info); + + return rv; } -static __devinit void acpi_find_bmc(void) +static void spmi_find_bmc(void) { acpi_status status; struct SPMITable *spmi; @@ -1577,21 +2201,141 @@ static __devinit void acpi_find_bmc(void) return; for (i = 0; ; i++) { - status = acpi_get_firmware_table("SPMI", i+1, - ACPI_LOGICAL_ADDRESSING, - (struct acpi_table_header **) - &spmi); + status = acpi_get_table(ACPI_SIG_SPMI, i+1, + (struct acpi_table_header **)&spmi); if (status != AE_OK) return; - try_init_acpi(spmi); + try_init_spmi(spmi); + } +} + +static int ipmi_pnp_probe(struct pnp_dev *dev, + const struct pnp_device_id *dev_id) +{ + struct acpi_device *acpi_dev; + struct smi_info *info; + struct resource *res, *res_second; + acpi_handle handle; + acpi_status status; + unsigned long long tmp; + int rv; + + acpi_dev = pnp_acpi_device(dev); + if (!acpi_dev) + return -ENODEV; + + info = smi_info_alloc(); + if (!info) + return -ENOMEM; + + info->addr_source = SI_ACPI; + printk(KERN_INFO PFX "probing via ACPI\n"); + + handle = acpi_dev->handle; + info->addr_info.acpi_info.acpi_handle = handle; + + /* _IFT tells us the interface type: KCS, BT, etc */ + status = acpi_evaluate_integer(handle, "_IFT", NULL, &tmp); + if (ACPI_FAILURE(status)) + goto err_free; + + switch (tmp) { + case 1: + info->si_type = SI_KCS; + break; + case 2: + info->si_type = SI_SMIC; + break; + case 3: + info->si_type = SI_BT; + break; + default: + dev_info(&dev->dev, "unknown IPMI type %lld\n", tmp); + goto err_free; + } + + res = pnp_get_resource(dev, IORESOURCE_IO, 0); + if (res) { + info->io_setup = port_setup; + info->io.addr_type = IPMI_IO_ADDR_SPACE; + } else { + res = pnp_get_resource(dev, IORESOURCE_MEM, 0); + if (res) { + info->io_setup = mem_setup; + info->io.addr_type = IPMI_MEM_ADDR_SPACE; + } + } + if (!res) { + dev_err(&dev->dev, "no I/O or memory address\n"); + goto err_free; + } + info->io.addr_data = res->start; + + info->io.regspacing = DEFAULT_REGSPACING; + res_second = pnp_get_resource(dev, + (info->io.addr_type == IPMI_IO_ADDR_SPACE) ? + IORESOURCE_IO : IORESOURCE_MEM, + 1); + if (res_second) { + if (res_second->start > info->io.addr_data) + info->io.regspacing = res_second->start - info->io.addr_data; } + info->io.regsize = DEFAULT_REGSPACING; + info->io.regshift = 0; + + /* If _GPE exists, use it; otherwise use standard interrupts */ + status = acpi_evaluate_integer(handle, "_GPE", NULL, &tmp); + if (ACPI_SUCCESS(status)) { + info->irq = tmp; + info->irq_setup = acpi_gpe_irq_setup; + } else if (pnp_irq_valid(dev, 0)) { + info->irq = pnp_irq(dev, 0); + info->irq_setup = std_irq_setup; + } + + info->dev = &dev->dev; + pnp_set_drvdata(dev, info); + + dev_info(info->dev, "%pR regsize %d spacing %d irq %d\n", + res, info->io.regsize, info->io.regspacing, + info->irq); + + rv = add_smi(info); + if (rv) + kfree(info); + + return rv; + +err_free: + kfree(info); + return -EINVAL; } + +static void ipmi_pnp_remove(struct pnp_dev *dev) +{ + struct smi_info *info = pnp_get_drvdata(dev); + + cleanup_one_si(info); +} + +static const struct pnp_device_id pnp_dev_table[] = { + {"IPI0001", 0}, + {"", 0}, +}; + +static struct pnp_driver ipmi_pnp_driver = { + .name = DEVICE_NAME, + .probe = ipmi_pnp_probe, + .remove = ipmi_pnp_remove, + .id_table = pnp_dev_table, +}; + +MODULE_DEVICE_TABLE(pnp, pnp_dev_table); #endif #ifdef CONFIG_DMI -struct dmi_ipmi_data -{ +struct dmi_ipmi_data { u8 type; u8 addr_space; unsigned long base_addr; @@ -1600,10 +2344,10 @@ struct dmi_ipmi_data u8 slave_addr; }; -static int __devinit decode_dmi(struct dmi_header *dm, +static int decode_dmi(const struct dmi_header *dm, struct dmi_ipmi_data *dmi) { - u8 *data = (u8 *)dm; + const u8 *data = (const u8 *)dm; unsigned long base_addr; u8 reg_spacing; u8 len = dm->length; @@ -1616,11 +2360,10 @@ static int __devinit decode_dmi(struct dmi_header *dm, /* I/O */ base_addr &= 0xFFFE; dmi->addr_space = IPMI_IO_ADDR_SPACE; - } - else { + } else /* Memory */ dmi->addr_space = IPMI_MEM_ADDR_SPACE; - } + /* If bit 4 of byte 0x10 is set, then the lsb for the address is odd. */ dmi->base_addr = base_addr | ((data[0x10] & 0x10) >> 4); @@ -1629,7 +2372,7 @@ static int __devinit decode_dmi(struct dmi_header *dm, /* The top two bits of byte 0x10 hold the register spacing. */ reg_spacing = (data[0x10] & 0xC0) >> 6; - switch(reg_spacing){ + switch (reg_spacing) { case 0x00: /* Byte boundaries */ dmi->offset = 1; break; @@ -1645,12 +2388,14 @@ static int __devinit decode_dmi(struct dmi_header *dm, } } else { /* Old DMI spec. */ - /* Note that technically, the lower bit of the base + /* + * Note that technically, the lower bit of the base * address should be 1 if the address is I/O and 0 if * the address is in memory. So many systems get that * wrong (and all that I have seen are I/O) so we just * ignore that bit and assume I/O. Systems that use - * memory should use the newer spec, anyway. */ + * memory should use the newer spec, anyway. + */ dmi->base_addr = base_addr & 0xfffe; dmi->addr_space = IPMI_IO_ADDR_SPACE; dmi->offset = 1; @@ -1661,18 +2406,18 @@ static int __devinit decode_dmi(struct dmi_header *dm, return 0; } -static __devinit void try_init_dmi(struct dmi_ipmi_data *ipmi_data) +static void try_init_dmi(struct dmi_ipmi_data *ipmi_data) { struct smi_info *info; - info = kzalloc(sizeof(*info), GFP_KERNEL); + info = smi_info_alloc(); if (!info) { - printk(KERN_ERR - "ipmi_si: Could not allocate SI data\n"); + printk(KERN_ERR PFX "Could not allocate SI data\n"); return; } - info->addr_source = "SMBIOS"; + info->addr_source = SI_SMBIOS; + printk(KERN_INFO PFX "probing via SMBIOS\n"); switch (ipmi_data->type) { case 0x01: /* KCS */ @@ -1685,6 +2430,7 @@ static __devinit void try_init_dmi(struct dmi_ipmi_data *ipmi_data) info->si_type = SI_BT; break; default: + kfree(info); return; } @@ -1701,8 +2447,7 @@ static __devinit void try_init_dmi(struct dmi_ipmi_data *ipmi_data) default: kfree(info); - printk(KERN_WARNING - "ipmi_si: Unknown SMBIOS I/O Address type: %d.\n", + printk(KERN_WARNING PFX "Unknown SMBIOS I/O Address type: %d\n", ipmi_data->addr_space); return; } @@ -1720,17 +2465,25 @@ static __devinit void try_init_dmi(struct dmi_ipmi_data *ipmi_data) if (info->irq) info->irq_setup = std_irq_setup; - try_smi_init(info); + pr_info("ipmi_si: SMBIOS: %s %#lx regsize %d spacing %d irq %d\n", + (info->io.addr_type == IPMI_IO_ADDR_SPACE) ? "io" : "mem", + info->io.addr_data, info->io.regsize, info->io.regspacing, + info->irq); + + if (add_smi(info)) + kfree(info); } -static void __devinit dmi_find_bmc(void) +static void dmi_find_bmc(void) { - struct dmi_device *dev = NULL; + const struct dmi_device *dev = NULL; struct dmi_ipmi_data data; int rv; while ((dev = dmi_find_device(DMI_DEV_TYPE_IPMI, NULL, dev))) { - rv = decode_dmi((struct dmi_header *) dev->device_data, &data); + memset(&data, 0, sizeof(data)); + rv = decode_dmi((const struct dmi_header *) dev->device_data, + &data); if (!rv) try_init_dmi(&data); } @@ -1757,19 +2510,51 @@ static void ipmi_pci_cleanup(struct smi_info *info) pci_disable_device(pdev); } -static int __devinit ipmi_pci_probe(struct pci_dev *pdev, +static int ipmi_pci_probe_regspacing(struct smi_info *info) +{ + if (info->si_type == SI_KCS) { + unsigned char status; + int regspacing; + + info->io.regsize = DEFAULT_REGSIZE; + info->io.regshift = 0; + info->io_size = 2; + info->handlers = &kcs_smi_handlers; + + /* detect 1, 4, 16byte spacing */ + for (regspacing = DEFAULT_REGSPACING; regspacing <= 16;) { + info->io.regspacing = regspacing; + if (info->io_setup(info)) { + dev_err(info->dev, + "Could not setup I/O space\n"); + return DEFAULT_REGSPACING; + } + /* write invalid cmd */ + info->io.outputb(&info->io, 1, 0x10); + /* read status back */ + status = info->io.inputb(&info->io, 1); + info->io_cleanup(info); + if (status) + return regspacing; + regspacing *= 4; + } + } + return DEFAULT_REGSPACING; +} + +static int ipmi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { int rv; int class_type = pdev->class & PCI_ERMC_CLASSCODE_TYPE_MASK; struct smi_info *info; - int first_reg_offset = 0; - info = kzalloc(sizeof(*info), GFP_KERNEL); + info = smi_info_alloc(); if (!info) - return ENOMEM; + return -ENOMEM; - info->addr_source = "PCI"; + info->addr_source = SI_PCI; + dev_info(&pdev->dev, "probing via PCI"); switch (class_type) { case PCI_ERMC_CLASSCODE_TYPE_SMIC: @@ -1786,15 +2571,13 @@ static int __devinit ipmi_pci_probe(struct pci_dev *pdev, default: kfree(info); - printk(KERN_INFO "ipmi_si: %s: Unknown IPMI type: %d\n", - pci_name(pdev), class_type); - return ENOMEM; + dev_info(&pdev->dev, "Unknown IPMI type: %d\n", class_type); + return -ENOMEM; } rv = pci_enable_device(pdev); if (rv) { - printk(KERN_ERR "ipmi_si: %s: couldn't enable PCI device\n", - pci_name(pdev)); + dev_err(&pdev->dev, "couldn't enable PCI device\n"); kfree(info); return rv; } @@ -1802,9 +2585,6 @@ static int __devinit ipmi_pci_probe(struct pci_dev *pdev, info->addr_source_cleanup = ipmi_pci_cleanup; info->addr_source_data = pdev; - if (pdev->subsystem_vendor == PCI_HP_VENDOR_ID) - first_reg_offset = 1; - if (pci_resource_flags(pdev, 0) & IORESOURCE_IO) { info->io_setup = port_setup; info->io.addr_type = IPMI_IO_ADDR_SPACE; @@ -1814,8 +2594,8 @@ static int __devinit ipmi_pci_probe(struct pci_dev *pdev, } info->io.addr_data = pci_resource_start(pdev, 0); - info->io.regspacing = DEFAULT_REGSPACING; - info->io.regsize = DEFAULT_REGSPACING; + info->io.regspacing = ipmi_pci_probe_regspacing(info); + info->io.regsize = DEFAULT_REGSIZE; info->io.regshift = 0; info->irq = pdev->irq; @@ -1823,164 +2603,443 @@ static int __devinit ipmi_pci_probe(struct pci_dev *pdev, info->irq_setup = std_irq_setup; info->dev = &pdev->dev; + pci_set_drvdata(pdev, info); + + dev_info(&pdev->dev, "%pR regsize %d spacing %d irq %d\n", + &pdev->resource[0], info->io.regsize, info->io.regspacing, + info->irq); + + rv = add_smi(info); + if (rv) { + kfree(info); + pci_disable_device(pdev); + } - return try_smi_init(info); + return rv; } -static void __devexit ipmi_pci_remove(struct pci_dev *pdev) +static void ipmi_pci_remove(struct pci_dev *pdev) { + struct smi_info *info = pci_get_drvdata(pdev); + cleanup_one_si(info); + pci_disable_device(pdev); } -#ifdef CONFIG_PM -static int ipmi_pci_suspend(struct pci_dev *pdev, pm_message_t state) +static struct pci_device_id ipmi_pci_devices[] = { + { PCI_DEVICE(PCI_HP_VENDOR_ID, PCI_MMC_DEVICE_ID) }, + { PCI_DEVICE_CLASS(PCI_ERMC_CLASSCODE, PCI_ERMC_CLASSCODE_MASK) }, + { 0, } +}; +MODULE_DEVICE_TABLE(pci, ipmi_pci_devices); + +static struct pci_driver ipmi_pci_driver = { + .name = DEVICE_NAME, + .id_table = ipmi_pci_devices, + .probe = ipmi_pci_probe, + .remove = ipmi_pci_remove, +}; +#endif /* CONFIG_PCI */ + +static struct of_device_id ipmi_match[]; +static int ipmi_probe(struct platform_device *dev) { +#ifdef CONFIG_OF + const struct of_device_id *match; + struct smi_info *info; + struct resource resource; + const __be32 *regsize, *regspacing, *regshift; + struct device_node *np = dev->dev.of_node; + int ret; + int proplen; + + dev_info(&dev->dev, "probing via device tree\n"); + + match = of_match_device(ipmi_match, &dev->dev); + if (!match) + return -EINVAL; + + ret = of_address_to_resource(np, 0, &resource); + if (ret) { + dev_warn(&dev->dev, PFX "invalid address from OF\n"); + return ret; + } + + regsize = of_get_property(np, "reg-size", &proplen); + if (regsize && proplen != 4) { + dev_warn(&dev->dev, PFX "invalid regsize from OF\n"); + return -EINVAL; + } + + regspacing = of_get_property(np, "reg-spacing", &proplen); + if (regspacing && proplen != 4) { + dev_warn(&dev->dev, PFX "invalid regspacing from OF\n"); + return -EINVAL; + } + + regshift = of_get_property(np, "reg-shift", &proplen); + if (regshift && proplen != 4) { + dev_warn(&dev->dev, PFX "invalid regshift from OF\n"); + return -EINVAL; + } + + info = smi_info_alloc(); + + if (!info) { + dev_err(&dev->dev, + "could not allocate memory for OF probe\n"); + return -ENOMEM; + } + + info->si_type = (enum si_type) match->data; + info->addr_source = SI_DEVICETREE; + info->irq_setup = std_irq_setup; + + if (resource.flags & IORESOURCE_IO) { + info->io_setup = port_setup; + info->io.addr_type = IPMI_IO_ADDR_SPACE; + } else { + info->io_setup = mem_setup; + info->io.addr_type = IPMI_MEM_ADDR_SPACE; + } + + info->io.addr_data = resource.start; + + info->io.regsize = regsize ? be32_to_cpup(regsize) : DEFAULT_REGSIZE; + info->io.regspacing = regspacing ? be32_to_cpup(regspacing) : DEFAULT_REGSPACING; + info->io.regshift = regshift ? be32_to_cpup(regshift) : 0; + + info->irq = irq_of_parse_and_map(dev->dev.of_node, 0); + info->dev = &dev->dev; + + dev_dbg(&dev->dev, "addr 0x%lx regsize %d spacing %d irq %d\n", + info->io.addr_data, info->io.regsize, info->io.regspacing, + info->irq); + + dev_set_drvdata(&dev->dev, info); + + ret = add_smi(info); + if (ret) { + kfree(info); + return ret; + } +#endif return 0; } -static int ipmi_pci_resume(struct pci_dev *pdev) +static int ipmi_remove(struct platform_device *dev) { +#ifdef CONFIG_OF + cleanup_one_si(dev_get_drvdata(&dev->dev)); +#endif return 0; } -#endif -static struct pci_device_id ipmi_pci_devices[] = { - { PCI_DEVICE(PCI_HP_VENDOR_ID, PCI_MMC_DEVICE_ID) }, - { PCI_DEVICE_CLASS(PCI_ERMC_CLASSCODE, PCI_ERMC_CLASSCODE) } +static struct of_device_id ipmi_match[] = +{ + { .type = "ipmi", .compatible = "ipmi-kcs", + .data = (void *)(unsigned long) SI_KCS }, + { .type = "ipmi", .compatible = "ipmi-smic", + .data = (void *)(unsigned long) SI_SMIC }, + { .type = "ipmi", .compatible = "ipmi-bt", + .data = (void *)(unsigned long) SI_BT }, + {}, }; -MODULE_DEVICE_TABLE(pci, ipmi_pci_devices); -static struct pci_driver ipmi_pci_driver = { - .name = DEVICE_NAME, - .id_table = ipmi_pci_devices, - .probe = ipmi_pci_probe, - .remove = __devexit_p(ipmi_pci_remove), -#ifdef CONFIG_PM - .suspend = ipmi_pci_suspend, - .resume = ipmi_pci_resume, -#endif +static struct platform_driver ipmi_driver = { + .driver = { + .name = DEVICE_NAME, + .owner = THIS_MODULE, + .of_match_table = ipmi_match, + }, + .probe = ipmi_probe, + .remove = ipmi_remove, }; -#endif /* CONFIG_PCI */ +#ifdef CONFIG_PARISC +static int ipmi_parisc_probe(struct parisc_device *dev) +{ + struct smi_info *info; + int rv; + + info = smi_info_alloc(); + + if (!info) { + dev_err(&dev->dev, + "could not allocate memory for PARISC probe\n"); + return -ENOMEM; + } + + info->si_type = SI_KCS; + info->addr_source = SI_DEVICETREE; + info->io_setup = mem_setup; + info->io.addr_type = IPMI_MEM_ADDR_SPACE; + info->io.addr_data = dev->hpa.start; + info->io.regsize = 1; + info->io.regspacing = 1; + info->io.regshift = 0; + info->irq = 0; /* no interrupt */ + info->irq_setup = NULL; + info->dev = &dev->dev; + + dev_dbg(&dev->dev, "addr 0x%lx\n", info->io.addr_data); + + dev_set_drvdata(&dev->dev, info); + + rv = add_smi(info); + if (rv) { + kfree(info); + return rv; + } + + return 0; +} + +static int ipmi_parisc_remove(struct parisc_device *dev) +{ + cleanup_one_si(dev_get_drvdata(&dev->dev)); + return 0; +} + +static struct parisc_device_id ipmi_parisc_tbl[] = { + { HPHW_MC, HVERSION_REV_ANY_ID, 0x004, 0xC0 }, + { 0, } +}; + +static struct parisc_driver ipmi_parisc_driver = { + .name = "ipmi", + .id_table = ipmi_parisc_tbl, + .probe = ipmi_parisc_probe, + .remove = ipmi_parisc_remove, +}; +#endif /* CONFIG_PARISC */ + +static int wait_for_msg_done(struct smi_info *smi_info) +{ + enum si_sm_result smi_result; + + smi_result = smi_info->handlers->event(smi_info->si_sm, 0); + for (;;) { + if (smi_result == SI_SM_CALL_WITH_DELAY || + smi_result == SI_SM_CALL_WITH_TICK_DELAY) { + schedule_timeout_uninterruptible(1); + smi_result = smi_info->handlers->event( + smi_info->si_sm, jiffies_to_usecs(1)); + } else if (smi_result == SI_SM_CALL_WITHOUT_DELAY) { + smi_result = smi_info->handlers->event( + smi_info->si_sm, 0); + } else + break; + } + if (smi_result == SI_SM_HOSED) + /* + * We couldn't get the state machine to run, so whatever's at + * the port is probably not an IPMI SMI interface. + */ + return -ENODEV; + + return 0; +} static int try_get_dev_id(struct smi_info *smi_info) { unsigned char msg[2]; unsigned char *resp; unsigned long resp_len; - enum si_sm_result smi_result; int rv = 0; resp = kmalloc(IPMI_MAX_MSG_LENGTH, GFP_KERNEL); if (!resp) return -ENOMEM; - /* Do a Get Device ID command, since it comes back with some - useful info. */ + /* + * Do a Get Device ID command, since it comes back with some + * useful info. + */ msg[0] = IPMI_NETFN_APP_REQUEST << 2; msg[1] = IPMI_GET_DEVICE_ID_CMD; smi_info->handlers->start_transaction(smi_info->si_sm, msg, 2); - smi_result = smi_info->handlers->event(smi_info->si_sm, 0); - for (;;) - { - if (smi_result == SI_SM_CALL_WITH_DELAY || - smi_result == SI_SM_CALL_WITH_TICK_DELAY) { - schedule_timeout_uninterruptible(1); - smi_result = smi_info->handlers->event( - smi_info->si_sm, 100); - } - else if (smi_result == SI_SM_CALL_WITHOUT_DELAY) - { - smi_result = smi_info->handlers->event( - smi_info->si_sm, 0); - } - else - break; - } - if (smi_result == SI_SM_HOSED) { - /* We couldn't get the state machine to run, so whatever's at - the port is probably not an IPMI SMI interface. */ - rv = -ENODEV; + rv = wait_for_msg_done(smi_info); + if (rv) + goto out; + + resp_len = smi_info->handlers->get_result(smi_info->si_sm, + resp, IPMI_MAX_MSG_LENGTH); + + /* Check and record info from the get device id, in case we need it. */ + rv = ipmi_demangle_device_id(resp, resp_len, &smi_info->device_id); + + out: + kfree(resp); + return rv; +} + +static int try_enable_event_buffer(struct smi_info *smi_info) +{ + unsigned char msg[3]; + unsigned char *resp; + unsigned long resp_len; + int rv = 0; + + resp = kmalloc(IPMI_MAX_MSG_LENGTH, GFP_KERNEL); + if (!resp) + return -ENOMEM; + + msg[0] = IPMI_NETFN_APP_REQUEST << 2; + msg[1] = IPMI_GET_BMC_GLOBAL_ENABLES_CMD; + smi_info->handlers->start_transaction(smi_info->si_sm, msg, 2); + + rv = wait_for_msg_done(smi_info); + if (rv) { + printk(KERN_WARNING PFX "Error getting response from get" + " global enables command, the event buffer is not" + " enabled.\n"); goto out; } - /* Otherwise, we got some data. */ resp_len = smi_info->handlers->get_result(smi_info->si_sm, resp, IPMI_MAX_MSG_LENGTH); - if (resp_len < 14) { - /* That's odd, it should be longer. */ + + if (resp_len < 4 || + resp[0] != (IPMI_NETFN_APP_REQUEST | 1) << 2 || + resp[1] != IPMI_GET_BMC_GLOBAL_ENABLES_CMD || + resp[2] != 0) { + printk(KERN_WARNING PFX "Invalid return from get global" + " enables command, cannot enable the event buffer.\n"); rv = -EINVAL; goto out; } - if ((resp[1] != IPMI_GET_DEVICE_ID_CMD) || (resp[2] != 0)) { - /* That's odd, it shouldn't be able to fail. */ - rv = -EINVAL; + if (resp[3] & IPMI_BMC_EVT_MSG_BUFF) + /* buffer is already enabled, nothing to do. */ + goto out; + + msg[0] = IPMI_NETFN_APP_REQUEST << 2; + msg[1] = IPMI_SET_BMC_GLOBAL_ENABLES_CMD; + msg[2] = resp[3] | IPMI_BMC_EVT_MSG_BUFF; + smi_info->handlers->start_transaction(smi_info->si_sm, msg, 3); + + rv = wait_for_msg_done(smi_info); + if (rv) { + printk(KERN_WARNING PFX "Error getting response from set" + " global, enables command, the event buffer is not" + " enabled.\n"); goto out; } - /* Record info from the get device id, in case we need it. */ - ipmi_demangle_device_id(resp+3, resp_len-3, &smi_info->device_id); + resp_len = smi_info->handlers->get_result(smi_info->si_sm, + resp, IPMI_MAX_MSG_LENGTH); + if (resp_len < 3 || + resp[0] != (IPMI_NETFN_APP_REQUEST | 1) << 2 || + resp[1] != IPMI_SET_BMC_GLOBAL_ENABLES_CMD) { + printk(KERN_WARNING PFX "Invalid return from get global," + "enables command, not enable the event buffer.\n"); + rv = -EINVAL; + goto out; + } + + if (resp[2] != 0) + /* + * An error when setting the event buffer bit means + * that the event buffer is not supported. + */ + rv = -ENOENT; out: kfree(resp); return rv; } -static int type_file_read_proc(char *page, char **start, off_t off, - int count, int *eof, void *data) +static int smi_type_proc_show(struct seq_file *m, void *v) { - char *out = (char *) page; - struct smi_info *smi = data; + struct smi_info *smi = m->private; - switch (smi->si_type) { - case SI_KCS: - return sprintf(out, "kcs\n"); - case SI_SMIC: - return sprintf(out, "smic\n"); - case SI_BT: - return sprintf(out, "bt\n"); - default: - return 0; - } + return seq_printf(m, "%s\n", si_to_str[smi->si_type]); } -static int stat_file_read_proc(char *page, char **start, off_t off, - int count, int *eof, void *data) +static int smi_type_proc_open(struct inode *inode, struct file *file) { - char *out = (char *) page; - struct smi_info *smi = data; + return single_open(file, smi_type_proc_show, PDE_DATA(inode)); +} - out += sprintf(out, "interrupts_enabled: %d\n", +static const struct file_operations smi_type_proc_ops = { + .open = smi_type_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int smi_si_stats_proc_show(struct seq_file *m, void *v) +{ + struct smi_info *smi = m->private; + + seq_printf(m, "interrupts_enabled: %d\n", smi->irq && !smi->interrupt_disabled); - out += sprintf(out, "short_timeouts: %ld\n", - smi->short_timeouts); - out += sprintf(out, "long_timeouts: %ld\n", - smi->long_timeouts); - out += sprintf(out, "timeout_restarts: %ld\n", - smi->timeout_restarts); - out += sprintf(out, "idles: %ld\n", - smi->idles); - out += sprintf(out, "interrupts: %ld\n", - smi->interrupts); - out += sprintf(out, "attentions: %ld\n", - smi->attentions); - out += sprintf(out, "flag_fetches: %ld\n", - smi->flag_fetches); - out += sprintf(out, "hosed_count: %ld\n", - smi->hosed_count); - out += sprintf(out, "complete_transactions: %ld\n", - smi->complete_transactions); - out += sprintf(out, "events: %ld\n", - smi->events); - out += sprintf(out, "watchdog_pretimeouts: %ld\n", - smi->watchdog_pretimeouts); - out += sprintf(out, "incoming_messages: %ld\n", - smi->incoming_messages); - - return (out - ((char *) page)); + seq_printf(m, "short_timeouts: %u\n", + smi_get_stat(smi, short_timeouts)); + seq_printf(m, "long_timeouts: %u\n", + smi_get_stat(smi, long_timeouts)); + seq_printf(m, "idles: %u\n", + smi_get_stat(smi, idles)); + seq_printf(m, "interrupts: %u\n", + smi_get_stat(smi, interrupts)); + seq_printf(m, "attentions: %u\n", + smi_get_stat(smi, attentions)); + seq_printf(m, "flag_fetches: %u\n", + smi_get_stat(smi, flag_fetches)); + seq_printf(m, "hosed_count: %u\n", + smi_get_stat(smi, hosed_count)); + seq_printf(m, "complete_transactions: %u\n", + smi_get_stat(smi, complete_transactions)); + seq_printf(m, "events: %u\n", + smi_get_stat(smi, events)); + seq_printf(m, "watchdog_pretimeouts: %u\n", + smi_get_stat(smi, watchdog_pretimeouts)); + seq_printf(m, "incoming_messages: %u\n", + smi_get_stat(smi, incoming_messages)); + return 0; } +static int smi_si_stats_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, smi_si_stats_proc_show, PDE_DATA(inode)); +} + +static const struct file_operations smi_si_stats_proc_ops = { + .open = smi_si_stats_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int smi_params_proc_show(struct seq_file *m, void *v) +{ + struct smi_info *smi = m->private; + + return seq_printf(m, + "%s,%s,0x%lx,rsp=%d,rsi=%d,rsh=%d,irq=%d,ipmb=%d\n", + si_to_str[smi->si_type], + addr_space_to_str[smi->io.addr_type], + smi->io.addr_data, + smi->io.regspacing, + smi->io.regsize, + smi->io.regshift, + smi->irq, + smi->slave_addr); +} + +static int smi_params_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, smi_params_proc_show, PDE_DATA(inode)); +} + +static const struct file_operations smi_params_proc_ops = { + .open = smi_params_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + /* * oem_data_avail_to_receive_msg_avail * @info - smi_info structure with msg_flags set @@ -1991,7 +3050,7 @@ static int stat_file_read_proc(char *page, char **start, off_t off, static int oem_data_avail_to_receive_msg_avail(struct smi_info *smi_info) { smi_info->msg_flags = ((smi_info->msg_flags & ~OEM_DATA_AVAIL) | - RECEIVE_MSG_AVAIL); + RECEIVE_MSG_AVAIL); return 1; } @@ -2033,10 +3092,9 @@ static void setup_dell_poweredge_oem_data_handler(struct smi_info *smi_info) id->ipmi_version == DELL_POWEREDGE_8G_BMC_IPMI_VERSION) { smi_info->oem_data_avail_handler = oem_data_avail_to_receive_msg_avail; - } - else if (ipmi_version_major(id) < 1 || - (ipmi_version_major(id) == 1 && - ipmi_version_minor(id) < 5)) { + } else if (ipmi_version_major(id) < 1 || + (ipmi_version_major(id) == 1 && + ipmi_version_minor(id) < 5)) { smi_info->oem_data_avail_handler = oem_data_avail_to_receive_msg_avail; } @@ -2048,7 +3106,7 @@ static void return_hosed_msg_badsize(struct smi_info *smi_info) { struct ipmi_smi_msg *msg = smi_info->curr_msg; - /* Make it a reponse */ + /* Make it a response */ msg->rsp[0] = msg->data[0] | 4; msg->rsp[1] = msg->data[1]; msg->rsp[2] = CANNOT_RETURN_REQUESTED_LENGTH; @@ -2128,15 +3186,17 @@ static void setup_xaction_handlers(struct smi_info *smi_info) static inline void wait_for_timer_and_thread(struct smi_info *smi_info) { if (smi_info->intf) { - /* The timer and thread are only running if the - interface has been started up and registered. */ + /* + * The timer and thread are only running if the + * interface has been started up and registered. + */ if (smi_info->thread != NULL) kthread_stop(smi_info->thread); del_timer_sync(&smi_info->si_timer); } } -static __devinitdata struct ipmi_default_vals +static struct ipmi_default_vals { int type; int port; @@ -2148,7 +3208,7 @@ static __devinitdata struct ipmi_default_vals { .port = 0 } }; -static __devinit void default_find_bmc(void) +static void default_find_bmc(void) { struct smi_info *info; int i; @@ -2156,12 +3216,15 @@ static __devinit void default_find_bmc(void) for (i = 0; ; i++) { if (!ipmi_defaults[i].port) break; - - info = kzalloc(sizeof(*info), GFP_KERNEL); +#ifdef CONFIG_PPC + if (check_legacy_ioport(ipmi_defaults[i].port)) + continue; +#endif + info = smi_info_alloc(); if (!info) return; - info->addr_source = NULL; + info->addr_source = SI_DEFAULT; info->si_type = ipmi_defaults[i].type; info->io_setup = port_setup; @@ -2173,14 +3236,18 @@ static __devinit void default_find_bmc(void) info->io.regsize = DEFAULT_REGSPACING; info->io.regshift = 0; - if (try_smi_init(info) == 0) { - /* Found one... */ - printk(KERN_INFO "ipmi_si: Found default %s state" - " machine at %s address 0x%lx\n", - si_to_str[info->si_type], - addr_space_to_str[info->io.addr_type], - info->io.addr_data); - return; + if (add_smi(info) == 0) { + if ((try_smi_init(info)) == 0) { + /* Found one... */ + printk(KERN_INFO PFX "Found default %s" + " state machine at %s address 0x%lx\n", + si_to_str[info->si_type], + addr_space_to_str[info->io.addr_type], + info->io.addr_data); + } else + cleanup_one_si(info); + } else { + kfree(info); } } } @@ -2199,33 +3266,48 @@ static int is_new_interface(struct smi_info *info) return 1; } -static int try_smi_init(struct smi_info *new_smi) +static int add_smi(struct smi_info *new_smi) { - int rv; - - if (new_smi->addr_source) { - printk(KERN_INFO "ipmi_si: Trying %s-specified %s state" - " machine at %s address 0x%lx, slave address 0x%x," - " irq %d\n", - new_smi->addr_source, - si_to_str[new_smi->si_type], - addr_space_to_str[new_smi->io.addr_type], - new_smi->io.addr_data, - new_smi->slave_addr, new_smi->irq); - } + int rv = 0; + printk(KERN_INFO PFX "Adding %s-specified %s state machine", + ipmi_addr_src_to_str[new_smi->addr_source], + si_to_str[new_smi->si_type]); mutex_lock(&smi_infos_lock); if (!is_new_interface(new_smi)) { - printk(KERN_WARNING "ipmi_si: duplicate interface\n"); + printk(KERN_CONT " duplicate interface\n"); rv = -EBUSY; goto out_err; } + printk(KERN_CONT "\n"); + /* So we know not to free it unless we have allocated one. */ new_smi->intf = NULL; new_smi->si_sm = NULL; new_smi->handlers = NULL; + list_add_tail(&new_smi->link, &smi_infos); + +out_err: + mutex_unlock(&smi_infos_lock); + return rv; +} + +static int try_smi_init(struct smi_info *new_smi) +{ + int rv = 0; + int i; + + printk(KERN_INFO PFX "Trying %s-specified %s state" + " machine at %s address 0x%lx, slave address 0x%x," + " irq %d\n", + ipmi_addr_src_to_str[new_smi->addr_source], + si_to_str[new_smi->si_type], + addr_space_to_str[new_smi->io.addr_type], + new_smi->io.addr_data, + new_smi->slave_addr, new_smi->irq); + switch (new_smi->si_type) { case SI_KCS: new_smi->handlers = &kcs_smi_handlers; @@ -2248,7 +3330,8 @@ static int try_smi_init(struct smi_info *new_smi) /* Allocate the state machine's data and initialize it. */ new_smi->si_sm = kmalloc(new_smi->handlers->size(), GFP_KERNEL); if (!new_smi->si_sm) { - printk(" Could not allocate state machine memory\n"); + printk(KERN_ERR PFX + "Could not allocate state machine memory\n"); rv = -ENOMEM; goto out_err; } @@ -2258,29 +3341,26 @@ static int try_smi_init(struct smi_info *new_smi) /* Now that we know the I/O size, we can set up the I/O. */ rv = new_smi->io_setup(new_smi); if (rv) { - printk(" Could not set up I/O space\n"); + printk(KERN_ERR PFX "Could not set up I/O space\n"); goto out_err; } - spin_lock_init(&(new_smi->si_lock)); - spin_lock_init(&(new_smi->msg_lock)); - spin_lock_init(&(new_smi->count_lock)); - /* Do low-level detection first. */ if (new_smi->handlers->detect(new_smi->si_sm)) { if (new_smi->addr_source) - printk(KERN_INFO "ipmi_si: Interface detection" - " failed\n"); + printk(KERN_INFO PFX "Interface detection failed\n"); rv = -ENODEV; goto out_err; } - /* Attempt a get device id command. If it fails, we probably - don't have a BMC here. */ + /* + * Attempt a get device id command. If it fails, we probably + * don't have a BMC here. + */ rv = try_get_dev_id(new_smi); if (rv) { if (new_smi->addr_source) - printk(KERN_INFO "ipmi_si: There appears to be no BMC" + printk(KERN_INFO PFX "There appears to be no BMC" " at this location\n"); goto out_err; } @@ -2288,91 +3368,97 @@ static int try_smi_init(struct smi_info *new_smi) setup_oem_data_handler(new_smi); setup_xaction_handlers(new_smi); - /* Try to claim any interrupts. */ - if (new_smi->irq_setup) - new_smi->irq_setup(new_smi); - INIT_LIST_HEAD(&(new_smi->xmit_msgs)); INIT_LIST_HEAD(&(new_smi->hp_xmit_msgs)); new_smi->curr_msg = NULL; atomic_set(&new_smi->req_events, 0); - new_smi->run_to_completion = 0; + new_smi->run_to_completion = false; + for (i = 0; i < SI_NUM_STATS; i++) + atomic_set(&new_smi->stats[i], 0); - new_smi->interrupt_disabled = 0; + new_smi->interrupt_disabled = true; atomic_set(&new_smi->stop_operation, 0); + atomic_set(&new_smi->need_watch, 0); new_smi->intf_num = smi_num; smi_num++; - /* Start clearing the flags before we enable interrupts or the - timer to avoid racing with the timer. */ + rv = try_enable_event_buffer(new_smi); + if (rv == 0) + new_smi->has_event_buffer = true; + + /* + * Start clearing the flags before we enable interrupts or the + * timer to avoid racing with the timer. + */ start_clear_flags(new_smi); /* IRQ is defined to be set when non-zero. */ if (new_smi->irq) new_smi->si_state = SI_CLEARING_FLAGS_THEN_SET_IRQ; if (!new_smi->dev) { - /* If we don't already have a device from something - * else (like PCI), then register a new one. */ + /* + * If we don't already have a device from something + * else (like PCI), then register a new one. + */ new_smi->pdev = platform_device_alloc("ipmi_si", new_smi->intf_num); - if (rv) { - printk(KERN_ERR - "ipmi_si_intf:" - " Unable to allocate platform device\n"); + if (!new_smi->pdev) { + printk(KERN_ERR PFX + "Unable to allocate platform device\n"); goto out_err; } new_smi->dev = &new_smi->pdev->dev; - new_smi->dev->driver = &ipmi_driver; + new_smi->dev->driver = &ipmi_driver.driver; - rv = platform_device_register(new_smi->pdev); + rv = platform_device_add(new_smi->pdev); if (rv) { - printk(KERN_ERR - "ipmi_si_intf:" - " Unable to register system interface device:" + printk(KERN_ERR PFX + "Unable to register system interface device:" " %d\n", rv); goto out_err; } - new_smi->dev_registered = 1; + new_smi->dev_registered = true; } rv = ipmi_register_smi(&handlers, new_smi, &new_smi->device_id, new_smi->dev, + "bmc", new_smi->slave_addr); if (rv) { - printk(KERN_ERR - "ipmi_si: Unable to register device: error %d\n", - rv); + dev_err(new_smi->dev, "Unable to register device: error %d\n", + rv); goto out_err_stop_timer; } rv = ipmi_smi_add_proc_entry(new_smi->intf, "type", - type_file_read_proc, NULL, - new_smi, THIS_MODULE); + &smi_type_proc_ops, + new_smi); if (rv) { - printk(KERN_ERR - "ipmi_si: Unable to create proc entry: %d\n", - rv); + dev_err(new_smi->dev, "Unable to create proc entry: %d\n", rv); goto out_err_stop_timer; } rv = ipmi_smi_add_proc_entry(new_smi->intf, "si_stats", - stat_file_read_proc, NULL, - new_smi, THIS_MODULE); + &smi_si_stats_proc_ops, + new_smi); if (rv) { - printk(KERN_ERR - "ipmi_si: Unable to create proc entry: %d\n", - rv); + dev_err(new_smi->dev, "Unable to create proc entry: %d\n", rv); goto out_err_stop_timer; } - list_add_tail(&new_smi->link, &smi_infos); - - mutex_unlock(&smi_infos_lock); + rv = ipmi_smi_add_proc_entry(new_smi->intf, "params", + &smi_params_proc_ops, + new_smi); + if (rv) { + dev_err(new_smi->dev, "Unable to create proc entry: %d\n", rv); + goto out_err_stop_timer; + } - printk(" IPMI %s interface initialized\n",si_to_str[new_smi->si_type]); + dev_info(new_smi->dev, "IPMI %s interface initialized\n", + si_to_str[new_smi->si_type]); return 0; @@ -2381,57 +3467,69 @@ static int try_smi_init(struct smi_info *new_smi) wait_for_timer_and_thread(new_smi); out_err: - if (new_smi->intf) + new_smi->interrupt_disabled = true; + + if (new_smi->intf) { ipmi_unregister_smi(new_smi->intf); + new_smi->intf = NULL; + } - if (new_smi->irq_cleanup) + if (new_smi->irq_cleanup) { new_smi->irq_cleanup(new_smi); + new_smi->irq_cleanup = NULL; + } - /* Wait until we know that we are out of any interrupt - handlers might have been running before we freed the - interrupt. */ + /* + * Wait until we know that we are out of any interrupt + * handlers might have been running before we freed the + * interrupt. + */ synchronize_sched(); if (new_smi->si_sm) { if (new_smi->handlers) new_smi->handlers->cleanup(new_smi->si_sm); kfree(new_smi->si_sm); + new_smi->si_sm = NULL; } - if (new_smi->addr_source_cleanup) + if (new_smi->addr_source_cleanup) { new_smi->addr_source_cleanup(new_smi); - if (new_smi->io_cleanup) + new_smi->addr_source_cleanup = NULL; + } + if (new_smi->io_cleanup) { new_smi->io_cleanup(new_smi); + new_smi->io_cleanup = NULL; + } - if (new_smi->dev_registered) + if (new_smi->dev_registered) { platform_device_unregister(new_smi->pdev); - - kfree(new_smi); - - mutex_unlock(&smi_infos_lock); + new_smi->dev_registered = false; + } return rv; } -static __devinit int init_ipmi_si(void) +static int init_ipmi_si(void) { int i; char *str; int rv; + struct smi_info *e; + enum ipmi_addr_src type = SI_INVALID; if (initialized) return 0; initialized = 1; - /* Register the device drivers. */ - rv = driver_register(&ipmi_driver); - if (rv) { - printk(KERN_ERR - "init_ipmi_si: Unable to register driver: %d\n", - rv); - return rv; + if (si_tryplatform) { + rv = platform_driver_register(&ipmi_driver); + if (rv) { + printk(KERN_ERR PFX "Unable to register " + "driver: %d\n", rv); + return rv; + } } - /* Parse out the si_type string into its components. */ str = si_type_str; if (*str != '\0') { @@ -2449,40 +3547,98 @@ static __devinit int init_ipmi_si(void) printk(KERN_INFO "IPMI System Interface driver.\n"); - hardcode_find_bmc(); + /* If the user gave us a device, they presumably want us to use it */ + if (!hardcode_find_bmc()) + return 0; + +#ifdef CONFIG_PCI + if (si_trypci) { + rv = pci_register_driver(&ipmi_pci_driver); + if (rv) + printk(KERN_ERR PFX "Unable to register " + "PCI driver: %d\n", rv); + else + pci_registered = true; + } +#endif + +#ifdef CONFIG_ACPI + if (si_tryacpi) { + pnp_register_driver(&ipmi_pnp_driver); + pnp_registered = true; + } +#endif #ifdef CONFIG_DMI - dmi_find_bmc(); + if (si_trydmi) + dmi_find_bmc(); #endif #ifdef CONFIG_ACPI - if (si_trydefaults) - acpi_find_bmc(); + if (si_tryacpi) + spmi_find_bmc(); #endif -#ifdef CONFIG_PCI - pci_module_init(&ipmi_pci_driver); +#ifdef CONFIG_PARISC + register_parisc_driver(&ipmi_parisc_driver); + parisc_registered = true; + /* poking PC IO addresses will crash machine, don't do it */ + si_trydefaults = 0; #endif + /* We prefer devices with interrupts, but in the case of a machine + with multiple BMCs we assume that there will be several instances + of a given type so if we succeed in registering a type then also + try to register everything else of the same type */ + + mutex_lock(&smi_infos_lock); + list_for_each_entry(e, &smi_infos, link) { + /* Try to register a device if it has an IRQ and we either + haven't successfully registered a device yet or this + device has the same type as one we successfully registered */ + if (e->irq && (!type || e->addr_source == type)) { + if (!try_smi_init(e)) { + type = e->addr_source; + } + } + } + + /* type will only have been set if we successfully registered an si */ + if (type) { + mutex_unlock(&smi_infos_lock); + return 0; + } + + /* Fall back to the preferred device */ + + list_for_each_entry(e, &smi_infos, link) { + if (!e->irq && (!type || e->addr_source == type)) { + if (!try_smi_init(e)) { + type = e->addr_source; + } + } + } + mutex_unlock(&smi_infos_lock); + + if (type) + return 0; + if (si_trydefaults) { mutex_lock(&smi_infos_lock); if (list_empty(&smi_infos)) { /* No BMC was found, try defaults. */ mutex_unlock(&smi_infos_lock); default_find_bmc(); - } else { + } else mutex_unlock(&smi_infos_lock); - } } mutex_lock(&smi_infos_lock); - if (list_empty(&smi_infos)) { + if (unload_when_empty && list_empty(&smi_infos)) { mutex_unlock(&smi_infos_lock); -#ifdef CONFIG_PCI - pci_unregister_driver(&ipmi_pci_driver); -#endif - driver_unregister(&ipmi_driver); - printk("ipmi_si: Unable to find any System Interface(s)\n"); + cleanup_ipmi_si(); + printk(KERN_WARNING PFX + "Unable to find any System Interface(s)\n"); return -ENODEV; } else { mutex_unlock(&smi_infos_lock); @@ -2491,9 +3647,9 @@ static __devinit int init_ipmi_si(void) } module_init(init_ipmi_si); -static void __devexit cleanup_one_si(struct smi_info *to_clean) +static void cleanup_one_si(struct smi_info *to_clean) { - int rv; + int rv = 0; unsigned long flags; if (!to_clean) @@ -2501,41 +3657,52 @@ static void __devexit cleanup_one_si(struct smi_info *to_clean) list_del(&to_clean->link); - /* Tell the timer and interrupt handlers that we are shutting - down. */ - spin_lock_irqsave(&(to_clean->si_lock), flags); - spin_lock(&(to_clean->msg_lock)); - + /* Tell the driver that we are shutting down. */ atomic_inc(&to_clean->stop_operation); - if (to_clean->irq_cleanup) - to_clean->irq_cleanup(to_clean); - - spin_unlock(&(to_clean->msg_lock)); - spin_unlock_irqrestore(&(to_clean->si_lock), flags); - - /* Wait until we know that we are out of any interrupt - handlers might have been running before we freed the - interrupt. */ - synchronize_sched(); - + /* + * Make sure the timer and thread are stopped and will not run + * again. + */ wait_for_timer_and_thread(to_clean); - /* Interrupts and timeouts are stopped, now make sure the - interface is in a clean state. */ + /* + * Timeouts are stopped, now make sure the interrupts are off + * for the device. A little tricky with locks to make sure + * there are no races. + */ + spin_lock_irqsave(&to_clean->si_lock, flags); + while (to_clean->curr_msg || (to_clean->si_state != SI_NORMAL)) { + spin_unlock_irqrestore(&to_clean->si_lock, flags); + poll(to_clean); + schedule_timeout_uninterruptible(1); + spin_lock_irqsave(&to_clean->si_lock, flags); + } + disable_si_irq(to_clean); + spin_unlock_irqrestore(&to_clean->si_lock, flags); while (to_clean->curr_msg || (to_clean->si_state != SI_NORMAL)) { poll(to_clean); schedule_timeout_uninterruptible(1); } - rv = ipmi_unregister_smi(to_clean->intf); + /* Clean up interrupts and make sure that everything is done. */ + if (to_clean->irq_cleanup) + to_clean->irq_cleanup(to_clean); + while (to_clean->curr_msg || (to_clean->si_state != SI_NORMAL)) { + poll(to_clean); + schedule_timeout_uninterruptible(1); + } + + if (to_clean->intf) + rv = ipmi_unregister_smi(to_clean->intf); + if (rv) { - printk(KERN_ERR - "ipmi_si: Unable to unregister device: errno=%d\n", + printk(KERN_ERR PFX "Unable to unregister device: errno=%d\n", rv); } - to_clean->handlers->cleanup(to_clean->si_sm); + if (to_clean->handlers) + to_clean->handlers->cleanup(to_clean->si_sm); kfree(to_clean->si_sm); @@ -2550,7 +3717,7 @@ static void __devexit cleanup_one_si(struct smi_info *to_clean) kfree(to_clean); } -static __exit void cleanup_ipmi_si(void) +static void cleanup_ipmi_si(void) { struct smi_info *e, *tmp_e; @@ -2558,18 +3725,28 @@ static __exit void cleanup_ipmi_si(void) return; #ifdef CONFIG_PCI - pci_unregister_driver(&ipmi_pci_driver); + if (pci_registered) + pci_unregister_driver(&ipmi_pci_driver); +#endif +#ifdef CONFIG_ACPI + if (pnp_registered) + pnp_unregister_driver(&ipmi_pnp_driver); +#endif +#ifdef CONFIG_PARISC + if (parisc_registered) + unregister_parisc_driver(&ipmi_parisc_driver); #endif + platform_driver_unregister(&ipmi_driver); + mutex_lock(&smi_infos_lock); list_for_each_entry_safe(e, tmp_e, &smi_infos, link) cleanup_one_si(e); mutex_unlock(&smi_infos_lock); - - driver_unregister(&ipmi_driver); } module_exit(cleanup_ipmi_si); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Corey Minyard <minyard@mvista.com>"); -MODULE_DESCRIPTION("Interface to the IPMI driver for the KCS, SMIC, and BT system interfaces."); +MODULE_DESCRIPTION("Interface to the IPMI driver for the KCS, SMIC, and BT" + " system interfaces."); diff --git a/drivers/char/ipmi/ipmi_si_sm.h b/drivers/char/ipmi/ipmi_si_sm.h index 4b731b24dc1..df89f73475f 100644 --- a/drivers/char/ipmi/ipmi_si_sm.h +++ b/drivers/char/ipmi/ipmi_si_sm.h @@ -34,22 +34,27 @@ * 675 Mass Ave, Cambridge, MA 02139, USA. */ -/* This is defined by the state machines themselves, it is an opaque - data type for them to use. */ +/* + * This is defined by the state machines themselves, it is an opaque + * data type for them to use. + */ struct si_sm_data; -/* The structure for doing I/O in the state machine. The state - machine doesn't have the actual I/O routines, they are done through - this interface. */ -struct si_sm_io -{ +/* + * The structure for doing I/O in the state machine. The state + * machine doesn't have the actual I/O routines, they are done through + * this interface. + */ +struct si_sm_io { unsigned char (*inputb)(struct si_sm_io *io, unsigned int offset); void (*outputb)(struct si_sm_io *io, unsigned int offset, unsigned char b); - /* Generic info used by the actual handling routines, the - state machine shouldn't touch these. */ + /* + * Generic info used by the actual handling routines, the + * state machine shouldn't touch these. + */ void __iomem *addr; int regspacing; int regsize; @@ -59,53 +64,67 @@ struct si_sm_io }; /* Results of SMI events. */ -enum si_sm_result -{ +enum si_sm_result { SI_SM_CALL_WITHOUT_DELAY, /* Call the driver again immediately */ SI_SM_CALL_WITH_DELAY, /* Delay some before calling again. */ - SI_SM_CALL_WITH_TICK_DELAY, /* Delay at least 1 tick before calling again. */ + SI_SM_CALL_WITH_TICK_DELAY,/* Delay >=1 tick before calling again. */ SI_SM_TRANSACTION_COMPLETE, /* A transaction is finished. */ SI_SM_IDLE, /* The SM is in idle state. */ SI_SM_HOSED, /* The hardware violated the state machine. */ - SI_SM_ATTN /* The hardware is asserting attn and the - state machine is idle. */ + + /* + * The hardware is asserting attn and the state machine is + * idle. + */ + SI_SM_ATTN }; /* Handlers for the SMI state machine. */ -struct si_sm_handlers -{ - /* Put the version number of the state machine here so the - upper layer can print it. */ +struct si_sm_handlers { + /* + * Put the version number of the state machine here so the + * upper layer can print it. + */ char *version; - /* Initialize the data and return the amount of I/O space to - reserve for the space. */ + /* + * Initialize the data and return the amount of I/O space to + * reserve for the space. + */ unsigned int (*init_data)(struct si_sm_data *smi, struct si_sm_io *io); - /* Start a new transaction in the state machine. This will - return -2 if the state machine is not idle, -1 if the size - is invalid (to large or too small), or 0 if the transaction - is successfully completed. */ + /* + * Start a new transaction in the state machine. This will + * return -2 if the state machine is not idle, -1 if the size + * is invalid (to large or too small), or 0 if the transaction + * is successfully completed. + */ int (*start_transaction)(struct si_sm_data *smi, unsigned char *data, unsigned int size); - /* Return the results after the transaction. This will return - -1 if the buffer is too small, zero if no transaction is - present, or the actual length of the result data. */ + /* + * Return the results after the transaction. This will return + * -1 if the buffer is too small, zero if no transaction is + * present, or the actual length of the result data. + */ int (*get_result)(struct si_sm_data *smi, unsigned char *data, unsigned int length); - /* Call this periodically (for a polled interface) or upon - receiving an interrupt (for a interrupt-driven interface). - If interrupt driven, you should probably poll this - periodically when not in idle state. This should be called - with the time that passed since the last call, if it is - significant. Time is in microseconds. */ + /* + * Call this periodically (for a polled interface) or upon + * receiving an interrupt (for a interrupt-driven interface). + * If interrupt driven, you should probably poll this + * periodically when not in idle state. This should be called + * with the time that passed since the last call, if it is + * significant. Time is in microseconds. + */ enum si_sm_result (*event)(struct si_sm_data *smi, long time); - /* Attempt to detect an SMI. Returns 0 on success or nonzero - on failure. */ + /* + * Attempt to detect an SMI. Returns 0 on success or nonzero + * on failure. + */ int (*detect)(struct si_sm_data *smi); /* The interface is shutting down, so clean it up. */ diff --git a/drivers/char/ipmi/ipmi_smic_sm.c b/drivers/char/ipmi/ipmi_smic_sm.c index 39d7e5ef1a2..c8e77afa8b9 100644 --- a/drivers/char/ipmi/ipmi_smic_sm.c +++ b/drivers/char/ipmi/ipmi_smic_sm.c @@ -80,11 +80,12 @@ enum smic_states { #define SMIC_MAX_ERROR_RETRIES 3 /* Timeouts in microseconds. */ -#define SMIC_RETRY_TIMEOUT 2000000 +#define SMIC_RETRY_TIMEOUT (2*USEC_PER_SEC) /* SMIC Flags Register Bits */ #define SMIC_RX_DATA_READY 0x80 #define SMIC_TX_DATA_READY 0x40 + /* * SMIC_SMI and SMIC_EVM_DATA_AVAIL are only used by * a few systems, and then only by Systems Management @@ -104,23 +105,22 @@ enum smic_states { #define EC_ILLEGAL_COMMAND 0x04 #define EC_BUFFER_FULL 0x05 -struct si_sm_data -{ +struct si_sm_data { enum smic_states state; struct si_sm_io *io; - unsigned char write_data[MAX_SMIC_WRITE_SIZE]; - int write_pos; - int write_count; - int orig_write_count; - unsigned char read_data[MAX_SMIC_READ_SIZE]; - int read_pos; - int truncated; - unsigned int error_retries; - long smic_timeout; + unsigned char write_data[MAX_SMIC_WRITE_SIZE]; + int write_pos; + int write_count; + int orig_write_count; + unsigned char read_data[MAX_SMIC_READ_SIZE]; + int read_pos; + int truncated; + unsigned int error_retries; + long smic_timeout; }; -static unsigned int init_smic_data (struct si_sm_data *smic, - struct si_sm_io *io) +static unsigned int init_smic_data(struct si_sm_data *smic, + struct si_sm_io *io) { smic->state = SMIC_IDLE; smic->io = io; @@ -141,18 +141,19 @@ static int start_smic_transaction(struct si_sm_data *smic, { unsigned int i; - if ((size < 2) || (size > MAX_SMIC_WRITE_SIZE)) { - return -1; - } - if ((smic->state != SMIC_IDLE) && (smic->state != SMIC_HOSED)) { - return -2; - } + if (size < 2) + return IPMI_REQ_LEN_INVALID_ERR; + if (size > MAX_SMIC_WRITE_SIZE) + return IPMI_REQ_LEN_EXCEEDED_ERR; + + if ((smic->state != SMIC_IDLE) && (smic->state != SMIC_HOSED)) + return IPMI_NOT_IN_MY_STATE_ERR; + if (smic_debug & SMIC_DEBUG_MSG) { - printk(KERN_INFO "start_smic_transaction -"); - for (i = 0; i < size; i ++) { - printk (" %02x", (unsigned char) (data [i])); - } - printk ("\n"); + printk(KERN_DEBUG "start_smic_transaction -"); + for (i = 0; i < size; i++) + printk(" %02x", (unsigned char) data[i]); + printk("\n"); } smic->error_retries = 0; memcpy(smic->write_data, data, size); @@ -171,11 +172,10 @@ static int smic_get_result(struct si_sm_data *smic, int i; if (smic_debug & SMIC_DEBUG_MSG) { - printk (KERN_INFO "smic_get result -"); - for (i = 0; i < smic->read_pos; i ++) { - printk (" %02x", (smic->read_data [i])); - } - printk ("\n"); + printk(KERN_DEBUG "smic_get result -"); + for (i = 0; i < smic->read_pos; i++) + printk(" %02x", smic->read_data[i]); + printk("\n"); } if (length < smic->read_pos) { smic->read_pos = length; @@ -221,8 +221,8 @@ static inline void write_smic_control(struct si_sm_data *smic, smic->io->outputb(smic->io, 1, control); } -static inline void write_si_sm_data (struct si_sm_data *smic, - unsigned char data) +static inline void write_si_sm_data(struct si_sm_data *smic, + unsigned char data) { smic->io->outputb(smic->io, 0, data); } @@ -231,10 +231,9 @@ static inline void start_error_recovery(struct si_sm_data *smic, char *reason) { (smic->error_retries)++; if (smic->error_retries > SMIC_MAX_ERROR_RETRIES) { - if (smic_debug & SMIC_DEBUG_ENABLE) { + if (smic_debug & SMIC_DEBUG_ENABLE) printk(KERN_WARNING "ipmi_smic_drv: smic hosed: %s\n", reason); - } smic->state = SMIC_HOSED; } else { smic->write_count = smic->orig_write_count; @@ -252,14 +251,14 @@ static inline void write_next_byte(struct si_sm_data *smic) (smic->write_count)--; } -static inline void read_next_byte (struct si_sm_data *smic) +static inline void read_next_byte(struct si_sm_data *smic) { if (smic->read_pos >= MAX_SMIC_READ_SIZE) { - read_smic_data (smic); + read_smic_data(smic); smic->truncated = 1; } else { smic->read_data[smic->read_pos] = read_smic_data(smic); - (smic->read_pos)++; + smic->read_pos++; } } @@ -334,7 +333,7 @@ static inline void read_next_byte (struct si_sm_data *smic) SMIC_SC_SMS_RD_END 0xC6 */ -static enum si_sm_result smic_event (struct si_sm_data *smic, long time) +static enum si_sm_result smic_event(struct si_sm_data *smic, long time) { unsigned char status; unsigned char flags; @@ -345,13 +344,15 @@ static enum si_sm_result smic_event (struct si_sm_data *smic, long time) return SI_SM_HOSED; } if (smic->state != SMIC_IDLE) { - if (smic_debug & SMIC_DEBUG_STATES) { - printk(KERN_INFO + if (smic_debug & SMIC_DEBUG_STATES) + printk(KERN_DEBUG "smic_event - smic->smic_timeout = %ld," " time = %ld\n", smic->smic_timeout, time); - } -/* FIXME: smic_event is sometimes called with time > SMIC_RETRY_TIMEOUT */ + /* + * FIXME: smic_event is sometimes called with time > + * SMIC_RETRY_TIMEOUT + */ if (time < SMIC_RETRY_TIMEOUT) { smic->smic_timeout -= time; if (smic->smic_timeout < 0) { @@ -364,9 +365,9 @@ static enum si_sm_result smic_event (struct si_sm_data *smic, long time) if (flags & SMIC_FLAG_BSY) return SI_SM_CALL_WITH_DELAY; - status = read_smic_status (smic); + status = read_smic_status(smic); if (smic_debug & SMIC_DEBUG_STATES) - printk(KERN_INFO + printk(KERN_DEBUG "smic_event - state = %d, flags = 0x%02x," " status = 0x%02x\n", smic->state, flags, status); @@ -375,9 +376,7 @@ static enum si_sm_result smic_event (struct si_sm_data *smic, long time) case SMIC_IDLE: /* in IDLE we check for available messages */ if (flags & SMIC_SMS_DATA_AVAIL) - { return SI_SM_ATTN; - } return SI_SM_IDLE; case SMIC_START_OP: @@ -389,7 +388,7 @@ static enum si_sm_result smic_event (struct si_sm_data *smic, long time) case SMIC_OP_OK: if (status != SMIC_SC_SMS_READY) { - /* this should not happen */ + /* this should not happen */ start_error_recovery(smic, "state = SMIC_OP_OK," " status != SMIC_SC_SMS_READY"); @@ -409,8 +408,10 @@ static enum si_sm_result smic_event (struct si_sm_data *smic, long time) "status != SMIC_SC_SMS_WR_START"); return SI_SM_CALL_WITH_DELAY; } - /* we must not issue WR_(NEXT|END) unless - TX_DATA_READY is set */ + /* + * we must not issue WR_(NEXT|END) unless + * TX_DATA_READY is set + * */ if (flags & SMIC_TX_DATA_READY) { if (smic->write_count == 1) { /* last byte */ @@ -422,10 +423,8 @@ static enum si_sm_result smic_event (struct si_sm_data *smic, long time) } write_next_byte(smic); write_smic_flags(smic, flags | SMIC_FLAG_BSY); - } - else { + } else return SI_SM_CALL_WITH_DELAY; - } break; case SMIC_WRITE_NEXT: @@ -440,52 +439,48 @@ static enum si_sm_result smic_event (struct si_sm_data *smic, long time) if (smic->write_count == 1) { write_smic_control(smic, SMIC_CC_SMS_WR_END); smic->state = SMIC_WRITE_END; - } - else { + } else { write_smic_control(smic, SMIC_CC_SMS_WR_NEXT); smic->state = SMIC_WRITE_NEXT; } write_next_byte(smic); write_smic_flags(smic, flags | SMIC_FLAG_BSY); - } - else { + } else return SI_SM_CALL_WITH_DELAY; - } break; case SMIC_WRITE_END: if (status != SMIC_SC_SMS_WR_END) { - start_error_recovery (smic, - "state = SMIC_WRITE_END, " - "status != SMIC_SC_SMS_WR_END"); + start_error_recovery(smic, + "state = SMIC_WRITE_END, " + "status != SMIC_SC_SMS_WR_END"); return SI_SM_CALL_WITH_DELAY; } /* data register holds an error code */ data = read_smic_data(smic); if (data != 0) { - if (smic_debug & SMIC_DEBUG_ENABLE) { - printk(KERN_INFO + if (smic_debug & SMIC_DEBUG_ENABLE) + printk(KERN_DEBUG "SMIC_WRITE_END: data = %02x\n", data); - } start_error_recovery(smic, "state = SMIC_WRITE_END, " "data != SUCCESS"); return SI_SM_CALL_WITH_DELAY; - } else { + } else smic->state = SMIC_WRITE2READ; - } break; case SMIC_WRITE2READ: - /* we must wait for RX_DATA_READY to be set before we - can continue */ + /* + * we must wait for RX_DATA_READY to be set before we + * can continue + */ if (flags & SMIC_RX_DATA_READY) { write_smic_control(smic, SMIC_CC_SMS_RD_START); write_smic_flags(smic, flags | SMIC_FLAG_BSY); smic->state = SMIC_READ_START; - } else { + } else return SI_SM_CALL_WITH_DELAY; - } break; case SMIC_READ_START: @@ -500,15 +495,16 @@ static enum si_sm_result smic_event (struct si_sm_data *smic, long time) write_smic_control(smic, SMIC_CC_SMS_RD_NEXT); write_smic_flags(smic, flags | SMIC_FLAG_BSY); smic->state = SMIC_READ_NEXT; - } else { + } else return SI_SM_CALL_WITH_DELAY; - } break; case SMIC_READ_NEXT: switch (status) { - /* smic tells us that this is the last byte to be read - --> clean up */ + /* + * smic tells us that this is the last byte to be read + * --> clean up + */ case SMIC_SC_SMS_RD_END: read_next_byte(smic); write_smic_control(smic, SMIC_CC_SMS_RD_END); @@ -521,9 +517,8 @@ static enum si_sm_result smic_event (struct si_sm_data *smic, long time) write_smic_control(smic, SMIC_CC_SMS_RD_NEXT); write_smic_flags(smic, flags | SMIC_FLAG_BSY); smic->state = SMIC_READ_NEXT; - } else { + } else return SI_SM_CALL_WITH_DELAY; - } break; default: start_error_recovery( @@ -544,10 +539,9 @@ static enum si_sm_result smic_event (struct si_sm_data *smic, long time) data = read_smic_data(smic); /* data register holds an error code */ if (data != 0) { - if (smic_debug & SMIC_DEBUG_ENABLE) { - printk(KERN_INFO + if (smic_debug & SMIC_DEBUG_ENABLE) + printk(KERN_DEBUG "SMIC_READ_END: data = %02x\n", data); - } start_error_recovery(smic, "state = SMIC_READ_END, " "data != SUCCESS"); @@ -563,7 +557,7 @@ static enum si_sm_result smic_event (struct si_sm_data *smic, long time) default: if (smic_debug & SMIC_DEBUG_ENABLE) { - printk(KERN_WARNING "smic->state = %d\n", smic->state); + printk(KERN_DEBUG "smic->state = %d\n", smic->state); start_error_recovery(smic, "state = UNKNOWN"); return SI_SM_CALL_WITH_DELAY; } @@ -574,10 +568,12 @@ static enum si_sm_result smic_event (struct si_sm_data *smic, long time) static int smic_detect(struct si_sm_data *smic) { - /* It's impossible for the SMIC fnags register to be all 1's, - (assuming a properly functioning, self-initialized BMC) - but that's what you get from reading a bogus address, so we - test that first. */ + /* + * It's impossible for the SMIC fnags register to be all 1's, + * (assuming a properly functioning, self-initialized BMC) + * but that's what you get from reading a bogus address, so we + * test that first. + */ if (read_smic_flags(smic) == 0xff) return 1; @@ -593,8 +589,7 @@ static int smic_size(void) return sizeof(struct si_sm_data); } -struct si_sm_handlers smic_smi_handlers = -{ +struct si_sm_handlers smic_smi_handlers = { .init_data = init_smic_data, .start_transaction = start_smic_transaction, .get_result = smic_get_result, diff --git a/drivers/char/ipmi/ipmi_watchdog.c b/drivers/char/ipmi/ipmi_watchdog.c index accaaf1a6b6..37b8be7cba9 100644 --- a/drivers/char/ipmi/ipmi_watchdog.c +++ b/drivers/char/ipmi/ipmi_watchdog.c @@ -35,10 +35,12 @@ #include <linux/moduleparam.h> #include <linux/ipmi.h> #include <linux/ipmi_smi.h> +#include <linux/mutex.h> #include <linux/watchdog.h> #include <linux/miscdevice.h> #include <linux/init.h> #include <linux/completion.h> +#include <linux/kdebug.h> #include <linux/rwsem.h> #include <linux/errno.h> #include <asm/uaccess.h> @@ -49,9 +51,22 @@ #include <linux/poll.h> #include <linux/string.h> #include <linux/ctype.h> -#include <asm/atomic.h> -#ifdef CONFIG_X86_LOCAL_APIC -#include <asm/apic.h> +#include <linux/delay.h> +#include <linux/atomic.h> + +#ifdef CONFIG_X86 +/* + * This is ugly, but I've determined that x86 is the only architecture + * that can reasonably support the IPMI NMI watchdog timeout at this + * time. If another architecture adds this capability somehow, it + * will have to be a somewhat different mechanism and I have no idea + * how it will work. So in the unlikely event that another + * architecture supports this, we can figure out a good generic + * mechanism for it at that time. + */ +#include <asm/kdebug.h> +#include <asm/nmi.h> +#define HAVE_DIE_NMI #endif #define PFX "IPMI Watchdog: " @@ -84,9 +99,8 @@ /* Operations that can be performed on a pretimout. */ #define WDOG_PREOP_NONE 0 #define WDOG_PREOP_PANIC 1 -#define WDOG_PREOP_GIVE_DATA 2 /* Cause data to be available to - read. Doesn't work in NMI - mode. */ +/* Cause data to be available to read. Doesn't work in NMI mode. */ +#define WDOG_PREOP_GIVE_DATA 2 /* Actions to perform on a full timeout. */ #define WDOG_SET_TIMEOUT_ACT(byte, use) \ @@ -97,8 +111,10 @@ #define WDOG_TIMEOUT_POWER_DOWN 2 #define WDOG_TIMEOUT_POWER_CYCLE 3 -/* Byte 3 of the get command, byte 4 of the get response is the - pre-timeout in seconds. */ +/* + * Byte 3 of the get command, byte 4 of the get response is the + * pre-timeout in seconds. + */ /* Bits for setting byte 4 of the set command, byte 5 of the get response. */ #define WDOG_EXPIRE_CLEAR_BIOS_FRB2 (1 << 1) @@ -107,11 +123,13 @@ #define WDOG_EXPIRE_CLEAR_SMS_OS (1 << 4) #define WDOG_EXPIRE_CLEAR_OEM (1 << 5) -/* Setting/getting the watchdog timer value. This is for bytes 5 and - 6 (the timeout time) of the set command, and bytes 6 and 7 (the - timeout time) and 8 and 9 (the current countdown value) of the - response. The timeout value is given in seconds (in the command it - is 100ms intervals). */ +/* + * Setting/getting the watchdog timer value. This is for bytes 5 and + * 6 (the timeout time) of the set command, and bytes 6 and 7 (the + * timeout time) and 8 and 9 (the current countdown value) of the + * response. The timeout value is given in seconds (in the command it + * is 100ms intervals). + */ #define WDOG_SET_TIMEOUT(byte1, byte2, val) \ (byte1) = (((val) * 10) & 0xff), (byte2) = (((val) * 10) >> 8) #define WDOG_GET_TIMEOUT(byte1, byte2) \ @@ -121,26 +139,19 @@ #define IPMI_WDOG_SET_TIMER 0x24 #define IPMI_WDOG_GET_TIMER 0x25 -/* These are here until the real ones get into the watchdog.h interface. */ -#ifndef WDIOC_GETTIMEOUT -#define WDIOC_GETTIMEOUT _IOW(WATCHDOG_IOCTL_BASE, 20, int) -#endif -#ifndef WDIOC_SET_PRETIMEOUT -#define WDIOC_SET_PRETIMEOUT _IOW(WATCHDOG_IOCTL_BASE, 21, int) -#endif -#ifndef WDIOC_GET_PRETIMEOUT -#define WDIOC_GET_PRETIMEOUT _IOW(WATCHDOG_IOCTL_BASE, 22, int) -#endif +#define IPMI_WDOG_TIMER_NOT_INIT_RESP 0x80 -static int nowayout = WATCHDOG_NOWAYOUT; +static DEFINE_MUTEX(ipmi_watchdog_mutex); +static bool nowayout = WATCHDOG_NOWAYOUT; -static ipmi_user_t watchdog_user = NULL; +static ipmi_user_t watchdog_user; +static int watchdog_ifnum; /* Default the timeout to 10 seconds. */ static int timeout = 10; /* The pre-timeout is disabled by default. */ -static int pretimeout = 0; +static int pretimeout; /* Default action is to reset the board on a timeout. */ static unsigned char action_val = WDOG_TIMEOUT_RESET; @@ -155,13 +166,13 @@ static unsigned char preop_val = WDOG_PREOP_NONE; static char preop[16] = "preop_none"; static DEFINE_SPINLOCK(ipmi_read_lock); -static char data_to_read = 0; +static char data_to_read; static DECLARE_WAIT_QUEUE_HEAD(read_q); -static struct fasync_struct *fasync_q = NULL; -static char pretimeout_since_last_heartbeat = 0; +static struct fasync_struct *fasync_q; +static char pretimeout_since_last_heartbeat; static char expect_close; -static DECLARE_RWSEM(register_sem); +static int ifnum_to_use = -1; /* Parameters to ipmi_set_timeout */ #define IPMI_SET_TIMEOUT_NO_HB 0 @@ -169,12 +180,16 @@ static DECLARE_RWSEM(register_sem); #define IPMI_SET_TIMEOUT_FORCE_HB 2 static int ipmi_set_timeout(int do_heartbeat); +static void ipmi_register_watchdog(int ipmi_intf); +static void ipmi_unregister_watchdog(int ipmi_intf); -/* If true, the driver will start running as soon as it is configured - and ready. */ -static int start_now = 0; +/* + * If true, the driver will start running as soon as it is configured + * and ready. + */ +static int start_now; -static int set_param_int(const char *val, struct kernel_param *kp) +static int set_param_timeout(const char *val, const struct kernel_param *kp) { char *endp; int l; @@ -186,19 +201,18 @@ static int set_param_int(const char *val, struct kernel_param *kp) if (endp == val) return -EINVAL; - down_read(®ister_sem); *((int *)kp->arg) = l; if (watchdog_user) rv = ipmi_set_timeout(IPMI_SET_TIMEOUT_HB_IF_NECESSARY); - up_read(®ister_sem); return rv; } -static int get_param_int(char *buffer, struct kernel_param *kp) -{ - return sprintf(buffer, "%i", *((int *)kp->arg)); -} +static struct kernel_param_ops param_ops_timeout = { + .set = set_param_timeout, + .get = param_get_int, +}; +#define param_check_timeout param_check_int typedef int (*action_fn)(const char *intval, char *outval); @@ -207,34 +221,31 @@ static int preaction_op(const char *inval, char *outval); static int preop_op(const char *inval, char *outval); static void check_parms(void); -static int set_param_str(const char *val, struct kernel_param *kp) +static int set_param_str(const char *val, const struct kernel_param *kp) { action_fn fn = (action_fn) kp->arg; int rv = 0; - char *dup, *s; + char valcp[16]; + char *s; - dup = kstrdup(val, GFP_KERNEL); - if (!dup) - return -ENOMEM; + strncpy(valcp, val, 16); + valcp[15] = '\0'; - s = strstrip(dup); + s = strstrip(valcp); - down_read(®ister_sem); rv = fn(s, NULL); if (rv) - goto out_unlock; + goto out; check_parms(); if (watchdog_user) rv = ipmi_set_timeout(IPMI_SET_TIMEOUT_HB_IF_NECESSARY); - out_unlock: - up_read(®ister_sem); - kfree(dup); + out: return rv; } -static int get_param_str(char *buffer, struct kernel_param *kp) +static int get_param_str(char *buffer, const struct kernel_param *kp) { action_fn fn = (action_fn) kp->arg; int rv; @@ -245,45 +256,79 @@ static int get_param_str(char *buffer, struct kernel_param *kp) return strlen(buffer); } -module_param_call(timeout, set_param_int, get_param_int, &timeout, 0644); + +static int set_param_wdog_ifnum(const char *val, const struct kernel_param *kp) +{ + int rv = param_set_int(val, kp); + if (rv) + return rv; + if ((ifnum_to_use < 0) || (ifnum_to_use == watchdog_ifnum)) + return 0; + + ipmi_unregister_watchdog(watchdog_ifnum); + ipmi_register_watchdog(ifnum_to_use); + return 0; +} + +static struct kernel_param_ops param_ops_wdog_ifnum = { + .set = set_param_wdog_ifnum, + .get = param_get_int, +}; + +#define param_check_wdog_ifnum param_check_int + +static struct kernel_param_ops param_ops_str = { + .set = set_param_str, + .get = get_param_str, +}; + +module_param(ifnum_to_use, wdog_ifnum, 0644); +MODULE_PARM_DESC(ifnum_to_use, "The interface number to use for the watchdog " + "timer. Setting to -1 defaults to the first registered " + "interface"); + +module_param(timeout, timeout, 0644); MODULE_PARM_DESC(timeout, "Timeout value in seconds."); -module_param_call(pretimeout, set_param_int, get_param_int, &pretimeout, 0644); +module_param(pretimeout, timeout, 0644); MODULE_PARM_DESC(pretimeout, "Pretimeout value in seconds."); -module_param_call(action, set_param_str, get_param_str, action_op, 0644); +module_param_cb(action, ¶m_ops_str, action_op, 0644); MODULE_PARM_DESC(action, "Timeout action. One of: " "reset, none, power_cycle, power_off."); -module_param_call(preaction, set_param_str, get_param_str, preaction_op, 0644); +module_param_cb(preaction, ¶m_ops_str, preaction_op, 0644); MODULE_PARM_DESC(preaction, "Pretimeout action. One of: " "pre_none, pre_smi, pre_nmi, pre_int."); -module_param_call(preop, set_param_str, get_param_str, preop_op, 0644); +module_param_cb(preop, ¶m_ops_str, preop_op, 0644); MODULE_PARM_DESC(preop, "Pretimeout driver operation. One of: " "preop_none, preop_panic, preop_give_data."); -module_param(start_now, int, 0); +module_param(start_now, int, 0444); MODULE_PARM_DESC(start_now, "Set to 1 to start the watchdog as" "soon as the driver is loaded."); -module_param(nowayout, int, 0644); -MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)"); +module_param(nowayout, bool, 0644); +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started " + "(default=CONFIG_WATCHDOG_NOWAYOUT)"); /* Default state of the timer. */ static unsigned char ipmi_watchdog_state = WDOG_TIMEOUT_NONE; /* If shutting down via IPMI, we ignore the heartbeat. */ -static int ipmi_ignore_heartbeat = 0; +static int ipmi_ignore_heartbeat; /* Is someone using the watchdog? Only one user is allowed. */ -static unsigned long ipmi_wdog_open = 0; +static unsigned long ipmi_wdog_open; -/* If set to 1, the heartbeat command will set the state to reset and - start the timer. The timer doesn't normally run when the driver is - first opened until the heartbeat is set the first time, this - variable is used to accomplish this. */ -static int ipmi_start_timer_on_heartbeat = 0; +/* + * If set to 1, the heartbeat command will set the state to reset and + * start the timer. The timer doesn't normally run when the driver is + * first opened until the heartbeat is set the first time, this + * variable is used to accomplish this. + */ +static int ipmi_start_timer_on_heartbeat; /* IPMI version of the BMC. */ static unsigned char ipmi_version_major; @@ -292,14 +337,19 @@ static unsigned char ipmi_version_minor; /* If a pretimeout occurs, this is used to allow only one panic to happen. */ static atomic_t preop_panic_excl = ATOMIC_INIT(-1); -static int ipmi_heartbeat(void); -static void panic_halt_ipmi_heartbeat(void); +#ifdef HAVE_DIE_NMI +static int testing_nmi; +static int nmi_handler_registered; +#endif +static int ipmi_heartbeat(void); -/* We use a mutex to make sure that only one thing can send a set - timeout at one time, because we only have one copy of the data. - The mutex is claimed when the set_timeout is sent and freed - when both messages are free. */ +/* + * We use a mutex to make sure that only one thing can send a set + * timeout at one time, because we only have one copy of the data. + * The mutex is claimed when the set_timeout is sent and freed + * when both messages are free. + */ static atomic_t set_timeout_tofree = ATOMIC_INIT(0); static DEFINE_MUTEX(set_timeout_lock); static DECLARE_COMPLETION(set_timeout_wait); @@ -313,15 +363,13 @@ static void set_timeout_free_recv(struct ipmi_recv_msg *msg) if (atomic_dec_and_test(&set_timeout_tofree)) complete(&set_timeout_wait); } -static struct ipmi_smi_msg set_timeout_smi_msg = -{ +static struct ipmi_smi_msg set_timeout_smi_msg = { .done = set_timeout_free_smi }; -static struct ipmi_recv_msg set_timeout_recv_msg = -{ +static struct ipmi_recv_msg set_timeout_recv_msg = { .done = set_timeout_free_recv }; - + static int i_ipmi_set_timeout(struct ipmi_smi_msg *smi_msg, struct ipmi_recv_msg *recv_msg, int *send_heartbeat_now) @@ -333,17 +381,21 @@ static int i_ipmi_set_timeout(struct ipmi_smi_msg *smi_msg, int hbnow = 0; + /* These can be cleared as we are setting the timeout. */ + pretimeout_since_last_heartbeat = 0; + data[0] = 0; WDOG_SET_TIMER_USE(data[0], WDOG_TIMER_USE_SMS_OS); if ((ipmi_version_major > 1) - || ((ipmi_version_major == 1) && (ipmi_version_minor >= 5))) - { + || ((ipmi_version_major == 1) && (ipmi_version_minor >= 5))) { /* This is an IPMI 1.5-only feature. */ data[0] |= WDOG_DONT_STOP_ON_SET; } else if (ipmi_watchdog_state != WDOG_TIMEOUT_NONE) { - /* In ipmi 1.0, setting the timer stops the watchdog, we - need to start it back up again. */ + /* + * In ipmi 1.0, setting the timer stops the watchdog, we + * need to start it back up again. + */ hbnow = 1; } @@ -407,55 +459,112 @@ static int ipmi_set_timeout(int do_heartbeat) wait_for_completion(&set_timeout_wait); + mutex_unlock(&set_timeout_lock); + if ((do_heartbeat == IPMI_SET_TIMEOUT_FORCE_HB) || ((send_heartbeat_now) && (do_heartbeat == IPMI_SET_TIMEOUT_HB_IF_NECESSARY))) - { rv = ipmi_heartbeat(); - } - mutex_unlock(&set_timeout_lock); out: return rv; } -static void dummy_smi_free(struct ipmi_smi_msg *msg) +static atomic_t panic_done_count = ATOMIC_INIT(0); + +static void panic_smi_free(struct ipmi_smi_msg *msg) { + atomic_dec(&panic_done_count); } -static void dummy_recv_free(struct ipmi_recv_msg *msg) +static void panic_recv_free(struct ipmi_recv_msg *msg) { + atomic_dec(&panic_done_count); } -static struct ipmi_smi_msg panic_halt_smi_msg = -{ - .done = dummy_smi_free + +static struct ipmi_smi_msg panic_halt_heartbeat_smi_msg = { + .done = panic_smi_free +}; +static struct ipmi_recv_msg panic_halt_heartbeat_recv_msg = { + .done = panic_recv_free }; -static struct ipmi_recv_msg panic_halt_recv_msg = + +static void panic_halt_ipmi_heartbeat(void) { - .done = dummy_recv_free + struct kernel_ipmi_msg msg; + struct ipmi_system_interface_addr addr; + int rv; + + /* + * Don't reset the timer if we have the timer turned off, that + * re-enables the watchdog. + */ + if (ipmi_watchdog_state == WDOG_TIMEOUT_NONE) + return; + + addr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; + addr.channel = IPMI_BMC_CHANNEL; + addr.lun = 0; + + msg.netfn = 0x06; + msg.cmd = IPMI_WDOG_RESET_TIMER; + msg.data = NULL; + msg.data_len = 0; + atomic_add(2, &panic_done_count); + rv = ipmi_request_supply_msgs(watchdog_user, + (struct ipmi_addr *) &addr, + 0, + &msg, + NULL, + &panic_halt_heartbeat_smi_msg, + &panic_halt_heartbeat_recv_msg, + 1); + if (rv) + atomic_sub(2, &panic_done_count); +} + +static struct ipmi_smi_msg panic_halt_smi_msg = { + .done = panic_smi_free +}; +static struct ipmi_recv_msg panic_halt_recv_msg = { + .done = panic_recv_free }; -/* Special call, doesn't claim any locks. This is only to be called - at panic or halt time, in run-to-completion mode, when the caller - is the only CPU and the only thing that will be going is these IPMI - calls. */ +/* + * Special call, doesn't claim any locks. This is only to be called + * at panic or halt time, in run-to-completion mode, when the caller + * is the only CPU and the only thing that will be going is these IPMI + * calls. + */ static void panic_halt_ipmi_set_timeout(void) { int send_heartbeat_now; int rv; + /* Wait for the messages to be free. */ + while (atomic_read(&panic_done_count) != 0) + ipmi_poll_interface(watchdog_user); + atomic_add(2, &panic_done_count); rv = i_ipmi_set_timeout(&panic_halt_smi_msg, &panic_halt_recv_msg, &send_heartbeat_now); - if (!rv) { + if (rv) { + atomic_sub(2, &panic_done_count); + printk(KERN_WARNING PFX + "Unable to extend the watchdog timeout."); + } else { if (send_heartbeat_now) panic_halt_ipmi_heartbeat(); } + while (atomic_read(&panic_done_count) != 0) + ipmi_poll_interface(watchdog_user); } -/* We use a semaphore to make sure that only one thing can send a - heartbeat at one time, because we only have one copy of the data. - The semaphore is claimed when the set_timeout is sent and freed - when both messages are free. */ +/* + * We use a mutex to make sure that only one thing can send a + * heartbeat at one time, because we only have one copy of the data. + * The semaphore is claimed when the set_timeout is sent and freed + * when both messages are free. + */ static atomic_t heartbeat_tofree = ATOMIC_INIT(0); static DEFINE_MUTEX(heartbeat_lock); static DECLARE_COMPLETION(heartbeat_wait); @@ -469,53 +578,46 @@ static void heartbeat_free_recv(struct ipmi_recv_msg *msg) if (atomic_dec_and_test(&heartbeat_tofree)) complete(&heartbeat_wait); } -static struct ipmi_smi_msg heartbeat_smi_msg = -{ +static struct ipmi_smi_msg heartbeat_smi_msg = { .done = heartbeat_free_smi }; -static struct ipmi_recv_msg heartbeat_recv_msg = -{ +static struct ipmi_recv_msg heartbeat_recv_msg = { .done = heartbeat_free_recv }; - -static struct ipmi_smi_msg panic_halt_heartbeat_smi_msg = -{ - .done = dummy_smi_free -}; -static struct ipmi_recv_msg panic_halt_heartbeat_recv_msg = -{ - .done = dummy_recv_free -}; - + static int ipmi_heartbeat(void) { struct kernel_ipmi_msg msg; int rv; struct ipmi_system_interface_addr addr; + int timeout_retries = 0; - if (ipmi_ignore_heartbeat) { + if (ipmi_ignore_heartbeat) return 0; - } if (ipmi_start_timer_on_heartbeat) { ipmi_start_timer_on_heartbeat = 0; ipmi_watchdog_state = action_val; return ipmi_set_timeout(IPMI_SET_TIMEOUT_FORCE_HB); } else if (pretimeout_since_last_heartbeat) { - /* A pretimeout occurred, make sure we set the timeout. - We don't want to set the action, though, we want to - leave that alone (thus it can't be combined with the - above operation. */ - pretimeout_since_last_heartbeat = 0; + /* + * A pretimeout occurred, make sure we set the timeout. + * We don't want to set the action, though, we want to + * leave that alone (thus it can't be combined with the + * above operation. + */ return ipmi_set_timeout(IPMI_SET_TIMEOUT_HB_IF_NECESSARY); } mutex_lock(&heartbeat_lock); +restart: atomic_set(&heartbeat_tofree, 2); - /* Don't reset the timer if we have the timer turned off, that - re-enables the watchdog. */ + /* + * Don't reset the timer if we have the timer turned off, that + * re-enables the watchdog. + */ if (ipmi_watchdog_state == WDOG_TIMEOUT_NONE) { mutex_unlock(&heartbeat_lock); return 0; @@ -547,62 +649,61 @@ static int ipmi_heartbeat(void) /* Wait for the heartbeat to be sent. */ wait_for_completion(&heartbeat_wait); - if (heartbeat_recv_msg.msg.data[0] != 0) { - /* Got an error in the heartbeat response. It was already - reported in ipmi_wdog_msg_handler, but we should return - an error here. */ - rv = -EINVAL; + if (heartbeat_recv_msg.msg.data[0] == IPMI_WDOG_TIMER_NOT_INIT_RESP) { + timeout_retries++; + if (timeout_retries > 3) { + printk(KERN_ERR PFX ": Unable to restore the IPMI" + " watchdog's settings, giving up.\n"); + rv = -EIO; + goto out_unlock; + } + + /* + * The timer was not initialized, that means the BMC was + * probably reset and lost the watchdog information. Attempt + * to restore the timer's info. Note that we still hold + * the heartbeat lock, to keep a heartbeat from happening + * in this process, so must say no heartbeat to avoid a + * deadlock on this mutex. + */ + rv = ipmi_set_timeout(IPMI_SET_TIMEOUT_NO_HB); + if (rv) { + printk(KERN_ERR PFX ": Unable to send the command to" + " set the watchdog's settings, giving up.\n"); + goto out_unlock; + } + + /* We might need a new heartbeat, so do it now */ + goto restart; + } else if (heartbeat_recv_msg.msg.data[0] != 0) { + /* + * Got an error in the heartbeat response. It was already + * reported in ipmi_wdog_msg_handler, but we should return + * an error here. + */ + rv = -EINVAL; } +out_unlock: mutex_unlock(&heartbeat_lock); return rv; } -static void panic_halt_ipmi_heartbeat(void) -{ - struct kernel_ipmi_msg msg; - struct ipmi_system_interface_addr addr; - - - /* Don't reset the timer if we have the timer turned off, that - re-enables the watchdog. */ - if (ipmi_watchdog_state == WDOG_TIMEOUT_NONE) - return; - - addr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; - addr.channel = IPMI_BMC_CHANNEL; - addr.lun = 0; - - msg.netfn = 0x06; - msg.cmd = IPMI_WDOG_RESET_TIMER; - msg.data = NULL; - msg.data_len = 0; - ipmi_request_supply_msgs(watchdog_user, - (struct ipmi_addr *) &addr, - 0, - &msg, - NULL, - &panic_halt_heartbeat_smi_msg, - &panic_halt_heartbeat_recv_msg, - 1); -} - -static struct watchdog_info ident = -{ +static struct watchdog_info ident = { .options = 0, /* WDIOF_SETTIMEOUT, */ .firmware_version = 1, .identity = "IPMI" }; -static int ipmi_ioctl(struct inode *inode, struct file *file, +static int ipmi_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { void __user *argp = (void __user *)arg; int i; int val; - switch(cmd) { + switch (cmd) { case WDIOC_GETSUPPORT: i = copy_to_user(argp, &ident, sizeof(ident)); return i ? -EFAULT : 0; @@ -620,14 +721,14 @@ static int ipmi_ioctl(struct inode *inode, struct file *file, return -EFAULT; return 0; - case WDIOC_SET_PRETIMEOUT: + case WDIOC_SETPRETIMEOUT: i = copy_from_user(&val, argp, sizeof(int)); if (i) return -EFAULT; pretimeout = val; return ipmi_set_timeout(IPMI_SET_TIMEOUT_HB_IF_NECESSARY); - case WDIOC_GET_PRETIMEOUT: + case WDIOC_GETPRETIMEOUT: i = copy_to_user(argp, &pretimeout, sizeof(pretimeout)); if (i) return -EFAULT; @@ -640,15 +741,13 @@ static int ipmi_ioctl(struct inode *inode, struct file *file, i = copy_from_user(&val, argp, sizeof(int)); if (i) return -EFAULT; - if (val & WDIOS_DISABLECARD) - { + if (val & WDIOS_DISABLECARD) { ipmi_watchdog_state = WDOG_TIMEOUT_NONE; ipmi_set_timeout(IPMI_SET_TIMEOUT_NO_HB); ipmi_start_timer_on_heartbeat = 0; } - if (val & WDIOS_ENABLECARD) - { + if (val & WDIOS_ENABLECARD) { ipmi_watchdog_state = action_val; ipmi_set_timeout(IPMI_SET_TIMEOUT_FORCE_HB); } @@ -666,6 +765,19 @@ static int ipmi_ioctl(struct inode *inode, struct file *file, } } +static long ipmi_unlocked_ioctl(struct file *file, + unsigned int cmd, + unsigned long arg) +{ + int ret; + + mutex_lock(&ipmi_watchdog_mutex); + ret = ipmi_ioctl(file, cmd, arg); + mutex_unlock(&ipmi_watchdog_mutex); + + return ret; +} + static ssize_t ipmi_write(struct file *file, const char __user *buf, size_t len, @@ -674,13 +786,13 @@ static ssize_t ipmi_write(struct file *file, int rv; if (len) { - if (!nowayout) { - size_t i; + if (!nowayout) { + size_t i; /* In case it was set long ago */ expect_close = 0; - for (i = 0; i != len; i++) { + for (i = 0; i != len; i++) { char c; if (get_user(c, buf + i)) @@ -692,9 +804,8 @@ static ssize_t ipmi_write(struct file *file, rv = ipmi_heartbeat(); if (rv) return rv; - return 1; } - return 0; + return len; } static ssize_t ipmi_read(struct file *file, @@ -708,15 +819,17 @@ static ssize_t ipmi_read(struct file *file, if (count <= 0) return 0; - /* Reading returns if the pretimeout has gone off, and it only does - it once per pretimeout. */ + /* + * Reading returns if the pretimeout has gone off, and it only does + * it once per pretimeout. + */ spin_lock(&ipmi_read_lock); if (!data_to_read) { if (file->f_flags & O_NONBLOCK) { rv = -EAGAIN; goto out; } - + init_waitqueue_entry(&wait, current); add_wait_queue(&read_q, &wait); while (!data_to_read) { @@ -726,7 +839,7 @@ static ssize_t ipmi_read(struct file *file, spin_lock(&ipmi_read_lock); } remove_wait_queue(&read_q, &wait); - + if (signal_pending(current)) { rv = -ERESTARTSYS; goto out; @@ -749,25 +862,28 @@ static ssize_t ipmi_read(struct file *file, static int ipmi_open(struct inode *ino, struct file *filep) { - switch (iminor(ino)) { - case WATCHDOG_MINOR: + switch (iminor(ino)) { + case WATCHDOG_MINOR: if (test_and_set_bit(0, &ipmi_wdog_open)) - return -EBUSY; + return -EBUSY; + - /* Don't start the timer now, let it start on the - first heartbeat. */ + /* + * Don't start the timer now, let it start on the + * first heartbeat. + */ ipmi_start_timer_on_heartbeat = 1; return nonseekable_open(ino, filep); default: return (-ENODEV); - } + } } static unsigned int ipmi_poll(struct file *file, poll_table *wait) { unsigned int mask = 0; - + poll_wait(file, &read_q, wait); spin_lock(&ipmi_read_lock); @@ -801,7 +917,6 @@ static int ipmi_close(struct inode *ino, struct file *filep) clear_bit(0, &ipmi_wdog_open); } - ipmi_fasync (-1, filep, 0); expect_close = 0; return 0; @@ -812,10 +927,11 @@ static const struct file_operations ipmi_wdog_fops = { .read = ipmi_read, .poll = ipmi_poll, .write = ipmi_write, - .ioctl = ipmi_ioctl, + .unlocked_ioctl = ipmi_unlocked_ioctl, .open = ipmi_open, .release = ipmi_close, .fasync = ipmi_fasync, + .llseek = no_llseek, }; static struct miscdevice ipmi_wdog_miscdev = { @@ -827,12 +943,16 @@ static struct miscdevice ipmi_wdog_miscdev = { static void ipmi_wdog_msg_handler(struct ipmi_recv_msg *msg, void *handler_data) { - if (msg->msg.data[0] != 0) { + if (msg->msg.cmd == IPMI_WDOG_RESET_TIMER && + msg->msg.data[0] == IPMI_WDOG_TIMER_NOT_INIT_RESP) + printk(KERN_INFO PFX "response: The IPMI controller appears" + " to have been reset, will attempt to reinitialize" + " the watchdog timer\n"); + else if (msg->msg.data[0] != 0) printk(KERN_ERR PFX "response: Error %x on cmd %x\n", msg->msg.data[0], msg->msg.cmd); - } - + ipmi_free_recv_msg(msg); } @@ -852,14 +972,14 @@ static void ipmi_wdog_pretimeout_handler(void *handler_data) } } - /* On some machines, the heartbeat will give - an error and not work unless we re-enable - the timer. So do so. */ + /* + * On some machines, the heartbeat will give an error and not + * work unless we re-enable the timer. So do so. + */ pretimeout_since_last_heartbeat = 1; } -static struct ipmi_user_hndl ipmi_hndlrs = -{ +static struct ipmi_user_hndl ipmi_hndlrs = { .ipmi_recv_hndl = ipmi_wdog_msg_handler, .ipmi_watchdog_pretimeout = ipmi_wdog_pretimeout_handler }; @@ -868,10 +988,14 @@ static void ipmi_register_watchdog(int ipmi_intf) { int rv = -EBUSY; - down_write(®ister_sem); if (watchdog_user) goto out; + if ((ifnum_to_use >= 0) && (ifnum_to_use != ipmi_intf)) + goto out; + + watchdog_ifnum = ipmi_intf; + rv = ipmi_create_user(ipmi_intf, &ipmi_hndlrs, NULL, &watchdog_user); if (rv < 0) { printk(KERN_CRIT PFX "Unable to register with ipmi\n"); @@ -889,29 +1013,122 @@ static void ipmi_register_watchdog(int ipmi_intf) printk(KERN_CRIT PFX "Unable to register misc device\n"); } - out: - up_write(®ister_sem); +#ifdef HAVE_DIE_NMI + if (nmi_handler_registered) { + int old_pretimeout = pretimeout; + int old_timeout = timeout; + int old_preop_val = preop_val; + /* + * Set the pretimeout to go off in a second and give + * ourselves plenty of time to stop the timer. + */ + ipmi_watchdog_state = WDOG_TIMEOUT_RESET; + preop_val = WDOG_PREOP_NONE; /* Make sure nothing happens */ + pretimeout = 99; + timeout = 100; + + testing_nmi = 1; + + rv = ipmi_set_timeout(IPMI_SET_TIMEOUT_FORCE_HB); + if (rv) { + printk(KERN_WARNING PFX "Error starting timer to" + " test NMI: 0x%x. The NMI pretimeout will" + " likely not work\n", rv); + rv = 0; + goto out_restore; + } + + msleep(1500); + + if (testing_nmi != 2) { + printk(KERN_WARNING PFX "IPMI NMI didn't seem to" + " occur. The NMI pretimeout will" + " likely not work\n"); + } + out_restore: + testing_nmi = 0; + preop_val = old_preop_val; + pretimeout = old_pretimeout; + timeout = old_timeout; + } +#endif + + out: if ((start_now) && (rv == 0)) { /* Run from startup, so start the timer now. */ start_now = 0; /* Disable this function after first startup. */ ipmi_watchdog_state = action_val; ipmi_set_timeout(IPMI_SET_TIMEOUT_FORCE_HB); printk(KERN_INFO PFX "Starting now!\n"); + } else { + /* Stop the timer now. */ + ipmi_watchdog_state = WDOG_TIMEOUT_NONE; + ipmi_set_timeout(IPMI_SET_TIMEOUT_NO_HB); + } +} + +static void ipmi_unregister_watchdog(int ipmi_intf) +{ + int rv; + + if (!watchdog_user) + goto out; + + if (watchdog_ifnum != ipmi_intf) + goto out; + + /* Make sure no one can call us any more. */ + misc_deregister(&ipmi_wdog_miscdev); + + /* + * Wait to make sure the message makes it out. The lower layer has + * pointers to our buffers, we want to make sure they are done before + * we release our memory. + */ + while (atomic_read(&set_timeout_tofree)) + schedule_timeout_uninterruptible(1); + + /* Disconnect from IPMI. */ + rv = ipmi_destroy_user(watchdog_user); + if (rv) { + printk(KERN_WARNING PFX "error unlinking from IPMI: %d\n", + rv); } + watchdog_user = NULL; + + out: + return; } -#ifdef HAVE_NMI_HANDLER +#ifdef HAVE_DIE_NMI static int -ipmi_nmi(void *dev_id, struct pt_regs *regs, int cpu, int handled) +ipmi_nmi(unsigned int val, struct pt_regs *regs) { - /* If we are not expecting a timeout, ignore it. */ + /* + * If we get here, it's an NMI that's not a memory or I/O + * error. We can't truly tell if it's from IPMI or not + * without sending a message, and sending a message is almost + * impossible because of locking. + */ + + if (testing_nmi) { + testing_nmi = 2; + return NMI_HANDLED; + } + + /* If we are not expecting a timeout, ignore it. */ if (ipmi_watchdog_state == WDOG_TIMEOUT_NONE) - return NOTIFY_DONE; + return NMI_DONE; + + if (preaction_val != WDOG_PRETIMEOUT_NMI) + return NMI_DONE; - /* If no one else handled the NMI, we assume it was the IPMI - watchdog. */ - if ((!handled) && (preop_val == WDOG_PREOP_PANIC)) { + /* + * If no one else handled the NMI, we assume it was the IPMI + * watchdog. + */ + if (preop_val == WDOG_PREOP_PANIC) { /* On some machines, the heartbeat will give an error and not work unless we re-enable the timer. So do so. */ @@ -920,34 +1137,24 @@ ipmi_nmi(void *dev_id, struct pt_regs *regs, int cpu, int handled) panic(PFX "pre-timeout"); } - return NOTIFY_DONE; + return NMI_HANDLED; } - -static struct nmi_handler ipmi_nmi_handler = -{ - .link = LIST_HEAD_INIT(ipmi_nmi_handler.link), - .dev_name = "ipmi_watchdog", - .dev_id = NULL, - .handler = ipmi_nmi, - .priority = 0, /* Call us last. */ -}; -int nmi_handler_registered; #endif static int wdog_reboot_handler(struct notifier_block *this, unsigned long code, void *unused) { - static int reboot_event_handled = 0; + static int reboot_event_handled; if ((watchdog_user) && (!reboot_event_handled)) { /* Make sure we only do this once. */ reboot_event_handled = 1; - if (code == SYS_DOWN || code == SYS_HALT) { + if (code == SYS_POWER_OFF || code == SYS_HALT) { /* Disable the WDT if we are shutting down. */ ipmi_watchdog_state = WDOG_TIMEOUT_NONE; - panic_halt_ipmi_set_timeout(); + ipmi_set_timeout(IPMI_SET_TIMEOUT_NO_HB); } else if (ipmi_watchdog_state != WDOG_TIMEOUT_NONE) { /* Set a long timer to let the reboot happens, but reboot if it hangs, but only if the watchdog @@ -955,7 +1162,7 @@ static int wdog_reboot_handler(struct notifier_block *this, timeout = 120; pretimeout = 0; ipmi_watchdog_state = WDOG_TIMEOUT_RESET; - panic_halt_ipmi_set_timeout(); + ipmi_set_timeout(IPMI_SET_TIMEOUT_NO_HB); } } return NOTIFY_OK; @@ -971,7 +1178,7 @@ static int wdog_panic_handler(struct notifier_block *this, unsigned long event, void *unused) { - static int panic_event_handled = 0; + static int panic_event_handled; /* On a panic, if we have a panic timeout, make sure to extend the watchdog timer to a reasonable value to complete the @@ -981,7 +1188,7 @@ static int wdog_panic_handler(struct notifier_block *this, ipmi_watchdog_state != WDOG_TIMEOUT_NONE) { /* Make sure we do this only once. */ panic_event_handled = 1; - + timeout = 255; pretimeout = 0; panic_halt_ipmi_set_timeout(); @@ -1004,13 +1211,10 @@ static void ipmi_new_smi(int if_num, struct device *device) static void ipmi_smi_gone(int if_num) { - /* This can never be called, because once the watchdog is - registered, the interface can't go away until the watchdog - is unregistered. */ + ipmi_unregister_watchdog(if_num); } -static struct ipmi_smi_watcher smi_watcher = -{ +static struct ipmi_smi_watcher smi_watcher = { .owner = THIS_MODULE, .new_smi = ipmi_new_smi, .smi_gone = ipmi_smi_gone @@ -1050,7 +1254,7 @@ static int preaction_op(const char *inval, char *outval) preaction_val = WDOG_PRETIMEOUT_NONE; else if (strcmp(inval, "pre_smi") == 0) preaction_val = WDOG_PRETIMEOUT_SMI; -#ifdef HAVE_NMI_HANDLER +#ifdef HAVE_DIE_NMI else if (strcmp(inval, "pre_nmi") == 0) preaction_val = WDOG_PRETIMEOUT_NMI; #endif @@ -1084,7 +1288,7 @@ static int preop_op(const char *inval, char *outval) static void check_parms(void) { -#ifdef HAVE_NMI_HANDLER +#ifdef HAVE_DIE_NMI int do_nmi = 0; int rv; @@ -1097,20 +1301,10 @@ static void check_parms(void) preop_op("preop_none", NULL); do_nmi = 0; } -#ifdef CONFIG_X86_LOCAL_APIC - if (nmi_watchdog == NMI_IO_APIC) { - printk(KERN_WARNING PFX "nmi_watchdog is set to IO APIC" - " mode (value is %d), that is incompatible" - " with using NMI in the IPMI watchdog." - " Disabling IPMI nmi pretimeout.\n", - nmi_watchdog); - preaction_val = WDOG_PRETIMEOUT_NONE; - do_nmi = 0; - } -#endif } if (do_nmi && !nmi_handler_registered) { - rv = request_nmi(&ipmi_nmi_handler); + rv = register_nmi_handler(NMI_UNKNOWN, ipmi_nmi, 0, + "ipmi"); if (rv) { printk(KERN_WARNING PFX "Can't register nmi handler\n"); @@ -1118,7 +1312,7 @@ static void check_parms(void) } else nmi_handler_registered = 1; } else if (!do_nmi && nmi_handler_registered) { - release_nmi(&ipmi_nmi_handler); + unregister_nmi_handler(NMI_UNKNOWN, "ipmi"); nmi_handler_registered = 0; } #endif @@ -1148,68 +1342,41 @@ static int __init ipmi_wdog_init(void) check_parms(); + register_reboot_notifier(&wdog_reboot_notifier); + atomic_notifier_chain_register(&panic_notifier_list, + &wdog_panic_notifier); + rv = ipmi_smi_watcher_register(&smi_watcher); if (rv) { -#ifdef HAVE_NMI_HANDLER - if (preaction_val == WDOG_PRETIMEOUT_NMI) - release_nmi(&ipmi_nmi_handler); +#ifdef HAVE_DIE_NMI + if (nmi_handler_registered) + unregister_nmi_handler(NMI_UNKNOWN, "ipmi"); #endif + atomic_notifier_chain_unregister(&panic_notifier_list, + &wdog_panic_notifier); + unregister_reboot_notifier(&wdog_reboot_notifier); printk(KERN_WARNING PFX "can't register smi watcher\n"); return rv; } - register_reboot_notifier(&wdog_reboot_notifier); - atomic_notifier_chain_register(&panic_notifier_list, - &wdog_panic_notifier); - printk(KERN_INFO PFX "driver initialized\n"); return 0; } -static __exit void ipmi_unregister_watchdog(void) +static void __exit ipmi_wdog_exit(void) { - int rv; - - down_write(®ister_sem); + ipmi_smi_watcher_unregister(&smi_watcher); + ipmi_unregister_watchdog(watchdog_ifnum); -#ifdef HAVE_NMI_HANDLER +#ifdef HAVE_DIE_NMI if (nmi_handler_registered) - release_nmi(&ipmi_nmi_handler); + unregister_nmi_handler(NMI_UNKNOWN, "ipmi"); #endif atomic_notifier_chain_unregister(&panic_notifier_list, - &wdog_panic_notifier); + &wdog_panic_notifier); unregister_reboot_notifier(&wdog_reboot_notifier); - - if (! watchdog_user) - goto out; - - /* Make sure no one can call us any more. */ - misc_deregister(&ipmi_wdog_miscdev); - - /* Wait to make sure the message makes it out. The lower layer has - pointers to our buffers, we want to make sure they are done before - we release our memory. */ - while (atomic_read(&set_timeout_tofree)) - schedule_timeout_uninterruptible(1); - - /* Disconnect from IPMI. */ - rv = ipmi_destroy_user(watchdog_user); - if (rv) { - printk(KERN_WARNING PFX "error unlinking from IPMI: %d\n", - rv); - } - watchdog_user = NULL; - - out: - up_write(®ister_sem); -} - -static void __exit ipmi_wdog_exit(void) -{ - ipmi_smi_watcher_unregister(&smi_watcher); - ipmi_unregister_watchdog(); } module_exit(ipmi_wdog_exit); module_init(ipmi_wdog_init); |
