aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMike Marciniszyn <mike.marciniszyn@intel.com>2016-10-10 09:14:28 -0400
committerDoug Ledford <dledford@redhat.com>2016-11-15 16:25:59 -0500
commit4e045572e2c2be674ed7e43cca7ca105e8a22f56 (patch)
tree9ebe69a7c48b530e40a11a95f6bafa82abe8254e
parenta909d3e636995ba7c349e2ca5dbb528154d4ac30 (diff)
IB/hfi1: Add unique txwait_lock for txreq events
Profiling suggests that the read_seqbegin() in the txreq put logic is colliding with other uses of the iowait lock. The packet at a time use of this lock dictates a unique lock to avoid reader/writer collisions when the number of vTxWait events is low. In order to support a unique lock the iowait struct embedded in the QP is extended to remember the lock that protects the queue head. The QP destroy removes that QP from any wait list. It doesn't need to know the head because of the linked list API, but it does need to know the lock required to protect the head. This also opens up the wait logic to have unique per resources locks which needs to be in future refinement. Reviewed-by: Sebastian Sanchez <sebastian.sanchez@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/iowait.h8
-rw-r--r--drivers/infiniband/hw/hfi1/pio.c1
-rw-r--r--drivers/infiniband/hw/hfi1/qp.c11
-rw-r--r--drivers/infiniband/hw/hfi1/verbs.c4
-rw-r--r--drivers/infiniband/hw/hfi1/verbs.h13
-rw-r--r--drivers/infiniband/hw/hfi1/verbs_txreq.c13
6 files changed, 35 insertions, 15 deletions
diff --git a/drivers/infiniband/hw/hfi1/iowait.h b/drivers/infiniband/hw/hfi1/iowait.h
index 2ec6ef38d389..d9740ddea6f1 100644
--- a/drivers/infiniband/hw/hfi1/iowait.h
+++ b/drivers/infiniband/hw/hfi1/iowait.h
@@ -64,6 +64,7 @@ struct sdma_engine;
64/** 64/**
65 * struct iowait - linkage for delayed progress/waiting 65 * struct iowait - linkage for delayed progress/waiting
66 * @list: used to add/insert into QP/PQ wait lists 66 * @list: used to add/insert into QP/PQ wait lists
67 * @lock: uses to record the list head lock
67 * @tx_head: overflow list of sdma_txreq's 68 * @tx_head: overflow list of sdma_txreq's
68 * @sleep: no space callback 69 * @sleep: no space callback
69 * @wakeup: space callback wakeup 70 * @wakeup: space callback wakeup
@@ -91,6 +92,11 @@ struct sdma_engine;
91 * so sleeping is not allowed. 92 * so sleeping is not allowed.
92 * 93 *
93 * The wait_dma member along with the iow 94 * The wait_dma member along with the iow
95 *
96 * The lock field is used by waiters to record
97 * the seqlock_t that guards the list head.
98 * Waiters explicity know that, but the destroy
99 * code that unwaits QPs does not.
94 */ 100 */
95 101
96struct iowait { 102struct iowait {
@@ -103,6 +109,7 @@ struct iowait {
103 unsigned seq); 109 unsigned seq);
104 void (*wakeup)(struct iowait *wait, int reason); 110 void (*wakeup)(struct iowait *wait, int reason);
105 void (*sdma_drained)(struct iowait *wait); 111 void (*sdma_drained)(struct iowait *wait);
112 seqlock_t *lock;
106 struct work_struct iowork; 113 struct work_struct iowork;
107 wait_queue_head_t wait_dma; 114 wait_queue_head_t wait_dma;
108 wait_queue_head_t wait_pio; 115 wait_queue_head_t wait_pio;
@@ -141,6 +148,7 @@ static inline void iowait_init(
141 void (*sdma_drained)(struct iowait *wait)) 148 void (*sdma_drained)(struct iowait *wait))
142{ 149{
143 wait->count = 0; 150 wait->count = 0;
151 wait->lock = NULL;
144 INIT_LIST_HEAD(&wait->list); 152 INIT_LIST_HEAD(&wait->list);
145 INIT_LIST_HEAD(&wait->tx_head); 153 INIT_LIST_HEAD(&wait->tx_head);
146 INIT_WORK(&wait->iowork, func); 154 INIT_WORK(&wait->iowork, func);
diff --git a/drivers/infiniband/hw/hfi1/pio.c b/drivers/infiniband/hw/hfi1/pio.c
index 50a3a36d9363..385e4dcf2cd3 100644
--- a/drivers/infiniband/hw/hfi1/pio.c
+++ b/drivers/infiniband/hw/hfi1/pio.c
@@ -1580,6 +1580,7 @@ static void sc_piobufavail(struct send_context *sc)
1580 qp = iowait_to_qp(wait); 1580 qp = iowait_to_qp(wait);
1581 priv = qp->priv; 1581 priv = qp->priv;
1582 list_del_init(&priv->s_iowait.list); 1582 list_del_init(&priv->s_iowait.list);
1583 priv->s_iowait.lock = NULL;
1583 /* refcount held until actual wake up */ 1584 /* refcount held until actual wake up */
1584 qps[n++] = qp; 1585 qps[n++] = qp;
1585 } 1586 }
diff --git a/drivers/infiniband/hw/hfi1/qp.c b/drivers/infiniband/hw/hfi1/qp.c
index 9fc75e7e8781..d752d6768a49 100644
--- a/drivers/infiniband/hw/hfi1/qp.c
+++ b/drivers/infiniband/hw/hfi1/qp.c
@@ -196,15 +196,18 @@ static void flush_tx_list(struct rvt_qp *qp)
196static void flush_iowait(struct rvt_qp *qp) 196static void flush_iowait(struct rvt_qp *qp)
197{ 197{
198 struct hfi1_qp_priv *priv = qp->priv; 198 struct hfi1_qp_priv *priv = qp->priv;
199 struct hfi1_ibdev *dev = to_idev(qp->ibqp.device);
200 unsigned long flags; 199 unsigned long flags;
200 seqlock_t *lock = priv->s_iowait.lock;
201 201
202 write_seqlock_irqsave(&dev->iowait_lock, flags); 202 if (!lock)
203 return;
204 write_seqlock_irqsave(lock, flags);
203 if (!list_empty(&priv->s_iowait.list)) { 205 if (!list_empty(&priv->s_iowait.list)) {
204 list_del_init(&priv->s_iowait.list); 206 list_del_init(&priv->s_iowait.list);
207 priv->s_iowait.lock = NULL;
205 rvt_put_qp(qp); 208 rvt_put_qp(qp);
206 } 209 }
207 write_sequnlock_irqrestore(&dev->iowait_lock, flags); 210 write_sequnlock_irqrestore(lock, flags);
208} 211}
209 212
210static inline int opa_mtu_enum_to_int(int mtu) 213static inline int opa_mtu_enum_to_int(int mtu)
@@ -543,6 +546,7 @@ static int iowait_sleep(
543 ibp->rvp.n_dmawait++; 546 ibp->rvp.n_dmawait++;
544 qp->s_flags |= RVT_S_WAIT_DMA_DESC; 547 qp->s_flags |= RVT_S_WAIT_DMA_DESC;
545 list_add_tail(&priv->s_iowait.list, &sde->dmawait); 548 list_add_tail(&priv->s_iowait.list, &sde->dmawait);
549 priv->s_iowait.lock = &dev->iowait_lock;
546 trace_hfi1_qpsleep(qp, RVT_S_WAIT_DMA_DESC); 550 trace_hfi1_qpsleep(qp, RVT_S_WAIT_DMA_DESC);
547 rvt_get_qp(qp); 551 rvt_get_qp(qp);
548 } 552 }
@@ -964,6 +968,7 @@ void notify_error_qp(struct rvt_qp *qp)
964 if (!list_empty(&priv->s_iowait.list) && !(qp->s_flags & RVT_S_BUSY)) { 968 if (!list_empty(&priv->s_iowait.list) && !(qp->s_flags & RVT_S_BUSY)) {
965 qp->s_flags &= ~RVT_S_ANY_WAIT_IO; 969 qp->s_flags &= ~RVT_S_ANY_WAIT_IO;
966 list_del_init(&priv->s_iowait.list); 970 list_del_init(&priv->s_iowait.list);
971 priv->s_iowait.lock = NULL;
967 rvt_put_qp(qp); 972 rvt_put_qp(qp);
968 } 973 }
969 write_sequnlock(&dev->iowait_lock); 974 write_sequnlock(&dev->iowait_lock);
diff --git a/drivers/infiniband/hw/hfi1/verbs.c b/drivers/infiniband/hw/hfi1/verbs.c
index 4b7a16ceb362..feecacb62162 100644
--- a/drivers/infiniband/hw/hfi1/verbs.c
+++ b/drivers/infiniband/hw/hfi1/verbs.c
@@ -694,6 +694,7 @@ static void mem_timer(unsigned long data)
694 qp = iowait_to_qp(wait); 694 qp = iowait_to_qp(wait);
695 priv = qp->priv; 695 priv = qp->priv;
696 list_del_init(&priv->s_iowait.list); 696 list_del_init(&priv->s_iowait.list);
697 priv->s_iowait.lock = NULL;
697 /* refcount held until actual wake up */ 698 /* refcount held until actual wake up */
698 if (!list_empty(list)) 699 if (!list_empty(list))
699 mod_timer(&dev->mem_timer, jiffies + 1); 700 mod_timer(&dev->mem_timer, jiffies + 1);
@@ -769,6 +770,7 @@ static int wait_kmem(struct hfi1_ibdev *dev,
769 mod_timer(&dev->mem_timer, jiffies + 1); 770 mod_timer(&dev->mem_timer, jiffies + 1);
770 qp->s_flags |= RVT_S_WAIT_KMEM; 771 qp->s_flags |= RVT_S_WAIT_KMEM;
771 list_add_tail(&priv->s_iowait.list, &dev->memwait); 772 list_add_tail(&priv->s_iowait.list, &dev->memwait);
773 priv->s_iowait.lock = &dev->iowait_lock;
772 trace_hfi1_qpsleep(qp, RVT_S_WAIT_KMEM); 774 trace_hfi1_qpsleep(qp, RVT_S_WAIT_KMEM);
773 rvt_get_qp(qp); 775 rvt_get_qp(qp);
774 } 776 }
@@ -980,6 +982,7 @@ static int pio_wait(struct rvt_qp *qp,
980 qp->s_flags |= flag; 982 qp->s_flags |= flag;
981 was_empty = list_empty(&sc->piowait); 983 was_empty = list_empty(&sc->piowait);
982 list_add_tail(&priv->s_iowait.list, &sc->piowait); 984 list_add_tail(&priv->s_iowait.list, &sc->piowait);
985 priv->s_iowait.lock = &dev->iowait_lock;
983 trace_hfi1_qpsleep(qp, RVT_S_WAIT_PIO); 986 trace_hfi1_qpsleep(qp, RVT_S_WAIT_PIO);
984 rvt_get_qp(qp); 987 rvt_get_qp(qp);
985 /* counting: only call wantpiobuf_intr if first user */ 988 /* counting: only call wantpiobuf_intr if first user */
@@ -1632,6 +1635,7 @@ int hfi1_register_ib_device(struct hfi1_devdata *dd)
1632 setup_timer(&dev->mem_timer, mem_timer, (unsigned long)dev); 1635 setup_timer(&dev->mem_timer, mem_timer, (unsigned long)dev);
1633 1636
1634 seqlock_init(&dev->iowait_lock); 1637 seqlock_init(&dev->iowait_lock);
1638 seqlock_init(&dev->txwait_lock);
1635 INIT_LIST_HEAD(&dev->txwait); 1639 INIT_LIST_HEAD(&dev->txwait);
1636 INIT_LIST_HEAD(&dev->memwait); 1640 INIT_LIST_HEAD(&dev->memwait);
1637 1641
diff --git a/drivers/infiniband/hw/hfi1/verbs.h b/drivers/infiniband/hw/hfi1/verbs.h
index 1c3815d89eb7..7a8af39e99d4 100644
--- a/drivers/infiniband/hw/hfi1/verbs.h
+++ b/drivers/infiniband/hw/hfi1/verbs.h
@@ -180,18 +180,19 @@ struct hfi1_ibdev {
180 struct rvt_dev_info rdi; /* Must be first */ 180 struct rvt_dev_info rdi; /* Must be first */
181 181
182 /* QP numbers are shared by all IB ports */ 182 /* QP numbers are shared by all IB ports */
183 /* protect wait lists */ 183 /* protect txwait list */
184 seqlock_t iowait_lock; 184 seqlock_t txwait_lock ____cacheline_aligned_in_smp;
185 struct list_head txwait; /* list for wait verbs_txreq */ 185 struct list_head txwait; /* list for wait verbs_txreq */
186 struct list_head memwait; /* list for wait kernel memory */ 186 struct list_head memwait; /* list for wait kernel memory */
187 struct list_head txreq_free;
188 struct kmem_cache *verbs_txreq_cache; 187 struct kmem_cache *verbs_txreq_cache;
189 struct timer_list mem_timer; 188 u64 n_txwait;
189 u64 n_kmem_wait;
190 190
191 /* protect iowait lists */
192 seqlock_t iowait_lock ____cacheline_aligned_in_smp;
191 u64 n_piowait; 193 u64 n_piowait;
192 u64 n_piodrain; 194 u64 n_piodrain;
193 u64 n_txwait; 195 struct timer_list mem_timer;
194 u64 n_kmem_wait;
195 196
196#ifdef CONFIG_DEBUG_FS 197#ifdef CONFIG_DEBUG_FS
197 /* per HFI debugfs */ 198 /* per HFI debugfs */
diff --git a/drivers/infiniband/hw/hfi1/verbs_txreq.c b/drivers/infiniband/hw/hfi1/verbs_txreq.c
index 094ab829ec42..5d23172c470f 100644
--- a/drivers/infiniband/hw/hfi1/verbs_txreq.c
+++ b/drivers/infiniband/hw/hfi1/verbs_txreq.c
@@ -72,22 +72,22 @@ void hfi1_put_txreq(struct verbs_txreq *tx)
72 kmem_cache_free(dev->verbs_txreq_cache, tx); 72 kmem_cache_free(dev->verbs_txreq_cache, tx);
73 73
74 do { 74 do {
75 seq = read_seqbegin(&dev->iowait_lock); 75 seq = read_seqbegin(&dev->txwait_lock);
76 if (!list_empty(&dev->txwait)) { 76 if (!list_empty(&dev->txwait)) {
77 struct iowait *wait; 77 struct iowait *wait;
78 78
79 write_seqlock_irqsave(&dev->iowait_lock, flags); 79 write_seqlock_irqsave(&dev->txwait_lock, flags);
80 wait = list_first_entry(&dev->txwait, struct iowait, 80 wait = list_first_entry(&dev->txwait, struct iowait,
81 list); 81 list);
82 qp = iowait_to_qp(wait); 82 qp = iowait_to_qp(wait);
83 priv = qp->priv; 83 priv = qp->priv;
84 list_del_init(&priv->s_iowait.list); 84 list_del_init(&priv->s_iowait.list);
85 /* refcount held until actual wake up */ 85 /* refcount held until actual wake up */
86 write_sequnlock_irqrestore(&dev->iowait_lock, flags); 86 write_sequnlock_irqrestore(&dev->txwait_lock, flags);
87 hfi1_qp_wakeup(qp, RVT_S_WAIT_TX); 87 hfi1_qp_wakeup(qp, RVT_S_WAIT_TX);
88 break; 88 break;
89 } 89 }
90 } while (read_seqretry(&dev->iowait_lock, seq)); 90 } while (read_seqretry(&dev->txwait_lock, seq));
91} 91}
92 92
93struct verbs_txreq *__get_txreq(struct hfi1_ibdev *dev, 93struct verbs_txreq *__get_txreq(struct hfi1_ibdev *dev,
@@ -96,7 +96,7 @@ struct verbs_txreq *__get_txreq(struct hfi1_ibdev *dev,
96{ 96{
97 struct verbs_txreq *tx = ERR_PTR(-EBUSY); 97 struct verbs_txreq *tx = ERR_PTR(-EBUSY);
98 98
99 write_seqlock(&dev->iowait_lock); 99 write_seqlock(&dev->txwait_lock);
100 if (ib_rvt_state_ops[qp->state] & RVT_PROCESS_RECV_OK) { 100 if (ib_rvt_state_ops[qp->state] & RVT_PROCESS_RECV_OK) {
101 struct hfi1_qp_priv *priv; 101 struct hfi1_qp_priv *priv;
102 102
@@ -108,13 +108,14 @@ struct verbs_txreq *__get_txreq(struct hfi1_ibdev *dev,
108 dev->n_txwait++; 108 dev->n_txwait++;
109 qp->s_flags |= RVT_S_WAIT_TX; 109 qp->s_flags |= RVT_S_WAIT_TX;
110 list_add_tail(&priv->s_iowait.list, &dev->txwait); 110 list_add_tail(&priv->s_iowait.list, &dev->txwait);
111 priv->s_iowait.lock = &dev->txwait_lock;
111 trace_hfi1_qpsleep(qp, RVT_S_WAIT_TX); 112 trace_hfi1_qpsleep(qp, RVT_S_WAIT_TX);
112 rvt_get_qp(qp); 113 rvt_get_qp(qp);
113 } 114 }
114 qp->s_flags &= ~RVT_S_BUSY; 115 qp->s_flags &= ~RVT_S_BUSY;
115 } 116 }
116out: 117out:
117 write_sequnlock(&dev->iowait_lock); 118 write_sequnlock(&dev->txwait_lock);
118 return tx; 119 return tx;
119} 120}
120 121