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 handle_one_recv_msg(ipmi_smi_t          intf,
+                              struct ipmi_smi_msg *msg);
 
 static int initialized;
 
 
        struct kref refcount;
 
+       /* Set when the interface is being unregistered. */
+       bool in_shutdown;
+
        /* Used for a list of interfaces. */
        struct list_head link;
 
        atomic_t         watchdog_pretimeouts_to_deliver;
        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
         * on this interface.
 static void smi_send(ipmi_smi_t intf, struct ipmi_smi_handlers *handlers,
                     struct ipmi_smi_msg *smi_msg, int priority)
 {
-       handlers->sender(intf->send_info, smi_msg, 0);
+       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);
 }
 
 /*
        struct ipmi_smi_msg      *smi_msg;
        struct ipmi_recv_msg     *recv_msg;
        unsigned long            flags;
-       struct ipmi_smi_handlers *handlers;
 
 
        if (supplied_recv)
        }
 
        rcu_read_lock();
-       handlers = intf->handlers;
-       if (!handlers) {
+       if (intf->in_shutdown) {
                rv = -ENODEV;
                goto out_err;
        }
        }
 #endif
 
-       smi_send(intf, handlers, smi_msg, priority);
+       smi_send(intf, intf->handlers, smi_msg, priority);
        rcu_read_unlock();
 
        return 0;
                     smi_recv_tasklet,
                     (unsigned long) intf);
        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);
        atomic_set(&intf->event_waiters, 0);
        intf->ticks_to_req_ev = IPMI_REQUEST_EV_TIME;
 }
 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)
 {
        int              i;
        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. */
+
+       /*
+        * 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++) {
                ent = &(intf->seq_table[i]);
                if (!ent->inuse)
 int ipmi_unregister_smi(ipmi_smi_t intf)
 {
        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);
 
        mutex_lock(&smi_watchers_mutex);
        mutex_lock(&ipmi_interfaces_mutex);
        intf->intf_num = -1;
-       intf->handlers = NULL;
+       intf->in_shutdown = true;
        list_del_rcu(&intf->link);
        mutex_unlock(&ipmi_interfaces_mutex);
        synchronize_rcu();
 
        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);
 
        /*
        ipmi_user_t              user = NULL;
        struct ipmi_ipmb_addr    *ipmb_addr;
        struct ipmi_recv_msg     *recv_msg;
-       struct ipmi_smi_handlers *handlers;
 
        if (msg->rsp_size < 10) {
                /* Message not big enough, just ignore it. */
        }
 #endif
                rcu_read_lock();
-               handlers = intf->handlers;
-               if (handlers) {
-                       smi_send(intf, handlers, msg, 0);
+               if (!intf->in_shutdown) {
+                       smi_send(intf, intf->handlers, msg, 0);
                        /*
                         * We used the message, so return the value
                         * that causes it to not be freed or
        while (!list_empty(&intf->waiting_rcv_msgs)) {
                smi_msg = list_entry(intf->waiting_rcv_msgs.next,
                                     struct ipmi_smi_msg, link);
-               list_del(&smi_msg->link);
                if (!run_to_completion)
                        spin_unlock_irqrestore(&intf->waiting_rcv_msgs_lock,
                                               flags);
                rv = handle_one_recv_msg(intf, smi_msg);
                if (!run_to_completion)
                        spin_lock_irqsave(&intf->waiting_rcv_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 {
+               if (rv > 0) {
                        /*
                         * To preserve message order, quit if we
                         * can't handle a message.
                         */
-                       list_add(&smi_msg->link, &intf->waiting_rcv_msgs);
                        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)
 
 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. */
                           struct ipmi_smi_msg *msg)
 {
        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)
            && (msg->data[0] == (IPMI_NETFN_APP_REQUEST << 2))
            && (msg->data[1] == IPMI_SEND_MSG_CMD)
            && (msg->user_data == NULL)) {
+
+               if (intf->in_shutdown)
+                       goto free_msg;
+
                /*
                 * This is the local response to a command send, start
                 * the timer for these.  The user_data will not be
                        /* The message was sent, start the timer. */
                        intf_start_seq_timer(intf, msg->msgid);
 
+free_msg:
                ipmi_free_smi_msg(msg);
-               goto out;
+       } else {
+               /*
+                * To preserve message order, we keep a queue and deliver from
+                * a tasklet.
+                */
+               if (!run_to_completion)
+                       spin_lock_irqsave(&intf->waiting_rcv_msgs_lock, flags);
+               list_add_tail(&msg->link, &intf->waiting_rcv_msgs);
+               if (!run_to_completion)
+                       spin_unlock_irqrestore(&intf->waiting_rcv_msgs_lock,
+                                              flags);
        }
 
-       /*
-        * To preserve message order, if the list is not empty, we
-        * tack this message onto the end of the list.
-        */
-       run_to_completion = intf->run_to_completion;
        if (!run_to_completion)
-               spin_lock_irqsave(&intf->waiting_rcv_msgs_lock, flags);
-       list_add_tail(&msg->link, &intf->waiting_rcv_msgs);
+               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->waiting_rcv_msgs_lock, flags);
+               spin_unlock_irqrestore(&intf->xmit_msgs_lock, flags);
 
-       tasklet_schedule(&intf->recv_tasklet);
- out:
-       return;
+       if (run_to_completion)
+               smi_recv_tasklet((unsigned long) intf);
+       else
+               tasklet_schedule(&intf->recv_tasklet);
 }
 EXPORT_SYMBOL(ipmi_smi_msg_received);
 
 void ipmi_smi_watchdog_pretimeout(ipmi_smi_t intf)
 {
+       if (intf->in_shutdown)
+               return;
+
        atomic_set(&intf->watchdog_pretimeouts_to_deliver, 1);
        tasklet_schedule(&intf->recv_tasklet);
 }
        struct ipmi_recv_msg     *msg;
        struct ipmi_smi_handlers *handlers;
 
-       if (intf->intf_num == -1)
+       if (intf->in_shutdown)
                return;
 
        if (!ent->inuse)
 
 static void ipmi_request_event(ipmi_smi_t intf)
 {
-       struct ipmi_smi_handlers *handlers;
-
        /* No event requests when in maintenance mode. */
        if (intf->maintenance_mode_enable)
                return;
 
-       handlers = intf->handlers;
-       if (handlers)
-               handlers->request_events(intf->send_info);
+       if (!intf->in_shutdown)
+               intf->handlers->request_events(intf->send_info);
 }
 
 static struct timer_list ipmi_timer;