Fixes for IPMI

Add limits on the number of users and messages, plus sysfs interfaces
 to control those limits.
 
 Other than that, little cleanups, use dev_xxx() insted of pr_xxx(),
 create initializers for structures, fix a refcount leak, etc.
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEE/Q1c5nzg9ZpmiCaGYfOMkJGb/4EFAmKLc1sACgkQYfOMkJGb
 /4GeJw//SVxwQT1EVqSy70bFg3fxPKvXgTfKWPRwiWURDzBaj+JL9trS0OAbQIh9
 OMUjOrxJcCu9sNtLJ8hRj9KZV0bLT1337HdQ2VXGRmhy4FPEg1YEoOT7Fwbfs3UM
 avy2doVgixMKH3OXwS3C8KGpRycHUjSNaSTFxdHgfJ7dC/3qwwctdI1lvbAPmuWd
 68NY7sEl5XH1Y6Tp68MoV7iMKe+i7bKS/I9C00AhDKOZwF7UG2azAG5WcKVRXNxC
 QGHgMXGTdUfMTz7bjAo8y+zxREYhOv+IMUoAMHJc+7/KQFPbOOGBwbpj7Q4puMbU
 1lyl+PAjl9MmBWQOwZSwvDBmma/2A138m5DB4/QUZxHKFCE9ESCpLgjk8lmhbxqU
 SUbU7H49GdoJt+V4THLVomEk8nS4MpMN2HfumIli2/OX5YSM2lY28jUPscATagyu
 29w2Yr3lMGGuIdLL8vaTuWn0/GauUjYCWlKOdtHh74OcokpAFgnD3Sk2oOlvnr9Q
 d6qhJVdefwRA0LoQAjouYT4ZVtIu+2S7ql1ngShKdObmx+Z6qQgfLyKUncIqgGjv
 c31REL15llyFXCq9HfcNbzhFAJpVd77AOoPT+T6oZPQSGtTHxWmwzrmOM6rzJSde
 xkW9e88ZuIV4E4Gme2MuM1Gj5OY7Y0eaTiMPL6O7++4H9ZgGhto=
 =fVOh
 -----END PGP SIGNATURE-----

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

Pull IPMI update from Corey Minyard:
 "Add limits on the number of users and messages, plus sysfs interfaces
  to control those limits.

  Other than that, little cleanups, use dev_xxx() insted of pr_xxx(),
  create initializers for structures, fix a refcount leak, etc"

* tag 'for-linus-4.19-1' of https://github.com/cminyard/linux-ipmi:
  ipmi:ipmb: Fix refcount leak in ipmi_ipmb_probe
  ipmi: remove unnecessary type castings
  ipmi: Make two logs unique
  ipmi:si: Convert pr_debug() to dev_dbg()
  ipmi: Convert pr_debug() to dev_dbg()
  ipmi: Fix pr_fmt to avoid compilation issues
  ipmi: Add an intializer for ipmi_recv_msg struct
  ipmi: Add an intializer for ipmi_smi_msg struct
  ipmi:ssif: Check for NULL msg when handling events and messages
  ipmi: use simple i2c probe function
  ipmi: Add a sysfs count of total outstanding messages for an interface
  ipmi: Add a sysfs interface to view the number of users
  ipmi: Limit the number of message a user may have outstanding
  ipmi: Add a limit on the number of users that may use IPMI
This commit is contained in:
Linus Torvalds 2022-05-24 14:50:54 -07:00
commit b1b5bf1640
9 changed files with 165 additions and 54 deletions

View File

@ -299,8 +299,7 @@ static int ipmb_slave_cb(struct i2c_client *client,
return 0;
}
static int ipmb_probe(struct i2c_client *client,
const struct i2c_device_id *id)
static int ipmb_probe(struct i2c_client *client)
{
struct ipmb_dev *ipmb_dev;
int ret;
@ -369,7 +368,7 @@ static struct i2c_driver ipmb_driver = {
.name = "ipmb-dev",
.acpi_match_table = ACPI_PTR(acpi_ipmb_id),
},
.probe = ipmb_probe,
.probe_new = ipmb_probe,
.remove = ipmb_remove,
.id_table = ipmb_id,
};

