Merge branch 'qed-Slowpath-Queue-bug-fixes'
authorDavid S. Miller <davem@davemloft.net>
Fri, 9 Nov 2018 03:38:19 +0000 (19:38 -0800)
committerDavid S. Miller <davem@davemloft.net>
Fri, 9 Nov 2018 03:38:19 +0000 (19:38 -0800)
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>
drivers/net/ethernet/qlogic/qed/qed_fcoe.c
drivers/net/ethernet/qlogic/qed/qed_iscsi.c
drivers/net/ethernet/qlogic/qed/qed_l2.c
drivers/net/ethernet/qlogic/qed/qed_rdma.c
drivers/net/ethernet/qlogic/qed/qed_roce.c
drivers/net/ethernet/qlogic/qed/qed_sp.h
drivers/net/ethernet/qlogic/qed/qed_sp_commands.c
drivers/net/ethernet/qlogic/qed/qed_spq.c
drivers/net/ethernet/qlogic/qed/qed_sriov.c

index cc1b373c0ace56e08564d3527de9f5da3f87b4e4..46dc93d3b9b53db6586b791bc6ffcf65b756daba 100644 (file)
@@ -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
index 1135387bd99d704f517679c4716760e39acce52c..4f8a685d1a55febcf78e3213a1c56130c8535213 100644 (file)
@@ -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;
        }
 
index 82a1bd1f8a8ce3fd66acc6b0cc0c9e7bf6a57305..67c02ea939062dea70ae6e806546fd8266dd08cf 100644 (file)
@@ -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,
index c71391b9c757a1b03f55f21cc641c4718bbce719..62113438c8809c34e5c2dc48bccda67fc9ae21ad 100644 (file)
@@ -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,
index f9167d1354bbef3ccf2e972e8c002e64bbc24cce..e49fada854108718bf1dc5ea45fda2d4d264ded2 100644 (file)
@@ -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;
        }
 
index e95431f6acd46fb6ace4c20cfe227388c890cdea..3157c0d9944177e784a62ef983dd2adc3b5c0f11 100644 (file)
@@ -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,
index 77b6248ad3b97d3a45caf27825faddabf9695a5b..888274fa208bc768b2ab9db2514407573bfab2e1 100644 (file)
 #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)
index c4a6274dd625c2bf419cc78dfab20f899ada494d..0a9c5bb0fa486658a23132680a1aeddb9a72b518 100644 (file)
@@ -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);
 
index 9b08a9d9e15130f0518b1f7608bbaa36e6eb15b0..ca6290fa0f30940265ca1590de148c94eb2cf18e 100644 (file)
@@ -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;
        }