[IB] umad: avoid potential deadlock when unregistering MAD agents
authorRoland Dreier <rolandd@cisco.com>
Mon, 7 Nov 2005 18:41:29 +0000 (10:41 -0800)
committerRoland Dreier <rolandd@cisco.com>
Thu, 10 Nov 2005 18:22:50 +0000 (10:22 -0800)
ib_unregister_mad_agent() completes all pending MAD sends and waits
for the agent's send_handler routine to return.  umad's send_handler()
calls queue_packet(), which does down_read() on the port mutex to look
up the agent ID.  This means that the port mutex cannot be held for
writing while calling ib_unregister_mad_agent(), or else it will
deadlock.  This patch fixes all the calls to ib_unregister_mad_agent()
in the umad module to avoid this deadlock.

Signed-off-by: Roland Dreier <rolandd@cisco.com>
drivers/infiniband/core/user_mad.c

index 6aefeed42ab376754abab084af96eb18a901ad78..f5ed36c2a06cbd3db539239435cfd90cf077b9c2 100644 (file)
@@ -505,8 +505,6 @@ found:
                goto out;
        }
 
-       file->agent[agent_id] = agent;
-
        file->mr[agent_id] = ib_get_dma_mr(agent->qp->pd, IB_ACCESS_LOCAL_WRITE);
        if (IS_ERR(file->mr[agent_id])) {
                ret = -ENOMEM;
@@ -519,14 +517,15 @@ found:
                goto err_mr;
        }
 
+       file->agent[agent_id] = agent;
        ret = 0;
+
        goto out;
 
 err_mr:
        ib_dereg_mr(file->mr[agent_id]);
 
 err:
-       file->agent[agent_id] = NULL;
        ib_unregister_mad_agent(agent);
 
 out:
@@ -536,27 +535,33 @@ out:
 
 static int ib_umad_unreg_agent(struct ib_umad_file *file, unsigned long arg)
 {
+       struct ib_mad_agent *agent = NULL;
+       struct ib_mr *mr = NULL;
        u32 id;
        int ret = 0;
 
-       down_write(&file->port->mutex);
+       if (get_user(id, (u32 __user *) arg))
+               return -EFAULT;
 
-       if (get_user(id, (u32 __user *) arg)) {
-               ret = -EFAULT;
-               goto out;
-       }
+       down_write(&file->port->mutex);
 
        if (id < 0 || id >= IB_UMAD_MAX_AGENTS || !file->agent[id]) {
                ret = -EINVAL;
                goto out;
        }
 
-       ib_dereg_mr(file->mr[id]);
-       ib_unregister_mad_agent(file->agent[id]);
+       agent = file->agent[id];
+       mr    = file->mr[id];
        file->agent[id] = NULL;
 
 out:
        up_write(&file->port->mutex);
+
+       if (agent) {
+               ib_unregister_mad_agent(agent);
+               ib_dereg_mr(mr);
+       }
+
        return ret;
 }
 
@@ -623,16 +628,16 @@ static int ib_umad_close(struct inode *inode, struct file *filp)
        struct ib_umad_packet *packet, *tmp;
        int i;
 
-       down_write(&file->port->mutex);
        for (i = 0; i < IB_UMAD_MAX_AGENTS; ++i)
                if (file->agent[i]) {
-                       ib_dereg_mr(file->mr[i]);
                        ib_unregister_mad_agent(file->agent[i]);
+                       ib_dereg_mr(file->mr[i]);
                }
 
        list_for_each_entry_safe(packet, tmp, &file->recv_list, list)
                kfree(packet);
 
+       down_write(&file->port->mutex);
        list_del(&file->port_list);
        up_write(&file->port->mutex);