Commit 7ea0ed2b authored by Corey Minyard's avatar Corey Minyard

ipmi: Make the message handler easier to use for SMI interfaces

The message handler expected the SMI interface to keep a queue of
messages, but that was kind of silly, the queue would be easier to
manage in the message handler itself.  As part of that, fix the
message cleanup to make sure no messages are outstanding when an
SMI interface is unregistered.  This makes it easier for an SMI
interface to unregister, it just has to call ipmi_unregister_smi()
first and all processing from the message handler will be cleaned
up.
Signed-off-by: default avatarCorey Minyard <cminyard@mvista.com>
parent 7f4a1c84
...@@ -56,6 +56,8 @@ static int ipmi_init_msghandler(void); ...@@ -56,6 +56,8 @@ static int ipmi_init_msghandler(void);
static void smi_recv_tasklet(unsigned long); static void smi_recv_tasklet(unsigned long);
static void handle_new_recv_msgs(ipmi_smi_t intf); static void handle_new_recv_msgs(ipmi_smi_t intf);
static void need_waiter(ipmi_smi_t intf); static void need_waiter(ipmi_smi_t intf);
static int handle_one_recv_msg(ipmi_smi_t intf,
struct ipmi_smi_msg *msg);
static int initialized; static int initialized;
...@@ -324,6 +326,9 @@ struct ipmi_smi { ...@@ -324,6 +326,9 @@ struct ipmi_smi {
struct kref refcount; struct kref refcount;
/* Set when the interface is being unregistered. */
bool in_shutdown;
/* Used for a list of interfaces. */ /* Used for a list of interfaces. */
struct list_head link; struct list_head link;
...@@ -382,6 +387,11 @@ struct ipmi_smi { ...@@ -382,6 +387,11 @@ struct ipmi_smi {
atomic_t watchdog_pretimeouts_to_deliver; atomic_t watchdog_pretimeouts_to_deliver;
struct tasklet_struct recv_tasklet; struct tasklet_struct recv_tasklet;
spinlock_t xmit_msgs_lock;
struct list_head xmit_msgs;
struct ipmi_smi_msg *curr_msg;
struct list_head hp_xmit_msgs;
/* /*
* The list of command receivers that are registered for commands * The list of command receivers that are registered for commands
* on this interface. * on this interface.
...@@ -1488,6 +1498,24 @@ static inline void format_lan_msg(struct ipmi_smi_msg *smi_msg, ...@@ -1488,6 +1498,24 @@ static inline void format_lan_msg(struct ipmi_smi_msg *smi_msg,
static void smi_send(ipmi_smi_t intf, struct ipmi_smi_handlers *handlers, static void smi_send(ipmi_smi_t intf, struct ipmi_smi_handlers *handlers,
struct ipmi_smi_msg *smi_msg, int priority) struct ipmi_smi_msg *smi_msg, int priority)
{ {
int run_to_completion = intf->run_to_completion;
unsigned long flags;
if (!run_to_completion)
spin_lock_irqsave(&intf->xmit_msgs_lock, flags);
if (intf->curr_msg) {
if (priority > 0)
list_add_tail(&smi_msg->link, &intf->hp_xmit_msgs);
else
list_add_tail(&smi_msg->link, &intf->xmit_msgs);
smi_msg = NULL;
} else {
intf->curr_msg = smi_msg;
}
if (!run_to_completion)
spin_unlock_irqrestore(&intf->xmit_msgs_lock, flags);
if (smi_msg)
handlers->sender(intf->send_info, smi_msg, 0); handlers->sender(intf->send_info, smi_msg, 0);
} }
...@@ -1515,7 +1543,6 @@ static int i_ipmi_request(ipmi_user_t user, ...@@ -1515,7 +1543,6 @@ static int i_ipmi_request(ipmi_user_t user,
struct ipmi_smi_msg *smi_msg; struct ipmi_smi_msg *smi_msg;
struct ipmi_recv_msg *recv_msg; struct ipmi_recv_msg *recv_msg;
unsigned long flags; unsigned long flags;
struct ipmi_smi_handlers *handlers;
if (supplied_recv) if (supplied_recv)
...@@ -1538,8 +1565,7 @@ static int i_ipmi_request(ipmi_user_t user, ...@@ -1538,8 +1565,7 @@ static int i_ipmi_request(ipmi_user_t user,
} }
rcu_read_lock(); rcu_read_lock();
handlers = intf->handlers; if (intf->in_shutdown) {
if (!handlers) {
rv = -ENODEV; rv = -ENODEV;
goto out_err; goto out_err;
} }
...@@ -1874,7 +1900,7 @@ static int i_ipmi_request(ipmi_user_t user, ...@@ -1874,7 +1900,7 @@ static int i_ipmi_request(ipmi_user_t user,
} }
#endif #endif
smi_send(intf, handlers, smi_msg, priority); smi_send(intf, intf->handlers, smi_msg, priority);
rcu_read_unlock(); rcu_read_unlock();
return 0; return 0;
...@@ -2810,6 +2836,9 @@ int ipmi_register_smi(struct ipmi_smi_handlers *handlers, ...@@ -2810,6 +2836,9 @@ int ipmi_register_smi(struct ipmi_smi_handlers *handlers,
smi_recv_tasklet, smi_recv_tasklet,
(unsigned long) intf); (unsigned long) intf);
atomic_set(&intf->watchdog_pretimeouts_to_deliver, 0); atomic_set(&intf->watchdog_pretimeouts_to_deliver, 0);
spin_lock_init(&intf->xmit_msgs_lock);
INIT_LIST_HEAD(&intf->xmit_msgs);
INIT_LIST_HEAD(&intf->hp_xmit_msgs);
spin_lock_init(&intf->events_lock); spin_lock_init(&intf->events_lock);
atomic_set(&intf->event_waiters, 0); atomic_set(&intf->event_waiters, 0);
intf->ticks_to_req_ev = IPMI_REQUEST_EV_TIME; intf->ticks_to_req_ev = IPMI_REQUEST_EV_TIME;
...@@ -2909,12 +2938,50 @@ int ipmi_register_smi(struct ipmi_smi_handlers *handlers, ...@@ -2909,12 +2938,50 @@ int ipmi_register_smi(struct ipmi_smi_handlers *handlers,
} }
EXPORT_SYMBOL(ipmi_register_smi); EXPORT_SYMBOL(ipmi_register_smi);
static void deliver_smi_err_response(ipmi_smi_t intf,
struct ipmi_smi_msg *msg,
unsigned char err)
{
msg->rsp[0] = msg->data[0] | 4;
msg->rsp[1] = msg->data[1];
msg->rsp[2] = err;
msg->rsp_size = 3;
/* It's an error, so it will never requeue, no need to check return. */
handle_one_recv_msg(intf, msg);
}
static void cleanup_smi_msgs(ipmi_smi_t intf) static void cleanup_smi_msgs(ipmi_smi_t intf)
{ {
int i; int i;
struct seq_table *ent; struct seq_table *ent;
struct ipmi_smi_msg *msg;
struct list_head *entry;
struct list_head tmplist;
/* Clear out our transmit queues and hold the messages. */
INIT_LIST_HEAD(&tmplist);
list_splice_tail(&intf->hp_xmit_msgs, &tmplist);
list_splice_tail(&intf->xmit_msgs, &tmplist);
/* Current message first, to preserve order */
while (intf->curr_msg && !list_empty(&intf->waiting_rcv_msgs)) {
/* Wait for the message to clear out. */
schedule_timeout(1);
}
/* No need for locks, the interface is down. */ /* No need for locks, the interface is down. */
/*
* Return errors for all pending messages in queue and in the
* tables waiting for remote responses.
*/
while (!list_empty(&tmplist)) {
entry = tmplist.next;
list_del(entry);
msg = list_entry(entry, struct ipmi_smi_msg, link);
deliver_smi_err_response(intf, msg, IPMI_ERR_UNSPECIFIED);
}
for (i = 0; i < IPMI_IPMB_NUM_SEQ; i++) { for (i = 0; i < IPMI_IPMB_NUM_SEQ; i++) {
ent = &(intf->seq_table[i]); ent = &(intf->seq_table[i]);
if (!ent->inuse) if (!ent->inuse)
...@@ -2927,19 +2994,32 @@ int ipmi_unregister_smi(ipmi_smi_t intf) ...@@ -2927,19 +2994,32 @@ int ipmi_unregister_smi(ipmi_smi_t intf)
{ {
struct ipmi_smi_watcher *w; struct ipmi_smi_watcher *w;
int intf_num = intf->intf_num; int intf_num = intf->intf_num;
ipmi_user_t user;
ipmi_bmc_unregister(intf); ipmi_bmc_unregister(intf);
mutex_lock(&smi_watchers_mutex); mutex_lock(&smi_watchers_mutex);
mutex_lock(&ipmi_interfaces_mutex); mutex_lock(&ipmi_interfaces_mutex);
intf->intf_num = -1; intf->intf_num = -1;
intf->handlers = NULL; intf->in_shutdown = true;
list_del_rcu(&intf->link); list_del_rcu(&intf->link);
mutex_unlock(&ipmi_interfaces_mutex); mutex_unlock(&ipmi_interfaces_mutex);
synchronize_rcu(); synchronize_rcu();
cleanup_smi_msgs(intf); cleanup_smi_msgs(intf);
/* Clean up the effects of users on the lower-level software. */
mutex_lock(&ipmi_interfaces_mutex);
rcu_read_lock();
list_for_each_entry_rcu(user, &intf->users, link) {
module_put(intf->handlers->owner);
if (intf->handlers->dec_usecount)
intf->handlers->dec_usecount(intf->send_info);
}
rcu_read_unlock();
intf->handlers = NULL;
mutex_unlock(&ipmi_interfaces_mutex);
remove_proc_entries(intf); remove_proc_entries(intf);
/* /*
...@@ -3029,7 +3109,6 @@ static int handle_ipmb_get_msg_cmd(ipmi_smi_t intf, ...@@ -3029,7 +3109,6 @@ static int handle_ipmb_get_msg_cmd(ipmi_smi_t intf,
ipmi_user_t user = NULL; ipmi_user_t user = NULL;
struct ipmi_ipmb_addr *ipmb_addr; struct ipmi_ipmb_addr *ipmb_addr;
struct ipmi_recv_msg *recv_msg; struct ipmi_recv_msg *recv_msg;
struct ipmi_smi_handlers *handlers;
if (msg->rsp_size < 10) { if (msg->rsp_size < 10) {
/* Message not big enough, just ignore it. */ /* Message not big enough, just ignore it. */
...@@ -3083,9 +3162,8 @@ static int handle_ipmb_get_msg_cmd(ipmi_smi_t intf, ...@@ -3083,9 +3162,8 @@ static int handle_ipmb_get_msg_cmd(ipmi_smi_t intf,
} }
#endif #endif
rcu_read_lock(); rcu_read_lock();
handlers = intf->handlers; if (!intf->in_shutdown) {
if (handlers) { smi_send(intf, intf->handlers, msg, 0);
smi_send(intf, handlers, msg, 0);
/* /*
* We used the message, so return the value * We used the message, so return the value
* that causes it to not be freed or * that causes it to not be freed or
...@@ -3756,25 +3834,24 @@ static void handle_new_recv_msgs(ipmi_smi_t intf) ...@@ -3756,25 +3834,24 @@ static void handle_new_recv_msgs(ipmi_smi_t intf)
while (!list_empty(&intf->waiting_rcv_msgs)) { while (!list_empty(&intf->waiting_rcv_msgs)) {
smi_msg = list_entry(intf->waiting_rcv_msgs.next, smi_msg = list_entry(intf->waiting_rcv_msgs.next,
struct ipmi_smi_msg, link); struct ipmi_smi_msg, link);
list_del(&smi_msg->link);
if (!run_to_completion) if (!run_to_completion)
spin_unlock_irqrestore(&intf->waiting_rcv_msgs_lock, spin_unlock_irqrestore(&intf->waiting_rcv_msgs_lock,
flags); flags);
rv = handle_one_recv_msg(intf, smi_msg); rv = handle_one_recv_msg(intf, smi_msg);
if (!run_to_completion) if (!run_to_completion)
spin_lock_irqsave(&intf->waiting_rcv_msgs_lock, flags); spin_lock_irqsave(&intf->waiting_rcv_msgs_lock, flags);
if (rv == 0) { 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 * To preserve message order, quit if we
* can't handle a message. * can't handle a message.
*/ */
list_add(&smi_msg->link, &intf->waiting_rcv_msgs);
break; break;
} else {
list_del(&smi_msg->link);
if (rv == 0)
/* Message handled */
ipmi_free_smi_msg(smi_msg);
/* If rv < 0, fatal error, del but don't free. */
} }
} }
if (!run_to_completion) if (!run_to_completion)
...@@ -3799,7 +3876,41 @@ static void handle_new_recv_msgs(ipmi_smi_t intf) ...@@ -3799,7 +3876,41 @@ static void handle_new_recv_msgs(ipmi_smi_t intf)
static void smi_recv_tasklet(unsigned long val) static void smi_recv_tasklet(unsigned long val)
{ {
handle_new_recv_msgs((ipmi_smi_t) val); unsigned long flags = 0; /* keep us warning-free. */
ipmi_smi_t intf = (ipmi_smi_t) val;
int run_to_completion = intf->run_to_completion;
struct ipmi_smi_msg *newmsg = NULL;
/*
* Start the next message if available.
*
* Do this here, not in the actual receiver, because we may deadlock
* because the lower layer is allowed to hold locks while calling
* message delivery.
*/
if (!run_to_completion)
spin_lock_irqsave(&intf->xmit_msgs_lock, flags);
if (intf->curr_msg == NULL && !intf->in_shutdown) {
struct list_head *entry = NULL;
/* Pick the high priority queue first. */
if (!list_empty(&intf->hp_xmit_msgs))
entry = intf->hp_xmit_msgs.next;
else if (!list_empty(&intf->xmit_msgs))
entry = intf->xmit_msgs.next;
if (entry) {
list_del(entry);
newmsg = list_entry(entry, struct ipmi_smi_msg, link);
intf->curr_msg = newmsg;
}
}
if (!run_to_completion)
spin_unlock_irqrestore(&intf->xmit_msgs_lock, flags);
if (newmsg)
intf->handlers->sender(intf->send_info, newmsg, 0);
handle_new_recv_msgs(intf);
} }
/* Handle a new message from the lower layer. */ /* Handle a new message from the lower layer. */
...@@ -3807,13 +3918,16 @@ void ipmi_smi_msg_received(ipmi_smi_t intf, ...@@ -3807,13 +3918,16 @@ void ipmi_smi_msg_received(ipmi_smi_t intf,
struct ipmi_smi_msg *msg) struct ipmi_smi_msg *msg)
{ {
unsigned long flags = 0; /* keep us warning-free. */ unsigned long flags = 0; /* keep us warning-free. */
int run_to_completion; int run_to_completion = intf->run_to_completion;
if ((msg->data_size >= 2) if ((msg->data_size >= 2)
&& (msg->data[0] == (IPMI_NETFN_APP_REQUEST << 2)) && (msg->data[0] == (IPMI_NETFN_APP_REQUEST << 2))
&& (msg->data[1] == IPMI_SEND_MSG_CMD) && (msg->data[1] == IPMI_SEND_MSG_CMD)
&& (msg->user_data == NULL)) { && (msg->user_data == NULL)) {
if (intf->in_shutdown)
goto free_msg;
/* /*
* This is the local response to a command send, start * This is the local response to a command send, start
* the timer for these. The user_data will not be * the timer for these. The user_data will not be
...@@ -3849,29 +3963,40 @@ void ipmi_smi_msg_received(ipmi_smi_t intf, ...@@ -3849,29 +3963,40 @@ void ipmi_smi_msg_received(ipmi_smi_t intf,
/* The message was sent, start the timer. */ /* The message was sent, start the timer. */
intf_start_seq_timer(intf, msg->msgid); intf_start_seq_timer(intf, msg->msgid);
free_msg:
ipmi_free_smi_msg(msg); ipmi_free_smi_msg(msg);
goto out; } else {
}
/* /*
* To preserve message order, if the list is not empty, we * To preserve message order, we keep a queue and deliver from
* tack this message onto the end of the list. * a tasklet.
*/ */
run_to_completion = intf->run_to_completion;
if (!run_to_completion) if (!run_to_completion)
spin_lock_irqsave(&intf->waiting_rcv_msgs_lock, flags); spin_lock_irqsave(&intf->waiting_rcv_msgs_lock, flags);
list_add_tail(&msg->link, &intf->waiting_rcv_msgs); list_add_tail(&msg->link, &intf->waiting_rcv_msgs);
if (!run_to_completion) if (!run_to_completion)
spin_unlock_irqrestore(&intf->waiting_rcv_msgs_lock, flags); spin_unlock_irqrestore(&intf->waiting_rcv_msgs_lock,
flags);
}
if (!run_to_completion)
spin_lock_irqsave(&intf->xmit_msgs_lock, flags);
if (msg == intf->curr_msg)
intf->curr_msg = NULL;
if (!run_to_completion)
spin_unlock_irqrestore(&intf->xmit_msgs_lock, flags);
if (run_to_completion)
smi_recv_tasklet((unsigned long) intf);
else
tasklet_schedule(&intf->recv_tasklet); tasklet_schedule(&intf->recv_tasklet);
out:
return;
} }
EXPORT_SYMBOL(ipmi_smi_msg_received); EXPORT_SYMBOL(ipmi_smi_msg_received);
void ipmi_smi_watchdog_pretimeout(ipmi_smi_t intf) void ipmi_smi_watchdog_pretimeout(ipmi_smi_t intf)
{ {
if (intf->in_shutdown)
return;
atomic_set(&intf->watchdog_pretimeouts_to_deliver, 1); atomic_set(&intf->watchdog_pretimeouts_to_deliver, 1);
tasklet_schedule(&intf->recv_tasklet); tasklet_schedule(&intf->recv_tasklet);
} }
...@@ -3913,7 +4038,7 @@ static void check_msg_timeout(ipmi_smi_t intf, struct seq_table *ent, ...@@ -3913,7 +4038,7 @@ static void check_msg_timeout(ipmi_smi_t intf, struct seq_table *ent,
struct ipmi_recv_msg *msg; struct ipmi_recv_msg *msg;
struct ipmi_smi_handlers *handlers; struct ipmi_smi_handlers *handlers;
if (intf->intf_num == -1) if (intf->in_shutdown)
return; return;
if (!ent->inuse) if (!ent->inuse)
...@@ -4040,15 +4165,12 @@ static unsigned int ipmi_timeout_handler(ipmi_smi_t intf, long timeout_period) ...@@ -4040,15 +4165,12 @@ static unsigned int ipmi_timeout_handler(ipmi_smi_t intf, long timeout_period)
static void ipmi_request_event(ipmi_smi_t intf) static void ipmi_request_event(ipmi_smi_t intf)
{ {
struct ipmi_smi_handlers *handlers;
/* No event requests when in maintenance mode. */ /* No event requests when in maintenance mode. */
if (intf->maintenance_mode_enable) if (intf->maintenance_mode_enable)
return; return;
handlers = intf->handlers; if (!intf->in_shutdown)
if (handlers) intf->handlers->request_events(intf->send_info);
handlers->request_events(intf->send_info);
} }
static struct timer_list ipmi_timer; static struct timer_list ipmi_timer;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment