diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2017-11-15 15:12:28 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2017-11-15 15:12:28 -0800 |
commit | 6363b3f3ac5be096d08c8c504128befa0c033529 (patch) | |
tree | 8bef82ead96f39bc09f4d1a1fe17dfa5d0c39d49 /drivers | |
parent | 1b6115fbe3b3db746d7baa11399dd617fc75e1c4 (diff) | |
parent | 6297fabd93f93182245383ba7de56bef829a796b (diff) |
Merge tag 'ipmi-for-4.15' of git://github.com/cminyard/linux-ipmi
Pull IPMI updates from Corey Minyard:
"This is a fairly large rework of the IPMI code, along with a bunch of
smaller fixes. The major changes have been in the next tree for a
couple of months, so they should be good to do in.
- Some users had IPMI systems where the GUID of the IPMI controller
could change. So rescanning of the GUID was added. The naming of
some sysfs things was dependent on the GUID, however, so this
resulted in the sysfs interface code in IPMI changing to remove
that dependency and name the IPMI BMCs like other sysfs devices.
- The ipmi_si_intf.c code was fairly bloated with all the different
discovery methods (PCI, ACPI, SMBIOS, OF, platform, module
parameters, hot add). The structure of how the interfaces were
added was redone to make them more modular, then the individual
methods were pulled out into their own files"
* tag 'ipmi-for-4.15' of git://github.com/cminyard/linux-ipmi: (48 commits)
ipmi_si: Delete an error message for a failed memory allocation in try_smi_init()
ipmi_si: fix memory leak on new_smi
ipmi: remove redundant initialization of bmc
ipmi: pr_err() strings should end with newlines
ipmi: Clean up some print operations
ipmi: Make the DMI probe into a generic platform probe
ipmi: Make the IPMI proc interface configurable
ipmi_ssif: Add device attrs for the things in proc
ipmi_si: Add device attrs for the things in proc
ipmi_si: remove ipmi_smi_alloc() function
ipmi_si: Move port and mem I/O handling to their own files
ipmi_si: Get rid of unused spacing and port fields
ipmi_si: Move PARISC handling to another file
ipmi_si: Move PCI setup to another file
ipmi_si: Move platform device handling to another file
ipmi_si: Move hardcode handling to a separate file.
ipmi_si: Move the hotmod handling to another file.
ipmi_si: Change ipmi_si_add_smi() to take just I/O info
ipmi_si: Move io setup into io structure
ipmi_si: Move irq setup handling into the io struct
...
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/char/ipmi/Kconfig | 35 | ||||
-rw-r--r-- | drivers/char/ipmi/Makefile | 10 | ||||
-rw-r--r-- | drivers/char/ipmi/ipmi_dmi.c | 76 | ||||
-rw-r--r-- | drivers/char/ipmi/ipmi_dmi.h | 8 | ||||
-rw-r--r-- | drivers/char/ipmi/ipmi_msghandler.c | 1275 | ||||
-rw-r--r-- | drivers/char/ipmi/ipmi_powernv.c | 4 | ||||
-rw-r--r-- | drivers/char/ipmi/ipmi_poweroff.c | 2 | ||||
-rw-r--r-- | drivers/char/ipmi/ipmi_si.h | 49 | ||||
-rw-r--r-- | drivers/char/ipmi/ipmi_si_hardcode.c | 146 | ||||
-rw-r--r-- | drivers/char/ipmi/ipmi_si_hotmod.c | 242 | ||||
-rw-r--r-- | drivers/char/ipmi/ipmi_si_intf.c | 2122 | ||||
-rw-r--r-- | drivers/char/ipmi/ipmi_si_mem_io.c | 144 | ||||
-rw-r--r-- | drivers/char/ipmi/ipmi_si_parisc.c | 58 | ||||
-rw-r--r-- | drivers/char/ipmi/ipmi_si_pci.c | 166 | ||||
-rw-r--r-- | drivers/char/ipmi/ipmi_si_platform.c | 593 | ||||
-rw-r--r-- | drivers/char/ipmi/ipmi_si_port_io.c | 112 | ||||
-rw-r--r-- | drivers/char/ipmi/ipmi_si_sm.h | 23 | ||||
-rw-r--r-- | drivers/char/ipmi/ipmi_ssif.c | 112 | ||||
-rw-r--r-- | drivers/char/ipmi/ipmi_watchdog.c | 11 |
19 files changed, 2987 insertions, 2201 deletions
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 43b7d86cc5f2..33b899fcf14a 100644 --- a/drivers/char/ipmi/Makefile +++ b/drivers/char/ipmi/Makefile @@ -3,7 +3,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 2059f79d669a..ab78b3be7e33 100644 --- a/drivers/char/ipmi/ipmi_dmi.c +++ b/drivers/char/ipmi/ipmi_dmi.c @@ -9,10 +9,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; @@ -23,6 +29,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, @@ -33,27 +48,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"; @@ -63,28 +65,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)); @@ -152,12 +179,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; @@ -240,7 +268,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 ea990a8e3b09..6c21018e3668 100644 --- a/drivers/char/ipmi/ipmi_dmi.h +++ b/drivers/char/ipmi/ipmi_dmi.h @@ -3,11 +3,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_operations smi_ipmb_proc_ops = { static int smi_version_proc_show(struct seq_file *m, void *v) { ipmi_smi_t intf = m->private; + struct ipmi_device_id id; + int rv; + + rv = bmc_get_device_id(intf, NULL, &id, NULL, NULL); + if (rv) + return rv; seq_printf(m, "%u.%u\n", - ipmi_version_major(&intf->bmc->id), - ipmi_version_minor(&intf->bmc->id)); + ipmi_version_major(&id), + ipmi_version_minor(&id)); return 0; } @@ -2120,14 +2493,12 @@ static const struct file_operations smi_stats_proc_ops = { .llseek = seq_lseek, .release = single_release, }; -#endif /* CONFIG_PROC_FS */ int ipmi_smi_add_proc_entry(ipmi_smi_t smi, char *name, c |