diff options
author | Dave Airlie <airlied@starflyer.(none)> | 2006-01-03 02:18:01 -0500 |
---|---|---|
committer | Dave Airlie <airlied@linux.ie> | 2006-01-03 02:18:01 -0500 |
commit | 97f2aab6698f3ab2552c41c1024a65ffd0763a6d (patch) | |
tree | bb6e3b2949459f54f884c710fc74d40eef00d834 /drivers/infiniband/core/user_mad.c | |
parent | d985c1088146607532093d9eaaaf99758f6a4d21 (diff) | |
parent | 88026842b0a760145aa71d69e74fbc9ec118ca44 (diff) |
drm: merge in Linus mainline
Diffstat (limited to 'drivers/infiniband/core/user_mad.c')
-rw-r--r-- | drivers/infiniband/core/user_mad.c | 172 |
1 files changed, 99 insertions, 73 deletions
diff --git a/drivers/infiniband/core/user_mad.c b/drivers/infiniband/core/user_mad.c index aed5ca23fb22..eb7f52537ccc 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,13 +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 | struct ib_mr *mr[IB_UMAD_MAX_AGENTS]; | 119 | int agents_dead; |
120 | }; | 120 | }; |
121 | 121 | ||
122 | struct ib_umad_packet { | 122 | struct ib_umad_packet { |
@@ -145,6 +145,12 @@ static void ib_umad_release_dev(struct kref *ref) | |||
145 | kfree(dev); | 145 | kfree(dev); |
146 | } | 146 | } |
147 | 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 | |||
148 | static int queue_packet(struct ib_umad_file *file, | 154 | static int queue_packet(struct ib_umad_file *file, |
149 | struct ib_mad_agent *agent, | 155 | struct ib_mad_agent *agent, |
150 | struct ib_umad_packet *packet) | 156 | struct ib_umad_packet *packet) |
@@ -152,10 +158,11 @@ static int queue_packet(struct ib_umad_file *file, | |||
152 | int ret = 1; | 158 | int ret = 1; |
153 | 159 | ||
154 | down_read(&file->port->mutex); | 160 | down_read(&file->port->mutex); |
161 | |||
155 | for (packet->mad.hdr.id = 0; | 162 | for (packet->mad.hdr.id = 0; |
156 | packet->mad.hdr.id < IB_UMAD_MAX_AGENTS; | 163 | packet->mad.hdr.id < IB_UMAD_MAX_AGENTS; |
157 | packet->mad.hdr.id++) | 164 | packet->mad.hdr.id++) |
158 | if (agent == file->agent[packet->mad.hdr.id]) { | 165 | if (agent == __get_agent(file, packet->mad.hdr.id)) { |
159 | spin_lock_irq(&file->recv_lock); | 166 | spin_lock_irq(&file->recv_lock); |
160 | list_add_tail(&packet->list, &file->recv_list); | 167 | list_add_tail(&packet->list, &file->recv_list); |
161 | spin_unlock_irq(&file->recv_lock); | 168 | spin_unlock_irq(&file->recv_lock); |
@@ -303,9 +310,9 @@ static ssize_t ib_umad_write(struct file *filp, const char __user *buf, | |||
303 | u8 method; | 310 | u8 method; |
304 | __be64 *tid; | 311 | __be64 *tid; |
305 | int ret, length, hdr_len, copy_offset; | 312 | int ret, length, hdr_len, copy_offset; |
306 | int rmpp_active = 0; | 313 | int rmpp_active, has_rmpp_header; |
307 | 314 | ||
308 | if (count < sizeof (struct ib_user_mad)) | 315 | if (count < sizeof (struct ib_user_mad) + IB_MGMT_RMPP_HDR) |
309 | return -EINVAL; | 316 | return -EINVAL; |
310 | 317 | ||
311 | length = count - sizeof (struct ib_user_mad); | 318 | length = count - sizeof (struct ib_user_mad); |
@@ -327,7 +334,7 @@ static ssize_t ib_umad_write(struct file *filp, const char __user *buf, | |||
327 | 334 | ||
328 | down_read(&file->port->mutex); | 335 | down_read(&file->port->mutex); |
329 | 336 | ||
330 | agent = file->agent[packet->mad.hdr.id]; | 337 | agent = __get_agent(file, packet->mad.hdr.id); |
331 | if (!agent) { | 338 | if (!agent) { |
332 | ret = -EINVAL; | 339 | ret = -EINVAL; |
333 | goto err_up; | 340 | goto err_up; |
@@ -353,28 +360,31 @@ static ssize_t ib_umad_write(struct file *filp, const char __user *buf, | |||
353 | } | 360 | } |
354 | 361 | ||
355 | rmpp_mad = (struct ib_rmpp_mad *) packet->mad.data; | 362 | rmpp_mad = (struct ib_rmpp_mad *) packet->mad.data; |
356 | if (ib_get_rmpp_flags(&rmpp_mad->rmpp_hdr) & IB_MGMT_RMPP_FLAG_ACTIVE) { | 363 | if (rmpp_mad->mad_hdr.mgmt_class == IB_MGMT_CLASS_SUBN_ADM) { |
357 | /* RMPP active */ | 364 | hdr_len = IB_MGMT_SA_HDR; |
358 | if (!agent->rmpp_version) { | ||
359 | ret = -EINVAL; | ||
360 | goto err_ah; | ||
361 | } | ||
362 | |||
363 | /* Validate that the management class can support RMPP */ | ||
364 | if (rmpp_mad->mad_hdr.mgmt_class == IB_MGMT_CLASS_SUBN_ADM) { | ||
365 | hdr_len = IB_MGMT_SA_HDR; | ||
366 | } else if ((rmpp_mad->mad_hdr.mgmt_class >= IB_MGMT_CLASS_VENDOR_RANGE2_START) && | ||
367 | (rmpp_mad->mad_hdr.mgmt_class <= IB_MGMT_CLASS_VENDOR_RANGE2_END)) { | ||
368 | hdr_len = IB_MGMT_VENDOR_HDR; | ||
369 | } else { | ||
370 | ret = -EINVAL; | ||
371 | goto err_ah; | ||
372 | } | ||
373 | rmpp_active = 1; | ||
374 | copy_offset = IB_MGMT_RMPP_HDR; | 365 | copy_offset = IB_MGMT_RMPP_HDR; |
366 | has_rmpp_header = 1; | ||
367 | } else if (rmpp_mad->mad_hdr.mgmt_class >= IB_MGMT_CLASS_VENDOR_RANGE2_START && | ||
368 | rmpp_mad->mad_hdr.mgmt_class <= IB_MGMT_CLASS_VENDOR_RANGE2_END) { | ||
369 | hdr_len = IB_MGMT_VENDOR_HDR; | ||
370 | copy_offset = IB_MGMT_RMPP_HDR; | ||
371 | has_rmpp_header = 1; | ||
375 | } else { | 372 | } else { |
376 | hdr_len = IB_MGMT_MAD_HDR; | 373 | hdr_len = IB_MGMT_MAD_HDR; |
377 | copy_offset = IB_MGMT_MAD_HDR; | 374 | copy_offset = IB_MGMT_MAD_HDR; |
375 | has_rmpp_header = 0; | ||
376 | } | ||
377 | |||
378 | if (has_rmpp_header) | ||
379 | rmpp_active = ib_get_rmpp_flags(&rmpp_mad->rmpp_hdr) & | ||
380 | IB_MGMT_RMPP_FLAG_ACTIVE; | ||
381 | else | ||
382 | rmpp_active = 0; | ||
383 | |||
384 | /* Validate that the management class can support RMPP */ | ||
385 | if (rmpp_active && !agent->rmpp_version) { | ||
386 | ret = -EINVAL; | ||
387 | goto err_ah; | ||
378 | } | 388 | } |
379 | 389 | ||
380 | packet->msg = ib_create_send_mad(agent, | 390 | packet->msg = ib_create_send_mad(agent, |
@@ -481,7 +491,7 @@ static int ib_umad_reg_agent(struct ib_umad_file *file, unsigned long arg) | |||
481 | } | 491 | } |
482 | 492 | ||
483 | for (agent_id = 0; agent_id < IB_UMAD_MAX_AGENTS; ++agent_id) | 493 | for (agent_id = 0; agent_id < IB_UMAD_MAX_AGENTS; ++agent_id) |
484 | if (!file->agent[agent_id]) | 494 | if (!__get_agent(file, agent_id)) |
485 | goto found; | 495 | goto found; |
486 | 496 | ||
487 | ret = -ENOMEM; | 497 | ret = -ENOMEM; |
@@ -505,29 +515,15 @@ found: | |||
505 | goto out; | 515 | goto out; |
506 | } | 516 | } |
507 | 517 | ||
508 | file->agent[agent_id] = agent; | ||
509 | |||
510 | file->mr[agent_id] = ib_get_dma_mr(agent->qp->pd, IB_ACCESS_LOCAL_WRITE); | ||
511 | if (IS_ERR(file->mr[agent_id])) { | ||
512 | ret = -ENOMEM; | ||
513 | goto err; | ||
514 | } | ||
515 | |||
516 | if (put_user(agent_id, | 518 | if (put_user(agent_id, |
517 | (u32 __user *) (arg + offsetof(struct ib_user_mad_reg_req, id)))) { | 519 | (u32 __user *) (arg + offsetof(struct ib_user_mad_reg_req, id)))) { |
518 | ret = -EFAULT; | 520 | ret = -EFAULT; |
519 | goto err_mr; | 521 | ib_unregister_mad_agent(agent); |
522 | goto out; | ||
520 | } | 523 | } |
521 | 524 | ||
525 | file->agent[agent_id] = agent; | ||
522 | ret = 0; | 526 | ret = 0; |
523 | goto out; | ||
524 | |||
525 | err_mr: | ||
526 | ib_dereg_mr(file->mr[agent_id]); | ||
527 | |||
528 | err: | ||
529 | file->agent[agent_id] = NULL; | ||
530 | ib_unregister_mad_agent(agent); | ||
531 | 527 | ||
532 | out: | 528 | out: |
533 | up_write(&file->port->mutex); | 529 | up_write(&file->port->mutex); |
@@ -536,27 +532,29 @@ out: | |||
536 | 532 | ||
537 | static int ib_umad_unreg_agent(struct ib_umad_file *file, unsigned long arg) | 533 | static int ib_umad_unreg_agent(struct ib_umad_file *file, unsigned long arg) |
538 | { | 534 | { |
535 | struct ib_mad_agent *agent = NULL; | ||
539 | u32 id; | 536 | u32 id; |
540 | int ret = 0; | 537 | int ret = 0; |
541 | 538 | ||
542 | down_write(&file->port->mutex); | 539 | if (get_user(id, (u32 __user *) arg)) |
540 | return -EFAULT; | ||
543 | 541 | ||
544 | if (get_user(id, (u32 __user *) arg)) { | 542 | down_write(&file->port->mutex); |
545 | ret = -EFAULT; | ||
546 | goto out; | ||
547 | } | ||
548 | 543 | ||
549 | if (id < 0 || id >= IB_UMAD_MAX_AGENTS || !file->agent[id]) { | 544 | if (id < 0 || id >= IB_UMAD_MAX_AGENTS || !__get_agent(file, id)) { |
550 | ret = -EINVAL; | 545 | ret = -EINVAL; |
551 | goto out; | 546 | goto out; |
552 | } | 547 | } |
553 | 548 | ||
554 | ib_dereg_mr(file->mr[id]); | 549 | agent = file->agent[id]; |
555 | ib_unregister_mad_agent(file->agent[id]); | ||
556 | file->agent[id] = NULL; | 550 | file->agent[id] = NULL; |
557 | 551 | ||
558 | out: | 552 | out: |
559 | up_write(&file->port->mutex); | 553 | up_write(&file->port->mutex); |
554 | |||
555 | if (agent) | ||
556 | ib_unregister_mad_agent(agent); | ||
557 | |||
560 | return ret; | 558 | return ret; |
561 | } | 559 | } |
562 | 560 | ||
@@ -621,23 +619,29 @@ static int ib_umad_close(struct inode *inode, struct file *filp) | |||
621 | struct ib_umad_file *file = filp->private_data; | 619 | struct ib_umad_file *file = filp->private_data; |
622 | struct ib_umad_device *dev = file->port->umad_dev; | 620 | struct ib_umad_device *dev = file->port->umad_dev; |
623 | struct ib_umad_packet *packet, *tmp; | 621 | struct ib_umad_packet *packet, *tmp; |
622 | int already_dead; | ||
624 | int i; | 623 | int i; |
625 | 624 | ||
626 | down_write(&file->port->mutex); | 625 | down_write(&file->port->mutex); |
627 | for (i = 0; i < IB_UMAD_MAX_AGENTS; ++i) | 626 | |
628 | if (file->agent[i]) { | 627 | already_dead = file->agents_dead; |
629 | ib_dereg_mr(file->mr[i]); | 628 | file->agents_dead = 1; |
630 | ib_unregister_mad_agent(file->agent[i]); | ||
631 | } | ||
632 | 629 | ||
633 | list_for_each_entry_safe(packet, tmp, &file->recv_list, list) | 630 | list_for_each_entry_safe(packet, tmp, &file->recv_list, list) |
634 | kfree(packet); | 631 | kfree(packet); |
635 | 632 | ||
636 | list_del(&file->port_list); | 633 | list_del(&file->port_list); |
637 | up_write(&file->port->mutex); | ||
638 | 634 | ||
639 | kfree(file); | 635 | downgrade_write(&file->port->mutex); |
636 | |||
637 | if (!already_dead) | ||
638 | for (i = 0; i < IB_UMAD_MAX_AGENTS; ++i) | ||
639 | if (file->agent[i]) | ||
640 | ib_unregister_mad_agent(file->agent[i]); | ||
640 | 641 | ||
642 | up_read(&file->port->mutex); | ||
643 | |||
644 | kfree(file); | ||
641 | kref_put(&dev->ref, ib_umad_release_dev); | 645 | kref_put(&dev->ref, ib_umad_release_dev); |
642 | 646 | ||
643 | return 0; | 647 | return 0; |
@@ -801,7 +805,7 @@ static int ib_umad_init_port(struct ib_device *device, int port_num, | |||
801 | goto err_class; | 805 | goto err_class; |
802 | port->sm_dev->owner = THIS_MODULE; | 806 | port->sm_dev->owner = THIS_MODULE; |
803 | port->sm_dev->ops = &umad_sm_fops; | 807 | port->sm_dev->ops = &umad_sm_fops; |
804 | kobject_set_name(&port->dev->kobj, "issm%d", port->dev_num); | 808 | kobject_set_name(&port->sm_dev->kobj, "issm%d", port->dev_num); |
805 | if (cdev_add(port->sm_dev, base_dev + port->dev_num + IB_UMAD_MAX_PORTS, 1)) | 809 | if (cdev_add(port->sm_dev, base_dev + port->dev_num + IB_UMAD_MAX_PORTS, 1)) |
806 | goto err_sm_cdev; | 810 | goto err_sm_cdev; |
807 | 811 | ||
@@ -863,14 +867,36 @@ static void ib_umad_kill_port(struct ib_umad_port *port) | |||
863 | 867 | ||
864 | port->ib_dev = NULL; | 868 | port->ib_dev = NULL; |
865 | 869 | ||
866 | list_for_each_entry(file, &port->file_list, port_list) | 870 | /* |
867 | for (id = 0; id < IB_UMAD_MAX_AGENTS; ++id) { | 871 | * Now go through the list of files attached to this port and |
868 | if (!file->agent[id]) | 872 | * unregister all of their MAD agents. We need to hold |
869 | continue; | 873 | * port->mutex while doing this to avoid racing with |
870 | ib_dereg_mr(file->mr[id]); | 874 | * ib_umad_close(), but we can't hold the mutex for writing |
871 | ib_unregister_mad_agent(file->agent[id]); | 875 | * while calling ib_unregister_mad_agent(), since that might |
872 | file->agent[id] = NULL; | 876 | * deadlock by calling back into queue_packet(). So we |
873 | } | 877 | * downgrade our lock to a read lock, and then drop and |
878 | * reacquire the write lock for the next iteration. | ||
879 | * | ||
880 | * We do list_del_init() on the file's list_head so that the | ||
881 | * list_del in ib_umad_close() is still OK, even after the | ||
882 | * file is removed from the list. | ||
883 | */ | ||
884 | while (!list_empty(&port->file_list)) { | ||
885 | file = list_entry(port->file_list.next, struct ib_umad_file, | ||
886 | port_list); | ||
887 | |||
888 | file->agents_dead = 1; | ||
889 | list_del_init(&file->port_list); | ||
890 | |||
891 | downgrade_write(&port->mutex); | ||
892 | |||
893 | for (id = 0; id < IB_UMAD_MAX_AGENTS; ++id) | ||
894 | if (file->agent[id]) | ||
895 | ib_unregister_mad_agent(file->agent[id]); | ||
896 | |||
897 | up_read(&port->mutex); | ||
898 | down_write(&port->mutex); | ||
899 | } | ||
874 | 900 | ||
875 | up_write(&port->mutex); | 901 | up_write(&port->mutex); |
876 | 902 | ||
@@ -913,7 +939,7 @@ static void ib_umad_add_one(struct ib_device *device) | |||
913 | 939 | ||
914 | err: | 940 | err: |
915 | while (--i >= s) | 941 | while (--i >= s) |
916 | ib_umad_kill_port(&umad_dev->port[i]); | 942 | ib_umad_kill_port(&umad_dev->port[i - s]); |
917 | 943 | ||
918 | kref_put(&umad_dev->ref, ib_umad_release_dev); | 944 | kref_put(&umad_dev->ref, ib_umad_release_dev); |
919 | } | 945 | } |