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 | ||
