net/ngbe: support MAC filters
[dpdk.git] / drivers / net / sfc / sfc_mae_counter.c
index c7646cf..418caff 100644 (file)
@@ -4,8 +4,10 @@
  */
 
 #include <rte_common.h>
+#include <rte_service_component.h>
 
 #include "efx.h"
+#include "efx_regs_counters_pkt_format.h"
 
 #include "sfc_ev.h"
 #include "sfc.h"
@@ -49,6 +51,530 @@ sfc_mae_counter_rxq_required(struct sfc_adapter *sa)
        return true;
 }
 
+int
+sfc_mae_counter_enable(struct sfc_adapter *sa,
+                      struct sfc_mae_counter_id *counterp)
+{
+       struct sfc_mae_counter_registry *reg = &sa->mae.counter_registry;
+       struct sfc_mae_counters *counters = &reg->counters;
+       struct sfc_mae_counter *p;
+       efx_counter_t mae_counter;
+       uint32_t generation_count;
+       uint32_t unused;
+       int rc;
+
+       /*
+        * The actual count of counters allocated is ignored since a failure
+        * to allocate a single counter is indicated by non-zero return code.
+        */
+       rc = efx_mae_counters_alloc(sa->nic, 1, &unused, &mae_counter,
+                                   &generation_count);
+       if (rc != 0) {
+               sfc_err(sa, "failed to alloc MAE counter: %s",
+                       rte_strerror(rc));
+               goto fail_mae_counter_alloc;
+       }
+
+       if (mae_counter.id >= counters->n_mae_counters) {
+               /*
+                * ID of a counter is expected to be within the range
+                * between 0 and the maximum count of counters to always
+                * fit into a pre-allocated array size of maximum counter ID.
+                */
+               sfc_err(sa, "MAE counter ID is out of expected range");
+               rc = EFAULT;
+               goto fail_counter_id_range;
+       }
+
+       counterp->mae_id = mae_counter;
+
+       p = &counters->mae_counters[mae_counter.id];
+
+       /*
+        * Ordering is relaxed since it is the only operation on counter value.
+        * And it does not depend on different stores/loads in other threads.
+        * Paired with relaxed ordering in counter increment.
+        */
+       __atomic_store(&p->reset.pkts_bytes.int128,
+                      &p->value.pkts_bytes.int128, __ATOMIC_RELAXED);
+       p->generation_count = generation_count;
+
+       p->ft_group_hit_counter = counterp->ft_group_hit_counter;
+
+       /*
+        * The flag is set at the very end of add operation and reset
+        * at the beginning of delete operation. Release ordering is
+        * paired with acquire ordering on load in counter increment operation.
+        */
+       __atomic_store_n(&p->inuse, true, __ATOMIC_RELEASE);
+
+       sfc_info(sa, "enabled MAE counter #%u with reset pkts=%" PRIu64
+                " bytes=%" PRIu64, mae_counter.id,
+                p->reset.pkts, p->reset.bytes);
+
+       return 0;
+
+fail_counter_id_range:
+       (void)efx_mae_counters_free(sa->nic, 1, &unused, &mae_counter, NULL);
+
+fail_mae_counter_alloc:
+       sfc_log_init(sa, "failed: %s", rte_strerror(rc));
+       return rc;
+}
+
+int
+sfc_mae_counter_disable(struct sfc_adapter *sa,
+                       struct sfc_mae_counter_id *counter)
+{
+       struct sfc_mae_counter_registry *reg = &sa->mae.counter_registry;
+       struct sfc_mae_counters *counters = &reg->counters;
+       struct sfc_mae_counter *p;
+       uint32_t unused;
+       int rc;
+
+       if (counter->mae_id.id == EFX_MAE_RSRC_ID_INVALID)
+               return 0;
+
+       SFC_ASSERT(counter->mae_id.id < counters->n_mae_counters);
+       /*
+        * The flag is set at the very end of add operation and reset
+        * at the beginning of delete operation. Release ordering is
+        * paired with acquire ordering on load in counter increment operation.
+        */
+       p = &counters->mae_counters[counter->mae_id.id];
+       __atomic_store_n(&p->inuse, false, __ATOMIC_RELEASE);
+
+       rc = efx_mae_counters_free(sa->nic, 1, &unused, &counter->mae_id, NULL);
+       if (rc != 0)
+               sfc_err(sa, "failed to free MAE counter %u: %s",
+                       counter->mae_id.id, rte_strerror(rc));
+
+       sfc_info(sa, "disabled MAE counter #%u with reset pkts=%" PRIu64
+                " bytes=%" PRIu64, counter->mae_id.id,
+                p->reset.pkts, p->reset.bytes);
+
+       /*
+        * Do this regardless of what efx_mae_counters_free() return value is.
+        * If there's some error, the resulting resource leakage is bad, but
+        * nothing sensible can be done in this case.
+        */
+       counter->mae_id.id = EFX_MAE_RSRC_ID_INVALID;
+
+       return rc;
+}
+
+static void
+sfc_mae_counter_increment(struct sfc_adapter *sa,
+                         struct sfc_mae_counters *counters,
+                         uint32_t mae_counter_id,
+                         uint32_t generation_count,
+                         uint64_t pkts, uint64_t bytes)
+{
+       struct sfc_mae_counter *p = &counters->mae_counters[mae_counter_id];
+       struct sfc_mae_counters_xstats *xstats = &counters->xstats;
+       union sfc_pkts_bytes cnt_val;
+       bool inuse;
+
+       /*
+        * Acquire ordering is paired with release ordering in counter add
+        * and delete operations.
+        */
+       __atomic_load(&p->inuse, &inuse, __ATOMIC_ACQUIRE);
+       if (!inuse) {
+               /*
+                * Two possible cases include:
+                * 1) Counter is just allocated. Too early counter update
+                *    cannot be processed properly.
+                * 2) Stale update of freed and not reallocated counter.
+                *    There is no point in processing that update.
+                */
+               xstats->not_inuse_update++;
+               return;
+       }
+
+       if (unlikely(generation_count < p->generation_count)) {
+               /*
+                * It is a stale update for the reallocated counter
+                * (i.e., freed and the same ID allocated again).
+                */
+               xstats->realloc_update++;
+               return;
+       }
+
+       cnt_val.pkts = p->value.pkts + pkts;
+       cnt_val.bytes = p->value.bytes + bytes;
+
+       /*
+        * Ordering is relaxed since it is the only operation on counter value.
+        * And it does not depend on different stores/loads in other threads.
+        * Paired with relaxed ordering on counter reset.
+        */
+       __atomic_store(&p->value.pkts_bytes,
+                      &cnt_val.pkts_bytes, __ATOMIC_RELAXED);
+
+       if (p->ft_group_hit_counter != NULL) {
+               uint64_t ft_group_hit_counter;
+
+               ft_group_hit_counter = *p->ft_group_hit_counter + pkts;
+               __atomic_store_n(p->ft_group_hit_counter, ft_group_hit_counter,
+                                __ATOMIC_RELAXED);
+       }
+
+       sfc_info(sa, "update MAE counter #%u: pkts+%" PRIu64 "=%" PRIu64
+                ", bytes+%" PRIu64 "=%" PRIu64, mae_counter_id,
+                pkts, cnt_val.pkts, bytes, cnt_val.bytes);
+}
+
+static void
+sfc_mae_parse_counter_packet(struct sfc_adapter *sa,
+                            struct sfc_mae_counter_registry *counter_registry,
+                            const struct rte_mbuf *m)
+{
+       uint32_t generation_count;
+       const efx_xword_t *hdr;
+       const efx_oword_t *counters_data;
+       unsigned int version;
+       unsigned int id;
+       unsigned int header_offset;
+       unsigned int payload_offset;
+       unsigned int counter_count;
+       unsigned int required_len;
+       unsigned int i;
+
+       if (unlikely(m->nb_segs != 1)) {
+               sfc_err(sa, "unexpectedly scattered MAE counters packet (%u segments)",
+                       m->nb_segs);
+               return;
+       }
+
+       if (unlikely(m->data_len < ER_RX_SL_PACKETISER_HEADER_WORD_SIZE)) {
+               sfc_err(sa, "too short MAE counters packet (%u bytes)",
+                       m->data_len);
+               return;
+       }
+
+       /*
+        * The generation count is located in the Rx prefix in the USER_MARK
+        * field which is written into hash.fdir.hi field of an mbuf. See
+        * SF-123581-TC SmartNIC Datapath Offloads section 4.7.5 Counters.
+        */
+       generation_count = m->hash.fdir.hi;
+
+       hdr = rte_pktmbuf_mtod(m, const efx_xword_t *);
+
+       version = EFX_XWORD_FIELD(*hdr, ERF_SC_PACKETISER_HEADER_VERSION);
+       if (unlikely(version != ERF_SC_PACKETISER_HEADER_VERSION_2)) {
+               sfc_err(sa, "unexpected MAE counters packet version %u",
+                       version);
+               return;
+       }
+
+       id = EFX_XWORD_FIELD(*hdr, ERF_SC_PACKETISER_HEADER_IDENTIFIER);
+       if (unlikely(id != ERF_SC_PACKETISER_HEADER_IDENTIFIER_AR)) {
+               sfc_err(sa, "unexpected MAE counters source identifier %u", id);
+               return;
+       }
+
+       /* Packet layout definitions assume fixed header offset in fact */
+       header_offset =
+               EFX_XWORD_FIELD(*hdr, ERF_SC_PACKETISER_HEADER_HEADER_OFFSET);
+       if (unlikely(header_offset !=
+                    ERF_SC_PACKETISER_HEADER_HEADER_OFFSET_DEFAULT)) {
+               sfc_err(sa, "unexpected MAE counters packet header offset %u",
+                       header_offset);
+               return;
+       }
+
+       payload_offset =
+               EFX_XWORD_FIELD(*hdr, ERF_SC_PACKETISER_HEADER_PAYLOAD_OFFSET);
+
+       counter_count = EFX_XWORD_FIELD(*hdr, ERF_SC_PACKETISER_HEADER_COUNT);
+
+       required_len = payload_offset +
+                       counter_count * sizeof(counters_data[0]);
+       if (unlikely(required_len > m->data_len)) {
+               sfc_err(sa, "truncated MAE counters packet: %u counters, packet length is %u vs %u required",
+                       counter_count, m->data_len, required_len);
+               /*
+                * In theory it is possible process available counters data,
+                * but such condition is really unexpected and it is
+                * better to treat entire packet as corrupted.
+                */
+               return;
+       }
+
+       /* Ensure that counters data is 32-bit aligned */
+       if (unlikely(payload_offset % sizeof(uint32_t) != 0)) {
+               sfc_err(sa, "unsupported MAE counters payload offset %u, must be 32-bit aligned",
+                       payload_offset);
+               return;
+       }
+       RTE_BUILD_BUG_ON(sizeof(counters_data[0]) !=
+                       ER_RX_SL_PACKETISER_PAYLOAD_WORD_SIZE);
+
+       counters_data =
+               rte_pktmbuf_mtod_offset(m, const efx_oword_t *, payload_offset);
+
+       sfc_info(sa, "update %u MAE counters with gc=%u",
+                counter_count, generation_count);
+
+       for (i = 0; i < counter_count; ++i) {
+               uint32_t packet_count_lo;
+               uint32_t packet_count_hi;
+               uint32_t byte_count_lo;
+               uint32_t byte_count_hi;
+
+               /*
+                * Use 32-bit field accessors below since counters data
+                * is not 64-bit aligned.
+                * 32-bit alignment is checked above taking into account
+                * that start of packet data is 32-bit aligned
+                * (cache-line size aligned in fact).
+                */
+               packet_count_lo =
+                       EFX_OWORD_FIELD32(counters_data[i],
+                               ERF_SC_PACKETISER_PAYLOAD_PACKET_COUNT_LO);
+               packet_count_hi =
+                       EFX_OWORD_FIELD32(counters_data[i],
+                               ERF_SC_PACKETISER_PAYLOAD_PACKET_COUNT_HI);
+               byte_count_lo =
+                       EFX_OWORD_FIELD32(counters_data[i],
+                               ERF_SC_PACKETISER_PAYLOAD_BYTE_COUNT_LO);
+               byte_count_hi =
+                       EFX_OWORD_FIELD32(counters_data[i],
+                               ERF_SC_PACKETISER_PAYLOAD_BYTE_COUNT_HI);
+               sfc_mae_counter_increment(sa,
+                       &counter_registry->counters,
+                       EFX_OWORD_FIELD32(counters_data[i],
+                               ERF_SC_PACKETISER_PAYLOAD_COUNTER_INDEX),
+                       generation_count,
+                       (uint64_t)packet_count_lo |
+                       ((uint64_t)packet_count_hi <<
+                        ERF_SC_PACKETISER_PAYLOAD_PACKET_COUNT_LO_WIDTH),
+                       (uint64_t)byte_count_lo |
+                       ((uint64_t)byte_count_hi <<
+                        ERF_SC_PACKETISER_PAYLOAD_BYTE_COUNT_LO_WIDTH));
+       }
+}
+
+static int32_t
+sfc_mae_counter_routine(void *arg)
+{
+       struct sfc_adapter *sa = arg;
+       struct sfc_mae_counter_registry *counter_registry =
+               &sa->mae.counter_registry;
+       struct rte_mbuf *mbufs[SFC_MAE_COUNTER_RX_BURST];
+       unsigned int pushed_diff;
+       unsigned int pushed;
+       unsigned int i;
+       uint16_t n;
+       int rc;
+
+       n = counter_registry->rx_pkt_burst(counter_registry->rx_dp, mbufs,
+                                          SFC_MAE_COUNTER_RX_BURST);
+
+       for (i = 0; i < n; i++)
+               sfc_mae_parse_counter_packet(sa, counter_registry, mbufs[i]);
+
+       rte_pktmbuf_free_bulk(mbufs, n);
+
+       if (!counter_registry->use_credits)
+               return 0;
+
+       pushed = sfc_rx_get_pushed(sa, counter_registry->rx_dp);
+       pushed_diff = pushed - counter_registry->pushed_n_buffers;
+
+       if (pushed_diff >= SFC_COUNTER_RXQ_REFILL_LEVEL) {
+               rc = efx_mae_counters_stream_give_credits(sa->nic, pushed_diff);
+               if (rc == 0) {
+                       counter_registry->pushed_n_buffers = pushed;
+               } else {
+                       /*
+                        * FIXME: counters might be important for the
+                        * application. Handle the error in order to recover
+                        * from the failure
+                        */
+                       SFC_GENERIC_LOG(DEBUG, "Give credits failed: %s",
+                                       rte_strerror(rc));
+               }
+       }
+
+       return 0;
+}
+
+static void
+sfc_mae_counter_service_unregister(struct sfc_adapter *sa)
+{
+       struct sfc_mae_counter_registry *registry =
+               &sa->mae.counter_registry;
+       const unsigned int wait_ms = 10000;
+       unsigned int i;
+
+       rte_service_runstate_set(registry->service_id, 0);
+       rte_service_component_runstate_set(registry->service_id, 0);
+
+       /*
+        * Wait for the counter routine to finish the last iteration.
+        * Give up on timeout.
+        */
+       for (i = 0; i < wait_ms; i++) {
+               if (rte_service_may_be_active(registry->service_id) == 0)
+                       break;
+
+               rte_delay_ms(1);
+       }
+       if (i == wait_ms)
+               sfc_warn(sa, "failed to wait for counter service to stop");
+
+       rte_service_map_lcore_set(registry->service_id,
+                                 registry->service_core_id, 0);
+
+       rte_service_component_unregister(registry->service_id);
+}
+
+static struct sfc_rxq_info *
+sfc_counter_rxq_info_get(struct sfc_adapter *sa)
+{
+       return &sfc_sa2shared(sa)->rxq_info[sa->counter_rxq.sw_index];
+}
+
+static int
+sfc_mae_counter_service_register(struct sfc_adapter *sa,
+                                uint32_t counter_stream_flags)
+{
+       struct rte_service_spec service;
+       char counter_service_name[sizeof(service.name)] = "counter_service";
+       struct sfc_mae_counter_registry *counter_registry =
+               &sa->mae.counter_registry;
+       uint32_t cid;
+       uint32_t sid;
+       int rc;
+
+       sfc_log_init(sa, "entry");
+
+       /* Prepare service info */
+       memset(&service, 0, sizeof(service));
+       rte_strscpy(service.name, counter_service_name, sizeof(service.name));
+       service.socket_id = sa->socket_id;
+       service.callback = sfc_mae_counter_routine;
+       service.callback_userdata = sa;
+       counter_registry->rx_pkt_burst = sa->eth_dev->rx_pkt_burst;
+       counter_registry->rx_dp = sfc_counter_rxq_info_get(sa)->dp;
+       counter_registry->pushed_n_buffers = 0;
+       counter_registry->use_credits = counter_stream_flags &
+               EFX_MAE_COUNTERS_STREAM_OUT_USES_CREDITS;
+
+       cid = sfc_get_service_lcore(sa->socket_id);
+       if (cid == RTE_MAX_LCORE && sa->socket_id != SOCKET_ID_ANY) {
+               /* Warn and try to allocate on any NUMA node */
+               sfc_warn(sa,
+                       "failed to get service lcore for counter service at socket %d",
+                       sa->socket_id);
+
+               cid = sfc_get_service_lcore(SOCKET_ID_ANY);
+       }
+       if (cid == RTE_MAX_LCORE) {
+               rc = ENOTSUP;
+               sfc_err(sa, "failed to get service lcore for counter service");
+               goto fail_get_service_lcore;
+       }
+
+       /* Service core may be in "stopped" state, start it */
+       rc = rte_service_lcore_start(cid);
+       if (rc != 0 && rc != -EALREADY) {
+               sfc_err(sa, "failed to start service core for counter service: %s",
+                       rte_strerror(-rc));
+               rc = ENOTSUP;
+               goto fail_start_core;
+       }
+
+       /* Register counter service */
+       rc = rte_service_component_register(&service, &sid);
+       if (rc != 0) {
+               rc = ENOEXEC;
+               sfc_err(sa, "failed to register counter service component");
+               goto fail_register;
+       }
+
+       /* Map the service with the service core */
+       rc = rte_service_map_lcore_set(sid, cid, 1);
+       if (rc != 0) {
+               rc = -rc;
+               sfc_err(sa, "failed to map lcore for counter service: %s",
+                       rte_strerror(rc));
+               goto fail_map_lcore;
+       }
+
+       /* Run the service */
+       rc = rte_service_component_runstate_set(sid, 1);
+       if (rc < 0) {
+               rc = -rc;
+               sfc_err(sa, "failed to run counter service component: %s",
+                       rte_strerror(rc));
+               goto fail_component_runstate_set;
+       }
+       rc = rte_service_runstate_set(sid, 1);
+       if (rc < 0) {
+               rc = -rc;
+               sfc_err(sa, "failed to run counter service");
+               goto fail_runstate_set;
+       }
+
+       counter_registry->service_core_id = cid;
+       counter_registry->service_id = sid;
+
+       sfc_log_init(sa, "done");
+
+       return 0;
+
+fail_runstate_set:
+       rte_service_component_runstate_set(sid, 0);
+
+fail_component_runstate_set:
+       rte_service_map_lcore_set(sid, cid, 0);
+
+fail_map_lcore:
+       rte_service_component_unregister(sid);
+
+fail_register:
+fail_start_core:
+fail_get_service_lcore:
+       sfc_log_init(sa, "failed: %s", rte_strerror(rc));
+
+       return rc;
+}
+
+int
+sfc_mae_counters_init(struct sfc_mae_counters *counters,
+                     uint32_t nb_counters_max)
+{
+       int rc;
+
+       SFC_GENERIC_LOG(DEBUG, "%s: entry", __func__);
+
+       counters->mae_counters = rte_zmalloc("sfc_mae_counters",
+               sizeof(*counters->mae_counters) * nb_counters_max, 0);
+       if (counters->mae_counters == NULL) {
+               rc = ENOMEM;
+               SFC_GENERIC_LOG(ERR, "%s: failed: %s", __func__,
+                               rte_strerror(rc));
+               return rc;
+       }
+
+       counters->n_mae_counters = nb_counters_max;
+
+       SFC_GENERIC_LOG(DEBUG, "%s: done", __func__);
+
+       return 0;
+}
+
+void
+sfc_mae_counters_fini(struct sfc_mae_counters *counters)
+{
+       rte_free(counters->mae_counters);
+       counters->mae_counters = NULL;
+}
+
 int
 sfc_mae_counter_rxq_attach(struct sfc_adapter *sa)
 {
@@ -215,3 +741,120 @@ sfc_mae_counter_rxq_fini(struct sfc_adapter *sa)
 
        sfc_log_init(sa, "done");
 }
