IPMI: A few minor fixes and some cosmetic changes.

Nothing big here, but some minor things that people have found and
 some minor reworks for names and include files.
 
 Thanks,
 
 -corey
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEE/Q1c5nzg9ZpmiCaGYfOMkJGb/4EFAl2Dp3wACgkQYfOMkJGb
 /4HKIA/+N0zGXn4Hgp7FJPobpZvlUCswHGUUmk7+Wibf8qXqObClvDLsu4nsblbX
 n8tmAVV8OzjZvyx1L49Lan8yBB4rtMg1Y4+WC8hWi2j6eB1JY2jNjHKIRIVFZMIX
 z2uHGIRxyL/vPbyDkUFLQxmxqjuGzQHivkGyaPopI/Z7N+LCZMhfTiUHt1lJkxcj
 Uka+XgVNfe1OOkBSeVQcIzwppQH83i2j6NL0BrZ3Cu9vTMJTqiP8TRrgendxGyxb
 Tk/zTrE9Ct7tz3pD2/nqtyo5xAL4uLPoxJmM916ucYvS5nkUfYDhuJxWDrcjWsZe
 jV2/knCNIJn90aIFNs/lQOKb+OzeitGz7IdlHzJyi/cU7VhcpsrxfBTADR6ydTMk
 bmr1vvPOhN0PekSH6r9ly5FECTOWIEhoy6XWpoDGYtWvHj5OEtk4vDdJYE4aw5cd
 tZMM+x7x3P427rb7Dna0MgyFuAipJhQMzr+AzWEOGNQw505iZP5/C1UbkUFE6lWJ
 nr3tBVr6ZkJdTaO/aY2bA02loDJyEL5m8W/XaU2PSlKZkGZk1pLiOiB1v88upSpz
 ZpJ5w+ItjqZFRnVaRxTjPUwaieAm6iswXUvXCpKUPfnOSKAkPpAFDIsEpmNIUtfD
 slvFfztFFS7o1VrguTIqIgZ6p4xJETNrlZqNhr7In/Z3p5jg13Y=
 =5uQL
 -----END PGP SIGNATURE-----

Merge tag 'for-linus-5.4-1' of git://github.com/cminyard/linux-ipmi

Pull IPMI updates from Corey Minyard:
 "A few minor fixes and some cosmetic changes.

  Nothing big here, but some minor things that people have found and
  some minor reworks for names and include files"

* tag 'for-linus-5.4-1' of git://github.com/cminyard/linux-ipmi:
  ipmi_si_intf: Fix race in timer shutdown handling
  ipmi: move message error checking to avoid deadlock
  ipmi_ssif: avoid registering duplicate ssif interface
  ipmi: Free receive messages when in an oops
  ipmi_si: Only schedule continuously in the thread in maintenance mode
  ipmi_si: Remove ipmi_ from the device attr names
  ipmi_si: Convert device attr permissions to octal
  ipmi_si: Rework some include files
  ipmi_si: Convert timespec64 to timespec
This commit is contained in:
Linus Torvalds 2019-09-19 14:08:45 -07:00
commit a9f8b38a07
11 changed files with 260 additions and 159 deletions

View File

@ -12,7 +12,6 @@
#include <linux/dmi.h>
#include <linux/platform_device.h>
#include <linux/property.h>
#include "ipmi_si_sm.h"
#include "ipmi_dmi.h"
#include "ipmi_plat_data.h"

View File

