root/drivers/net/ethernet/netronome/nfp/abm/main.h
/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
/* Copyright (C) 2018 Netronome Systems, Inc. */

#ifndef __NFP_ABM_H__
#define __NFP_ABM_H__ 1

#include <linux/bits.h>
#include <linux/list.h>
#include <linux/radix-tree.h>
#include <net/devlink.h>
#include <net/pkt_cls.h>
#include <net/pkt_sched.h>

/* Dump of 64 PRIOs and 256 REDs seems to take 850us on Xeon v4 @ 2.20GHz;
 * 2.5ms / 400Hz seems more than sufficient for stats resolution.
 */
#define NFP_ABM_STATS_REFRESH_IVAL      (2500 * 1000) /* ns */

#define NFP_ABM_LVL_INFINITY            S32_MAX

struct nfp_app;
struct nfp_net;

#define NFP_ABM_PORTID_TYPE     GENMASK(23, 16)
#define NFP_ABM_PORTID_ID       GENMASK(7, 0)

/* The possible actions if thresholds are exceeded */
enum nfp_abm_q_action {
        /* mark if ECN capable, otherwise drop */
        NFP_ABM_ACT_MARK_DROP           = 0,
        /* mark if ECN capable, otherwise goto QM */
        NFP_ABM_ACT_MARK_QUEUE          = 1,
        NFP_ABM_ACT_DROP                = 2,
        NFP_ABM_ACT_QUEUE               = 3,
        NFP_ABM_ACT_NOQUEUE             = 4,
};

/**
 * struct nfp_abm - ABM NIC app structure
 * @app:        back pointer to nfp_app
 * @pf_id:      ID of our PF link
 *
 * @red_support:        is RED offload supported
 * @num_prios:  number of supported DSCP priorities
 * @num_bands:  number of supported DSCP priority bands
 * @action_mask:        bitmask of supported actions
 *
 * @thresholds:         current threshold configuration
 * @threshold_undef:    bitmap of thresholds which have not been set
 * @actions:            current FW action configuration
 * @num_thresholds:     number of @thresholds and bits in @threshold_undef
 *
 * @prio_map_len:       computed length of FW priority map (in bytes)
 * @dscp_mask:          mask FW will apply on DSCP field
 *
 * @eswitch_mode:       devlink eswitch mode, advanced functions only visible
 *                      in switchdev mode
 *
 * @q_lvls:     queue level control area
 * @qm_stats:   queue statistics symbol
 * @q_stats:    basic queue statistics (only in per-band case)
 */
struct nfp_abm {
        struct nfp_app *app;
        unsigned int pf_id;

        unsigned int red_support;
        unsigned int num_prios;
        unsigned int num_bands;
        unsigned int action_mask;

        u32 *thresholds;
        unsigned long *threshold_undef;
        u8 *actions;
        size_t num_thresholds;

        unsigned int prio_map_len;
        u8 dscp_mask;

        enum devlink_eswitch_mode eswitch_mode;

        const struct nfp_rtsym *q_lvls;
        const struct nfp_rtsym *qm_stats;
        const struct nfp_rtsym *q_stats;
};

/**
 * struct nfp_alink_stats - ABM NIC statistics
 * @tx_pkts:            number of TXed packets
 * @tx_bytes:           number of TXed bytes
 * @backlog_pkts:       momentary backlog length (packets)
 * @backlog_bytes:      momentary backlog length (bytes)
 * @overlimits:         number of ECN marked TXed packets (accumulative)
 * @drops:              number of tail-dropped packets (accumulative)
 */
struct nfp_alink_stats {
        u64 tx_pkts;
        u64 tx_bytes;
        u64 backlog_pkts;
        u64 backlog_bytes;
        u64 overlimits;
        u64 drops;
};

/**
 * struct nfp_alink_xstats - extended ABM NIC statistics
 * @ecn_marked:         number of ECN marked TXed packets
 * @pdrop:              number of hard drops due to queue limit
 */
struct nfp_alink_xstats {
        u64 ecn_marked;
        u64 pdrop;
};

enum nfp_qdisc_type {
        NFP_QDISC_NONE = 0,
        NFP_QDISC_MQ,
        NFP_QDISC_RED,
        NFP_QDISC_GRED,
};

#define NFP_QDISC_UNTRACKED     ((struct nfp_qdisc *)1UL)

/**
 * struct nfp_qdisc - tracked TC Qdisc
 * @netdev:             netdev on which Qdisc was created
 * @type:               Qdisc type
 * @handle:             handle of this Qdisc
 * @parent_handle:      handle of the parent (unreliable if Qdisc was grafted)
 * @use_cnt:            number of attachment points in the hierarchy
 * @num_children:       current size of the @children array
 * @children:           pointers to children
 *
 * @params_ok:          parameters of this Qdisc are OK for offload
 * @offload_mark:       offload refresh state - selected for offload
 * @offloaded:          Qdisc is currently offloaded to the HW
 *
 * @mq:                 MQ Qdisc specific parameters and state
 * @mq.stats:           current stats of the MQ Qdisc
 * @mq.prev_stats:      previously reported @mq.stats
 *
 * @red:                RED Qdisc specific parameters and state
 * @red.num_bands:      Number of valid entries in the @red.band table
 * @red.band:           Per-band array of RED instances
 * @red.band.ecn:               ECN marking is enabled (rather than drop)
 * @red.band.threshold:         ECN marking threshold
 * @red.band.stats:             current stats of the RED Qdisc
 * @red.band.prev_stats:        previously reported @red.stats
 * @red.band.xstats:            extended stats for RED - current
 * @red.band.prev_xstats:       extended stats for RED - previously reported
 */
