diff options
author | Mike Marciniszyn <mike.marciniszyn@intel.com> | 2017-04-09 13:16:35 -0400 |
---|---|---|
committer | Doug Ledford <dledford@redhat.com> | 2017-04-28 13:48:01 -0400 |
commit | b6eac931b9bb2bce4db7032c35b41e5e34ec22a5 (patch) | |
tree | 9f6757a5a525248cb8ed496ad39ce8bad60c24da | |
parent | 3d591099a0a2b45a50913130f0599ab838002fc3 (diff) |
IB/hfi1: Prevent kernel QP post send hard lockups
The driver progress routines can call cond_resched() when
a timeslice is exhausted and irqs are enabled.
If the ULP had been holding a spin lock without disabling irqs and
the post send directly called the progress routine, the cond_resched()
could yield allowing another thread from the same ULP to deadlock
on that same lock.
Correct by replacing the current hfi1_do_send() calldown with a unique
one for post send and adding an argument to hfi1_do_send() to indicate
that the send engine is running in a thread. If the routine is not
running in a thread, avoid calling cond_resched().
CC: <stable@vger.kernel.org> # 4.7.x-
Fixes: Commit 831464ce4b74 ("IB/hfi1: Don't call cond_resched in atomic mode when sending packets")
Reviewed-by: Dennis Dalessandro <dennis.dalessandro@intel.com>
Signed-off-by: Mike Marciniszyn <mike.marciniszyn@intel.com>
Signed-off-by: Dennis Dalessandro <dennis.dalessandro@intel.com>
Signed-off-by: Doug Ledford <dledford@redhat.com>
-rw-r--r-- | drivers/infiniband/hw/hfi1/ruc.c | 26 | ||||
-rw-r--r-- | drivers/infiniband/hw/hfi1/verbs.c | 4 | ||||
-rw-r--r-- | drivers/infiniband/hw/hfi1/verbs.h | 6 |
3 files changed, 22 insertions, 14 deletions
diff --git a/drivers/infiniband/hw/hfi1/ruc.c b/drivers/infiniband/hw/hfi1/ruc.c index 879eb9b31954..ccf8d8037355 100644 --- a/drivers/infiniband/hw/hfi1/ruc.c +++ b/drivers/infiniband/hw/hfi1/ruc.c | |||
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | * Copyright(c) 2015, 2016 Intel Corporation. | 2 | * Copyright(c) 2015 - 2017 Intel Corporation. |
3 | * | 3 | * |
4 | * This file is provided under a dual BSD/GPLv2 license. When using or | 4 | * This file is provided under a dual BSD/GPLv2 license. When using or |
5 | * redistributing this file, you may do so under either license. | 5 | * redistributing this file, you may do so under either license. |
@@ -784,23 +784,29 @@ void hfi1_make_ruc_header(struct rvt_qp *qp, struct ib_other_headers *ohdr, | |||
784 | /* when sending, force a reschedule every one of these periods */ | 784 | /* when sending, force a reschedule every one of these periods */ |
785 | #define SEND_RESCHED_TIMEOUT (5 * HZ) /* 5s in jiffies */ | 785 | #define SEND_RESCHED_TIMEOUT (5 * HZ) /* 5s in jiffies */ |
786 | 786 | ||
787 | void hfi1_do_send_from_rvt(struct rvt_qp *qp) | ||
788 | { | ||
789 | hfi1_do_send(qp, false); | ||
790 | } | ||
791 | |||
787 | void _hfi1_do_send(struct work_struct *work) | 792 | void _hfi1_do_send(struct work_struct *work) |
788 | { | 793 | { |
789 | struct iowait *wait = container_of(work, struct iowait, iowork); | 794 | struct iowait *wait = container_of(work, struct iowait, iowork); |
790 | struct rvt_qp *qp = iowait_to_qp(wait); | 795 | struct rvt_qp *qp = iowait_to_qp(wait); |
791 | 796 | ||
792 | hfi1_do_send(qp); | 797 | hfi1_do_send(qp, true); |
793 | } | 798 | } |
794 | 799 | ||
795 | /** | 800 | /** |
796 | * hfi1_do_send - perform a send on a QP | 801 | * hfi1_do_send - perform a send on a QP |
797 | * @work: contains a pointer to the QP | 802 | * @work: contains a pointer to the QP |
803 | * @in_thread: true if in a workqueue thread | ||
798 | * | 804 | * |
799 | * Process entries in the send work queue until credit or queue is | 805 | * Process entries in the send work queue until credit or queue is |
800 | * exhausted. Only allow one CPU to send a packet per QP. | 806 | * exhausted. Only allow one CPU to send a packet per QP. |
801 | * Otherwise, two threads could send packets out of order. | 807 | * Otherwise, two threads could send packets out of order. |
802 | */ | 808 | */ |
803 | void hfi1_do_send(struct rvt_qp *qp) | 809 | void hfi1_do_send(struct rvt_qp *qp, bool in_thread) |
804 | { | 810 | { |
805 | struct hfi1_pkt_state ps; | 811 | struct hfi1_pkt_state ps; |
806 | struct hfi1_qp_priv *priv = qp->priv; | 812 | struct hfi1_qp_priv *priv = qp->priv; |
@@ -868,8 +874,10 @@ void hfi1_do_send(struct rvt_qp *qp) | |||
868 | qp->s_hdrwords = 0; | 874 | qp->s_hdrwords = 0; |
869 | /* allow other tasks to run */ | 875 | /* allow other tasks to run */ |
870 | if (unlikely(time_after(jiffies, timeout))) { | 876 | if (unlikely(time_after(jiffies, timeout))) { |
871 | if (workqueue_congested(cpu, | 877 | if (!in_thread || |
872 | ps.ppd->hfi1_wq)) { | 878 | workqueue_congested( |
879 | cpu, | ||
880 | ps.ppd->hfi1_wq)) { | ||
873 | spin_lock_irqsave( | 881 | spin_lock_irqsave( |
874 | &qp->s_lock, | 882 | &qp->s_lock, |
875 | ps.flags); | 883 | ps.flags); |
@@ -882,11 +890,9 @@ void hfi1_do_send(struct rvt_qp *qp) | |||
882 | *ps.ppd->dd->send_schedule); | 890 | *ps.ppd->dd->send_schedule); |
883 | return; | 891 | return; |
884 | } | 892 | } |
885 | if (!irqs_disabled()) { | 893 | cond_resched(); |
886 | cond_resched(); | 894 | this_cpu_inc( |
887 | this_cpu_inc( | 895 | *ps.ppd->dd->send_schedule); |
888 | *ps.ppd->dd->send_schedule); | ||
889 | } | ||
890 | timeout = jiffies + (timeout_int) / 8; | 896 | timeout = jiffies + (timeout_int) / 8; |
891 | } | 897 | } |
892 | spin_lock_irqsave(&qp->s_lock, ps.flags); | 898 | spin_lock_irqsave(&qp->s_lock, ps.flags); |
diff --git a/drivers/infiniband/hw/hfi1/verbs.c b/drivers/infiniband/hw/hfi1/verbs.c index 57036e545bdb..7174a18ebaac 100644 --- a/drivers/infiniband/hw/hfi1/verbs.c +++ b/drivers/infiniband/hw/hfi1/verbs.c | |||
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | * Copyright(c) 2015, 2016 Intel Corporation. | 2 | * Copyright(c) 2015 - 2017 Intel Corporation. |
3 | * | 3 | * |
4 | * This file is provided under a dual BSD/GPLv2 license. When using or | 4 | * This file is provided under a dual BSD/GPLv2 license. When using or |
5 | * redistributing this file, you may do so under either license. | 5 | * redistributing this file, you may do so under either license. |
@@ -1820,7 +1820,7 @@ int hfi1_register_ib_device(struct hfi1_devdata *dd) | |||
1820 | dd->verbs_dev.rdi.driver_f.qp_priv_free = qp_priv_free; | 1820 | dd->verbs_dev.rdi.driver_f.qp_priv_free = qp_priv_free; |
1821 | dd->verbs_dev.rdi.driver_f.free_all_qps = free_all_qps; | 1821 | dd->verbs_dev.rdi.driver_f.free_all_qps = free_all_qps; |
1822 | dd->verbs_dev.rdi.driver_f.notify_qp_reset = notify_qp_reset; | 1822 | dd->verbs_dev.rdi.driver_f.notify_qp_reset = notify_qp_reset; |
1823 | dd->verbs_dev.rdi.driver_f.do_send = hfi1_do_send; | 1823 | dd->verbs_dev.rdi.driver_f.do_send = hfi1_do_send_from_rvt; |
1824 | dd->verbs_dev.rdi.driver_f.schedule_send = hfi1_schedule_send; | 1824 | dd->verbs_dev.rdi.driver_f.schedule_send = hfi1_schedule_send; |
1825 | dd->verbs_dev.rdi.driver_f.schedule_send_no_lock = _hfi1_schedule_send; | 1825 | dd->verbs_dev.rdi.driver_f.schedule_send_no_lock = _hfi1_schedule_send; |
1826 | dd->verbs_dev.rdi.driver_f.get_pmtu_from_attr = get_pmtu_from_attr; | 1826 | dd->verbs_dev.rdi.driver_f.get_pmtu_from_attr = get_pmtu_from_attr; |
diff --git a/drivers/infiniband/hw/hfi1/verbs.h b/drivers/infiniband/hw/hfi1/verbs.h index 6c549e7a25e7..46b00ed9f2dc 100644 --- a/drivers/infiniband/hw/hfi1/verbs.h +++ b/drivers/infiniband/hw/hfi1/verbs.h | |||
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | * Copyright(c) 2015, 2016 Intel Corporation. | 2 | * Copyright(c) 2015 - 2017 Intel Corporation. |
3 | * | 3 | * |
4 | * This file is provided under a dual BSD/GPLv2 license. When using or | 4 | * This file is provided under a dual BSD/GPLv2 license. When using or |
5 | * redistributing this file, you may do so under either license. | 5 | * redistributing this file, you may do so under either license. |
@@ -355,7 +355,9 @@ void hfi1_make_ruc_header(struct rvt_qp *qp, struct ib_other_headers *ohdr, | |||
355 | 355 | ||
356 | void _hfi1_do_send(struct work_struct *work); | 356 | void _hfi1_do_send(struct work_struct *work); |
357 | 357 | ||
358 | void hfi1_do_send(struct rvt_qp *qp); | 358 | void hfi1_do_send_from_rvt(struct rvt_qp *qp); |
359 | |||
360 | void hfi1_do_send(struct rvt_qp *qp, bool in_thread); | ||
359 | 361 | ||
360 | void hfi1_send_complete(struct rvt_qp *qp, struct rvt_swqe *wqe, | 362 | void hfi1_send_complete(struct rvt_qp *qp, struct rvt_swqe *wqe, |
361 | enum ib_wc_status status); | 363 | enum ib_wc_status status); |