View File

@ -442,8 +442,7 @@ static int ipmi_ipmb_remove(struct i2c_client *client)
return 0;
}
static int ipmi_ipmb_probe(struct i2c_client *client,
const struct i2c_device_id *id)
static int ipmi_ipmb_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
struct ipmi_ipmb_dev *iidev;
@ -476,6 +475,7 @@ static int ipmi_ipmb_probe(struct i2c_client *client,
slave_np = of_parse_phandle(dev->of_node, "slave-dev", 0);
if (slave_np) {
slave_adap = of_get_i2c_adapter_by_node(slave_np);
of_node_put(slave_np);
if (!slave_adap) {
dev_notice(&client->dev,
"Could not find slave adapter\n");
@ -570,7 +570,7 @@ static struct i2c_driver ipmi_ipmb_driver = {
.name = DEVICE_NAME,
.of_match_table = of_ipmi_ipmb_match,
},
.probe = ipmi_ipmb_probe,
.probe_new = ipmi_ipmb_probe,
.remove = ipmi_ipmb_remove,
.id_table = ipmi_ipmb_id,
};

View File

@ -11,8 +11,8 @@
* Copyright 2002 MontaVista Software Inc.
*/
#define pr_fmt(fmt) "%s" fmt, "IPMI message handler: "
#define dev_fmt pr_fmt
#define pr_fmt(fmt) "IPMI message handler: " fmt
#define dev_fmt(fmt) pr_fmt(fmt)
#include <linux/module.h>
#include <linux/errno.h>
@ -145,6 +145,18 @@ module_param(default_max_retries, uint, 0644);
MODULE_PARM_DESC(default_max_retries,
"The time (milliseconds) between retry sends in maintenance mode");
/* The default maximum number of users that may register. */
static unsigned int max_users = 30;
module_param(max_users, uint, 0644);
MODULE_PARM_DESC(max_users,
"The most users that may use the IPMI stack at one time.");
/* The default maximum number of message a user may have outstanding. */
static unsigned int max_msgs_per_user = 100;
module_param(max_msgs_per_user, uint, 0644);
MODULE_PARM_DESC(max_msgs_per_user,
"The most message a user may have outstanding.");
/* Call every ~1000 ms. */
#define IPMI_TIMEOUT_TIME 1000
@ -187,6 +199,8 @@ struct ipmi_user {
/* Does this interface receive IPMI events? */
bool gets_events;
atomic_t nr_msgs;
/* Free must run in process context for RCU cleanup. */
struct work_struct remove_work;
};
@ -442,6 +456,10 @@ struct ipmi_smi {
*/
struct list_head users;
struct srcu_struct users_srcu;
atomic_t nr_users;
struct device_attribute nr_users_devattr;
struct device_attribute nr_msgs_devattr;
/* Used for wake ups at startup. */
wait_queue_head_t waitq;
@ -927,11 +945,13 @@ static int deliver_response(struct ipmi_smi *intf, struct ipmi_recv_msg *msg)
* risk. At this moment, simply skip it in that case.
*/
ipmi_free_recv_msg(msg);
atomic_dec(&msg->user->nr_msgs);
} else {
int index;
struct ipmi_user *user = acquire_ipmi_user(msg->user, &index);
if (user) {
atomic_dec(&user->nr_msgs);
user->handler->ipmi_recv_hndl(msg, user->handler_data);
release_ipmi_user(user, index);
} else {
@ -1230,6 +1250,11 @@ int ipmi_create_user(unsigned int if_num,
goto out_kfree;
found:
if (atomic_add_return(1, &intf->nr_users) > max_users) {
rv = -EBUSY;
goto out_kfree;
}
INIT_WORK(&new_user->remove_work, free_user_work);
rv = init_srcu_struct(&new_user->release_barrier);
@ -1244,6 +1269,7 @@ int ipmi_create_user(unsigned int if_num,
/* Note that each existing user holds a refcount to the interface. */
kref_get(&intf->refcount);
atomic_set(&new_user->nr_msgs, 0);
kref_init(&new_user->refcount);
new_user->handler = handler;
new_user->handler_data = handler_data;
@ -1262,6 +1288,7 @@ int ipmi_create_user(unsigned int if_num,
return 0;
out_kfree:
atomic_dec(&intf->nr_users);
srcu_read_unlock(&ipmi_interfaces_srcu, index);
vfree(new_user);
return rv;
@ -1336,6 +1363,7 @@ static void _ipmi_destroy_user(struct ipmi_user *user)
/* Remove the user from the interface's sequence table. */
spin_lock_irqsave(&intf->seq_lock, flags);
list_del_rcu(&user->link);
atomic_dec(&intf->nr_users);
for (i = 0; i < IPMI_IPMB_NUM_SEQ; i++) {
if (intf->seq_table[i].inuse
@ -2284,6 +2312,14 @@ static int i_ipmi_request(struct ipmi_user *user,
struct ipmi_recv_msg *recv_msg;
int rv = 0;
if (user) {
if (atomic_add_return(1, &user->nr_msgs) > max_msgs_per_user) {
/* Decrement will happen at the end of the routine. */
rv = -EBUSY;
goto out;
}
}
if (supplied_recv)
recv_msg = supplied_recv;
else {
@ -2296,7 +2332,7 @@ static int i_ipmi_request(struct ipmi_user *user,
recv_msg->user_msg_data = user_msg_data;
if (supplied_smi)
smi_msg = (struct ipmi_smi_msg *) supplied_smi;
smi_msg = supplied_smi;
else {
smi_msg = ipmi_alloc_smi_msg();
if (smi_msg == NULL) {
@ -2348,13 +2384,16 @@ out_err:
ipmi_free_smi_msg(smi_msg);
ipmi_free_recv_msg(recv_msg);
} else {
pr_debug("Send: %*ph\n", smi_msg->data_size, smi_msg->data);
dev_dbg(intf->si_dev, "Send: %*ph\n",
smi_msg->data_size, smi_msg->data);
smi_send(intf, intf->handlers, smi_msg, priority);
}
rcu_read_unlock();
out:
if (rv && user)
atomic_dec(&user->nr_msgs);
return rv;
}
@ -3471,6 +3510,36 @@ void ipmi_poll_interface(struct ipmi_user *user)
}
EXPORT_SYMBOL(ipmi_poll_interface);
static ssize_t nr_users_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct ipmi_smi *intf = container_of(attr,
struct ipmi_smi, nr_users_devattr);
return sysfs_emit(buf, "%d\n", atomic_read(&intf->nr_users));
}
static DEVICE_ATTR_RO(nr_users);
static ssize_t nr_msgs_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct ipmi_smi *intf = container_of(attr,
struct ipmi_smi, nr_msgs_devattr);
struct ipmi_user *user;
int index;
unsigned int count = 0;
index = srcu_read_lock(&intf->users_srcu);
list_for_each_entry_rcu(user, &intf->users, link)
count += atomic_read(&user->nr_msgs);
srcu_read_unlock(&intf->users_srcu, index);
return sysfs_emit(buf, "%u\n", count);
}
static DEVICE_ATTR_RO(nr_msgs);
static void redo_bmc_reg(struct work_struct *work)
{
struct ipmi_smi *intf = container_of(work, struct ipmi_smi,
@ -3529,6 +3598,7 @@ int ipmi_add_smi(struct module *owner,
if (slave_addr != 0)
intf->addrinfo[0].address = slave_addr;
INIT_LIST_HEAD(&intf->users);
atomic_set(&intf->nr_users, 0);
intf->handlers = handlers;
intf->send_info = send_info;
spin_lock_init(&intf->seq_lock);
@ -3592,6 +3662,20 @@ int ipmi_add_smi(struct module *owner,
if (rv)
goto out_err_bmc_reg;
intf->nr_users_devattr = dev_attr_nr_users;
sysfs_attr_init(&intf->nr_users_devattr.attr);
rv = device_create_file(intf->si_dev, &intf->nr_users_devattr);
if (rv)
goto out_err_bmc_reg;
intf->nr_msgs_devattr = dev_attr_nr_msgs;
sysfs_attr_init(&intf->nr_msgs_devattr.attr);
rv = device_create_file(intf->si_dev, &intf->nr_msgs_devattr);
if (rv) {
device_remove_file(intf->si_dev, &intf->nr_users_devattr);
goto out_err_bmc_reg;
}
/*
* Keep memory order straight for RCU readers. Make
* sure everything else is committed to memory before
@ -3691,6 +3775,9 @@ void ipmi_unregister_smi(struct ipmi_smi *intf)
/* At this point no users can be added to the interface. */
device_remove_file(intf->si_dev, &intf->nr_msgs_devattr);
device_remove_file(intf->si_dev, &intf->nr_users_devattr);
/*
* Call all the watcher interfaces to tell them that
* an interface is going away.
@ -3839,7 +3926,8 @@ static int handle_ipmb_get_msg_cmd(struct ipmi_smi *intf,
msg->data[10] = ipmb_checksum(&msg->data[6], 4);
msg->data_size = 11;
pr_debug("Invalid command: %*ph\n", msg->data_size, msg->data);
dev_dbg(intf->si_dev, "Invalid command: %*ph\n",
msg->data_size, msg->data);
rcu_read_lock();
if (!intf->in_shutdown) {
@ -3992,10 +4080,10 @@ static int handle_ipmb_direct_rcv_rsp(struct ipmi_smi *intf,
struct ipmi_recv_msg *recv_msg;
struct ipmi_ipmb_direct_addr *daddr;
recv_msg = (struct ipmi_recv_msg *) msg->user_data;
recv_msg = msg->user_data;
if (recv_msg == NULL) {
dev_warn(intf->si_dev,
"IPMI message received with no owner. This could be because of a malformed message, or because of a hardware error. Contact your hardware vendor for assistance.\n");
"IPMI direct message received with no owner. This could be because of a malformed message, or because of a hardware error. Contact your hardware vendor for assistance.\n");
return 0;
}
@ -4410,10 +4498,10 @@ static int handle_bmc_rsp(struct ipmi_smi *intf,
struct ipmi_recv_msg *recv_msg;
struct ipmi_system_interface_addr *smi_addr;
recv_msg = (struct ipmi_recv_msg *) msg->user_data;
recv_msg = msg->user_data;
if (recv_msg == NULL) {
dev_warn(intf->si_dev,
"IPMI message received with no owner. This could be because of a malformed message, or because of a hardware error. Contact your hardware vendor for assistance.\n");
"IPMI SMI message received with no owner. This could be because of a malformed message, or because of a hardware error. Contact your hardware vendor for assistance.\n");
return 0;
}
@ -4447,7 +4535,7 @@ static int handle_one_recv_msg(struct ipmi_smi *intf,
unsigned char cc;
bool is_cmd = !((msg->rsp[0] >> 2) & 1);
pr_debug("Recv: %*ph\n", msg->rsp_size, msg->rsp);
dev_dbg(intf->si_dev, "Recv: %*ph\n", msg->rsp_size, msg->rsp);
if (msg->rsp_size < 2) {
/* Message is too small to be correct. */
@ -4831,7 +4919,8 @@ smi_from_recv_msg(struct ipmi_smi *intf, struct ipmi_recv_msg *recv_msg,
smi_msg->data_size = recv_msg->msg.data_len;
smi_msg->msgid = STORE_SEQ_IN_MSGID(seq, seqid);
pr_debug("Resend: %*ph\n", smi_msg->data_size, smi_msg->data);
dev_dbg(intf->si_dev, "Resend: %*ph\n",
smi_msg->data_size, smi_msg->data);
return smi_msg;
}

View File

@ -94,12 +94,8 @@ static void dummy_recv_free(struct ipmi_recv_msg *msg)
{
atomic_dec(&dummy_count);
}
static struct ipmi_smi_msg halt_smi_msg = {
.done = dummy_smi_free
};
static struct ipmi_recv_msg halt_recv_msg = {
.done = dummy_recv_free
};
static struct ipmi_smi_msg halt_smi_msg = INIT_IPMI_SMI_MSG(dummy_smi_free);
static struct ipmi_recv_msg halt_recv_msg = INIT_IPMI_RECV_MSG(dummy_recv_free);
/*

View File

@ -264,15 +264,16 @@ static void cleanup_one_si(struct smi_info *smi_info);
static void cleanup_ipmi_si(void);
#ifdef DEBUG_TIMING
void debug_timestamp(char *msg)
void debug_timestamp(struct smi_info *smi_info, char *msg)
{
struct timespec64 t;
ktime_get_ts64(&t);
pr_debug("**%s: %lld.%9.9ld\n", msg, t.tv_sec, t.tv_nsec);
dev_dbg(smi_info->io.dev, "**%s: %lld.%9.9ld\n",
msg, t.tv_sec, t.tv_nsec);
}
#else
#define debug_timestamp(x)
#define debug_timestamp(smi_info, x)
#endif
static ATOMIC_NOTIFIER_HEAD(xaction_notifier_list);
@ -318,7 +319,7 @@ static enum si_sm_result start_next_msg(struct smi_info *smi_info)
smi_info->curr_msg = smi_info->waiting_msg;
smi_info->waiting_msg = NULL;
debug_timestamp("Start2");
debug_timestamp(smi_info, "Start2");
err = atomic_notifier_call_chain(&xaction_notifier_list,
0, smi_info);
if (err & NOTIFY_STOP_MASK) {
@ -538,7 +539,7 @@ static void handle_transaction_done(struct smi_info *smi_info)
{
struct ipmi_smi_msg *msg;
debug_timestamp("Done");
debug_timestamp(smi_info, "Done");
switch (smi_info->si_state) {
case SI_NORMAL:
if (!smi_info->curr_msg)
@ -901,7 +902,7 @@ static void sender(void *send_info,
struct smi_info *smi_info = send_info;
unsigned long flags;
debug_timestamp("Enqueue");
debug_timestamp(smi_info, "Enqueue");
if (smi_info->run_to_completion) {
/*
@ -1079,7 +1080,7 @@ static void smi_timeout(struct timer_list *t)
long timeout;
spin_lock_irqsave(&(smi_info->si_lock), flags);
debug_timestamp("Timer");
debug_timestamp(smi_info, "Timer");
jiffies_now = jiffies;
time_diff = (((long)jiffies_now - (long)smi_info->last_timeout_jiffies)
@ -1128,7 +1129,7 @@ irqreturn_t ipmi_si_irq_handler(int irq, void *data)
smi_inc_stat(smi_info, interrupts);
debug_timestamp("Interrupt");
debug_timestamp(smi_info, "Interrupt");
smi_event_handler(smi_info, 0);
spin_unlock_irqrestore(&(smi_info->si_lock), flags);

View File

@ -814,6 +814,14 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result,
break;
case SSIF_GETTING_EVENTS:
if (!msg) {
/* Should never happen, but just in case. */
dev_warn(&ssif_info->client->dev,
"No message set while getting events\n");
ipmi_ssif_unlock_cond(ssif_info, flags);
break;
}
if ((result < 0) || (len < 3) || (msg->rsp[2] != 0)) {
/* Error getting event, probably done. */
msg->done(msg);
@ -838,6 +846,14 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result,
break;
case SSIF_GETTING_MESSAGES:
if (!msg) {
/* Should never happen, but just in case. */
dev_warn(&ssif_info->client->dev,
"No message set while getting messages\n");
ipmi_ssif_unlock_cond(ssif_info, flags);
break;
}
if ((result < 0) || (len < 3) || (msg->rsp[2] != 0)) {
/* Error getting event, probably done. */
msg->done(msg);
@ -861,6 +877,13 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result,
deliver_recv_msg(ssif_info, msg);
}
break;
default:
/* Should never happen, but just in case. */
dev_warn(&ssif_info->client->dev,
"Invalid state in message done handling: %d\n",
ssif_info->ssif_state);
ipmi_ssif_unlock_cond(ssif_info, flags);
}
flags = ipmi_ssif_lock_cond(ssif_info, &oflags);
@ -1053,7 +1076,7 @@ static void start_next_msg(struct ssif_info *ssif_info, unsigned long *flags)
static void sender(void *send_info,
struct ipmi_smi_msg *msg)
{
struct ssif_info *ssif_info = (struct ssif_info *) send_info;
struct ssif_info *ssif_info = send_info;
unsigned long oflags, *flags;
BUG_ON(ssif_info->waiting_msg);
@ -1090,7 +1113,7 @@ static int get_smi_info(void *send_info, struct ipmi_smi_info *data)
*/
static void request_events(void *send_info)
{
struct ssif_info *ssif_info = (struct ssif_info *) send_info;
struct ssif_info *ssif_info = send_info;
unsigned long oflags, *flags;
if (!ssif_info->has_event_buffer)
@ -1107,7 +1130,7 @@ static void request_events(void *send_info)
*/
static void ssif_set_need_watch(void *send_info, unsigned int watch_mask)
{
struct ssif_info *ssif_info = (struct ssif_info *) send_info;
struct ssif_info *ssif_info = send_info;
unsigned long oflags, *flags;
long timeout = 0;
@ -1619,7 +1642,7 @@ static int ssif_check_and_remove(struct i2c_client *client,
return 0;
}
static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id)
static int ssif_probe(struct i2c_client *client)
{
unsigned char msg[3];
unsigned char *resp;
@ -2037,7 +2060,7 @@ static struct i2c_driver ssif_i2c_driver = {
.driver = {
.name = DEVICE_NAME
},
.probe = ssif_probe,
.probe_new = ssif_probe,
.remove = ssif_remove,
.alert = ssif_alert,
.id_table = ssif_id,

View File

@ -354,12 +354,8 @@ static void msg_free_recv(struct ipmi_recv_msg *msg)
complete(&msg_wait);
}
}
static struct ipmi_smi_msg smi_msg = {
.done = msg_free_smi
};
static struct ipmi_recv_msg recv_msg = {
.done = msg_free_recv
};
static struct ipmi_smi_msg smi_msg = INIT_IPMI_SMI_MSG(msg_free_smi);
static struct ipmi_recv_msg recv_msg = INIT_IPMI_RECV_MSG(msg_free_recv);
static int __ipmi_set_timeout(struct ipmi_smi_msg *smi_msg,
struct ipmi_recv_msg *recv_msg,
@ -475,12 +471,10 @@ static void panic_recv_free(struct ipmi_recv_msg *msg)
atomic_dec(&panic_done_count);
}
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_smi_msg panic_halt_heartbeat_smi_msg =
INIT_IPMI_SMI_MSG(panic_smi_free);
static struct ipmi_recv_msg panic_halt_heartbeat_recv_msg =
INIT_IPMI_RECV_MSG(panic_recv_free);
static void panic_halt_ipmi_heartbeat(void)
{
@ -516,12 +510,10 @@ static void panic_halt_ipmi_heartbeat(void)
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
};
static struct ipmi_smi_msg panic_halt_smi_msg =
INIT_IPMI_SMI_MSG(panic_smi_free);
static struct ipmi_recv_msg panic_halt_recv_msg =
INIT_IPMI_RECV_MSG(panic_recv_free);
/*
* Special call, doesn't claim any locks. This is only to be called

View File

@ -72,6 +72,11 @@ struct ipmi_recv_msg {
unsigned char msg_data[IPMI_MAX_MSG_LENGTH];
};
#define INIT_IPMI_RECV_MSG(done_handler) \
{ \
.done = done_handler \
}
/* Allocate and free the receive message. */
void ipmi_free_recv_msg(struct ipmi_recv_msg *msg);

View File

@ -125,6 +125,12 @@ struct ipmi_smi_msg {
void (*done)(struct ipmi_smi_msg *msg);
};
#define INIT_IPMI_SMI_MSG(done_handler) \
{ \
.done = done_handler, \
.type = IPMI_SMI_MSG_TYPE_NORMAL \
}
struct ipmi_smi_handlers {
struct module *owner;