+
+void
+sfc_mae_counter_stop(struct sfc_adapter *sa)
+{
+       struct sfc_mae *mae = &sa->mae;
+
+       sfc_log_init(sa, "entry");
+
+       if (!mae->counter_rxq_running) {
+               sfc_log_init(sa, "counter queue is not running - skip");
+               return;
+       }
+
+       sfc_mae_counter_service_unregister(sa);
+       efx_mae_counters_stream_stop(sa->nic, sa->counter_rxq.sw_index, NULL);
+
+       mae->counter_rxq_running = false;
+
+       sfc_log_init(sa, "done");
+}
+
+int
+sfc_mae_counter_start(struct sfc_adapter *sa)
+{
+       struct sfc_mae *mae = &sa->mae;
+       uint32_t flags;
+       int rc;
+
+       SFC_ASSERT(sa->counter_rxq.state & SFC_COUNTER_RXQ_ATTACHED);
+
+       if (mae->counter_rxq_running)
+               return 0;
+
+       sfc_log_init(sa, "entry");
+
+       rc = efx_mae_counters_stream_start(sa->nic, sa->counter_rxq.sw_index,
+                                          SFC_MAE_COUNTER_STREAM_PACKET_SIZE,
+                                          0 /* No flags required */, &flags);
+       if (rc != 0) {
+               sfc_err(sa, "failed to start MAE counters stream: %s",
+                       rte_strerror(rc));
+               goto fail_counter_stream;
+       }
+
+       sfc_log_init(sa, "stream start flags: 0x%x", flags);
+
+       rc = sfc_mae_counter_service_register(sa, flags);
+       if (rc != 0)
+               goto fail_service_register;
+
+       mae->counter_rxq_running = true;
+
+       return 0;
+
+fail_service_register:
+       efx_mae_counters_stream_stop(sa->nic, sa->counter_rxq.sw_index, NULL);
+
+fail_counter_stream:
+       sfc_log_init(sa, "failed: %s", rte_strerror(rc));
+
+       return rc;
+}
+
+int
+sfc_mae_counter_get(struct sfc_mae_counters *counters,
+                   const struct sfc_mae_counter_id *counter,
+                   struct rte_flow_query_count *data)
+{
+       struct sfc_flow_tunnel *ft = counter->ft;
+       uint64_t non_reset_jump_hit_counter;
+       struct sfc_mae_counter *p;
+       union sfc_pkts_bytes value;
+
+       SFC_ASSERT(counter->mae_id.id < counters->n_mae_counters);
+       p = &counters->mae_counters[counter->mae_id.id];
+
+       /*
+        * Ordering is relaxed since it is the only operation on counter value.
+        * And it does not depend on different stores/loads in other threads.
+        * Paired with relaxed ordering in counter increment.
+        */
+       value.pkts_bytes.int128 = __atomic_load_n(&p->value.pkts_bytes.int128,
+                                                 __ATOMIC_RELAXED);
+
+       data->hits_set = 1;
+       data->hits = value.pkts - p->reset.pkts;
+
+       if (ft != NULL) {
+               data->hits += ft->group_hit_counter;
+               non_reset_jump_hit_counter = data->hits;
+               data->hits -= ft->reset_jump_hit_counter;
+       } else {
+               data->bytes_set = 1;
+               data->bytes = value.bytes - p->reset.bytes;
+       }
+
+       if (data->reset != 0) {
+               if (ft != NULL) {
+                       ft->reset_jump_hit_counter = non_reset_jump_hit_counter;
+               } else {
+                       p->reset.pkts = value.pkts;
+                       p->reset.bytes = value.bytes;
+               }
+       }
+
+       return 0;
+}
+
+bool
+sfc_mae_counter_stream_enabled(struct sfc_adapter *sa)
+{
+       if ((sa->counter_rxq.state & SFC_COUNTER_RXQ_INITIALIZED) == 0 ||
+           sfc_get_service_lcore(SOCKET_ID_ANY) == RTE_MAX_LCORE)
+               return B_FALSE;
+       else
+               return B_TRUE;
+}