diff options
-rw-r--r-- | drivers/infiniband/core/user_mad.c | 87 |
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 | ||
112 | struct ib_umad_file { | 112 | struct 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 | ||
121 | struct ib_umad_packet { | 122 | struct 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 */ | ||
149 | static 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 | |||
147 | static int queue_packet(struct ib_umad_file *file, | 154 | static 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 | ||