struct nfp_qdisc {
        struct net_device *netdev;
        enum nfp_qdisc_type type;
        u32 handle;
        u32 parent_handle;
        unsigned int use_cnt;
        unsigned int num_children;
        struct nfp_qdisc **children;

        bool params_ok;
        bool offload_mark;
        bool offloaded;

        union {
                /* NFP_QDISC_MQ */
                struct {
                        struct nfp_alink_stats stats;
                        struct nfp_alink_stats prev_stats;
                } mq;
                /* TC_SETUP_QDISC_RED, TC_SETUP_QDISC_GRED */
                struct {
                        unsigned int num_bands;

                        struct {
                                bool ecn;
                                u32 threshold;
                                struct nfp_alink_stats stats;
                                struct nfp_alink_stats prev_stats;
                                struct nfp_alink_xstats xstats;
                                struct nfp_alink_xstats prev_xstats;
                        } band[MAX_DPs];
                } red;
        };
};

/**
 * struct nfp_abm_link - port tuple of a ABM NIC
 * @abm:        back pointer to nfp_abm
 * @vnic:       data vNIC
 * @id:         id of the data vNIC
 * @queue_base: id of base to host queue within PCIe (not QC idx)
 * @total_queues:       number of PF queues
 *
 * @last_stats_update:  ktime of last stats update
 *
 * @prio_map:           current map of priorities
 * @has_prio:           @prio_map is valid
 *
 * @def_band:           default band to use
 * @dscp_map:           list of DSCP to band mappings
 *
 * @root_qdisc: pointer to the current root of the Qdisc hierarchy
 * @qdiscs:     all qdiscs recorded by major part of the handle
 */
struct nfp_abm_link {
        struct nfp_abm *abm;
        struct nfp_net *vnic;
        unsigned int id;
        unsigned int queue_base;
        unsigned int total_queues;

        u64 last_stats_update;

        u32 *prio_map;
        bool has_prio;

        u8 def_band;
        struct list_head dscp_map;

        struct nfp_qdisc *root_qdisc;
        struct radix_tree_root qdiscs;
};

static inline bool nfp_abm_has_prio(struct nfp_abm *abm)
{
        return abm->num_bands > 1;
}

static inline bool nfp_abm_has_drop(struct nfp_abm *abm)
{
        return abm->action_mask & BIT(NFP_ABM_ACT_DROP);
}

static inline bool nfp_abm_has_mark(struct nfp_abm *abm)
{
        return abm->action_mask & BIT(NFP_ABM_ACT_MARK_DROP);
}

void nfp_abm_qdisc_offload_update(struct nfp_abm_link *alink);
int nfp_abm_setup_root(struct net_device *netdev, struct nfp_abm_link *alink,
                       struct tc_root_qopt_offload *opt);
int nfp_abm_setup_tc_red(struct net_device *netdev, struct nfp_abm_link *alink,
                         struct tc_red_qopt_offload *opt);
int nfp_abm_setup_tc_mq(struct net_device *netdev, struct nfp_abm_link *alink,
                        struct tc_mq_qopt_offload *opt);
int nfp_abm_setup_tc_gred(struct net_device *netdev, struct nfp_abm_link *alink,
                          struct tc_gred_qopt_offload *opt);
int nfp_abm_setup_cls_block(struct net_device *netdev, struct nfp_repr *repr,
                            struct flow_block_offload *opt);

int nfp_abm_ctrl_read_params(struct nfp_abm_link *alink);
int nfp_abm_ctrl_find_addrs(struct nfp_abm *abm);
int __nfp_abm_ctrl_set_q_lvl(struct nfp_abm *abm, unsigned int id, u32 val);
int nfp_abm_ctrl_set_q_lvl(struct nfp_abm_link *alink, unsigned int band,
                           unsigned int queue, u32 val);
int __nfp_abm_ctrl_set_q_act(struct nfp_abm *abm, unsigned int id,
                             enum nfp_abm_q_action act);
int nfp_abm_ctrl_set_q_act(struct nfp_abm_link *alink, unsigned int band,
                           unsigned int queue, enum nfp_abm_q_action act);
int nfp_abm_ctrl_read_q_stats(struct nfp_abm_link *alink,
                              unsigned int band, unsigned int queue,
                              struct nfp_alink_stats *stats);
int nfp_abm_ctrl_read_q_xstats(struct nfp_abm_link *alink,
                               unsigned int band, unsigned int queue,
                               struct nfp_alink_xstats *xstats);
u64 nfp_abm_ctrl_stat_non_sto(struct nfp_abm_link *alink, unsigned int i);
u64 nfp_abm_ctrl_stat_sto(struct nfp_abm_link *alink, unsigned int i);
int nfp_abm_ctrl_qm_enable(struct nfp_abm *abm);
int nfp_abm_ctrl_qm_disable(struct nfp_abm *abm);
void nfp_abm_prio_map_update(struct nfp_abm *abm);
int nfp_abm_ctrl_prio_map_update(struct nfp_abm_link *alink, u32 *packed);
#endif