summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/IPMI.txt4
-rw-r--r--drivers/char/ipmi/Kconfig35
-rw-r--r--drivers/char/ipmi/Makefile10
-rw-r--r--drivers/char/ipmi/ipmi_dmi.c76
-rw-r--r--drivers/char/ipmi/ipmi_dmi.h8
-rw-r--r--drivers/char/ipmi/ipmi_msghandler.c1275
-rw-r--r--drivers/char/ipmi/ipmi_powernv.c4
-rw-r--r--drivers/char/ipmi/ipmi_poweroff.c2
-rw-r--r--drivers/char/ipmi/ipmi_si.h49
-rw-r--r--drivers/char/ipmi/ipmi_si_hardcode.c146
-rw-r--r--drivers/char/ipmi/ipmi_si_hotmod.c242
-rw-r--r--drivers/char/ipmi/ipmi_si_intf.c2122
-rw-r--r--drivers/char/ipmi/ipmi_si_mem_io.c144
-rw-r--r--drivers/char/ipmi/ipmi_si_parisc.c58
-rw-r--r--drivers/char/ipmi/ipmi_si_pci.c166
-rw-r--r--drivers/char/ipmi/ipmi_si_platform.c593
-rw-r--r--drivers/char/ipmi/ipmi_si_port_io.c112
-rw-r--r--drivers/char/ipmi/ipmi_si_sm.h23
-rw-r--r--drivers/char/ipmi/ipmi_ssif.c112
-rw-r--r--drivers/char/ipmi/ipmi_watchdog.c11
-rw-r--r--include/linux/ipmi.h8
-rw-r--r--include/linux/ipmi_smi.h27
22 files changed, 3008 insertions, 2219 deletions
diff --git a/Documentation/IPMI.txt b/Documentation/IPMI.txt
index aa77a25a0940..5ef1047e2e66 100644
--- a/Documentation/IPMI.txt
+++ b/Documentation/IPMI.txt
@@ -81,7 +81,9 @@ If you want the driver to put an event into the event log on a panic,
enable the 'Generate a panic event to all BMCs on a panic' option. If
you want the whole panic string put into the event log using OEM
events, enable the 'Generate OEM events containing the panic string'
-option.
+option. You can also enable these dynamically by setting the module
+parameter named "panic_op" in the ipmi_msghandler module to "event"
+or "string". Setting that parameter to "none" disables this function.
Basic Design
------------
diff --git a/drivers/char/ipmi/Kconfig b/drivers/char/ipmi/Kconfig
index f6fa056a52fc..3544abc0f9f9 100644
--- a/drivers/char/ipmi/Kconfig
+++ b/drivers/char/ipmi/Kconfig
@@ -22,24 +22,39 @@ config IPMI_DMI_DECODE
if IPMI_HANDLER
+config IPMI_PROC_INTERFACE
+ bool 'Provide an interface for IPMI stats in /proc (deprecated)'
+ depends on PROC_FS
+ default y
+ help
+ Do not use this any more, use sysfs for this info. It will be
+ removed in future kernel versions.
+
config IPMI_PANIC_EVENT
bool 'Generate a panic event to all BMCs on a panic'
help
- When a panic occurs, this will cause the IPMI message handler to
- generate an IPMI event describing the panic to each interface
- registered with the message handler.
+ When a panic occurs, this will cause the IPMI message handler to,
+ by default, generate an IPMI event describing the panic to each
+ interface registered with the message handler. This is always
+ available, the module parameter for ipmi_msghandler named
+ panic_op can be set to "event" to chose this value, this config
+ simply causes the default value to be set to "event".
config IPMI_PANIC_STRING
bool 'Generate OEM events containing the panic string'
depends on IPMI_PANIC_EVENT
help
- When a panic occurs, this will cause the IPMI message handler to
- generate IPMI OEM type f0 events holding the IPMB address of the
- panic generator (byte 4 of the event), a sequence number for the
- string (byte 5 of the event) and part of the string (the rest of the
- event). Bytes 1, 2, and 3 are the normal usage for an OEM event.
- You can fetch these events and use the sequence numbers to piece the
- string together.
+ When a panic occurs, this will cause the IPMI message handler to,
+ by default, generate IPMI OEM type f0 events holding the IPMB
+ address of the panic generator (byte 4 of the event), a sequence
+ number for the string (byte 5 of the event) and part of the
+ string (the rest of the event). Bytes 1, 2, and 3 are the normal
+ usage for an OEM event. You can fetch these events and use the
+ sequence numbers to piece the string together. This config
+ parameter sets the default value to generate these events,
+ the module parameter for ipmi_msghandler named panic_op can
+ be set to "string" to chose this value, this config simply
+ causes the default value to be set to "string".
config IPMI_DEVICE_INTERFACE
tristate 'Device interface for IPMI'
diff --git a/drivers/char/ipmi/Makefile b/drivers/char/ipmi/Makefile
index eefb0b301e83..312396c1a8e5 100644
--- a/drivers/char/ipmi/Makefile
+++ b/drivers/char/ipmi/Makefile
@@ -2,7 +2,15 @@
# Makefile for the ipmi drivers.
#
-ipmi_si-y := 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 \
+ ipmi_si_hotmod.o ipmi_si_hardcode.o ipmi_si_platform.o \
+ ipmi_si_port_io.o ipmi_si_mem_io.o
+ifdef CONFIG_PCI
+ipmi_si-y += ipmi_si_pci.o
+endif
+ifdef CONFIG_PARISC
+ipmi_si-y += ipmi_si_parisc.o
+endif
obj-$(CONFIG_IPMI_HANDLER) += ipmi_msghandler.o
obj-$(CONFIG_IPMI_DEVICE_INTERFACE) += ipmi_devintf.o
diff --git a/drivers/char/ipmi/ipmi_dmi.c b/drivers/char/ipmi/ipmi_dmi.c
index 2a84401dea05..bb984176047b 100644
--- a/drivers/char/ipmi/ipmi_dmi.c
+++ b/drivers/char/ipmi/ipmi_dmi.c
@@ -8,10 +8,16 @@
#include <linux/dmi.h>
#include <linux/platform_device.h>
#include <linux/property.h>
+#include "ipmi_si_sm.h"
#include "ipmi_dmi.h"
+#define IPMI_DMI_TYPE_KCS 0x01
+#define IPMI_DMI_TYPE_SMIC 0x02
+#define IPMI_DMI_TYPE_BT 0x03
+#define IPMI_DMI_TYPE_SSIF 0x04
+
struct ipmi_dmi_info {
- int type;
+ enum si_type si_type;
u32 flags;
unsigned long addr;
u8 slave_addr;
@@ -22,6 +28,15 @@ static struct ipmi_dmi_info *ipmi_dmi_infos;
static int ipmi_dmi_nr __initdata;
+#define set_prop_entry(_p_, _name_, type, val) \
+do { \
+ struct property_entry *_p = &_p_; \
+ _p->name = _name_; \
+ _p->length = sizeof(type); \
+ _p->is_string = false; \
+ _p->value.type##_data = val; \
+} while(0)
+
static void __init dmi_add_platform_ipmi(unsigned long base_addr,
u32 flags,
u8 slave_addr,
@@ -32,27 +47,14 @@ static void __init dmi_add_platform_ipmi(unsigned long base_addr,
struct platform_device *pdev;
struct resource r[4];
unsigned int num_r = 1, size;
- struct property_entry p[4] = {
- PROPERTY_ENTRY_U8("slave-addr", slave_addr),
- PROPERTY_ENTRY_U8("ipmi-type", type),
- PROPERTY_ENTRY_U16("i2c-addr", base_addr),
- { }
- };
+ struct property_entry p[5];
+ unsigned int pidx = 0;
char *name, *override;
int rv;
+ enum si_type si_type;
struct ipmi_dmi_info *info;
- info = kmalloc(sizeof(*info), GFP_KERNEL);
- if (!info) {
- pr_warn("ipmi:dmi: Could not allocate dmi info\n");
- } else {
- info->type = type;
- info->flags = flags;
- info->addr = base_addr;
- info->slave_addr = slave_addr;
- info->next = ipmi_dmi_infos;
- ipmi_dmi_infos = info;
- }
+ memset(p, 0, sizeof(p));
name = "dmi-ipmi-si";
override = "ipmi_si";
@@ -62,28 +64,53 @@ static void __init dmi_add_platform_ipmi(unsigned long base_addr,
override = "ipmi_ssif";
offset = 1;
size = 1;
+ si_type = SI_TYPE_INVALID;
break;
case IPMI_DMI_TYPE_BT:
size = 3;
+ si_type = SI_BT;
break;
case IPMI_DMI_TYPE_KCS:
+ size = 2;
+ si_type = SI_KCS;
+ break;
case IPMI_DMI_TYPE_SMIC:
size = 2;
+ si_type = SI_SMIC;
break;
default:
- pr_err("ipmi:dmi: Invalid IPMI type: %d", type);
+ pr_err("ipmi:dmi: Invalid IPMI type: %d\n", type);
return;
}
+ if (si_type != SI_TYPE_INVALID)
+ set_prop_entry(p[pidx++], "ipmi-type", u8, si_type);
+ set_prop_entry(p[pidx++], "slave-addr", u8, slave_addr);
+ set_prop_entry(p[pidx++], "addr-source", u8, SI_SMBIOS);
+
+ info = kmalloc(sizeof(*info), GFP_KERNEL);
+ if (!info) {
+ pr_warn("ipmi:dmi: Could not allocate dmi info\n");
+ } else {
+ info->si_type = si_type;
+ info->flags = flags;
+ info->addr = base_addr;
+ info->slave_addr = slave_addr;
+ info->next = ipmi_dmi_infos;
+ ipmi_dmi_infos = info;
+ }
+
pdev = platform_device_alloc(name, ipmi_dmi_nr);
if (!pdev) {
- pr_err("ipmi:dmi: Error allocation IPMI platform device");
+ pr_err("ipmi:dmi: Error allocation IPMI platform device\n");
return;
}
pdev->driver_override = override;
- if (type == IPMI_DMI_TYPE_SSIF)
+ if (type == IPMI_DMI_TYPE_SSIF) {
+ set_prop_entry(p[pidx++], "i2c-addr", u16, base_addr);
goto add_properties;
+ }
memset(r, 0, sizeof(r));
@@ -151,12 +178,13 @@ err:
* This function allows an ACPI-specified IPMI device to look up the
* slave address from the DMI table.
*/
-int ipmi_dmi_get_slave_addr(int type, u32 flags, unsigned long base_addr)
+int ipmi_dmi_get_slave_addr(enum si_type si_type, u32 flags,
+ unsigned long base_addr)
{
struct ipmi_dmi_info *info = ipmi_dmi_infos;
while (info) {
- if (info->type == type &&
+ if (info->si_type == si_type &&
info->flags == flags &&
info->addr == base_addr)
return info->slave_addr;
@@ -239,7 +267,7 @@ static void __init dmi_decode_ipmi(const struct dmi_header *dm)
offset = 16;
break;
default:
- pr_err("ipmi:dmi: Invalid offset: 0");
+ pr_err("ipmi:dmi: Invalid offset: 0\n");
return;
}
}
diff --git a/drivers/char/ipmi/ipmi_dmi.h b/drivers/char/ipmi/ipmi_dmi.h
index 0a1afe5ceb1e..062015b8f520 100644
--- a/drivers/char/ipmi/ipmi_dmi.h
+++ b/drivers/char/ipmi/ipmi_dmi.h
@@ -2,11 +2,7 @@
* DMI defines for use by IPMI
*/
-#define IPMI_DMI_TYPE_KCS 0x01
-#define IPMI_DMI_TYPE_SMIC 0x02
-#define IPMI_DMI_TYPE_BT 0x03
-#define IPMI_DMI_TYPE_SSIF 0x04
-
#ifdef CONFIG_IPMI_DMI_DECODE
-int ipmi_dmi_get_slave_addr(int type, u32 flags, unsigned long base_addr);
+int ipmi_dmi_get_slave_addr(enum si_type si_type, u32 flags,
+ unsigned long base_addr);
#endif
diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c
index 810b138f5897..9de189db2cc3 100644
--- a/drivers/char/ipmi/ipmi_msghandler.c
+++ b/drivers/char/ipmi/ipmi_msghandler.c
@@ -46,6 +46,9 @@
#include <linux/proc_fs.h>
#include <linux/rcupdate.h>
#include <linux/interrupt.h>
+#include <linux/moduleparam.h>
+#include <linux/workqueue.h>
+#include <linux/uuid.h>
#define PFX "IPMI message handler: "
@@ -61,9 +64,77 @@ static int handle_one_recv_msg(ipmi_smi_t intf,
static int initialized;
-#ifdef CONFIG_PROC_FS
+enum ipmi_panic_event_op {
+ IPMI_SEND_PANIC_EVENT_NONE,
+ IPMI_SEND_PANIC_EVENT,
+ IPMI_SEND_PANIC_EVENT_STRING
+};
+#ifdef CONFIG_IPMI_PANIC_STRING
+#define IPMI_PANIC_DEFAULT IPMI_SEND_PANIC_EVENT_STRING
+#elif defined(CONFIG_IPMI_PANIC_EVENT)
+#define IPMI_PANIC_DEFAULT IPMI_SEND_PANIC_EVENT
+#else
+#define IPMI_PANIC_DEFAULT IPMI_SEND_PANIC_EVENT_NONE
+#endif
+static enum ipmi_panic_event_op ipmi_send_panic_event = IPMI_PANIC_DEFAULT;
+
+static int panic_op_write_handler(const char *val,
+ const struct kernel_param *kp)
+{
+ char valcp[16];
+ char *s;
+
+ strncpy(valcp, val, 16);
+ valcp[15] = '\0';
+
+ s = strstrip(valcp);
+
+ if (strcmp(s, "none") == 0)
+ ipmi_send_panic_event = IPMI_SEND_PANIC_EVENT_NONE;
+ else if (strcmp(s, "event") == 0)
+ ipmi_send_panic_event = IPMI_SEND_PANIC_EVENT;
+ else if (strcmp(s, "string") == 0)
+ ipmi_send_panic_event = IPMI_SEND_PANIC_EVENT_STRING;
+ else
+ return -EINVAL;
+
+ return 0;
+}
+
+static int panic_op_read_handler(char *buffer, const struct kernel_param *kp)
+{
+ switch (ipmi_send_panic_event) {
+ case IPMI_SEND_PANIC_EVENT_NONE:
+ strcpy(buffer, "none");
+ break;
+
+ case IPMI_SEND_PANIC_EVENT:
+ strcpy(buffer, "event");
+ break;
+
+ case IPMI_SEND_PANIC_EVENT_STRING:
+ strcpy(buffer, "string");
+ break;
+
+ default:
+ strcpy(buffer, "???");
+ break;
+ }
+
+ return strlen(buffer);
+}
+
+static const struct kernel_param_ops panic_op_ops = {
+ .set = panic_op_write_handler,
+ .get = panic_op_read_handler
+};
+module_param_cb(panic_op, &panic_op_ops, NULL, 0600);
+MODULE_PARM_DESC(panic_op, "Sets if the IPMI driver will attempt to store panic information in the event log in the event of a panic. Set to 'none' for no, 'event' for a single event, or 'string' for a generic event and the panic string in IPMI OEM events.");
+
+
+#ifdef CONFIG_IPMI_PROC_INTERFACE
static struct proc_dir_entry *proc_ipmi_root;
-#endif /* CONFIG_PROC_FS */
+#endif /* CONFIG_IPMI_PROC_INTERFACE */
/* Remain in auto-maintenance mode for this amount of time (in ms). */
#define IPMI_MAINTENANCE_MODE_TIMEOUT 30000
@@ -90,6 +161,9 @@ static struct proc_dir_entry *proc_ipmi_root;
*/
#define IPMI_REQUEST_EV_TIME (1000 / (IPMI_TIMEOUT_TIME))
+/* How long should we cache dynamic device IDs? */
+#define IPMI_DYN_DEV_ID_EXPIRY (10 * HZ)
+
/*
* The main "user" data structure.
*/
@@ -169,10 +243,17 @@ struct seq_table {
#define NEXT_SEQID(seqid) (((seqid) + 1) & 0x3ffffff)
+#define IPMI_MAX_CHANNELS 16
struct ipmi_channel {
unsigned char medium;
unsigned char protocol;
+};
+struct ipmi_channel_set {
+ struct ipmi_channel c[IPMI_MAX_CHANNELS];
+};
+
+struct ipmi_my_addrinfo {
/*
* My slave address. This is initialized to IPMI_BMC_SLAVE_ADDR,
* but may be changed by the user.
@@ -186,23 +267,38 @@ struct ipmi_channel {
unsigned char lun;
};
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_IPMI_PROC_INTERFACE
struct ipmi_proc_entry {
char *name;
struct ipmi_proc_entry *next;
};
#endif
+/*
+ * Note that the product id, manufacturer id, guid, and device id are
+ * immutable in this structure, so dyn_mutex is not required for
+ * accessing those. If those change on a BMC, a new BMC is allocated.
+ */
struct bmc_device {
struct platform_device pdev;
+ struct list_head intfs; /* Interfaces on this BMC. */
struct ipmi_device_id id;
- unsigned char guid[16];
- int guid_set;
- char name[16];
+ struct ipmi_device_id fetch_id;
+ int dyn_id_set;
+ unsigned long dyn_id_expiry;
+ struct mutex dyn_mutex; /* Protects id, intfs, & dyn* */
+ guid_t guid;
+ guid_t fetch_guid;
+ int dyn_guid_set;
struct kref usecount;
+ struct work_struct remove_work;
};
#define to_bmc_device(x) container_of((x), struct bmc_device, pdev.dev)
+static int bmc_get_device_id(ipmi_smi_t intf, struct bmc_device *bmc,
+ struct ipmi_device_id *id,
+ bool *guid_set, guid_t *guid);
+
/*
* Various statistics for IPMI, these index stats[] in the ipmi_smi
* structure.
@@ -308,7 +404,6 @@ enum ipmi_stat_indexes {
#define IPMI_IPMB_NUM_SEQ 64
-#define IPMI_MAX_CHANNELS 16
struct ipmi_smi {
/* What interface number are we? */
int intf_num;
@@ -327,15 +422,23 @@ struct ipmi_smi {
*/
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;
+ /*
+ * Prevents the interface from being unregistered when the
+ * interface is used by being looked up through the BMC
+ * structure.
+ */
+ struct mutex bmc_reg_mutex;
+
+ struct bmc_device tmp_bmc;
struct bmc_device *bmc;
+ bool bmc_registered;
+ struct list_head bmc_link;
char *my_dev_name;
+ bool in_bmc_register; /* Handle recursive situations. Yuck. */
+ struct work_struct bmc_reg_work;
/*
* This is the lower-layer's sender routine. Note that you
@@ -346,10 +449,13 @@ struct ipmi_smi {
const struct ipmi_smi_handlers *handlers;
void *send_info;
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_IPMI_PROC_INTERFACE
/* A list of proc entries for this interface. */
struct mutex proc_entry_lock;
struct ipmi_proc_entry *proc_entries;
+
+ struct proc_dir_entry *proc_dir;
+ char proc_dir_name[10];
#endif
/* Driver-model device for the system interface. */
@@ -421,6 +527,8 @@ struct ipmi_smi {
* 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.
+ *
+ * Protected by bmc_reg_mutex.
*/
void (*null_user_handler)(ipmi_smi_t intf, struct ipmi_recv_msg *msg);
@@ -431,11 +539,11 @@ struct ipmi_smi {
int curr_channel;
/* Channel information */
- struct ipmi_channel channels[IPMI_MAX_CHANNELS];
-
- /* Proc FS stuff. */
- struct proc_dir_entry *proc_dir;
- char proc_dir_name[10];
+ struct ipmi_channel_set *channel_list;
+ unsigned int curr_working_cset; /* First index into the following. */
+ struct ipmi_channel_set wchannels[2];
+ struct ipmi_my_addrinfo addrinfo[IPMI_MAX_CHANNELS];
+ bool channels_ready;
atomic_t stats[IPMI_NUM_STATS];
@@ -448,6 +556,14 @@ struct ipmi_smi {
};
#define to_si_intf_from_dev(device) container_of(device, struct ipmi_smi, dev)
+static void __get_guid(ipmi_smi_t intf);
+static void __ipmi_bmc_unregister(ipmi_smi_t intf);
+static int __ipmi_bmc_register(ipmi_smi_t intf,
+ struct ipmi_device_id *id,
+ bool guid_set, guid_t *guid, int intf_num);
+static int __scan_channels(ipmi_smi_t intf, struct ipmi_device_id *id);
+
+
/**
* The driver model view of the IPMI messaging driver.
*/
@@ -457,6 +573,9 @@ static struct platform_driver ipmidriver = {
.bus = &platform_bus_type
}
};
+/*
+ * This mutex keeps us from adding the same BMC twice.
+ */
static DEFINE_MUTEX(ipmidriver_mutex);
static LIST_HEAD(ipmi_interfaces);
@@ -475,7 +594,7 @@ static DEFINE_MUTEX(smi_watchers_mutex);
static const char * const addr_src_to_str[] = {
"invalid", "hotmod", "hardcoded", "SPMI", "ACPI", "SMBIOS", "PCI",
- "device-tree"
+ "device-tree", "platform"
};
const char *ipmi_addr_src_to_str(enum ipmi_addr_src src)
@@ -1119,12 +1238,21 @@ int ipmi_destroy_user(ipmi_user_t user)
}
EXPORT_SYMBOL(ipmi_destroy_user);
-void ipmi_get_version(ipmi_user_t user,
- unsigned char *major,
- unsigned char *minor)
+int ipmi_get_version(ipmi_user_t user,
+ unsigned char *major,
+ unsigned char *minor)
{
- *major = user->intf->ipmi_version_major;
- *minor = user->intf->ipmi_version_minor;
+ struct ipmi_device_id id;
+ int rv;
+
+ rv = bmc_get_device_id(user->intf, NULL, &id, NULL, NULL);
+ if (rv)
+ return rv;
+
+ *major = ipmi_version_major(&id);
+ *minor = ipmi_version_minor(&id);
+
+ return 0;
}
EXPORT_SYMBOL(ipmi_get_version);
@@ -1134,7 +1262,7 @@ int ipmi_set_my_address(ipmi_user_t user,
{
if (channel >= IPMI_MAX_CHANNELS)
return -EINVAL;
- user->intf->channels[channel].address = address;
+ user->intf->addrinfo[channel].address = address;
return 0;
}
EXPORT_SYMBOL(ipmi_set_my_address);
@@ -1145,7 +1273,7 @@ int ipmi_get_my_address(ipmi_user_t user,
{
if (channel >= IPMI_MAX_CHANNELS)
return -EINVAL;
- *address = user->intf->channels[channel].address;
+ *address = user->intf->addrinfo[channel].address;
return 0;
}
EXPORT_SYMBOL(ipmi_get_my_address);
@@ -1156,7 +1284,7 @@ int ipmi_set_my_LUN(ipmi_user_t user,
{
if (channel >= IPMI_MAX_CHANNELS)
return -EINVAL;
- user->intf->channels[channel].lun = LUN & 0x3;
+ user->intf->addrinfo[channel].lun = LUN & 0x3;
return 0;
}
EXPORT_SYMBOL(ipmi_set_my_LUN);
@@ -1167,7 +1295,7 @@ int ipmi_get_my_LUN(ipmi_user_t user,
{
if (channel >= IPMI_MAX_CHANNELS)
return -EINVAL;
- *address = user->intf->channels[channel].lun;
+ *address = user->intf->addrinfo[channel].lun;
return 0;
}
EXPORT_SYMBOL(ipmi_get_my_LUN);
@@ -1264,8 +1392,8 @@ int ipmi_set_gets_events(ipmi_user_t user, bool val)
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");
+ dev_warn(intf->si_dev,
+ PFX "Event queue no longer full\n");
intf->event_msg_printed = 0;
}
@@ -1655,6 +1783,7 @@ static int i_ipmi_request(ipmi_user_t user,
unsigned char ipmb_seq;
long seqid;
int broadcast = 0;
+ struct ipmi_channel *chans;
if (addr->channel >= IPMI_MAX_CHANNELS) {
ipmi_inc_stat(intf, sent_invalid_commands);
@@ -1662,8 +1791,9 @@ static int i_ipmi_request(ipmi_user_t user,
goto out_err;
}
- if (intf->channels[addr->channel].medium
- != IPMI_CHANNEL_MEDIUM_IPMB) {
+ chans = READ_ONCE(intf->channel_list)->c;
+
+ if (chans[addr->channel].medium != IPMI_CHANNEL_MEDIUM_IPMB) {
ipmi_inc_stat(intf, sent_invalid_commands);
rv = -EINVAL;
goto out_err;
@@ -1785,6 +1915,7 @@ static int i_ipmi_request(ipmi_user_t user,
struct ipmi_lan_addr *lan_addr;
unsigned char ipmb_seq;
long seqid;
+ struct ipmi_channel *chans;
if (addr->channel >= IPMI_MAX_CHANNELS) {
ipmi_inc_stat(intf, sent_invalid_commands);
@@ -1792,9 +1923,11 @@ static int i_ipmi_request(ipmi_user_t user,
goto out_err;
}
- if ((intf->channels[addr->channel].medium
+ chans = READ_ONCE(intf->channel_list)->c;
+
+ if ((chans[addr->channel].medium
!= IPMI_CHANNEL_MEDIUM_8023LAN)
- && (intf->channels[addr->channel].medium
+ && (chans[addr->channel].medium
!= IPMI_CHANNEL_MEDIUM_ASYNC)) {
ipmi_inc_stat(intf, sent_invalid_commands);
rv = -EINVAL;
@@ -1928,8 +2061,8 @@ static int check_addr(ipmi_smi_t intf,
{
if (addr->channel >= IPMI_MAX_CHANNELS)
return -EINVAL;
- *lun = intf->channels[addr->channel].lun;
- *saddr = intf->channels[addr->channel].address;
+ *lun = intf->addrinfo[addr->channel].lun;
+ *saddr = intf->addrinfo[addr->channel].address;
return 0;
}
@@ -1997,15 +2130,249 @@ int ipmi_request_supply_msgs(ipmi_user_t user,
}
EXPORT_SYMBOL(ipmi_request_supply_msgs);
-#ifdef CONFIG_PROC_FS
+static void bmc_device_id_handler(ipmi_smi_t intf, struct ipmi_recv_msg *msg)
+{
+ int rv;
+
+ 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)) {
+ dev_warn(intf->si_dev,
+ PFX "invalid device_id msg: addr_type=%d netfn=%x cmd=%x\n",
+ msg->addr.addr_type, msg->msg.netfn, msg->msg.cmd);
+ return;
+ }
+
+ rv = ipmi_demangle_device_id(msg->msg.netfn, msg->msg.cmd,
+ msg->msg.data, msg->msg.data_len, &intf->bmc->fetch_id);
+ if (rv) {
+ dev_warn(intf->si_dev,
+ PFX "device id demangle failed: %d\n", rv);
+ intf->bmc->dyn_id_set = 0;
+ } else {
+ /*
+ * Make sure the id data is available before setting
+ * dyn_id_set.
+ */
+ smp_wmb();
+ intf->bmc->dyn_id_set = 1;
+ }
+
+ wake_up(&intf->waitq);
+}
+
+static int
+send_get_device_id_cmd(ipmi_smi_t intf)
+{
+ struct ipmi_system_interface_addr si;
+ struct kernel_ipmi_msg msg;
+
+ si.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
+ si.channel = IPMI_BMC_CHANNEL;
+ si.lun = 0;
+
+ msg.netfn = IPMI_NETFN_APP_REQUEST;
+ msg.cmd = IPMI_GET_DEVICE_ID_CMD;
+ msg.data = NULL;
+ msg.data_len = 0;
+
+ return i_ipmi_request(NULL,
+ intf,
+ (struct ipmi_addr *) &si,
+ 0,
+ &msg,
+ intf,
+ NULL,
+ NULL,
+ 0,
+ intf->addrinfo[0].address,
+ intf->addrinfo[0].lun,
+ -1, 0);
+}
+
+static int __get_device_id(ipmi_smi_t intf, struct bmc_device *bmc)
+{
+ int rv;
+
+ bmc->dyn_id_set = 2;
+
+ intf->null_user_handler = bmc_device_id_handler;
+
+ rv = send_get_device_id_cmd(intf);
+ if (rv)
+ return rv;
+
+ wait_event(intf->waitq, bmc->dyn_id_set != 2);
+
+ if (!bmc->dyn_id_set)
+ rv = -EIO; /* Something went wrong in the fetch. */
+
+ /* dyn_id_set makes the id data available. */
+ smp_rmb();
+
+ intf->null_user_handler = NULL;
+
+ return rv;
+}
+
+/*
+ * Fetch the device id for the bmc/interface. You must pass in either
+ * bmc or intf, this code will get the other one. If the data has
+ * been recently fetched, this will just use the cached data. Otherwise
+ * it will run a new fetch.
+ *
+ * Except for the first time this is called (in ipmi_register_smi()),
+ * this will always return good data;
+ */
+static int __bmc_get_device_id(ipmi_smi_t intf, struct bmc_device *bmc,
+ struct ipmi_device_id *id,
+ bool *guid_set, guid_t *guid, int intf_num)
+{
+ int rv = 0;
+ int prev_dyn_id_set, prev_guid_set;
+ bool intf_set = intf != NULL;
+
+ if (!intf) {
+ mutex_lock(&bmc->dyn_mutex);
+retry_bmc_lock:
+ if (list_empty(&bmc->intfs)) {
+ mutex_unlock(&bmc->dyn_mutex);
+ return -ENOENT;
+ }
+ intf = list_first_entry(&bmc->intfs, struct ipmi_smi,
+ bmc_link);
+ kref_get(&intf->refcount);
+ mutex_unlock(&bmc->dyn_mutex);
+ mutex_lock(&intf->bmc_reg_mutex);
+ mutex_lock(&bmc->dyn_mutex);
+ if (intf != list_first_entry(&bmc->intfs, struct ipmi_smi,
+ bmc_link)) {
+ mutex_unlock(&intf->bmc_reg_mutex);
+ kref_put(&intf->refcount, intf_free);
+ goto retry_bmc_lock;
+ }
+ } else {
+ mutex_lock(&intf->bmc_reg_mutex);
+ bmc = intf->bmc;
+ mutex_lock(&bmc->dyn_mutex);
+ kref_get(&intf->refcount);
+ }
+
+ /* If we have a valid and current ID, just return that. */
+ if (intf->in_bmc_register ||
+ (bmc->dyn_id_set && time_is_after_jiffies(bmc->dyn_id_expiry)))
+ goto out_noprocessing;
+
+ prev_guid_set = bmc->dyn_guid_set;
+ __get_guid(intf);
+
+ prev_dyn_id_set = bmc->dyn_id_set;
+ rv = __get_device_id(intf, bmc);
+ if (rv)
+ goto out;
+
+ /*
+ * The guid, device id, manufacturer id, and product id should
+ * not change on a BMC. If it does we have to do some dancing.
+ */
+ if (!intf->bmc_registered
+ || (!prev_guid_set && bmc->dyn_guid_set)
+ || (!prev_dyn_id_set && bmc->dyn_id_set)
+ || (prev_guid_set && bmc->dyn_guid_set
+ && !guid_equal(&bmc->guid, &bmc->fetch_guid))
+ || bmc->id.device_id != bmc->fetch_id.device_id
+ || bmc->id.manufacturer_id != bmc->fetch_id.manufacturer_id
+ || bmc->id.product_id != bmc->fetch_id.product_id) {
+ struct ipmi_device_id id = bmc->fetch_id;
+ int guid_set = bmc->dyn_guid_set;
+ guid_t guid;
+
+ guid = bmc->fetch_guid;
+ mutex_unlock(&bmc->dyn_mutex);
+
+ __ipmi_bmc_unregister(intf);
+ /* Fill in the temporary BMC for good measure. */
+ intf->bmc->id = id;
+ intf->bmc->dyn_guid_set = guid_set;
+ intf->bmc->guid = guid;
+ if (__ipmi_bmc_register(intf, &id, guid_set, &guid, intf_num))
+ need_waiter(intf); /* Retry later on an error. */
+ else
+ __scan_channels(intf, &id);
+
+
+ if (!intf_set) {
+ /*
+ * We weren't given the interface on the
+ * command line, so restart the operation on
+ * the next interface for the BMC.
+ */
+ mutex_unlock(&intf->bmc_reg_mutex);
+ mutex_lock(&bmc->dyn_mutex);
+ goto retry_bmc_lock;
+ }
+
+ /* We have a new BMC, set it up. */
+ bmc = intf->bmc;
+ mutex_lock(&bmc->dyn_mutex);
+ goto out_noprocessing;
+ } else if (memcmp(&bmc->fetch_id, &bmc->id, sizeof(bmc->id)))
+ /* Version info changes, scan the channels again. */
+ __scan_channels(intf, &bmc->fetch_id);
+
+ bmc->dyn_id_expiry = jiffies + IPMI_DYN_DEV_ID_EXPIRY;
+
+out:
+ if (rv && prev_dyn_id_set) {
+ rv = 0; /* Ignore failures if we have previous data. */
+ bmc->dyn_id_set = prev_dyn_id_set;
+ }
+ if (!rv) {
+ bmc->id = bmc->fetch_id;
+ if (bmc->dyn_guid_set)
+ bmc->guid = bmc->fetch_guid;
+ else if (prev_guid_set)
+ /*
+ * The guid used to be valid and it failed to fetch,
+ * just use the cached value.
+ */
+ bmc->dyn_guid_set = prev_guid_set;
+ }
+out_noprocessing:
+ if (!rv) {
+ if (id)
+ *id = bmc->id;
+
+ if (guid_set)
+ *guid_set = bmc->dyn_guid_set;
+
+ if (guid && bmc->dyn_guid_set)
+ *guid = bmc->guid;
+ }
+
+ mutex_unlock(&bmc->dyn_mutex);
+ mutex_unlock(&intf->bmc_reg_mutex);
+
+ kref_put(&intf->refcount, intf_free);
+ return rv;
+}
+
+static int bmc_get_device_id(ipmi_smi_t intf, struct bmc_device *bmc,
+ struct ipmi_device_id *id,
+ bool *guid_set, guid_t *guid)
+{
+ return __bmc_get_device_id(intf, bmc, id, guid_set, guid, -1);
+}
+
+#ifdef CONFIG_IPMI_PROC_INTERFACE
static int smi_ipmb_proc_show(struct seq_file *m, void *v)
{
ipmi_smi_t intf = m->private;
int i;
- seq_printf(m, "%x", intf->channels[0].address);
+ seq_printf(m, "%x", intf->addrinfo[0].address);
for (i = 1; i < IPMI_MAX_CHANNELS; i++)
- seq_printf(m, " %x", intf->channels[i].address);
+ seq_printf(m, " %x", intf->addrinfo[i].address);
seq_putc(m, '\n');
return 0;
@@ -2026,10 +2393,16 @@ static const struct file_operatio