diff options
Diffstat (limited to 'drivers/char/ipmi/ipmi_msghandler.c')
| -rw-r--r-- | drivers/char/ipmi/ipmi_msghandler.c | 902 |
1 files changed, 592 insertions, 310 deletions
diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c index 7a88dfd4427..e6db9381b2c 100644 --- a/drivers/char/ipmi/ipmi_msghandler.c +++ b/drivers/char/ipmi/ipmi_msghandler.c @@ -33,8 +33,9 @@ #include <linux/module.h> #include <linux/errno.h> -#include <asm/system.h> #include <linux/poll.h> +#include <linux/sched.h> +#include <linux/seq_file.h> #include <linux/spinlock.h> #include <linux/mutex.h> #include <linux/slab.h> @@ -44,6 +45,7 @@ #include <linux/init.h> #include <linux/proc_fs.h> #include <linux/rcupdate.h> +#include <linux/interrupt.h> #define PFX "IPMI message handler: " @@ -51,6 +53,9 @@ static struct ipmi_recv_msg *ipmi_alloc_recv_msg(void); static int ipmi_init_msghandler(void); +static void smi_recv_tasklet(unsigned long); +static void handle_new_recv_msgs(ipmi_smi_t intf); +static void need_waiter(ipmi_smi_t intf); static int initialized; @@ -69,14 +74,28 @@ static struct proc_dir_entry *proc_ipmi_root; */ #define MAX_MSG_TIMEOUT 60000 +/* Call every ~1000 ms. */ +#define IPMI_TIMEOUT_TIME 1000 + +/* How many jiffies does it take to get to the timeout time. */ +#define IPMI_TIMEOUT_JIFFIES ((IPMI_TIMEOUT_TIME * HZ) / 1000) + +/* + * Request events from the queue every second (this is the number of + * IPMI_TIMEOUT_TIMES between event requests). Hopefully, in the + * future, IPMI will add a way to know immediately if an event is in + * the queue and this silliness can go away. + */ +#define IPMI_REQUEST_EV_TIME (1000 / (IPMI_TIMEOUT_TIME)) + /* * The main "user" data structure. */ struct ipmi_user { struct list_head link; - /* Set to "0" when the user is destroyed. */ - int valid; + /* Set to false when the user is destroyed. */ + bool valid; struct kref refcount; @@ -88,7 +107,7 @@ struct ipmi_user { ipmi_smi_t intf; /* Does this interface receive IPMI events? */ - int gets_events; + bool gets_events; }; struct cmd_rcvr { @@ -285,6 +304,11 @@ enum ipmi_stat_indexes { /* Events that were received with the proper format. */ IPMI_STAT_events, + /* Retransmissions on IPMB that failed. */ + IPMI_STAT_dropped_rexmit_ipmb_commands, + + /* Retransmissions on LAN that failed. */ + IPMI_STAT_dropped_rexmit_lan_commands, /* This *must* remain last, add new values above this. */ IPMI_NUM_STATS @@ -348,12 +372,15 @@ struct ipmi_smi { int curr_seq; /* - * Messages that were delayed for some reason (out of memory, - * for instance), will go in here to be processed later in a - * periodic timer interrupt. + * Messages queued for delivery. If delivery fails (out of memory + * for instance), They will stay in here to be processed later in a + * periodic timer interrupt. The tasklet is for handling received + * messages directly from the handler. */ spinlock_t waiting_msgs_lock; struct list_head waiting_msgs; + atomic_t watchdog_pretimeouts_to_deliver; + struct tasklet_struct recv_tasklet; /* * The list of command receivers that are registered for commands @@ -371,6 +398,9 @@ struct ipmi_smi { unsigned int waiting_events_count; /* How many events in queue? */ char delivering_events; char event_msg_printed; + atomic_t event_waiters; + unsigned int ticks_to_req_ev; + int last_needs_timer; /* * The event receiver for my BMC, only really used at panic @@ -383,7 +413,7 @@ struct ipmi_smi { /* For handling of maintenance mode. */ int maintenance_mode; - int maintenance_mode_enable; + bool maintenance_mode_enable; int auto_maintenance_timeout; spinlock_t maintenance_mode_lock; /* Used in a timer... */ @@ -439,12 +469,25 @@ static DEFINE_MUTEX(ipmi_interfaces_mutex); static LIST_HEAD(smi_watchers); static DEFINE_MUTEX(smi_watchers_mutex); - #define ipmi_inc_stat(intf, stat) \ atomic_inc(&(intf)->stats[IPMI_STAT_ ## stat]) #define ipmi_get_stat(intf, stat) \ ((unsigned int) atomic_read(&(intf)->stats[IPMI_STAT_ ## stat])) +static int is_lan_addr(struct ipmi_addr *addr) +{ + return addr->addr_type == IPMI_LAN_ADDR_TYPE; +} + +static int is_ipmb_addr(struct ipmi_addr *addr) +{ + return addr->addr_type == IPMI_IPMB_ADDR_TYPE; +} + +static int is_ipmb_bcast_addr(struct ipmi_addr *addr) +{ + return addr->addr_type == IPMI_IPMB_BROADCAST_ADDR_TYPE; +} static void free_recv_msg_list(struct list_head *q) { @@ -472,6 +515,8 @@ static void clean_up_interface_data(ipmi_smi_t intf) struct cmd_rcvr *rcvr, *rcvr2; struct list_head list; + tasklet_kill(&intf->recv_tasklet); + free_smi_msg_list(&intf->waiting_msgs); free_recv_msg_list(&intf->waiting_events); @@ -601,8 +646,7 @@ ipmi_addr_equal(struct ipmi_addr *addr1, struct ipmi_addr *addr2) return (smi_addr1->lun == smi_addr2->lun); } - if ((addr1->addr_type == IPMI_IPMB_ADDR_TYPE) - || (addr1->addr_type == IPMI_IPMB_BROADCAST_ADDR_TYPE)) { + if (is_ipmb_addr(addr1) || is_ipmb_bcast_addr(addr1)) { struct ipmi_ipmb_addr *ipmb_addr1 = (struct ipmi_ipmb_addr *) addr1; struct ipmi_ipmb_addr *ipmb_addr2 @@ -612,7 +656,7 @@ ipmi_addr_equal(struct ipmi_addr *addr1, struct ipmi_addr *addr2) && (ipmb_addr1->lun == ipmb_addr2->lun)); } - if (addr1->addr_type == IPMI_LAN_ADDR_TYPE) { + if (is_lan_addr(addr1)) { struct ipmi_lan_addr *lan_addr1 = (struct ipmi_lan_addr *) addr1; struct ipmi_lan_addr *lan_addr2 @@ -644,14 +688,13 @@ int ipmi_validate_addr(struct ipmi_addr *addr, int len) || (addr->channel < 0)) return -EINVAL; - if ((addr->addr_type == IPMI_IPMB_ADDR_TYPE) - || (addr->addr_type == IPMI_IPMB_BROADCAST_ADDR_TYPE)) { + if (is_ipmb_addr(addr) || is_ipmb_bcast_addr(addr)) { if (len < sizeof(struct ipmi_ipmb_addr)) return -EINVAL; return 0; } - if (addr->addr_type == IPMI_LAN_ADDR_TYPE) { + if (is_lan_addr(addr)) { if (len < sizeof(struct ipmi_lan_addr)) return -EINVAL; return 0; @@ -746,6 +789,7 @@ static int intf_next_seq(ipmi_smi_t intf, *seq = i; *seqid = intf->seq_table[i].seqid; intf->curr_seq = (i+1)%IPMI_IPMB_NUM_SEQ; + need_waiter(intf); } else { rv = -EAGAIN; } @@ -915,7 +959,7 @@ int ipmi_create_user(unsigned int if_num, new_user->handler = handler; new_user->handler_data = handler_data; new_user->intf = intf; - new_user->gets_events = 0; + new_user->gets_events = false; if (!try_module_get(intf->handlers->owner)) { rv = -ENODEV; @@ -936,10 +980,15 @@ int ipmi_create_user(unsigned int if_num, */ mutex_unlock(&ipmi_interfaces_mutex); - new_user->valid = 1; + new_user->valid = true; spin_lock_irqsave(&intf->seq_lock, flags); list_add_rcu(&new_user->link, &intf->users); spin_unlock_irqrestore(&intf->seq_lock, flags); + if (handler->ipmi_watchdog_pretimeout) { + /* User wants pretimeouts, so make sure to watch for them. */ + if (atomic_inc_return(&intf->event_waiters) == 1) + need_waiter(intf); + } *user = new_user; return 0; @@ -952,6 +1001,33 @@ out_kfree: } EXPORT_SYMBOL(ipmi_create_user); +int ipmi_get_smi_info(int if_num, struct ipmi_smi_info *data) +{ + int rv = 0; + ipmi_smi_t intf; + struct ipmi_smi_handlers *handlers; + + mutex_lock(&ipmi_interfaces_mutex); + list_for_each_entry_rcu(intf, &ipmi_interfaces, link) { + if (intf->intf_num == if_num) + goto found; + } + /* Not found, return an error */ + rv = -EINVAL; + mutex_unlock(&ipmi_interfaces_mutex); + return rv; + +found: + handlers = intf->handlers; + rv = -ENOSYS; + if (handlers->get_smi_info) + rv = handlers->get_smi_info(intf->send_info, data); + mutex_unlock(&ipmi_interfaces_mutex); + + return rv; +} +EXPORT_SYMBOL(ipmi_get_smi_info); + static void free_user(struct kref *ref) { ipmi_user_t user = container_of(ref, struct ipmi_user, refcount); @@ -966,7 +1042,13 @@ int ipmi_destroy_user(ipmi_user_t user) struct cmd_rcvr *rcvr; struct cmd_rcvr *rcvrs = NULL; - user->valid = 0; + user->valid = false; + + if (user->handler->ipmi_watchdog_pretimeout) + atomic_dec(&intf->event_waiters); + + if (user->gets_events) + atomic_dec(&intf->event_waiters); /* Remove the user from the interface's sequence table. */ spin_lock_irqsave(&intf->seq_lock, flags); @@ -1102,25 +1184,23 @@ int ipmi_set_maintenance_mode(ipmi_user_t user, int mode) if (intf->maintenance_mode != mode) { switch (mode) { case IPMI_MAINTENANCE_MODE_AUTO: - intf->maintenance_mode = mode; intf->maintenance_mode_enable = (intf->auto_maintenance_timeout > 0); break; case IPMI_MAINTENANCE_MODE_OFF: - intf->maintenance_mode = mode; - intf->maintenance_mode_enable = 0; + intf->maintenance_mode_enable = false; break; case IPMI_MAINTENANCE_MODE_ON: - intf->maintenance_mode = mode; - intf->maintenance_mode_enable = 1; + intf->maintenance_mode_enable = true; break; default: rv = -EINVAL; goto out_unlock; } + intf->maintenance_mode = mode; maintenance_mode_update(intf); } @@ -1131,7 +1211,7 @@ int ipmi_set_maintenance_mode(ipmi_user_t user, int mode) } EXPORT_SYMBOL(ipmi_set_maintenance_mode); -int ipmi_set_gets_events(ipmi_user_t user, int val) +int ipmi_set_gets_events(ipmi_user_t user, bool val) { unsigned long flags; ipmi_smi_t intf = user->intf; @@ -1141,8 +1221,18 @@ int ipmi_set_gets_events(ipmi_user_t user, int val) INIT_LIST_HEAD(&msgs); spin_lock_irqsave(&intf->events_lock, flags); + if (user->gets_events == val) + goto out; + user->gets_events = val; + if (val) { + if (atomic_inc_return(&intf->event_waiters) == 1) + need_waiter(intf); + } else { + atomic_dec(&intf->event_waiters); + } + if (intf->delivering_events) /* * Another thread is delivering events for this, so @@ -1236,6 +1326,9 @@ int ipmi_register_for_cmd(ipmi_user_t user, goto out_unlock; } + if (atomic_inc_return(&intf->event_waiters) == 1) + need_waiter(intf); + list_add_rcu(&rcvr->link, &intf->cmd_rcvrs); out_unlock: @@ -1277,6 +1370,7 @@ int ipmi_unregister_for_cmd(ipmi_user_t user, mutex_unlock(&intf->cmd_rcvrs_mutex); synchronize_rcu(); while (rcvrs) { + atomic_dec(&intf->event_waiters); rcvr = rcvrs; rcvrs = rcvr->next; kfree(rcvr); @@ -1482,7 +1576,7 @@ static int i_ipmi_request(ipmi_user_t user, = IPMI_MAINTENANCE_MODE_TIMEOUT; if (!intf->maintenance_mode && !intf->maintenance_mode_enable) { - intf->maintenance_mode_enable = 1; + intf->maintenance_mode_enable = true; maintenance_mode_update(intf); } spin_unlock_irqrestore(&intf->maintenance_mode_lock, @@ -1503,8 +1597,7 @@ static int i_ipmi_request(ipmi_user_t user, memcpy(&(smi_msg->data[2]), msg->data, msg->data_len); smi_msg->data_size = msg->data_len + 2; ipmi_inc_stat(intf, sent_local_commands); - } else if ((addr->addr_type == IPMI_IPMB_ADDR_TYPE) - || (addr->addr_type == IPMI_IPMB_BROADCAST_ADDR_TYPE)) { + } else if (is_ipmb_addr(addr) || is_ipmb_bcast_addr(addr)) { struct ipmi_ipmb_addr *ipmb_addr; unsigned char ipmb_seq; long seqid; @@ -1583,8 +1676,6 @@ static int i_ipmi_request(ipmi_user_t user, spin_lock_irqsave(&(intf->seq_lock), flags); - ipmi_inc_stat(intf, sent_ipmb_commands); - /* * Create a sequence number with a 1 second * timeout and 4 retries. @@ -1606,6 +1697,8 @@ static int i_ipmi_request(ipmi_user_t user, goto out_err; } + ipmi_inc_stat(intf, sent_ipmb_commands); + /* * Store the sequence number in the message, * so that when the send message response @@ -1635,7 +1728,7 @@ static int i_ipmi_request(ipmi_user_t user, */ spin_unlock_irqrestore(&(intf->seq_lock), flags); } - } else if (addr->addr_type == IPMI_LAN_ADDR_TYPE) { + } else if (is_lan_addr(addr)) { struct ipmi_lan_addr *lan_addr; unsigned char ipmb_seq; long seqid; @@ -1696,8 +1789,6 @@ static int i_ipmi_request(ipmi_user_t user, spin_lock_irqsave(&(intf->seq_lock), flags); - ipmi_inc_stat(intf, sent_lan_commands); - /* * Create a sequence number with a 1 second * timeout and 4 retries. @@ -1719,6 +1810,8 @@ static int i_ipmi_request(ipmi_user_t user, goto out_err; } + ipmi_inc_stat(intf, sent_lan_commands); + /* * Store the sequence number in the message, * so that when the send message response @@ -1796,7 +1889,7 @@ int ipmi_request_settime(ipmi_user_t user, int retries, unsigned int retry_time_ms) { - unsigned char saddr, lun; + unsigned char saddr = 0, lun = 0; int rv; if (!user) @@ -1828,7 +1921,7 @@ int ipmi_request_supply_msgs(ipmi_user_t user, struct ipmi_recv_msg *supplied_recv, int priority) { - unsigned char saddr, lun; + unsigned char saddr = 0, lun = 0; int rv; if (!user) @@ -1852,99 +1945,129 @@ int ipmi_request_supply_msgs(ipmi_user_t user, EXPORT_SYMBOL(ipmi_request_supply_msgs); #ifdef CONFIG_PROC_FS -static int ipmb_file_read_proc(char *page, char **start, off_t off, - int count, int *eof, void *data) +static int smi_ipmb_proc_show(struct seq_file *m, void *v) { - char *out = (char *) page; - ipmi_smi_t intf = data; + ipmi_smi_t intf = m->private; int i; - int rv = 0; - for (i = 0; i < IPMI_MAX_CHANNELS; i++) - rv += sprintf(out+rv, "%x ", intf->channels[i].address); - out[rv-1] = '\n'; /* Replace the final space with a newline */ - out[rv] = '\0'; - rv++; - return rv; + seq_printf(m, "%x", intf->channels[0].address); + for (i = 1; i < IPMI_MAX_CHANNELS; i++) + seq_printf(m, " %x", intf->channels[i].address); + return seq_putc(m, '\n'); +} + +static int smi_ipmb_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, smi_ipmb_proc_show, PDE_DATA(inode)); } -static int version_file_read_proc(char *page, char **start, off_t off, - int count, int *eof, void *data) +static const struct file_operations smi_ipmb_proc_ops = { + .open = smi_ipmb_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int smi_version_proc_show(struct seq_file *m, void *v) { - char *out = (char *) page; - ipmi_smi_t intf = data; + ipmi_smi_t intf = m->private; - return sprintf(out, "%u.%u\n", + return seq_printf(m, "%u.%u\n", ipmi_version_major(&intf->bmc->id), ipmi_version_minor(&intf->bmc->id)); } -static int stat_file_read_proc(char *page, char **start, off_t off, - int count, int *eof, void *data) +static int smi_version_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, smi_version_proc_show, PDE_DATA(inode)); +} + +static const struct file_operations smi_version_proc_ops = { + .open = smi_version_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int smi_stats_proc_show(struct seq_file *m, void *v) { - char *out = (char *) page; - ipmi_smi_t intf = data; + ipmi_smi_t intf = m->private; - out += sprintf(out, "sent_invalid_commands: %u\n", + seq_printf(m, "sent_invalid_commands: %u\n", ipmi_get_stat(intf, sent_invalid_commands)); - out += sprintf(out, "sent_local_commands: %u\n", + seq_printf(m, "sent_local_commands: %u\n", ipmi_get_stat(intf, sent_local_commands)); - out += sprintf(out, "handled_local_responses: %u\n", + seq_printf(m, "handled_local_responses: %u\n", ipmi_get_stat(intf, handled_local_responses)); - out += sprintf(out, "unhandled_local_responses: %u\n", + seq_printf(m, "unhandled_local_responses: %u\n", ipmi_get_stat(intf, unhandled_local_responses)); - out += sprintf(out, "sent_ipmb_commands: %u\n", + seq_printf(m, "sent_ipmb_commands: %u\n", ipmi_get_stat(intf, sent_ipmb_commands)); - out += sprintf(out, "sent_ipmb_command_errs: %u\n", + seq_printf(m, "sent_ipmb_command_errs: %u\n", ipmi_get_stat(intf, sent_ipmb_command_errs)); - out += sprintf(out, "retransmitted_ipmb_commands: %u\n", + seq_printf(m, "retransmitted_ipmb_commands: %u\n", ipmi_get_stat(intf, retransmitted_ipmb_commands)); - out += sprintf(out, "timed_out_ipmb_commands: %u\n", + seq_printf(m, "timed_out_ipmb_commands: %u\n", ipmi_get_stat(intf, timed_out_ipmb_commands)); - out += sprintf(out, "timed_out_ipmb_broadcasts: %u\n", + seq_printf(m, "timed_out_ipmb_broadcasts: %u\n", ipmi_get_stat(intf, timed_out_ipmb_broadcasts)); - out += sprintf(out, "sent_ipmb_responses: %u\n", + seq_printf(m, "sent_ipmb_responses: %u\n", ipmi_get_stat(intf, sent_ipmb_responses)); - out += sprintf(out, "handled_ipmb_responses: %u\n", + seq_printf(m, "handled_ipmb_responses: %u\n", ipmi_get_stat(intf, handled_ipmb_responses)); - out += sprintf(out, "invalid_ipmb_responses: %u\n", + seq_printf(m, "invalid_ipmb_responses: %u\n", ipmi_get_stat(intf, invalid_ipmb_responses)); - out += sprintf(out, "unhandled_ipmb_responses: %u\n", + seq_printf(m, "unhandled_ipmb_responses: %u\n", ipmi_get_stat(intf, unhandled_ipmb_responses)); - out += sprintf(out, "sent_lan_commands: %u\n", + seq_printf(m, "sent_lan_commands: %u\n", ipmi_get_stat(intf, sent_lan_commands)); - out += sprintf(out, "sent_lan_command_errs: %u\n", + seq_printf(m, "sent_lan_command_errs: %u\n", ipmi_get_stat(intf, sent_lan_command_errs)); - out += sprintf(out, "retransmitted_lan_commands: %u\n", + seq_printf(m, "retransmitted_lan_commands: %u\n", ipmi_get_stat(intf, retransmitted_lan_commands)); - out += sprintf(out, "timed_out_lan_commands: %u\n", + seq_printf(m, "timed_out_lan_commands: %u\n", ipmi_get_stat(intf, timed_out_lan_commands)); - out += sprintf(out, "sent_lan_responses: %u\n", + seq_printf(m, "sent_lan_responses: %u\n", ipmi_get_stat(intf, sent_lan_responses)); - out += sprintf(out, "handled_lan_responses: %u\n", + seq_printf(m, "handled_lan_responses: %u\n", ipmi_get_stat(intf, handled_lan_responses)); - out += sprintf(out, "invalid_lan_responses: %u\n", + seq_printf(m, "invalid_lan_responses: %u\n", ipmi_get_stat(intf, invalid_lan_responses)); - out += sprintf(out, "unhandled_lan_responses: %u\n", + seq_printf(m, "unhandled_lan_responses: %u\n", ipmi_get_stat(intf, unhandled_lan_responses)); - out += sprintf(out, "handled_commands: %u\n", + seq_printf(m, "handled_commands: %u\n", ipmi_get_stat(intf, handled_commands)); - out += sprintf(out, "invalid_commands: %u\n", + seq_printf(m, "invalid_commands: %u\n", ipmi_get_stat(intf, invalid_commands)); - out += sprintf(out, "unhandled_commands: %u\n", + seq_printf(m, "unhandled_commands: %u\n", ipmi_get_stat(intf, unhandled_commands)); - out += sprintf(out, "invalid_events: %u\n", + seq_printf(m, "invalid_events: %u\n", ipmi_get_stat(intf, invalid_events)); - out += sprintf(out, "events: %u\n", + seq_printf(m, "events: %u\n", ipmi_get_stat(intf, events)); + seq_printf(m, "failed rexmit LAN msgs: %u\n", + ipmi_get_stat(intf, dropped_rexmit_lan_commands)); + seq_printf(m, "failed rexmit IPMB msgs: %u\n", + ipmi_get_stat(intf, dropped_rexmit_ipmb_commands)); + return 0; +} - return (out - ((char *) page)); +static int smi_stats_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, smi_stats_proc_show, PDE_DATA(inode)); } + +static const struct file_operations smi_stats_proc_ops = { + .open = smi_stats_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; #endif /* CONFIG_PROC_FS */ int ipmi_smi_add_proc_entry(ipmi_smi_t smi, char *name, - read_proc_t *read_proc, - void *data, struct module *owner) + const struct file_operations *proc_ops, + void *data) { int rv = 0; #ifdef CONFIG_PROC_FS @@ -1955,23 +2078,18 @@ int ipmi_smi_add_proc_entry(ipmi_smi_t smi, char *name, entry = kmalloc(sizeof(*entry), GFP_KERNEL); if (!entry) return -ENOMEM; - entry->name = kmalloc(strlen(name)+1, GFP_KERNEL); + entry->name = kstrdup(name, GFP_KERNEL); if (!entry->name) { kfree(entry); return -ENOMEM; } - strcpy(entry->name, name); - file = create_proc_entry(name, 0, smi->proc_dir); + file = proc_create_data(name, 0, smi->proc_dir, proc_ops, data); if (!file) { kfree(entry->name); kfree(entry); rv = -ENOMEM; } else { - file->data = data; - file->read_proc = read_proc; - file->owner = owner; - mutex_lock(&smi->proc_entry_lock); /* Stick it on the list. */ entry->next = smi->proc_entries; @@ -1993,23 +2111,21 @@ static int add_proc_entries(ipmi_smi_t smi, int num) smi->proc_dir = proc_mkdir(smi->proc_dir_name, proc_ipmi_root); if (!smi->proc_dir) rv = -ENOMEM; - else - smi->proc_dir->owner = THIS_MODULE; if (rv == 0) rv = ipmi_smi_add_proc_entry(smi, "stats", - stat_file_read_proc, - smi, THIS_MODULE); + &smi_stats_proc_ops, + smi); if (rv == 0) rv = ipmi_smi_add_proc_entry(smi, "ipmb", - ipmb_file_read_proc, - smi, THIS_MODULE); + &smi_ipmb_proc_ops, + smi); if (rv == 0) rv = ipmi_smi_add_proc_entry(smi, "version", - version_file_read_proc, - smi, THIS_MODULE); + &smi_version_proc_ops, + smi); #endif /* CONFIG_PROC_FS */ return rv; @@ -2254,42 +2370,52 @@ static int create_files(struct bmc_device *bmc) bmc->device_id_attr.attr.name = "device_id"; bmc->device_id_attr.attr.mode = S_IRUGO; bmc->device_id_attr.show = device_id_show; + sysfs_attr_init(&bmc->device_id_attr.attr); bmc->provides_dev_sdrs_attr.attr.name = "provides_device_sdrs"; bmc->provides_dev_sdrs_attr.attr.mode = S_IRUGO; bmc->provides_dev_sdrs_attr.show = provides_dev_sdrs_show; + sysfs_attr_init(&bmc->provides_dev_sdrs_attr.attr); bmc->revision_attr.attr.name = "revision"; bmc->revision_attr.attr.mode = S_IRUGO; bmc->revision_attr.show = revision_show; + sysfs_attr_init(&bmc->revision_attr.attr); bmc->firmware_rev_attr.attr.name = "firmware_revision"; bmc->firmware_rev_attr.attr.mode = S_IRUGO; bmc->firmware_rev_attr.show = firmware_rev_show; + sysfs_attr_init(&bmc->firmware_rev_attr.attr); bmc->version_attr.attr.name = "ipmi_version"; bmc->version_attr.attr.mode = S_IRUGO; bmc->version_attr.show = ipmi_version_show; + sysfs_attr_init(&bmc->version_attr.attr); bmc->add_dev_support_attr.attr.name = "additional_device_support"; bmc->add_dev_support_attr.attr.mode = S_IRUGO; bmc->add_dev_support_attr.show = add_dev_support_show; + sysfs_attr_init(&bmc->add_dev_support_attr.attr); bmc->manufacturer_id_attr.attr.name = "manufacturer_id"; bmc->manufacturer_id_attr.attr.mode = S_IRUGO; bmc->manufacturer_id_attr.show = manufacturer_id_show; + sysfs_attr_init(&bmc->manufacturer_id_attr.attr); bmc->product_id_attr.attr.name = "product_id"; bmc->product_id_attr.attr.mode = S_IRUGO; bmc->product_id_attr.show = product_id_show; + sysfs_attr_init(&bmc->product_id_attr.attr); bmc->guid_attr.attr.name = "guid"; bmc->guid_attr.attr.mode = S_IRUGO; bmc->guid_attr.show = guid_show; + sysfs_attr_init(&bmc->guid_attr.attr); bmc->aux_firmware_rev_attr.attr.name = "aux_firmware_revision"; bmc->aux_firmware_rev_attr.attr.mode = S_IRUGO; bmc->aux_firmware_rev_attr.show = aux_firmware_rev_show; + sysfs_attr_init(&bmc->aux_firmware_rev_attr.attr); err = device_create_file(&bmc->dev->dev, &bmc->device_id_attr); @@ -2477,12 +2603,11 @@ static int ipmi_bmc_register(ipmi_smi_t intf, int ifnum, return rv; } - printk(KERN_INFO - "ipmi: Found new BMC (man_id: 0x%6.6x, " - " prod_id: 0x%4.4x, dev_id: 0x%2.2x)\n", - bmc->id.manufacturer_id, - bmc->id.product_id, - bmc->id.device_id); + dev_info(intf->si_dev, "Found new BMC (man_id: 0x%6.6x, " + "prod_id: 0x%4.4x, dev_id: 0x%2.2x)\n", + bmc->id.manufacturer_id, + bmc->id.product_id, + bmc->id.device_id); } /* @@ -2708,12 +2833,17 @@ channel_handler(ipmi_smi_t intf, struct ipmi_recv_msg *msg) return; } -void ipmi_poll_interface(ipmi_user_t user) +static void ipmi_poll(ipmi_smi_t intf) { - ipmi_smi_t intf = user->intf; - if (intf->handlers->poll) intf->handlers->poll(intf->send_info); + /* In case something came in */ + handle_new_recv_msgs(intf); +} + +void ipmi_poll_interface(ipmi_user_t user) +{ + ipmi_poll(user->intf); } EXPORT_SYMBOL(ipmi_poll_interface); @@ -2782,7 +2912,13 @@ int ipmi_register_smi(struct ipmi_smi_handlers *handlers, #endif spin_lock_init(&intf->waiting_msgs_lock); INIT_LIST_HEAD(&intf->waiting_msgs); + tasklet_init(&intf->recv_tasklet, + smi_recv_tasklet, + (unsigned long) intf); + atomic_set(&intf->watchdog_pretimeouts_to_deliver, 0); spin_lock_init(&intf->events_lock); + atomic_set(&intf->event_waiters, 0); + intf->ticks_to_req_ev = IPMI_REQUEST_EV_TIME; INIT_LIST_HEAD(&intf->waiting_events); intf->waiting_events_count = 0; mutex_init(&intf->cmd_rcvrs_mutex); @@ -2839,6 +2975,7 @@ int ipmi_register_smi(struct ipmi_smi_handlers *handlers, /* Assume a single IPMB channel at zero. */ intf->channels[0].medium = IPMI_CHANNEL_MEDIUM_IPMB; intf->channels[0].protocol = IPMI_CHANNEL_PROTOCOL_IPMB; + intf->curr_channel = IPMI_MAX_CHANNELS; } if (rv == 0) @@ -3267,6 +3404,114 @@ static int handle_lan_get_msg_cmd(ipmi_smi_t intf, return rv; } +/* + * This routine will handle "Get Message" command responses with + * channels that use an OEM Medium. The message format belongs to + * the OEM. See IPMI 2.0 specification, Chapter 6 and + * Chapter 22, sections 22.6 and 22.24 for more details. + */ +static int handle_oem_get_msg_cmd(ipmi_smi_t intf, + struct ipmi_smi_msg *msg) +{ + struct cmd_rcvr *rcvr; + int rv = 0; + unsigned char netfn; + unsigned char cmd; + unsigned char chan; + ipmi_user_t user = NULL; + struct ipmi_system_interface_addr *smi_addr; + struct ipmi_recv_msg *recv_msg; + + /* + * We expect the OEM SW to perform error checking + * so we just do some basic sanity checks + */ + if (msg->rsp_size < 4) { + /* Message not big enough, just ignore it. */ + ipmi_inc_stat(intf, invalid_commands); + return 0; + } + + if (msg->rsp[2] != 0) { + /* An error getting the response, just ignore it. */ + return 0; + } + + /* + * This is an OEM Message so the OEM needs to know how + * handle the message. We do no interpretation. + */ + netfn = msg->rsp[0] >> 2; + cmd = msg->rsp[1]; + chan = msg->rsp[3] & 0xf; + + rcu_read_lock(); + rcvr = find_cmd_rcvr(intf, netfn, cmd, chan); + if (rcvr) { + user = rcvr->user; + kref_get(&user->refcount); + } else + user = NULL; + rcu_read_unlock(); + + if (user == NULL) { + /* We didn't find a user, just give up. */ + ipmi_inc_stat(intf, unhandled_commands); + + /* + * Don't do anything with these messages, just allow + * them to be freed. + */ + + rv = 0; + } else { + /* Deliver the message to the user. */ + ipmi_inc_stat(intf, handled_commands); + + recv_msg = ipmi_alloc_recv_msg(); + if (!recv_msg) { + /* + * We couldn't allocate memory for the + * message, so requeue it for handling + * later. + */ + rv = 1; + kref_put(&user->refcount, free_user); + } else { + /* + * OEM Messages are expected to be delivered via + * the system interface to SMS software. We might + * need to visit this again depending on OEM + * requirements + */ + smi_addr = ((struct ipmi_system_interface_addr *) + &(recv_msg->addr)); + smi_addr->addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; + smi_addr->channel = IPMI_BMC_CHANNEL; + smi_addr->lun = msg->rsp[0] & 3; + + recv_msg->user = user; + recv_msg->user_msg_data = NULL; + recv_msg->recv_type = IPMI_OEM_RECV_TYPE; + recv_msg->msg.netfn = msg->rsp[0] >> 2; + recv_msg->msg.cmd = msg->rsp[1]; + recv_msg->msg.data = recv_msg->msg_data; + + /* + * The message starts at byte 4 which follows the + * the Channel Byte in the "GET MESSAGE" command + */ + recv_msg->msg.data_len = msg->rsp_size - 4; + memcpy(recv_msg->msg_data, + &(msg->rsp[4]), + msg->rsp_size - 4); + deliver_response(recv_msg); + } + } + + return rv; +} + static void copy_event_into_recv_msg(struct ipmi_recv_msg *recv_msg, struct ipmi_smi_msg *msg) { @@ -3435,11 +3680,11 @@ static int handle_bmc_rsp(ipmi_smi_t intf, } /* - * Handle a new message. Return 1 if the message should be requeued, + * Handle a received message. Return 1 if the message should be requeued, * 0 if the message should be freed, or -1 if the message should not * be freed or requeued. */ -static int handle_new_recv_msg(ipmi_smi_t intf, +static int handle_one_recv_msg(ipmi_smi_t intf, struct ipmi_smi_msg *msg) { int requeue; @@ -3522,6 +3767,17 @@ static int handle_new_recv_msg(ipmi_smi_t intf, goto out; } + /* + * We need to make sure the channels have been initialized. + * The channel_handler routine will set the "curr_channel" + * equal to or greater than IPMI_MAX_CHANNELS when all the + * channels for this interface have been initialized. + */ + if (intf->curr_channel < IPMI_MAX_CHANNELS) { + requeue = 0; /* Throw the message away */ + goto out; + } + switch (intf->channels[chan].medium) { case IPMI_CHANNEL_MEDIUM_IPMB: if (msg->rsp[4] & 0x04) { @@ -3557,16 +3813,25 @@ static int handle_new_recv_msg(ipmi_smi_t intf, break; default: - /* - * We don't handle the channel type, so just - * free the message. - */ - requeue = 0; + /* Check for OEM Channels. Clients had better + register for these commands. */ + if ((intf->channels[chan].medium + >= IPMI_CHANNEL_MEDIUM_OEM_MIN) + && (intf->channels[chan].medium + <= IPMI_CHANNEL_MEDIUM_OEM_MAX)) { + requeue = handle_oem_get_msg_cmd(intf, msg); + } else { + /* + * We don't handle the channel type, so just + * free the message. + */ + requeue = 0; + } } } else if ((msg->rsp[0] == ((IPMI_NETFN_APP_REQUEST|1) << 2)) && (msg->rsp[1] == IPMI_READ_EVENT_MSG_BUFFER_CMD)) { - /* It's an asyncronous event. */ + /* It's an asynchronous event. */ requeue = handle_read_event_rsp(intf, msg); } else { /* It's a response from the local BMC. */ @@ -3577,12 +3842,72 @@ static int handle_new_recv_msg(ipmi_smi_t intf, return requeue; } +/* + * If there are messages in the queue or pretimeouts, handle them. + */ +static void handle_new_recv_msgs(ipmi_smi_t intf) +{ + struct ipmi_smi_msg *smi_msg; + unsigned long flags = 0; + int rv; + int run_to_completion = intf->run_to_completion; + + /* See if any waiting messages need to be processed. */ + if (!run_to_completion) + spin_lock_irqsave(&intf->waiting_msgs_lock, flags); + while (!list_empty(&intf->waiting_msgs)) { + smi_msg = list_entry(intf->waiting_msgs.next, + struct ipmi_smi_msg, link); + list_del(&smi_msg->link); + if (!run_to_completion) + spin_unlock_irqrestore(&intf->waiting_msgs_lock, flags); + rv = handle_one_recv_msg(intf, smi_msg); + if (!run_to_completion) + spin_lock_irqsave(&intf->waiting_msgs_lock, flags); + if (rv == 0) { + /* Message handled */ + ipmi_free_smi_msg(smi_msg); + } else if (rv < 0) { + /* Fatal error on the message, del but don't free. */ + } else { + /* + * To preserve message order, quit if we + * can't handle a message. + */ + list_add(&smi_msg->link, &intf->waiting_msgs); + break; + } + } + if (!run_to_completion) + spin_unlock_irqrestore(&intf->waiting_msgs_lock, flags); + + /* + * If the pretimout count is non-zero, decrement one from it and + * deliver pretimeouts to all the users. + */ + if (atomic_add_unless(&intf->watchdog_pretimeouts_to_deliver, -1, 0)) { + ipmi_user_t user; + + rcu_read_lock(); + list_for_each_entry_rcu(user, &intf->users, link) { + if (user->handler->ipmi_watchdog_pretimeout) + user->handler->ipmi_watchdog_pretimeout( + user->handler_data); + } + rcu_read_unlock(); + } +} + +static void smi_recv_tasklet(unsigned long val) +{ + handle_new_recv_msgs((ipmi_smi_t) val); +} + /* Handle a new message from the lower layer. */ void ipmi_smi_msg_received(ipmi_smi_t intf, struct ipmi_smi_msg *msg) { unsigned long flags = 0; /* keep us warning-free. */ - int rv; int run_to_completion; @@ -3636,31 +3961,11 @@ void ipmi_smi_msg_received(ipmi_smi_t intf, run_to_completion = intf->run_to_completion; if (!run_to_completion) spin_lock_irqsave(&intf->waiting_msgs_lock, flags); - if (!list_empty(&intf->waiting_msgs)) { - list_add_tail(&msg->link, &intf->waiting_msgs); - if (!run_to_completion) - spin_unlock_irqrestore(&intf->waiting_msgs_lock, flags); - goto out; - } + list_add_tail(&msg->link, &intf->waiting_msgs); if (!run_to_completion) spin_unlock_irqrestore(&intf->waiting_msgs_lock, flags); - rv = handle_new_recv_msg(intf, msg); - if (rv > 0) { - /* - * Could not handle the message now, just add it to a - * list to handle later. - */ - run_to_completion = intf->run_to_completion; - if (!run_to_completion) - spin_lock_irqsave(&intf->waiting_msgs_lock, flags); - list_add_tail(&msg->link, &intf->waiting_msgs); - if (!run_to_completion) - spin_unlock_irqrestore(&intf->waiting_msgs_lock, flags); - } else if (rv == 0) { - ipmi_free_smi_msg(msg); - } - + tasklet_schedule(&intf->recv_tasklet); out: return; } @@ -3668,16 +3973,8 @@ EXPORT_SYMBOL(ipmi_smi_msg_received); void ipmi_smi_watchdog_pretimeout(ipmi_smi_t intf) { - ipmi_user_t user; - - rcu_read_lock(); - list_for_each_entry_rcu(user, &intf->users, link) { - if (!user->handler->ipmi_watchdog_pretimeout) - continue; - - user->handler->ipmi_watchdog_pretimeout(user->handler_data); - } - rcu_read_unlock(); + atomic_set(&intf->watchdog_pretimeouts_to_deliver, 1); + tasklet_schedule(&intf->recv_tasklet); } EXPORT_SYMBOL(ipmi_smi_watchdog_pretimeout); @@ -3711,7 +4008,8 @@ smi_from_recv_msg(ipmi_smi_t intf, struct ipmi_recv_msg *recv_msg, static void check_msg_timeout(ipmi_smi_t intf, struct seq_table *ent, struct list_head *timeouts, long timeout_period, - int slot, unsigned long *flags) + int slot, unsigned long *flags, + unsigned int *waiting_msgs) { struct ipmi_recv_msg *msg; struct ipmi_smi_handlers *handlers; @@ -3723,8 +4021,10 @@ static void check_msg_timeout(ipmi_smi_t intf, struct seq_table *ent, return; ent->timeout -= timeout_period; - if (ent->timeout > 0) + if (ent->timeout > 0) { + (*waiting_msgs)++; return; + } if (ent->retries_left == 0) { /* The message has used all its retries. */ @@ -3733,7 +4033,7 @@ static void check_msg_timeout(ipmi_smi_t intf, struct seq_table *ent, list_add_tail(&msg->link, timeouts); if (ent->broadcast) ipmi_inc_stat(intf, timed_out_ipmb_broadcasts); - else if (ent->recv_msg->addr.addr_type == IPMI_LAN_ADDR_TYPE) + else if (is_lan_addr(&ent->recv_msg->addr)) ipmi_inc_stat(intf, timed_out_lan_commands); else ipmi_inc_stat(intf, timed_out_ipmb_commands); @@ -3741,21 +4041,25 @@ static void check_msg_timeout(ipmi_smi_t intf, struct seq_table *ent, struct ipmi_smi_msg *smi_msg; /* More retries, send again. */ + (*waiting_msgs)++; + /* * Start with the max timer, set to normal timer after * the message is sent. */ ent->timeout = MAX_MSG_TIMEOUT; ent->retries_left--; - if (ent->recv_msg->addr.addr_type == IPMI_LAN_ADDR_TYPE) - ipmi_inc_stat(intf, retransmitted_lan_commands); - else - ipmi_inc_stat(intf, retransmitted_ipmb_commands); - smi_msg = smi_from_recv_msg(intf, ent->recv_msg, slot, ent->seqid); - if (!smi_msg) + if (!smi_msg) { + if (is_lan_addr(&ent->recv_msg->addr)) + ipmi_inc_stat(intf, + dropped_rexmit_lan_commands); + else + ipmi_inc_stat(intf, + dropped_rexmit_ipmb_commands); return; + } spin_unlock_irqrestore(&intf->seq_lock, *flags); @@ -3767,143 +4071,135 @@ static void check_msg_timeout(ipmi_smi_t intf, struct seq_table *ent, * resent. */ handlers = intf->handlers; - if (handlers) + if (handlers) { + if (is_lan_addr(&ent->recv_msg->addr)) + ipmi_inc_stat(intf, + retransmitted_lan_commands); + else + ipmi_inc_stat(intf, + retransmitted_ipmb_commands); + intf->handlers->sender(intf->send_info, smi_msg, 0); - else + } else ipmi_free_smi_msg(smi_msg); spin_lock_irqsave(&intf->seq_lock, *flags); } } -static void ipmi_timeout_handler(long timeout_period) +static unsigned int ipmi_timeout_handler(ipmi_smi_t intf, long timeout_period) { - ipmi_smi_t intf; struct list_head timeouts; struct ipmi_recv_msg *msg, *msg2; - struct ipmi_smi_msg *smi_msg, *smi_msg2; unsigned long flags; int i; + unsigned int waiting_msgs = 0; - rcu_read_lock(); - list_for_each_entry_rcu(intf, &ipmi_interfaces, link) { - /* See if any waiting messages need to be processed. */ - spin_lock_irqsave(&intf->waiting_msgs_lock, flags); - list_for_each_entry_safe(smi_msg, smi_msg2, - &intf->waiting_msgs, link) { - if (!handle_new_recv_msg(intf, smi_msg)) { - list_del(&smi_msg->link); - ipmi_free_smi_msg(smi_msg); - } else { - /* - * To preserve message order, quit if we - * can't handle a message. - */ - break; - } - } - spin_unlock_irqrestore(&intf->waiting_msgs_lock, flags); - - /* - * Go through the seq table and find any messages that - * have timed out, putting them in the timeouts - * list. - */ - INIT_LIST_HEAD(&timeouts); - spin_lock_irqsave(&intf->seq_lock, flags); - for (i = 0; i < IPMI_IPMB_NUM_SEQ; i++) - check_msg_timeout(intf, &(intf->seq_table[i]), - &timeouts, timeout_period, i, - &flags); - spin_unlock_irqrestore(&intf->seq_lock, flags); + /* + * Go through the seq table and find any messages that + * have timed out, putting them in the timeouts + * list. + */ + INIT_LIST_HEAD(&timeouts); + spin_lock_irqsave(&intf->seq_lock, flags); + for (i = 0; i < IPMI_IPMB_NUM_SEQ; i++) + check_msg_timeout(intf, &(intf->seq_table[i]), + &timeouts, timeout_period, i, + &flags, &waiting_msgs); + spin_unlock_irqrestore(&intf->seq_lock, flags); - list_for_each_entry_safe(msg, msg2, &timeouts, link) - deliver_err_response(msg, IPMI_TIMEOUT_COMPLETION_CODE); + list_for_each_entry_safe(msg, msg2, &timeouts, link) + deliver_err_response(msg, IPMI_TIMEOUT_COMPLETION_CODE); - /* - * Maintenance mode handling. Check the timeout - * optimistically before we claim the lock. It may - * mean a timeout gets missed occasionally, but that - * only means the timeout gets extended by one period - * in that case. No big deal, and it avoids the lock - * most of the time. - */ + /* + * Maintenance mode handling. Check the timeout + * optimistically before we claim the lock. It may + * mean a timeout gets missed occasionally, but that + * only means the timeout gets extended by one period + * in that case. No big deal, and it avoids the lock + * most of the time. + */ + if (intf->auto_maintenance_timeout > 0) { + spin_lock_irqsave(&intf->maintenance_mode_lock, flags); if (intf->auto_maintenance_timeout > 0) { - spin_lock_irqsave(&intf->maintenance_mode_lock, flags); - if (intf->auto_maintenance_timeout > 0) { - intf->auto_maintenance_timeout - -= timeout_period; - if (!intf->maintenance_mode - && (intf->auto_maintenance_timeout <= 0)) { - intf->maintenance_mode_enable = 0; - maintenance_mode_update(intf); - } + intf->auto_maintenance_timeout + -= timeout_period; + if (!intf->maintenance_mode + && (intf->auto_maintenance_timeout <= 0)) { + intf->maintenance_mode_enable = false; + maintenance_mode_update(intf); } - spin_unlock_irqrestore(&intf->maintenance_mode_lock, - flags); } + spin_unlock_irqrestore(&intf->maintenance_mode_lock, + flags); } - rcu_read_unlock(); + + tasklet_schedule(&intf->recv_tasklet); + + return waiting_msgs; } -static void ipmi_request_event(void) +static void ipmi_request_event(ipmi_smi_t intf) { - ipmi_smi_t intf; struct ipmi_smi_handlers *handlers; - rcu_read_lock(); - /* - * Called from the timer, no need to check if handlers is - * valid. - */ - list_for_each_entry_rcu(intf, &ipmi_interfaces, link) { - /* No event requests when in maintenance mode. */ - if (intf->maintenance_mode_enable) - continue; + /* No event requests when in maintenance mode. */ + if (intf->maintenance_mode_enable) + return; - handlers = intf->handlers; - if (handlers) - handlers->request_events(intf->send_info); - } - rcu_read_unlock(); + handlers = intf->handlers; + if (handlers) + handlers->request_events(intf->send_info); } static struct timer_list ipmi_timer; -/* Call every ~100 ms. */ -#define IPMI_TIMEOUT_TIME 100 - -/* How many jiffies does it take to get to the timeout time. */ -#define IPMI_TIMEOUT_JIFFIES ((IPMI_TIMEOUT_TIME * HZ) / 1000) - -/* - * Request events from the queue every second (this is the number of - * IPMI_TIMEOUT_TIMES between event requests). Hopefully, in the - * future, IPMI will add a way to know immediately if an event is in - * the queue and this silliness can go away. - */ -#define IPMI_REQUEST_EV_TIME (1000 / (IPMI_TIMEOUT_TIME)) - static atomic_t stop_operation; -static unsigned int ticks_to_req_ev = IPMI_REQUEST_EV_TIME; static void ipmi_timeout(unsigned long data) { + ipmi_smi_t intf; + int nt = 0; + if (atomic_read(&stop_operation)) return; - ticks_to_req_ev--; - if (ticks_to_req_ev == 0) { - ipmi_request_event(); - ticks_to_req_ev = IPMI_REQUEST_EV_TIME; - } + rcu_read_lock(); + list_for_each_entry_rcu(intf, &ipmi_interfaces, link) { + int lnt = 0; - ipmi_timeout_handler(IPMI_TIMEOUT_TIME); + if (atomic_read(&intf->event_waiters)) { + intf->ticks_to_req_ev--; + if (intf->ticks_to_req_ev == 0) { + ipmi_request_event(intf); + intf->ticks_to_req_ev = IPMI_REQUEST_EV_TIME; + } + lnt++; + } - mod_timer(&ipmi_timer, jiffies + IPMI_TIMEOUT_JIFFIES); + lnt += ipmi_timeout_handler(intf, IPMI_TIMEOUT_TIME); + + lnt = !!lnt; + if (lnt != intf->last_needs_timer && + intf->handlers->set_need_watch) + intf->handlers->set_need_watch(intf->send_info, lnt); + intf->last_needs_timer = lnt; + + nt += lnt; + } + rcu_read_unlock(); + + if (nt) + mod_timer(&ipmi_timer, jiffies + IPMI_TIMEOUT_JIFFIES); } +static void need_waiter(ipmi_smi_t intf) +{ + /* Racy, but worst case we start the timer twice. */ + if (!timer_pending(&ipmi_timer)) + mod_timer(&ipmi_timer, jiffies + IPMI_TIMEOUT_JIFFIES); +} static atomic_t smi_msg_inuse_count = ATOMIC_INIT(0); static atomic_t recv_msg_inuse_count = ATOMIC_INIT(0); @@ -3957,12 +4253,48 @@ EXPORT_SYMBOL(ipmi_free_recv_msg); #ifdef CONFIG_IPMI_PANIC_EVENT +static atomic_t panic_done_count = ATOMIC_INIT(0); + static void dummy_smi_done_handler(struct ipmi_smi_msg *msg) { + atomic_dec(&panic_done_count); } static void dummy_recv_done_handler(struct ipmi_recv_msg *msg) { + atomic_dec(&panic_done_count); +} + +/* + * Inside a panic, send a message and wait for a response. + */ +static void ipmi_panic_request_and_wait(ipmi_smi_t intf, + struct ipmi_addr *addr, + struct kernel_ipmi_msg *msg) +{ + struct ipmi_smi_msg smi_msg; + struct ipmi_recv_msg recv_msg; + int rv; + + smi_msg.done = dummy_smi_done_handler; + recv_msg.done = dummy_recv_done_handler; + atomic_add(2, &panic_done_count); + rv = i_ipmi_request(NULL, + intf, + addr, + 0, + msg, + intf, + &smi_msg, + &recv_msg, + 0, + intf->channels[0].address, + intf->channels[0].lun, + 0, 1); /* Don't retry, and don't wait. */ + if (rv) + atomic_sub(2, &panic_done_count); + while (atomic_read(&panic_done_count) != 0) + ipmi_poll(intf); } #ifdef CONFIG_IPMI_PANIC_STRING @@ -4001,8 +4333,6 @@ static void send_panic_events(char *str) unsigned char data[16]; struct ipmi_system_interface_addr *si; struct ipmi_addr addr; - struct ipmi_smi_msg smi_msg; - struct ipmi_recv_msg recv_msg; si = (struct ipmi_system_interface_addr *) &addr; si->addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE; @@ -4030,9 +4360,6 @@ static void send_panic_events(char *str) data[7] = str[2]; } - smi_msg.done = dummy_smi_done_handler; - recv_msg.done = dummy_recv_done_handler; - /* For every registered interface, send the event. */ list_for_each_entry_rcu(intf, &ipmi_interfaces, link) { if (!intf->handlers) @@ -4042,18 +4369,7 @@ static void send_panic_events(char *str) intf->run_to_completion = 1; /* Send the event announcing the panic. */ intf->handlers->set_run_to_completion(intf->send_info, 1); - i_ipmi_request(NULL, - intf, - &addr, - 0, - &msg, - intf, - &smi_msg, - &recv_msg, - 0, - intf->channels[0].address, - intf->channels[0].lun, - 0, 1); /* Don't retry, and don't wait. */ + ipmi_panic_request_and_wait(intf, &addr, &msg); } #ifdef CONFIG_IPMI_PANIC_STRING @@ -4101,18 +4417,7 @@ static void send_panic_events(char *str) msg.data = NULL; msg.data_len = 0; intf->null_user_handler = device_id_fetcher; - i_ipmi_request(NULL, - intf, - &addr, - 0, - &msg, - intf, - &smi_msg, - &recv_msg, - 0, - intf->channels[0].address, - intf->channels[0].lun, - 0, 1); /* Don't retry, and don't wait. */ + ipmi_panic_request_and_wait(intf, &addr, &msg); if (intf->local_event_generator) { /* Request the event receiver from the local MC. */ @@ -4121,18 +4426,7 @@ static void send_panic_events(char *str) msg.data = NULL; msg.data_len = 0; intf->null_user_handler = event_receiver_fetcher; - i_ipmi_request(NULL, - intf, - &addr, - 0, - &msg, - intf, - &smi_msg, - &recv_msg, - 0, - intf->channels[0].address, - intf->channels[0].lun, - 0, 1); /* no retry, and no wait. */ + ipmi_panic_request_and_wait(intf, &addr, &msg); } intf->null_user_handler = NULL; @@ -4189,18 +4483,7 @@ static void send_panic_events(char *str) strncpy(data+5, p, 11); p += size; - i_ipmi_request(NULL, - intf, - &addr, - 0, - &msg, - intf, - &smi_msg, - &recv_msg, - 0, - intf->channels[0].address, - intf->channels[0].lun, - 0, 1); /* no retry, and no wait. */ + ipmi_panic_request_and_wait(intf, &addr, &msg); } } #endif /* CONFIG_IPMI_PANIC_STRING */ @@ -4265,7 +4548,6 @@ static int ipmi_init_msghandler(void) return -ENOMEM; } - proc_ipmi_root->owner = THIS_MODULE; #endif /* CONFIG_PROC_FS */ setup_timer(&ipmi_timer, ipmi_timeout, 0); @@ -4278,13 +4560,13 @@ static int ipmi_init_msghandler(void) return 0; } -static __init int ipmi_init_msghandler_mod(void) +static int __init ipmi_init_msghandler_mod(void) { ipmi_init_msghandler(); return 0; } -static __exit void cleanup_ipmi(void) +static void __exit cleanup_ipmi(void) { int count; @@ -4307,7 +4589,7 @@ static __exit void cleanup_ipmi(void) del_timer_sync(&ipmi_timer); #ifdef CONFIG_PROC_FS - remove_proc_entry(proc_ipmi_root->name, NULL); + proc_remove(proc_ipmi_root); #endif /* CONFIG_PROC_FS */ driver_unregister(&ipmidriver.driver); |
