Commit 2632f22e authored by Denis Bolotin's avatar Denis Bolotin Committed by David S. Miller

qed: Fix blocking/unlimited SPQ entries leak

When there are no SPQ entries left in the free_pool, new entries are
allocated and are added to the unlimited list. When an entry in the pool
is available, the content is copied from the original entry, and the new
entry is sent to the device. qed_spq_post() is not aware of that, so the
additional entry is stored in the original entry as p_post_ent, which can
later be returned to the pool.
Signed-off-by: default avatarDenis Bolotin <denis.bolotin@cavium.com>
Signed-off-by: default avatarMichal Kalderon <michal.kalderon@cavium.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 39477551
...@@ -167,6 +167,9 @@ struct qed_spq_entry { ...@@ -167,6 +167,9 @@ struct qed_spq_entry {
enum spq_mode comp_mode; enum spq_mode comp_mode;
struct qed_spq_comp_cb comp_cb; struct qed_spq_comp_cb comp_cb;
struct qed_spq_comp_done comp_done; /* SPQ_MODE_EBLOCK */ struct qed_spq_comp_done comp_done; /* SPQ_MODE_EBLOCK */
/* Posted entry for unlimited list entry in EBLOCK mode */
struct qed_spq_entry *post_ent;
}; };
struct qed_eq { struct qed_eq {
......
...@@ -685,6 +685,8 @@ static int qed_spq_add_entry(struct qed_hwfn *p_hwfn, ...@@ -685,6 +685,8 @@ static int qed_spq_add_entry(struct qed_hwfn *p_hwfn,
/* EBLOCK responsible to free the allocated p_ent */ /* EBLOCK responsible to free the allocated p_ent */
if (p_ent->comp_mode != QED_SPQ_MODE_EBLOCK) if (p_ent->comp_mode != QED_SPQ_MODE_EBLOCK)
kfree(p_ent); kfree(p_ent);
else
p_ent->post_ent = p_en2;
p_ent = p_en2; p_ent = p_en2;
} }
...@@ -767,6 +769,25 @@ static int qed_spq_pend_post(struct qed_hwfn *p_hwfn) ...@@ -767,6 +769,25 @@ static int qed_spq_pend_post(struct qed_hwfn *p_hwfn)
SPQ_HIGH_PRI_RESERVE_DEFAULT); SPQ_HIGH_PRI_RESERVE_DEFAULT);
} }
/* Avoid overriding of SPQ entries when getting out-of-order completions, by
* marking the completions in a bitmap and increasing the chain consumer only
* for the first successive completed entries.
*/
static void qed_spq_comp_bmap_update(struct qed_hwfn *p_hwfn, __le16 echo)
{
u16 pos = le16_to_cpu(echo) % SPQ_RING_SIZE;
struct qed_spq *p_spq = p_hwfn->p_spq;
__set_bit(pos, p_spq->p_comp_bitmap);
while (test_bit(p_spq->comp_bitmap_idx,
p_spq->p_comp_bitmap)) {
__clear_bit(p_spq->comp_bitmap_idx,
p_spq->p_comp_bitmap);
p_spq->comp_bitmap_idx++;
qed_chain_return_produced(&p_spq->chain);
}
}
int qed_spq_post(struct qed_hwfn *p_hwfn, int qed_spq_post(struct qed_hwfn *p_hwfn,
struct qed_spq_entry *p_ent, u8 *fw_return_code) struct qed_spq_entry *p_ent, u8 *fw_return_code)
{ {
...@@ -824,11 +845,12 @@ int qed_spq_post(struct qed_hwfn *p_hwfn, ...@@ -824,11 +845,12 @@ int qed_spq_post(struct qed_hwfn *p_hwfn,
p_ent->queue == &p_spq->unlimited_pending); p_ent->queue == &p_spq->unlimited_pending);
if (p_ent->queue == &p_spq->unlimited_pending) { if (p_ent->queue == &p_spq->unlimited_pending) {
/* This is an allocated p_ent which does not need to struct qed_spq_entry *p_post_ent = p_ent->post_ent;
* return to pool.
*/
kfree(p_ent); kfree(p_ent);
return rc;
/* Return the entry which was actually posted */
p_ent = p_post_ent;
} }
if (rc) if (rc)
...@@ -842,7 +864,7 @@ int qed_spq_post(struct qed_hwfn *p_hwfn, ...@@ -842,7 +864,7 @@ int qed_spq_post(struct qed_hwfn *p_hwfn,
spq_post_fail2: spq_post_fail2:
spin_lock_bh(&p_spq->lock); spin_lock_bh(&p_spq->lock);
list_del(&p_ent->list); list_del(&p_ent->list);
qed_chain_return_produced(&p_spq->chain); qed_spq_comp_bmap_update(p_hwfn, p_ent->elem.hdr.echo);
spq_post_fail: spq_post_fail:
/* return to the free pool */ /* return to the free pool */
...@@ -874,25 +896,8 @@ int qed_spq_completion(struct qed_hwfn *p_hwfn, ...@@ -874,25 +896,8 @@ int qed_spq_completion(struct qed_hwfn *p_hwfn,
spin_lock_bh(&p_spq->lock); spin_lock_bh(&p_spq->lock);
list_for_each_entry_safe(p_ent, tmp, &p_spq->completion_pending, list) { list_for_each_entry_safe(p_ent, tmp, &p_spq->completion_pending, list) {
if (p_ent->elem.hdr.echo == echo) { if (p_ent->elem.hdr.echo == echo) {
u16 pos = le16_to_cpu(echo) % SPQ_RING_SIZE;
list_del(&p_ent->list); list_del(&p_ent->list);
qed_spq_comp_bmap_update(p_hwfn, echo);
/* Avoid overriding of SPQ entries when getting
* out-of-order completions, by marking the completions
* in a bitmap and increasing the chain consumer only
* for the first successive completed entries.
*/
__set_bit(pos, p_spq->p_comp_bitmap);
while (test_bit(p_spq->comp_bitmap_idx,
p_spq->p_comp_bitmap)) {
__clear_bit(p_spq->comp_bitmap_idx,
p_spq->p_comp_bitmap);
p_spq->comp_bitmap_idx++;
qed_chain_return_produced(&p_spq->chain);
}
p_spq->comp_count++; p_spq->comp_count++;
found = p_ent; found = p_ent;
break; break;
...@@ -931,11 +936,9 @@ int qed_spq_completion(struct qed_hwfn *p_hwfn, ...@@ -931,11 +936,9 @@ int qed_spq_completion(struct qed_hwfn *p_hwfn,
QED_MSG_SPQ, QED_MSG_SPQ,
"Got a completion without a callback function\n"); "Got a completion without a callback function\n");
if ((found->comp_mode != QED_SPQ_MODE_EBLOCK) || if (found->comp_mode != QED_SPQ_MODE_EBLOCK)
(found->queue == &p_spq->unlimited_pending))
/* EBLOCK is responsible for returning its own entry into the /* EBLOCK is responsible for returning its own entry into the
* free list, unless it originally added the entry into the * free list.
* unlimited pending list.
*/ */
qed_spq_return_entry(p_hwfn, found); qed_spq_return_entry(p_hwfn, found);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment