@@ -112,6 +112,117 @@ int efx_mae_lookup_mport(struct efx_nic *efx, u32 selector, u32 *id)
return 0;
}
+int efx_mae_start_counters(struct efx_nic *efx, struct efx_rx_queue *rx_queue)
+{
+ MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_COUNTERS_STREAM_START_V2_IN_LEN);
+ MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_COUNTERS_STREAM_START_OUT_LEN);
+ u32 out_flags;
+ size_t outlen;
+ int rc;
+
+ MCDI_SET_WORD(inbuf, MAE_COUNTERS_STREAM_START_V2_IN_QID,
+ efx_rx_queue_index(rx_queue));
+ MCDI_SET_WORD(inbuf, MAE_COUNTERS_STREAM_START_V2_IN_PACKET_SIZE,
+ efx->net_dev->mtu);
+ MCDI_SET_DWORD(inbuf, MAE_COUNTERS_STREAM_START_V2_IN_COUNTER_TYPES_MASK,
+ BIT(MAE_COUNTER_TYPE_AR) | BIT(MAE_COUNTER_TYPE_CT) |
+ BIT(MAE_COUNTER_TYPE_OR));
+ rc = efx_mcdi_rpc(efx, MC_CMD_MAE_COUNTERS_STREAM_START,
+ inbuf, sizeof(inbuf), outbuf, sizeof(outbuf), &outlen);
+ if (rc)
+ return rc;
+ if (outlen < sizeof(outbuf))
+ return -EIO;
+ out_flags = MCDI_DWORD(outbuf, MAE_COUNTERS_STREAM_START_OUT_FLAGS);
+ if (out_flags & BIT(MC_CMD_MAE_COUNTERS_STREAM_START_OUT_USES_CREDITS_OFST)) {
+ netif_dbg(efx, drv, efx->net_dev,
+ "MAE counter stream uses credits\n");
+ rx_queue->grant_credits = true;
+ out_flags &= ~BIT(MC_CMD_MAE_COUNTERS_STREAM_START_OUT_USES_CREDITS_OFST);
+ }
+ if (out_flags) {
+ netif_err(efx, drv, efx->net_dev,
+ "MAE counter stream start: unrecognised flags %x\n",
+ out_flags);
+ goto out_stop;
+ }
+ return 0;
+out_stop:
+ efx_mae_stop_counters(efx, rx_queue);
+ return -EOPNOTSUPP;
+}
+
+static bool efx_mae_counters_flushed(u32 *flush_gen, u32 *seen_gen)
+{
+ int i;
+
+ for (i = 0; i < EFX_TC_COUNTER_TYPE_MAX; i++)
+ if ((s32)(flush_gen[i] - seen_gen[i]) > 0)
+ return false;
+ return true;
+}
+
+int efx_mae_stop_counters(struct efx_nic *efx, struct efx_rx_queue *rx_queue)
+{
+ MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_COUNTERS_STREAM_STOP_V2_OUT_LENMAX);
+ MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_COUNTERS_STREAM_STOP_IN_LEN);
+ size_t outlen;
+ int rc, i;
+
+ MCDI_SET_WORD(inbuf, MAE_COUNTERS_STREAM_STOP_IN_QID,
+ efx_rx_queue_index(rx_queue));
+ rc = efx_mcdi_rpc(efx, MC_CMD_MAE_COUNTERS_STREAM_STOP,
+ inbuf, sizeof(inbuf), outbuf, sizeof(outbuf), &outlen);
+
+ if (rc)
+ return rc;
+
+ netif_dbg(efx, drv, efx->net_dev, "Draining counters:\n");
+ /* Only process received generation counts */
+ for (i = 0; (i < (outlen / 4)) && (i < EFX_TC_COUNTER_TYPE_MAX); i++) {
+ efx->tc->flush_gen[i] = MCDI_ARRAY_DWORD(outbuf,
+ MAE_COUNTERS_STREAM_STOP_V2_OUT_GENERATION_COUNT,
+ i);
+ netif_dbg(efx, drv, efx->net_dev,
+ "\ttype %u, awaiting gen %u\n", i,
+ efx->tc->flush_gen[i]);
+ }
+
+ efx->tc->flush_counters = true;
+
+ /* Drain can take up to 2 seconds owing to FWRIVERHD-2884; whatever
+ * timeout we use, that delay is added to unload on nonresponsive
+ * hardware, so 2500ms seems like a reasonable compromise.
+ */
+ if (!wait_event_timeout(efx->tc->flush_wq,
+ efx_mae_counters_flushed(efx->tc->flush_gen,
+ efx->tc->seen_gen),
+ msecs_to_jiffies(2500)))
+ netif_warn(efx, drv, efx->net_dev,
+ "Failed to drain counters RXQ, FW may be unhappy\n");
+
+ efx->tc->flush_counters = false;
+
+ return rc;
+}
+
+void efx_mae_counters_grant_credits(struct work_struct *work)
+{
+ MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_COUNTERS_STREAM_GIVE_CREDITS_IN_LEN);
+ struct efx_rx_queue *rx_queue = container_of(work, struct efx_rx_queue,
+ grant_work);
+ struct efx_nic *efx = rx_queue->efx;
+ unsigned int credits;
+
+ BUILD_BUG_ON(MC_CMD_MAE_COUNTERS_STREAM_GIVE_CREDITS_OUT_LEN);
+ credits = READ_ONCE(rx_queue->notified_count) - rx_queue->granted_count;
+ MCDI_SET_DWORD(inbuf, MAE_COUNTERS_STREAM_GIVE_CREDITS_IN_NUM_CREDITS,
+ credits);
+ if (!efx_mcdi_rpc(efx, MC_CMD_MAE_COUNTERS_STREAM_GIVE_CREDITS,
+ inbuf, sizeof(inbuf), NULL, 0, NULL))
+ rx_queue->granted_count += credits;
+}
+
static int efx_mae_get_basic_caps(struct efx_nic *efx, struct mae_caps *caps)
{
MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_GET_CAPS_OUT_LEN);
@@ -27,6 +27,10 @@ void efx_mae_mport_mport(struct efx_nic *efx, u32 mport_id, u32 *out);
int efx_mae_lookup_mport(struct efx_nic *efx, u32 selector, u32 *id);
+int efx_mae_start_counters(struct efx_nic *efx, struct efx_rx_queue *rx_queue);
+int efx_mae_stop_counters(struct efx_nic *efx, struct efx_rx_queue *rx_queue);
+void efx_mae_counters_grant_credits(struct work_struct *work);
+
#define MAE_NUM_FIELDS (MAE_FIELD_ENC_VNET_ID + 1)
struct mae_caps {
@@ -221,6 +221,11 @@ void efx_mcdi_sensor_event(struct efx_nic *efx, efx_qword_t *ev);
#define MCDI_BYTE(_buf, _field) \
((void)BUILD_BUG_ON_ZERO(MC_CMD_ ## _field ## _LEN != 1), \
*MCDI_PTR(_buf, _field))
+#define MCDI_SET_WORD(_buf, _field, _value) do { \
+ BUILD_BUG_ON(MC_CMD_ ## _field ## _LEN != 2); \
+ BUILD_BUG_ON(MC_CMD_ ## _field ## _OFST & 1); \
+ *(__force __le16 *)MCDI_PTR(_buf, _field) = cpu_to_le16(_value);\
+ } while (0)
#define MCDI_WORD(_buf, _field) \
((u16)BUILD_BUG_ON_ZERO(MC_CMD_ ## _field ## _LEN != 2) + \
le16_to_cpu(*(__force const __le16 *)MCDI_PTR(_buf, _field)))
@@ -751,6 +751,7 @@ int efx_init_struct_tc(struct efx_nic *efx)
INIT_LIST_HEAD(&efx->tc->block_list);
mutex_init(&efx->tc->mutex);
+ init_waitqueue_head(&efx->tc->flush_wq);
rc = rhashtable_init(&efx->tc->match_action_ht, &efx_tc_match_action_ht_params);
if (rc < 0)
goto fail_match_action_ht;
@@ -14,6 +14,14 @@
#include <net/flow_offload.h>
#include <linux/rhashtable.h>
#include "net_driver.h"
+#include "mcdi_pcol.h" /* for MAE_COUNTER_TYPE_* */
+
+enum efx_tc_counter_type {
+ EFX_TC_COUNTER_TYPE_AR = MAE_COUNTER_TYPE_AR,
+ EFX_TC_COUNTER_TYPE_CT = MAE_COUNTER_TYPE_CT,
+ EFX_TC_COUNTER_TYPE_OR = MAE_COUNTER_TYPE_OR,
+ EFX_TC_COUNTER_TYPE_MAX
+};
#define IS_ALL_ONES(v) (!(typeof (v))~(v))
@@ -79,6 +87,12 @@ enum efx_tc_rule_prios {
* @reps_filter_uc: VNIC filter for representor unicast RX (promisc)
* @reps_filter_mc: VNIC filter for representor multicast RX (allmulti)
* @reps_mport_vport_id: vport_id for representor RX filters
+ * @flush_counters: counters have been stopped, waiting for drain
+ * @flush_gen: final generation count per type array as reported by
+ * MC_CMD_MAE_COUNTERS_STREAM_STOP
+ * @seen_gen: most recent generation count per type as seen by efx_tc_rx()
+ * @flush_wq: wait queue used by efx_mae_stop_counters() to wait for
+ * MAE counters RXQ to finish draining
* @dflt: Match-action rules for default switching; at priority
* %EFX_TC_PRIO_DFLT. Named by *ingress* port
* @dflt.pf: rule for traffic ingressing from PF (egresses to wire)
@@ -92,6 +106,10 @@ struct efx_tc_state {
struct rhashtable match_action_ht;
u32 reps_mport_id, reps_mport_vport_id;
s32 reps_filter_uc, reps_filter_mc;
+ bool flush_counters;
+ u32 flush_gen[EFX_TC_COUNTER_TYPE_MAX];
+ u32 seen_gen[EFX_TC_COUNTER_TYPE_MAX];
+ wait_queue_head_t flush_wq;
struct {
struct efx_tc_flow_rule pf;
struct efx_tc_flow_rule wire;