@ -2,6 +2,7 @@
/*
* DMI defines for use by IPMI
*/
#include "ipmi_si.h"
#ifdef CONFIG_IPMI_DMI_DECODE
int ipmi_dmi_get_slave_addr(enum si_type si_type, unsigned int space,

View File

@ -904,12 +904,14 @@ static int deliver_response(struct ipmi_smi *intf, struct ipmi_recv_msg *msg)
rv = -EINVAL;
}
ipmi_free_recv_msg(msg);
} else if (!oops_in_progress) {
} else if (oops_in_progress) {
/*
* If we are running in the panic context, calling the
* receive handler doesn't much meaning and has a deadlock
* risk. At this moment, simply skip it in that case.
*/
ipmi_free_recv_msg(msg);
} else {
int index;
struct ipmi_user *user = acquire_ipmi_user(msg->user, &index);
@ -2220,7 +2222,8 @@ static int i_ipmi_request(struct ipmi_user *user,
else {
smi_msg = ipmi_alloc_smi_msg();
if (smi_msg == NULL) {
ipmi_free_recv_msg(recv_msg);
if (!supplied_recv)
ipmi_free_recv_msg(recv_msg);
rv = -ENOMEM;
goto out;
}
@ -4215,7 +4218,53 @@ static int handle_one_recv_msg(struct ipmi_smi *intf,
int chan;
ipmi_debug_msg("Recv:", msg->rsp, msg->rsp_size);
if (msg->rsp_size < 2) {
if ((msg->data_size >= 2)
&& (msg->data[0] == (IPMI_NETFN_APP_REQUEST << 2))
&& (msg->data[1] == IPMI_SEND_MSG_CMD)
&& (msg->user_data == NULL)) {
if (intf->in_shutdown)
goto free_msg;
/*
* 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_BUS_ERR)
&& (msg->rsp[2] != IPMI_NAK_ON_WRITE_ERR)) {
int ch = msg->rsp[3] & 0xf;
struct ipmi_channel *chans;
/* Got an error sending the message, handle it. */
chans = READ_ONCE(intf->channel_list)->c;
if ((chans[ch].medium == IPMI_CHANNEL_MEDIUM_8023LAN)
|| (chans[ch].medium == IPMI_CHANNEL_MEDIUM_ASYNC))
ipmi_inc_stat(intf, sent_lan_command_errs);
else
ipmi_inc_stat(intf, sent_ipmb_command_errs);
intf_err_seq(intf, msg->msgid, msg->rsp[2]);
} else
/* The message was sent, start the timer. */
intf_start_seq_timer(intf, msg->msgid);
free_msg:
requeue = 0;
goto out;
} else if (msg->rsp_size < 2) {
/* Message is too small to be correct. */
dev_warn(intf->si_dev,
"BMC returned too small a message for netfn %x cmd %x, got %d bytes\n",
@ -4472,62 +4521,16 @@ void ipmi_smi_msg_received(struct ipmi_smi *intf,
unsigned long flags = 0; /* keep us warning-free. */
int run_to_completion = intf->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)) {
if (intf->in_shutdown)
goto free_msg;
/*
* 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_BUS_ERR)
&& (msg->rsp[2] != IPMI_NAK_ON_WRITE_ERR)) {
int ch = msg->rsp[3] & 0xf;
struct ipmi_channel *chans;
/* Got an error sending the message, handle it. */
chans = READ_ONCE(intf->channel_list)->c;
if ((chans[ch].medium == IPMI_CHANNEL_MEDIUM_8023LAN)
|| (chans[ch].medium == IPMI_CHANNEL_MEDIUM_ASYNC))
ipmi_inc_stat(intf, sent_lan_command_errs);
else
ipmi_inc_stat(intf, sent_ipmb_command_errs);
intf_err_seq(intf, msg->msgid, msg->rsp[2]);
} else
/* The message was sent, start the timer. */
intf_start_seq_timer(intf, msg->msgid);
free_msg:
ipmi_free_smi_msg(msg);
} else {
/*
* To preserve message order, we keep a queue and deliver from
* a tasklet.
*/
if (!run_to_completion)
spin_lock_irqsave(&intf->waiting_rcv_msgs_lock, flags);
list_add_tail(&msg->link, &intf->waiting_rcv_msgs);
if (!run_to_completion)
spin_unlock_irqrestore(&intf->waiting_rcv_msgs_lock,
flags);
}
/*
* To preserve message order, we keep a queue and deliver from
* a tasklet.
*/
if (!run_to_completion)
spin_lock_irqsave(&intf->waiting_rcv_msgs_lock, flags);
list_add_tail(&msg->link, &intf->waiting_rcv_msgs);
if (!run_to_completion)
spin_unlock_irqrestore(&intf->waiting_rcv_msgs_lock,
flags);
if (!run_to_completion)
spin_lock_irqsave(&intf->xmit_msgs_lock, flags);

View File

@ -6,14 +6,65 @@
* etc) to the base ipmi system interface code.
*/
#ifndef __IPMI_SI_H__
#define __IPMI_SI_H__
#include <linux/ipmi.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include "ipmi_si_sm.h"
#define SI_DEVICE_NAME "ipmi_si"
#define DEFAULT_REGSPACING 1
#define DEFAULT_REGSIZE 1
#define DEVICE_NAME "ipmi_si"
enum si_type {
SI_TYPE_INVALID, SI_KCS, SI_SMIC, SI_BT
};
enum ipmi_addr_space {
IPMI_IO_ADDR_SPACE, IPMI_MEM_ADDR_SPACE
};
/*
* 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)(const struct si_sm_io *io, unsigned int offset);
void (*outputb)(const 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.
*/
void __iomem *addr;
unsigned int regspacing;
unsigned int regsize;
unsigned int regshift;
enum ipmi_addr_space addr_space;
unsigned long addr_data;
enum ipmi_addr_src addr_source; /* ACPI, PCI, SMBIOS, hardcode, etc. */
void (*addr_source_cleanup)(struct si_sm_io *io);
void *addr_source_data;
union ipmi_smi_info_union addr_info;
int (*io_setup)(struct si_sm_io *info);
void (*io_cleanup)(struct si_sm_io *info);
unsigned int io_size;
int irq;
int (*irq_setup)(struct si_sm_io *io);
void *irq_handler_data;
void (*irq_cleanup)(struct si_sm_io *io);
u8 slave_addr;
enum si_type si_type;
struct device *dev;
};
int ipmi_si_add_smi(struct si_sm_io *io);
irqreturn_t ipmi_si_irq_handler(int irq, void *data);
@ -50,3 +101,5 @@ static inline void ipmi_si_parisc_shutdown(void) { }
int ipmi_si_port_setup(struct si_sm_io *io);
int ipmi_si_mem_setup(struct si_sm_io *io);
#endif /* __IPMI_SI_H__ */

View File

@ -40,6 +40,7 @@
#include <linux/ipmi.h>
#include <linux/ipmi_smi.h>
#include "ipmi_si.h"
#include "ipmi_si_sm.h"
#include <linux/string.h>
#include <linux/ctype.h>
@ -221,6 +222,9 @@ struct smi_info {
*/
bool irq_enable_broken;
/* Is the driver in maintenance mode? */
bool in_maintenance_mode;
/*
* Did we get an attention that we did not handle?
*/
@ -261,10 +265,10 @@ static void cleanup_ipmi_si(void);
#ifdef DEBUG_TIMING
void debug_timestamp(char *msg)
{
struct timespec64 t;
struct timespec t;
ktime_get_ts64(&t);
pr_debug("**%s: %lld.%9.9ld\n", msg, (long long) t.tv_sec, t.tv_nsec);
ktime_get_ts(&t);
pr_debug("**%s: %ld.%9.9ld\n", msg, (long) t.tv_sec, t.tv_nsec);
}
#else
#define debug_timestamp(x)
@ -935,18 +939,18 @@ static void set_run_to_completion(void *send_info, bool i_run_to_completion)
* we are spinning in kipmid looking for something and not delaying
* between checks
*/
static inline void ipmi_si_set_not_busy(struct timespec64 *ts)
static inline void ipmi_si_set_not_busy(struct timespec *ts)
{
ts->tv_nsec = -1;
}
static inline int ipmi_si_is_busy(struct timespec64 *ts)
static inline int ipmi_si_is_busy(struct timespec *ts)
{
return ts->tv_nsec != -1;
}
static inline int ipmi_thread_busy_wait(enum si_sm_result smi_result,
const struct smi_info *smi_info,
struct timespec64 *busy_until)
static inline bool 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;
@ -955,18 +959,18 @@ static inline int ipmi_thread_busy_wait(enum si_sm_result smi_result,
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)) {
ktime_get_ts64(busy_until);
timespec64_add_ns(busy_until, max_busy_us*NSEC_PER_USEC);
ktime_get_ts(busy_until);
timespec_add_ns(busy_until, max_busy_us * NSEC_PER_USEC);
} else {
struct timespec64 now;
struct timespec now;
ktime_get_ts64(&now);
if (unlikely(timespec64_compare(&now, busy_until) > 0)) {
ktime_get_ts(&now);
if (unlikely(timespec_compare(&now, busy_until) > 0)) {
ipmi_si_set_not_busy(busy_until);
return 0;
return false;
}
}
return 1;
return true;
}
@ -984,7 +988,7 @@ static int ipmi_thread(void *data)
struct smi_info *smi_info = data;
unsigned long flags;
enum si_sm_result smi_result;
struct timespec64 busy_until;
struct timespec busy_until = { 0, 0 };
ipmi_si_set_not_busy(&busy_until);
set_user_nice(current, MAX_NICE);
@ -1007,11 +1011,20 @@ static int ipmi_thread(void *data)
spin_unlock_irqrestore(&(smi_info->si_lock), flags);
busy_wait = ipmi_thread_busy_wait(smi_result, smi_info,
&busy_until);
if (smi_result == SI_SM_CALL_WITHOUT_DELAY)
if (smi_result == SI_SM_CALL_WITHOUT_DELAY) {
; /* do nothing */
else if (smi_result == SI_SM_CALL_WITH_DELAY && busy_wait)
schedule();
else if (smi_result == SI_SM_IDLE) {
} else if (smi_result == SI_SM_CALL_WITH_DELAY && busy_wait) {
/*
* In maintenance mode we run as fast as
* possible to allow firmware updates to
* complete as fast as possible, but normally
* don't bang on the scheduler.
*/
if (smi_info->in_maintenance_mode)
schedule();
else
usleep_range(100, 200);
} else if (smi_result == SI_SM_IDLE) {
if (atomic_read(&smi_info->need_watch)) {
schedule_timeout_interruptible(100);
} else {
@ -1019,8 +1032,9 @@ static int ipmi_thread(void *data)
__set_current_state(TASK_INTERRUPTIBLE);
schedule();
}
} else
} else {
schedule_timeout_interruptible(1);
}
}
return 0;
}
@ -1198,6 +1212,7 @@ static void set_maintenance_mode(void *send_info, bool enable)
if (!enable)
atomic_set(&smi_info->req_events, 0);
smi_info->in_maintenance_mode = enable;
}
static void shutdown_smi(void *send_info);
@ -1266,12 +1281,12 @@ int ipmi_std_irq_setup(struct si_sm_io *io)
rv = request_irq(io->irq,
ipmi_si_irq_handler,
IRQF_SHARED,
DEVICE_NAME,
SI_DEVICE_NAME,
io->irq_handler_data);
if (rv) {
dev_warn(io->dev, "%s unable to claim interrupt %d,"
" running polled\n",
DEVICE_NAME, io->irq);
SI_DEVICE_NAME, io->irq);
io->irq = 0;
} else {
io->irq_cleanup = std_irq_cleanup;
@ -1586,37 +1601,37 @@ out:
}
#define IPMI_SI_ATTR(name) \
static ssize_t ipmi_##name##_show(struct device *dev, \
struct device_attribute *attr, \
char *buf) \
static ssize_t name##_show(struct device *dev, \
struct device_attribute *attr, \
char *buf) \
{ \
struct smi_info *smi_info = dev_get_drvdata(dev); \
\
return snprintf(buf, 10, "%u\n", smi_get_stat(smi_info, name)); \
} \
static DEVICE_ATTR(name, S_IRUGO, ipmi_##name##_show, NULL)
static DEVICE_ATTR(name, 0444, name##_show, NULL)
static ssize_t ipmi_type_show(struct device *dev,
struct device_attribute *attr,
char *buf)
static ssize_t type_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct smi_info *smi_info = dev_get_drvdata(dev);
return snprintf(buf, 10, "%s\n", si_to_str[smi_info->io.si_type]);
}
static DEVICE_ATTR(type, S_IRUGO, ipmi_type_show, NULL);
static DEVICE_ATTR(type, 0444, type_show, NULL);
static ssize_t ipmi_interrupts_enabled_show(struct device *dev,
struct device_attribute *attr,
char *buf)
static ssize_t interrupts_enabled_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct smi_info *smi_info = dev_get_drvdata(dev);
int enabled = smi_info->io.irq && !smi_info->interrupt_disabled;
return snprintf(buf, 10, "%d\n", enabled);
}
static DEVICE_ATTR(interrupts_enabled, S_IRUGO,
ipmi_interrupts_enabled_show, NULL);
static DEVICE_ATTR(interrupts_enabled, 0444,
interrupts_enabled_show, NULL);
IPMI_SI_ATTR(short_timeouts);
IPMI_SI_ATTR(long_timeouts);
@ -1630,9 +1645,9 @@ IPMI_SI_ATTR(events);
IPMI_SI_ATTR(watchdog_pretimeouts);
IPMI_SI_ATTR(incoming_messages);
static ssize_t ipmi_params_show(struct device *dev,
struct device_attribute *attr,
char *buf)
static ssize_t params_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct smi_info *smi_info = dev_get_drvdata(dev);
@ -1647,7 +1662,7 @@ static ssize_t ipmi_params_show(struct device *dev,
smi_info->io.irq,
smi_info->io.slave_addr);
}
static DEVICE_ATTR(params, S_IRUGO, ipmi_params_show, NULL);
static DEVICE_ATTR(params, 0444, params_show, NULL);
static struct attribute *ipmi_si_dev_attrs[] = {
&dev_attr_type.attr,
@ -1828,8 +1843,7 @@ static inline void stop_timer_and_thread(struct smi_info *smi_info)
}
smi_info->timer_can_start = false;
if (smi_info->timer_running)
del_timer_sync(&smi_info->si_timer);
del_timer_sync(&smi_info->si_timer);
}
static struct smi_info *find_dup_si(struct smi_info *info)

