Merge branch 'qed-Slowpath-Queue-bug-fixes'
Denis Bolotin says: ==================== qed: Slowpath Queue bug fixes This patch series fixes several bugs in the SPQ mechanism. It deals with SPQ entries management, preventing resource leaks, memory corruptions and handles error cases throughout the driver. Please consider applying to net. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
81fe16e053
|
@ -147,7 +147,8 @@ qed_sp_fcoe_func_start(struct qed_hwfn *p_hwfn,
|
|||
"Cannot satisfy CQ amount. CQs requested %d, CQs available %d. Aborting function start\n",
|
||||
fcoe_pf_params->num_cqs,
|
||||
p_hwfn->hw_info.feat_num[QED_FCOE_CQ]);
|
||||
return -EINVAL;
|
||||
rc = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
p_data->mtu = cpu_to_le16(fcoe_pf_params->mtu);
|
||||
|
@ -156,14 +157,14 @@ qed_sp_fcoe_func_start(struct qed_hwfn *p_hwfn,
|
|||
|
||||
rc = qed_cxt_acquire_cid(p_hwfn, PROTOCOLID_FCOE, &dummy_cid);
|
||||
if (rc)
|
||||
return rc;
|
||||
goto err;
|
||||
|
||||
cxt_info.iid = dummy_cid;
|
||||
rc = qed_cxt_get_cid_info(p_hwfn, &cxt_info);
|
||||
if (rc) {
|
||||
DP_NOTICE(p_hwfn, "Cannot find context info for dummy cid=%d\n",
|
||||
dummy_cid);
|
||||
return rc;
|
||||
goto err;
|
||||
}
|
||||
p_cxt = cxt_info.p_cxt;
|
||||
SET_FIELD(p_cxt->tstorm_ag_context.flags3,
|
||||
|
@ -240,6 +241,10 @@ qed_sp_fcoe_func_start(struct qed_hwfn *p_hwfn,
|
|||
rc = qed_spq_post(p_hwfn, p_ent, NULL);
|
||||
|
||||
return rc;
|
||||
|
||||
err:
|
||||
qed_sp_destroy_request(p_hwfn, p_ent);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
|
|
|
@ -200,6 +200,7 @@ qed_sp_iscsi_func_start(struct qed_hwfn *p_hwfn,
|
|||
"Cannot satisfy CQ amount. Queues requested %d, CQs available %d. Aborting function start\n",
|
||||
p_params->num_queues,
|
||||
p_hwfn->hw_info.feat_num[QED_ISCSI_CQ]);
|
||||
qed_sp_destroy_request(p_hwfn, p_ent);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
|
|
@ -740,8 +740,7 @@ int qed_sp_vport_update(struct qed_hwfn *p_hwfn,
|
|||
|
||||
rc = qed_sp_vport_update_rss(p_hwfn, p_ramrod, p_rss_params);
|
||||
if (rc) {
|
||||
/* Return spq entry which is taken in qed_sp_init_request()*/
|
||||
qed_spq_return_entry(p_hwfn, p_ent);
|
||||
qed_sp_destroy_request(p_hwfn, p_ent);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@ -1355,6 +1354,7 @@ qed_filter_ucast_common(struct qed_hwfn *p_hwfn,
|
|||
DP_NOTICE(p_hwfn,
|
||||
"%d is not supported yet\n",
|
||||
p_filter_cmd->opcode);
|
||||
qed_sp_destroy_request(p_hwfn, *pp_ent);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
@ -2056,13 +2056,13 @@ qed_configure_rfs_ntuple_filter(struct qed_hwfn *p_hwfn,
|
|||
} else {
|
||||
rc = qed_fw_vport(p_hwfn, p_params->vport_id, &abs_vport_id);
|
||||
if (rc)
|
||||
return rc;
|
||||
goto err;
|
||||
|
||||
if (p_params->qid != QED_RFS_NTUPLE_QID_RSS) {
|
||||
rc = qed_fw_l2_queue(p_hwfn, p_params->qid,
|
||||
&abs_rx_q_id);
|
||||
if (rc)
|
||||
return rc;
|
||||
goto err;
|
||||
|
||||
p_ramrod->rx_qid_valid = 1;
|
||||
p_ramrod->rx_qid = cpu_to_le16(abs_rx_q_id);
|
||||
|
@ -2083,6 +2083,10 @@ qed_configure_rfs_ntuple_filter(struct qed_hwfn *p_hwfn,
|
|||
(u64)p_params->addr, p_params->length);
|
||||
|
||||
return qed_spq_post(p_hwfn, p_ent, NULL);
|
||||
|
||||
err:
|
||||
qed_sp_destroy_request(p_hwfn, p_ent);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int qed_get_rxq_coalesce(struct qed_hwfn *p_hwfn,
|
||||
|
|
|
@ -1514,6 +1514,7 @@ qed_rdma_register_tid(void *rdma_cxt,
|
|||
default:
|
||||
rc = -EINVAL;
|
||||
DP_VERBOSE(p_hwfn, QED_MSG_RDMA, "rc = %d\n", rc);
|
||||
qed_sp_destroy_request(p_hwfn, p_ent);
|
||||
return rc;
|
||||
}
|
||||
SET_FIELD(p_ramrod->flags1,
|
||||
|
|
|
@ -745,6 +745,7 @@ static int qed_roce_sp_destroy_qp_responder(struct qed_hwfn *p_hwfn,
|
|||
DP_NOTICE(p_hwfn,
|
||||
"qed destroy responder failed: cannot allocate memory (ramrod). rc = %d\n",
|
||||
rc);
|
||||
qed_sp_destroy_request(p_hwfn, p_ent);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
|
|
@ -167,6 +167,9 @@ struct qed_spq_entry {
|
|||
enum spq_mode comp_mode;
|
||||
struct qed_spq_comp_cb comp_cb;
|
||||
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 {
|
||||
|
@ -396,6 +399,17 @@ struct qed_sp_init_data {
|
|||
struct qed_spq_comp_cb *p_comp_data;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Returns a SPQ entry to the pool / frees the entry if allocated.
|
||||
* Should be called on in error flows after initializing the SPQ entry
|
||||
* and before posting it.
|
||||
*
|
||||
* @param p_hwfn
|
||||
* @param p_ent
|
||||
*/
|
||||
void qed_sp_destroy_request(struct qed_hwfn *p_hwfn,
|
||||
struct qed_spq_entry *p_ent);
|
||||
|
||||
int qed_sp_init_request(struct qed_hwfn *p_hwfn,
|
||||
struct qed_spq_entry **pp_ent,
|
||||
u8 cmd,
|
||||
|
|
|
@ -47,6 +47,19 @@
|
|||
#include "qed_sp.h"
|
||||
#include "qed_sriov.h"
|
||||
|
||||
void qed_sp_destroy_request(struct qed_hwfn *p_hwfn,
|
||||
struct qed_spq_entry *p_ent)
|
||||
{
|
||||
/* qed_spq_get_entry() can either get an entry from the free_pool,
|
||||
* or, if no entries are left, allocate a new entry and add it to
|
||||
* the unlimited_pending list.
|
||||
*/
|
||||
if (p_ent->queue == &p_hwfn->p_spq->unlimited_pending)
|
||||
kfree(p_ent);
|
||||
else
|
||||
qed_spq_return_entry(p_hwfn, p_ent);
|
||||
}
|
||||
|
||||
int qed_sp_init_request(struct qed_hwfn *p_hwfn,
|
||||
struct qed_spq_entry **pp_ent,
|
||||
u8 cmd, u8 protocol, struct qed_sp_init_data *p_data)
|
||||
|
@ -80,7 +93,7 @@ int qed_sp_init_request(struct qed_hwfn *p_hwfn,
|
|||
|
||||
case QED_SPQ_MODE_BLOCK:
|
||||
if (!p_data->p_comp_data)
|
||||
return -EINVAL;
|
||||
goto err;
|
||||
|
||||
p_ent->comp_cb.cookie = p_data->p_comp_data->cookie;
|
||||
break;
|
||||
|
@ -95,7 +108,7 @@ int qed_sp_init_request(struct qed_hwfn *p_hwfn,
|
|||
default:
|
||||
DP_NOTICE(p_hwfn, "Unknown SPQE completion mode %d\n",
|
||||
p_ent->comp_mode);
|
||||
return -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
DP_VERBOSE(p_hwfn, QED_MSG_SPQ,
|
||||
|
@ -109,6 +122,11 @@ int qed_sp_init_request(struct qed_hwfn *p_hwfn,
|
|||
memset(&p_ent->ramrod, 0, sizeof(p_ent->ramrod));
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
qed_sp_destroy_request(p_hwfn, p_ent);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static enum tunnel_clss qed_tunn_clss_to_fw_clss(u8 type)
|
||||
|
|
|
@ -142,6 +142,7 @@ static int qed_spq_block(struct qed_hwfn *p_hwfn,
|
|||
|
||||
DP_INFO(p_hwfn, "Ramrod is stuck, requesting MCP drain\n");
|
||||
rc = qed_mcp_drain(p_hwfn, p_ptt);
|
||||
qed_ptt_release(p_hwfn, p_ptt);
|
||||
if (rc) {
|
||||
DP_NOTICE(p_hwfn, "MCP drain failed\n");
|
||||
goto err;
|
||||
|
@ -150,18 +151,15 @@ static int qed_spq_block(struct qed_hwfn *p_hwfn,
|
|||
/* Retry after drain */
|
||||
rc = __qed_spq_block(p_hwfn, p_ent, p_fw_ret, true);
|
||||
if (!rc)
|
||||
goto out;
|
||||
return 0;
|
||||
|
||||
comp_done = (struct qed_spq_comp_done *)p_ent->comp_cb.cookie;
|
||||
if (comp_done->done == 1)
|
||||
if (comp_done->done == 1) {
|
||||
if (p_fw_ret)
|
||||
*p_fw_ret = comp_done->fw_return_code;
|
||||
out:
|
||||
qed_ptt_release(p_hwfn, p_ptt);
|
||||
return 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
err:
|
||||
qed_ptt_release(p_hwfn, p_ptt);
|
||||
DP_NOTICE(p_hwfn,
|
||||
"Ramrod is stuck [CID %08x cmd %02x protocol %02x echo %04x]\n",
|
||||
le32_to_cpu(p_ent->elem.hdr.cid),
|
||||
|
@ -685,6 +683,8 @@ static int qed_spq_add_entry(struct qed_hwfn *p_hwfn,
|
|||
/* EBLOCK responsible to free the allocated p_ent */
|
||||
if (p_ent->comp_mode != QED_SPQ_MODE_EBLOCK)
|
||||
kfree(p_ent);
|
||||
else
|
||||
p_ent->post_ent = p_en2;
|
||||
|
||||
p_ent = p_en2;
|
||||
}
|
||||
|
@ -767,6 +767,25 @@ static int qed_spq_pend_post(struct qed_hwfn *p_hwfn)
|
|||
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,
|
||||
struct qed_spq_entry *p_ent, u8 *fw_return_code)
|
||||
{
|
||||
|
@ -824,11 +843,12 @@ int qed_spq_post(struct qed_hwfn *p_hwfn,
|
|||
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
|
||||
* return to pool.
|
||||
*/
|
||||
struct qed_spq_entry *p_post_ent = p_ent->post_ent;
|
||||
|
||||
kfree(p_ent);
|
||||
return rc;
|
||||
|
||||
/* Return the entry which was actually posted */
|
||||
p_ent = p_post_ent;
|
||||
}
|
||||
|
||||
if (rc)
|
||||
|
@ -842,7 +862,7 @@ int qed_spq_post(struct qed_hwfn *p_hwfn,
|
|||
spq_post_fail2:
|
||||
spin_lock_bh(&p_spq->lock);
|
||||
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:
|
||||
/* return to the free pool */
|
||||
|
@ -874,25 +894,8 @@ int qed_spq_completion(struct qed_hwfn *p_hwfn,
|
|||
spin_lock_bh(&p_spq->lock);
|
||||
list_for_each_entry_safe(p_ent, tmp, &p_spq->completion_pending, list) {
|
||||
if (p_ent->elem.hdr.echo == echo) {
|
||||
u16 pos = le16_to_cpu(echo) % SPQ_RING_SIZE;
|
||||
|
||||
list_del(&p_ent->list);
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
||||
qed_spq_comp_bmap_update(p_hwfn, echo);
|
||||
p_spq->comp_count++;
|
||||
found = p_ent;
|
||||
break;
|
||||
|
@ -931,11 +934,9 @@ int qed_spq_completion(struct qed_hwfn *p_hwfn,
|
|||
QED_MSG_SPQ,
|
||||
"Got a completion without a callback function\n");
|
||||
|
||||
if ((found->comp_mode != QED_SPQ_MODE_EBLOCK) ||
|
||||
(found->queue == &p_spq->unlimited_pending))
|
||||
if (found->comp_mode != QED_SPQ_MODE_EBLOCK)
|
||||
/* EBLOCK is responsible for returning its own entry into the
|
||||
* free list, unless it originally added the entry into the
|
||||
* unlimited pending list.
|
||||
* free list.
|
||||
*/
|
||||
qed_spq_return_entry(p_hwfn, found);
|
||||
|
||||
|
|
|
@ -101,6 +101,7 @@ static int qed_sp_vf_start(struct qed_hwfn *p_hwfn, struct qed_vf_info *p_vf)
|
|||
default:
|
||||
DP_NOTICE(p_hwfn, "Unknown VF personality %d\n",
|
||||
p_hwfn->hw_info.personality);
|
||||
qed_sp_destroy_request(p_hwfn, p_ent);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue