diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2022-05-24 14:50:54 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2022-05-24 14:50:54 -0700 |
commit | b1b5bf1640165063fd9b7c6aeb5c7d63c4cb3c1d (patch) | |
tree | 2f78b02d832b2dbf621c745b13ce01b5064a8b82 /drivers/char | |
parent | d335371940179318df97d66baef13987ee6e796b (diff) | |
parent | a508e33956b538e034ed5df619a73ec7c15bda72 (diff) |
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
Diffstat (limited to 'drivers/char')
-rw-r--r-- | drivers/char/ipmi/ipmb_dev_int.c | 5 | ||||
-rw-r--r-- | drivers/char/ipmi/ipmi_ipmb.c | 6 | ||||
-rw-r--r-- | drivers/char/ipmi/ipmi_msghandler.c | 111 | ||||
-rw-r--r-- | drivers/char/ipmi/ipmi_poweroff.c | 8 | ||||
-rw-r--r-- | drivers/char/ipmi/ipmi_si_intf.c | 17 | ||||
-rw-r--r-- | drivers/char/ipmi/ipmi_ssif.c | 33 | ||||
-rw-r--r-- | drivers/char/ipmi/ipmi_watchdog.c | 28 |
7 files changed, 154 insertions, 54 deletions
diff --git a/drivers/char/ipmi/ipmb_dev_int.c b/drivers/char/ipmi/ipmb_dev_int.c index 49b8f22fdcf0..db40037eb347 100644 --- a/drivers/char/ipmi/ipmb_dev_int.c +++ b/drivers/char/ipmi/ipmb_dev_int.c @@ -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, }; diff --git a/drivers/char/ipmi/ipmi_ipmb.c b/drivers/char/ipmi/ipmi_ipmb.c index b81b862532fb..ab19b4b3317e 100644 --- a/drivers/char/ipmi/ipmi_ipmb.c +++ b/drivers/char/ipmi/ipmi_ipmb.c @@ -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, }; diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c index f1827257ef0e..703433493c85 100644 --- a/drivers/char/ipmi/ipmi_msghandler.c +++ b/drivers/char/ipmi/ipmi_msghandler.c @@ -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; } diff --git a/drivers/char/ipmi/ipmi_poweroff.c b/drivers/char/ipmi/ipmi_poweroff.c index bc3a18daf97a..163ec9749e55 100644 --- a/drivers/char/ipmi/ipmi_poweroff.c +++ b/drivers/char/ipmi/ipmi_poweroff.c @@ -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); /* diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c index 5604a810fb3d..6e357ad76f2e 100644 --- a/drivers/char/ipmi/ipmi_si_intf.c +++ b/drivers/char/ipmi/ipmi_si_intf.c @@ -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); diff --git a/drivers/char/ipmi/ipmi_ssif.c b/drivers/char/ipmi/ipmi_ssif.c index f199cc194844..fc742ee9c046 100644 --- a/drivers/char/ipmi/ipmi_ssif.c +++ b/drivers/char/ipmi/ipmi_ssif.c @@ -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, diff --git a/drivers/char/ipmi/ipmi_watchdog.c b/drivers/char/ipmi/ipmi_watchdog.c index 0604abdd249a..5b4e677929ca 100644 --- a/drivers/char/ipmi/ipmi_watchdog.c +++ b/drivers/char/ipmi/ipmi_watchdog.c @@ -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 |