View File

@ -118,7 +118,7 @@ int ipmi_si_mem_setup(struct si_sm_io *io)
*/
for (idx = 0; idx < io->io_size; idx++) {
if (request_mem_region(addr + idx * io->regspacing,
io->regsize, DEVICE_NAME) == NULL) {
io->regsize, SI_DEVICE_NAME) == NULL) {
/* Undo allocations */
mem_region_cleanup(io, idx);
return -EIO;

View File

@ -150,7 +150,7 @@ static const struct pci_device_id ipmi_pci_devices[] = {
MODULE_DEVICE_TABLE(pci, ipmi_pci_devices);
static struct pci_driver ipmi_pci_driver = {
.name = DEVICE_NAME,
.name = SI_DEVICE_NAME,
.id_table = ipmi_pci_devices,
.probe = ipmi_pci_probe,
.remove = ipmi_pci_remove,

View File

@ -457,7 +457,7 @@ static const struct platform_device_id si_plat_ids[] = {
struct platform_driver ipmi_platform_driver = {
.driver = {
.name = DEVICE_NAME,
.name = SI_DEVICE_NAME,
.of_match_table = of_ipmi_match,
.acpi_match_table = ACPI_PTR(acpi_ipmi_match),
},

View File

@ -99,7 +99,7 @@ int ipmi_si_port_setup(struct si_sm_io *io)
*/
for (idx = 0; idx < io->io_size; idx++) {
if (request_region(addr + idx * io->regspacing,
io->regsize, DEVICE_NAME) == NULL) {
io->regsize, SI_DEVICE_NAME) == NULL) {
/* Undo allocations */
while (idx--)
release_region(addr + idx * io->regspacing,

View File

@ -14,7 +14,10 @@
* Copyright 2002 MontaVista Software Inc.
*/
#include <linux/ipmi.h>
#ifndef __IPMI_SI_SM_H__
#define __IPMI_SI_SM_H__
#include "ipmi_si.h"
/*
* This is defined by the state machines themselves, it is an opaque
@ -22,54 +25,6 @@
*/
struct si_sm_data;
enum si_type {
SI_TYPE_INVALID, SI_KCS, SI_SMIC, SI_BT
};
enum ipmi_addr_space {
IPMI_IO_ADDR_SPACE, IPMI_MEM_ADDR_SPACE
};
/*
* 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)(const struct si_sm_io *io, unsigned int offset);
void (*outputb)(const 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.
*/
void __iomem *addr;
unsigned int regspacing;
unsigned int regsize;
unsigned int regshift;
enum ipmi_addr_space addr_space;
unsigned long addr_data;
enum ipmi_addr_src addr_source; /* ACPI, PCI, SMBIOS, hardcode, etc. */
void (*addr_source_cleanup)(struct si_sm_io *io);
void *addr_source_data;
union ipmi_smi_info_union addr_info;
int (*io_setup)(struct si_sm_io *info);
void (*io_cleanup)(struct si_sm_io *info);
unsigned int io_size;
int irq;
int (*irq_setup)(struct si_sm_io *io);
void *irq_handler_data;
void (*irq_cleanup)(struct si_sm_io *io);
u8 slave_addr;
enum si_type si_type;
struct device *dev;
};
/* Results of SMI events. */
enum si_sm_result {
SI_SM_CALL_WITHOUT_DELAY, /* Call the driver again immediately */
@ -146,3 +101,4 @@ extern const struct si_sm_handlers kcs_smi_handlers;
extern const struct si_sm_handlers smic_smi_handlers;
extern const struct si_sm_handlers bt_smi_handlers;
#endif /* __IPMI_SI_SM_H__ */

View File

@ -52,7 +52,6 @@
#include <linux/acpi.h>
#include <linux/ctype.h>
#include <linux/time64.h>
#include "ipmi_si_sm.h"
#include "ipmi_dmi.h"
#define DEVICE_NAME "ipmi_ssif"
@ -1428,6 +1427,10 @@ static struct ssif_addr_info *ssif_info_find(unsigned short addr,
restart:
list_for_each_entry(info, &ssif_infos, link) {
if (info->binfo.addr == addr) {
if (info->addr_src == SI_SMBIOS)
info->adapter_name = kstrdup(adapter_name,
GFP_KERNEL);
if (info->adapter_name || adapter_name) {
if (!info->adapter_name != !adapter_name) {
/* One is NULL and one is not */
@ -1603,6 +1606,60 @@ out_no_multi_part:
#define GLOBAL_ENABLES_MASK (IPMI_BMC_EVT_MSG_BUFF | IPMI_BMC_RCV_MSG_INTR | \
IPMI_BMC_EVT_MSG_INTR)
static void ssif_remove_dup(struct i2c_client *client)
{
struct ssif_info *ssif_info = i2c_get_clientdata(client);
ipmi_unregister_smi(ssif_info->intf);
kfree(ssif_info);
}
static int ssif_add_infos(struct i2c_client *client)
{
struct ssif_addr_info *info;
info = kzalloc(sizeof(*info), GFP_KERNEL);
if (!info)
return -ENOMEM;
info->addr_src = SI_ACPI;
info->client = client;
info->adapter_name = kstrdup(client->adapter->name, GFP_KERNEL);
info->binfo.addr = client->addr;
list_add_tail(&info->link, &ssif_infos);
return 0;
}
/*
* Prefer ACPI over SMBIOS, if both are available.
* So if we get an ACPI interface and have already registered a SMBIOS
* interface at the same address, remove the SMBIOS and add the ACPI one.
*/
static int ssif_check_and_remove(struct i2c_client *client,
struct ssif_info *ssif_info)
{
struct ssif_addr_info *info;
list_for_each_entry(info, &ssif_infos, link) {
if (!info->client)
return 0;
if (!strcmp(info->adapter_name, client->adapter->name) &&
info->binfo.addr == client->addr) {
if (info->addr_src == SI_ACPI)
return -EEXIST;
if (ssif_info->addr_source == SI_ACPI &&
info->addr_src == SI_SMBIOS) {
dev_info(&client->dev,
"Removing %s-specified SSIF interface in favor of ACPI\n",
ipmi_addr_src_to_str(info->addr_src));
ssif_remove_dup(info->client);
return 0;
}
}
}
return 0;
}
static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
unsigned char msg[3];
@ -1614,13 +1671,17 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id)
u8 slave_addr = 0;
struct ssif_addr_info *addr_info = NULL;
mutex_lock(&ssif_infos_mutex);
resp = kmalloc(IPMI_MAX_MSG_LENGTH, GFP_KERNEL);
if (!resp)
if (!resp) {
mutex_unlock(&ssif_infos_mutex);
return -ENOMEM;
}
ssif_info = kzalloc(sizeof(*ssif_info), GFP_KERNEL);
if (!ssif_info) {
kfree(resp);
mutex_unlock(&ssif_infos_mutex);
return -ENOMEM;
}
@ -1639,6 +1700,19 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id)
}
}
rv = ssif_check_and_remove(client, ssif_info);
/* If rv is 0 and addr source is not SI_ACPI, continue probing */
if (!rv && ssif_info->addr_source == SI_ACPI) {
rv = ssif_add_infos(client);
if (rv) {
dev_err(&client->dev, "Out of memory!, exiting ..\n");
goto out;
}
} else if (rv) {
dev_err(&client->dev, "Not probing, Interface already present\n");
goto out;
}
slave_addr = find_slave_address(client, slave_addr);
dev_info(&client->dev,
@ -1851,6 +1925,7 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id)
kfree(ssif_info);
}
kfree(resp);
mutex_unlock(&ssif_infos_mutex);
return rv;
out_remove_attr: