aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/infiniband
diff options
context:
space:
mode:
authorRoland Dreier <rolandd@cisco.com>2005-11-10 13:18:23 -0500
committerRoland Dreier <rolandd@cisco.com>2005-11-10 13:22:51 -0500
commit94382f3562e350ed7c8f7dcd6fc968bdece31328 (patch)
treecf4cb4d1d1dc79326f68511addc1391d7d81e0ce /drivers/infiniband
parentae57e24a4006fd46b73d842ee99db9580ef74a02 (diff)
[IB] umad: further ib_unregister_mad_agent() deadlock fixes
The previous umad deadlock fix left ib_umad_kill_port() still vulnerable to deadlocking. This patch fixes that by downgrading our lock to a read lock when we might end up trying to reacquire the lock for reading. Signed-off-by: Roland Dreier <rolandd@cisco.com>
Diffstat (limited to 'drivers/infiniband')
-rw-r--r--drivers/infiniband/core/user_mad.c87
1 files changed, 63 insertions, 24 deletions
diff --git a/drivers/infiniband/core/user_mad.c b/drivers/infiniband/core/user_mad.c
index d61f544f19e0..5ea741f47fc8 100644
--- a/drivers/infiniband/core/user_mad.c
+++ b/drivers/infiniband/core/user_mad.c
@@ -31,7 +31,7 @@
31 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 31 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
32 * SOFTWARE. 32 * SOFTWARE.
33 * 33 *
34 * $Id: user_mad.c 2814 2005-07-06 19:14:09Z halr $ 34 * $Id: user_mad.c 4010 2005-11-09 23:11:56Z roland $
35 */ 35 */
36 36
37#include <linux/module.h> 37#include <linux/module.h>
@@ -110,12 +110,13 @@ struct ib_umad_device {
110}; 110};
111 111
112struct ib_umad_file { 112struct ib_umad_file {
113 struct ib_umad_port *port; 113 struct ib_umad_port *port;
114 struct list_head recv_list; 114 struct list_head recv_list;
115 struct list_head port_list; 115 struct list_head port_list;
116 spinlock_t recv_lock; 116 spinlock_t recv_lock;
117 wait_queue_head_t recv_wait; 117 wait_queue_head_t recv_wait;
118 struct ib_mad_agent *agent[IB_UMAD_MAX_AGENTS]; 118 struct ib_mad_agent *agent[IB_UMAD_MAX_AGENTS];
119 int agents_dead;
119}; 120};
120 121
121struct ib_umad_packet { 122struct ib_umad_packet {
@@ -144,6 +145,12 @@ static void ib_umad_release_dev(struct kref *ref)
144 kfree(dev); 145 kfree(dev);
145} 146}
146 147
148/* caller must hold port->mutex at least for reading */
149static struct ib_mad_agent *__get_agent(struct ib_umad_file *file, int id)
150{
151 return file->agents_dead ? NULL : file->agent[id];
152}
153
147static int queue_packet(struct ib_umad_file *file, 154static int queue_packet(struct ib_umad_file *file,
148 struct ib_mad_agent *agent, 155 struct ib_mad_agent *agent,
149 struct ib_umad_packet *packet) 156 struct ib_umad_packet *packet)
@@ -151,10 +158,11 @@ static int queue_packet(struct ib_umad_file *file,
151 int ret = 1; 158 int ret = 1;
152 159
153 down_read(&file->port->mutex); 160 down_read(&file->port->mutex);
161
154 for (packet->mad.hdr.id = 0; 162 for (packet->mad.hdr.id = 0;
155 packet->mad.hdr.id < IB_UMAD_MAX_AGENTS; 163 packet->mad.hdr.id < IB_UMAD_MAX_AGENTS;
156 packet->mad.hdr.id++) 164 packet->mad.hdr.id++)
157 if (agent == file->agent[packet->mad.hdr.id]) { 165 if (agent == __get_agent(file, packet->mad.hdr.id)) {
158 spin_lock_irq(&file->recv_lock); 166 spin_lock_irq(&file->recv_lock);
159 list_add_tail(&packet->list, &file->recv_list); 167 list_add_tail(&packet->list, &file->recv_list);
160 spin_unlock_irq(&file->recv_lock); 168 spin_unlock_irq(&file->recv_lock);
@@ -326,7 +334,7 @@ static ssize_t ib_umad_write(struct file *filp, const char __user *buf,
326 334
327 down_read(&file->port->mutex); 335 down_read(&file->port->mutex);
328 336
329 agent = file->agent[packet->mad.hdr.id]; 337 agent = __get_agent(file, packet->mad.hdr.id);
330 if (!agent) { 338 if (!agent) {
331 ret = -EINVAL; 339 ret = -EINVAL;
332 goto err_up; 340 goto err_up;
@@ -480,7 +488,7 @@ static int ib_umad_reg_agent(struct ib_umad_file *file, unsigned long arg)
480 } 488 }
481 489
482 for (agent_id = 0; agent_id < IB_UMAD_MAX_AGENTS; ++agent_id) 490 for (agent_id = 0; agent_id < IB_UMAD_MAX_AGENTS; ++agent_id)
483 if (!file->agent[agent_id]) 491 if (!__get_agent(file, agent_id))
484 goto found; 492 goto found;
485 493
486 ret = -ENOMEM; 494 ret = -ENOMEM;
@@ -530,7 +538,7 @@ static int ib_umad_unreg_agent(struct ib_umad_file *file, unsigned long arg)
530 538
531 down_write(&file->port->mutex); 539 down_write(&file->port->mutex);
532 540
533 if (id < 0 || id >= IB_UMAD_MAX_AGENTS || !file->agent[id]) { 541 if (id < 0 || id >= IB_UMAD_MAX_AGENTS || !__get_agent(file, id)) {
534 ret = -EINVAL; 542 ret = -EINVAL;
535 goto out; 543 goto out;
536 } 544 }
@@ -608,21 +616,29 @@ static int ib_umad_close(struct inode *inode, struct file *filp)
608 struct ib_umad_file *file = filp->private_data; 616 struct ib_umad_file *file = filp->private_data;
609 struct ib_umad_device *dev = file->port->umad_dev; 617 struct ib_umad_device *dev = file->port->umad_dev;
610 struct ib_umad_packet *packet, *tmp; 618 struct ib_umad_packet *packet, *tmp;
619 int already_dead;
611 int i; 620 int i;
612 621
613 for (i = 0; i < IB_UMAD_MAX_AGENTS; ++i) 622 down_write(&file->port->mutex);
614 if (file->agent[i]) 623
615 ib_unregister_mad_agent(file->agent[i]); 624 already_dead = file->agents_dead;
625 file->agents_dead = 1;
616 626
617 list_for_each_entry_safe(packet, tmp, &file->recv_list, list) 627 list_for_each_entry_safe(packet, tmp, &file->recv_list, list)
618 kfree(packet); 628 kfree(packet);
619 629
620 down_write(&file->port->mutex);
621 list_del(&file->port_list); 630 list_del(&file->port_list);
622 up_write(&file->port->mutex);
623 631
624 kfree(file); 632 downgrade_write(&file->port->mutex);
633
634 if (!already_dead)
635 for (i = 0; i < IB_UMAD_MAX_AGENTS; ++i)
636 if (file->agent[i])
637 ib_unregister_mad_agent(file->agent[i]);
625 638
639 up_read(&file->port->mutex);
640
641 kfree(file);
626 kref_put(&dev->ref, ib_umad_release_dev); 642 kref_put(&dev->ref, ib_umad_release_dev);
627 643
628 return 0; 644 return 0;
@@ -848,13 +864,36 @@ static void ib_umad_kill_port(struct ib_umad_port *port)
848 864
849 port->ib_dev = NULL; 865 port->ib_dev = NULL;
850 866
851 list_for_each_entry(file, &port->file_list, port_list) 867 /*
852 for (id = 0; id < IB_UMAD_MAX_AGENTS; ++id) { 868 * Now go through the list of files attached to this port and
853 if (!file->agent[id]) 869 * unregister all of their MAD agents. We need to hold
854 continue; 870 * port->mutex while doing this to avoid racing with
855 ib_unregister_mad_agent(file->agent[id]); 871 * ib_umad_close(), but we can't hold the mutex for writing
856 file->agent[id] = NULL; 872 * while calling ib_unregister_mad_agent(), since that might
857 } 873 * deadlock by calling back into queue_packet(). So we
874 * downgrade our lock to a read lock, and then drop and
875 * reacquire the write lock for the next iteration.
876 *
877 * We do list_del_init() on the file's list_head so that the
878 * list_del in ib_umad_close() is still OK, even after the
879 * file is removed from the list.
880 */
881 while (!list_empty(&port->file_list)) {
882 file = list_entry(port->file_list.next, struct ib_umad_file,
883 port_list);
884
885 file->agents_dead = 1;
886 list_del_init(&file->port_list);
887
888 downgrade_write(&port->mutex);
889
890 for (id = 0; id < IB_UMAD_MAX_AGENTS; ++id)
891 if (file->agent[id])
892 ib_unregister_mad_agent(file->agent[id]);
893
894 up_read(&port->mutex);
895 down_write(&port->mutex);
896 }
858 897
859 up_write(&port->mutex); 898 up_write(&port->mutex);
860 899