|
|
@@ -325,6 +325,16 @@ static struct mvpp2_cls_flow cls_flows[MVPP2_N_FLOWS] = {
|
|
|
0, 0),
|
|
|
};
|
|
|
|
|
|
+static void mvpp2_cls_flow_read(struct mvpp2 *priv, int index,
|
|
|
+ struct mvpp2_cls_flow_entry *fe)
|
|
|
+{
|
|
|
+ fe->index = index;
|
|
|
+ mvpp2_write(priv, MVPP2_CLS_FLOW_INDEX_REG, index);
|
|
|
+ fe->data[0] = mvpp2_read(priv, MVPP2_CLS_FLOW_TBL0_REG);
|
|
|
+ fe->data[1] = mvpp2_read(priv, MVPP2_CLS_FLOW_TBL1_REG);
|
|
|
+ fe->data[2] = mvpp2_read(priv, MVPP2_CLS_FLOW_TBL2_REG);
|
|
|
+}
|
|
|
+
|
|
|
/* Update classification flow table registers */
|
|
|
static void mvpp2_cls_flow_write(struct mvpp2 *priv,
|
|
|
struct mvpp2_cls_flow_entry *fe)
|
|
|
@@ -346,6 +356,34 @@ static void mvpp2_cls_lookup_write(struct mvpp2 *priv,
|
|
|
mvpp2_write(priv, MVPP2_CLS_LKP_TBL_REG, le->data);
|
|
|
}
|
|
|
|
|
|
+/* Operations on flow entry */
|
|
|
+static int mvpp2_cls_flow_hek_num_get(struct mvpp2_cls_flow_entry *fe)
|
|
|
+{
|
|
|
+ return fe->data[1] & MVPP2_CLS_FLOW_TBL1_N_FIELDS_MASK;
|
|
|
+}
|
|
|
+
|
|
|
+static void mvpp2_cls_flow_hek_num_set(struct mvpp2_cls_flow_entry *fe,
|
|
|
+ int num_of_fields)
|
|
|
+{
|
|
|
+ fe->data[1] &= ~MVPP2_CLS_FLOW_TBL1_N_FIELDS_MASK;
|
|
|
+ fe->data[1] |= MVPP2_CLS_FLOW_TBL1_N_FIELDS(num_of_fields);
|
|
|
+}
|
|
|
+
|
|
|
+static int mvpp2_cls_flow_hek_get(struct mvpp2_cls_flow_entry *fe,
|
|
|
+ int field_index)
|
|
|
+{
|
|
|
+ return (fe->data[2] >> MVPP2_CLS_FLOW_TBL2_FLD_OFFS(field_index)) &
|
|
|
+ MVPP2_CLS_FLOW_TBL2_FLD_MASK;
|
|
|
+}
|
|
|
+
|
|
|
+static void mvpp2_cls_flow_hek_set(struct mvpp2_cls_flow_entry *fe,
|
|
|
+ int field_index, int field_id)
|
|
|
+{
|
|
|
+ fe->data[2] &= ~MVPP2_CLS_FLOW_TBL2_FLD(field_index,
|
|
|
+ MVPP2_CLS_FLOW_TBL2_FLD_MASK);
|
|
|
+ fe->data[2] |= MVPP2_CLS_FLOW_TBL2_FLD(field_index, field_id);
|
|
|
+}
|
|
|
+
|
|
|
static void mvpp2_cls_flow_eng_set(struct mvpp2_cls_flow_entry *fe,
|
|
|
int engine)
|
|
|
{
|
|
|
@@ -430,15 +468,93 @@ static void mvpp2_cls_flow_init(struct mvpp2 *priv, struct mvpp2_cls_flow *flow)
|
|
|
|
|
|
mvpp2_cls_flow_eng_set(&fe, MVPP22_CLS_ENGINE_C2);
|
|
|
mvpp2_cls_flow_port_id_sel(&fe, true);
|
|
|
- mvpp2_cls_flow_last_set(&fe, 1);
|
|
|
+ mvpp2_cls_flow_last_set(&fe, 0);
|
|
|
mvpp2_cls_flow_pri_set(&fe, 0);
|
|
|
- mvpp2_cls_flow_seq_set(&fe, MVPP2_CLS_FLOW_SEQ_LAST);
|
|
|
+ mvpp2_cls_flow_seq_set(&fe, MVPP2_CLS_FLOW_SEQ_FIRST1);
|
|
|
|
|
|
/* Add all ports */
|
|
|
for (i = 0; i < MVPP2_MAX_PORTS; i++)
|
|
|
mvpp2_cls_flow_port_add(&fe, BIT(i));
|
|
|
|
|
|
mvpp2_cls_flow_write(priv, &fe);
|
|
|
+
|
|
|
+ /* C3Hx lookups */
|
|
|
+ for (i = 0; i < MVPP2_MAX_PORTS; i++) {
|
|
|
+ memset(&fe, 0, sizeof(fe));
|
|
|
+ fe.index = MVPP2_PORT_FLOW_HASH_ENTRY(i, flow->flow_id);
|
|
|
+
|
|
|
+ mvpp2_cls_flow_port_id_sel(&fe, true);
|
|
|
+ mvpp2_cls_flow_pri_set(&fe, i + 1);
|
|
|
+ mvpp2_cls_flow_seq_set(&fe, MVPP2_CLS_FLOW_SEQ_MIDDLE);
|
|
|
+ mvpp2_cls_flow_port_add(&fe, BIT(i));
|
|
|
+
|
|
|
+ mvpp2_cls_flow_write(priv, &fe);
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Update the last entry */
|
|
|
+ mvpp2_cls_flow_last_set(&fe, 1);
|
|
|
+ mvpp2_cls_flow_seq_set(&fe, MVPP2_CLS_FLOW_SEQ_LAST);
|
|
|
+
|
|
|
+ mvpp2_cls_flow_write(priv, &fe);
|
|
|
+}
|
|
|
+
|
|
|
+/* Adds a field to the Header Extracted Key generation parameters*/
|
|
|
+static int mvpp2_flow_add_hek_field(struct mvpp2_cls_flow_entry *fe,
|
|
|
+ u32 field_id)
|
|
|
+{
|
|
|
+ int nb_fields = mvpp2_cls_flow_hek_num_get(fe);
|
|
|
+
|
|
|
+ if (nb_fields == MVPP2_FLOW_N_FIELDS)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ mvpp2_cls_flow_hek_set(fe, nb_fields, field_id);
|
|
|
+
|
|
|
+ mvpp2_cls_flow_hek_num_set(fe, nb_fields + 1);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int mvpp2_flow_set_hek_fields(struct mvpp2_cls_flow_entry *fe,
|
|
|
+ unsigned long hash_opts)
|
|
|
+{
|
|
|
+ u32 field_id;
|
|
|
+ int i;
|
|
|
+
|
|
|
+ /* Clear old fields */
|
|
|
+ mvpp2_cls_flow_hek_num_set(fe, 0);
|
|
|
+ fe->data[2] = 0;
|
|
|
+
|
|
|
+ for_each_set_bit(i, &hash_opts, MVPP22_CLS_HEK_N_FIELDS) {
|
|
|
+ switch (BIT(i)) {
|
|
|
+ case MVPP22_CLS_HEK_OPT_VLAN:
|
|
|
+ field_id = MVPP22_CLS_FIELD_VLAN;
|
|
|
+ break;
|
|
|
+ case MVPP22_CLS_HEK_OPT_IP4SA:
|
|
|
+ field_id = MVPP22_CLS_FIELD_IP4SA;
|
|
|
+ break;
|
|
|
+ case MVPP22_CLS_HEK_OPT_IP4DA:
|
|
|
+ field_id = MVPP22_CLS_FIELD_IP4DA;
|
|
|
+ break;
|
|
|
+ case MVPP22_CLS_HEK_OPT_IP6SA:
|
|
|
+ field_id = MVPP22_CLS_FIELD_IP6SA;
|
|
|
+ break;
|
|
|
+ case MVPP22_CLS_HEK_OPT_IP6DA:
|
|
|
+ field_id = MVPP22_CLS_FIELD_IP6DA;
|
|
|
+ break;
|
|
|
+ case MVPP22_CLS_HEK_OPT_L4SIP:
|
|
|
+ field_id = MVPP22_CLS_FIELD_L4SIP;
|
|
|
+ break;
|
|
|
+ case MVPP22_CLS_HEK_OPT_L4DIP:
|
|
|
+ field_id = MVPP22_CLS_FIELD_L4DIP;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+ if (mvpp2_flow_add_hek_field(fe, field_id))
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
struct mvpp2_cls_flow *mvpp2_cls_flow_get(int flow)
|
|
|
@@ -449,6 +565,104 @@ struct mvpp2_cls_flow *mvpp2_cls_flow_get(int flow)
|
|
|
return &cls_flows[flow];
|
|
|
}
|
|
|
|
|
|
+/* Set the hash generation options for the given traffic flow.
|
|
|
+ * One traffic flow (in the ethtool sense) has multiple classification flows,
|
|
|
+ * to handle specific cases such as fragmentation, or the presence of a
|
|
|
+ * VLAN / DSA Tag.
|
|
|
+ *
|
|
|
+ * Each of these individual flows has different constraints, for example we
|
|
|
+ * can't hash fragmented packets on L4 data (else we would risk having packet
|
|
|
+ * re-ordering), so each classification flows masks the options with their
|
|
|
+ * supported ones.
|
|
|
+ *
|
|
|
+ */
|
|
|
+static int mvpp2_port_rss_hash_opts_set(struct mvpp2_port *port, int flow_type,
|
|
|
+ u16 requested_opts)
|
|
|
+{
|
|
|
+ struct mvpp2_cls_flow_entry fe;
|
|
|
+ struct mvpp2_cls_flow *flow;
|
|
|
+ int i, engine, flow_index;
|
|
|
+ u16 hash_opts;
|
|
|
+
|
|
|
+ for (i = 0; i < MVPP2_N_FLOWS; i++) {
|
|
|
+ flow = mvpp2_cls_flow_get(i);
|
|
|
+ if (!flow)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ if (flow->flow_type != flow_type)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ flow_index = MVPP2_PORT_FLOW_HASH_ENTRY(port->id,
|
|
|
+ flow->flow_id);
|
|
|
+
|
|
|
+ mvpp2_cls_flow_read(port->priv, flow_index, &fe);
|
|
|
+
|
|
|
+ hash_opts = flow->supported_hash_opts & requested_opts;
|
|
|
+
|
|
|
+ /* Use C3HB engine to access L4 infos. This adds L4 infos to the
|
|
|
+ * hash parameters
|
|
|
+ */
|
|
|
+ if (hash_opts & MVPP22_CLS_HEK_L4_OPTS)
|
|
|
+ engine = MVPP22_CLS_ENGINE_C3HB;
|
|
|
+ else
|
|
|
+ engine = MVPP22_CLS_ENGINE_C3HA;
|
|
|
+
|
|
|
+ if (mvpp2_flow_set_hek_fields(&fe, hash_opts))
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ mvpp2_cls_flow_eng_set(&fe, engine);
|
|
|
+
|
|
|
+ mvpp2_cls_flow_write(port->priv, &fe);
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+u16 mvpp2_flow_get_hek_fields(struct mvpp2_cls_flow_entry *fe)
|
|
|
+{
|
|
|
+ u16 hash_opts = 0;
|
|
|
+ int n_fields, i, field;
|
|
|
+
|
|
|
+ n_fields = mvpp2_cls_flow_hek_num_get(fe);
|
|
|
+
|
|
|
+ for (i = 0; i < n_fields; i++) {
|
|
|
+ field = mvpp2_cls_flow_hek_get(fe, i);
|
|
|
+
|
|
|
+ switch (field) {
|
|
|
+ case MVPP22_CLS_FIELD_MAC_DA:
|
|
|
+ hash_opts |= MVPP22_CLS_HEK_OPT_MAC_DA;
|
|
|
+ break;
|
|
|
+ case MVPP22_CLS_FIELD_VLAN:
|
|
|
+ hash_opts |= MVPP22_CLS_HEK_OPT_VLAN;
|
|
|
+ break;
|
|
|
+ case MVPP22_CLS_FIELD_L3_PROTO:
|
|
|
+ hash_opts |= MVPP22_CLS_HEK_OPT_L3_PROTO;
|
|
|
+ break;
|
|
|
+ case MVPP22_CLS_FIELD_IP4SA:
|
|
|
+ hash_opts |= MVPP22_CLS_HEK_OPT_IP4SA;
|
|
|
+ break;
|
|
|
+ case MVPP22_CLS_FIELD_IP4DA:
|
|
|
+ hash_opts |= MVPP22_CLS_HEK_OPT_IP4DA;
|
|
|
+ break;
|
|
|
+ case MVPP22_CLS_FIELD_IP6SA:
|
|
|
+ hash_opts |= MVPP22_CLS_HEK_OPT_IP6SA;
|
|
|
+ break;
|
|
|
+ case MVPP22_CLS_FIELD_IP6DA:
|
|
|
+ hash_opts |= MVPP22_CLS_HEK_OPT_IP6DA;
|
|
|
+ break;
|
|
|
+ case MVPP22_CLS_FIELD_L4SIP:
|
|
|
+ hash_opts |= MVPP22_CLS_HEK_OPT_L4SIP;
|
|
|
+ break;
|
|
|
+ case MVPP22_CLS_FIELD_L4DIP:
|
|
|
+ hash_opts |= MVPP22_CLS_HEK_OPT_L4DIP;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return hash_opts;
|
|
|
+}
|
|
|
+
|
|
|
static void mvpp2_cls_port_init_flows(struct mvpp2 *priv)
|
|
|
{
|
|
|
struct mvpp2_cls_flow *flow;
|
|
|
@@ -485,6 +699,27 @@ static void mvpp2_cls_c2_write(struct mvpp2 *priv,
|
|
|
mvpp2_write(priv, MVPP22_CLS_C2_ATTR3, c2->attr[3]);
|
|
|
}
|
|
|
|
|
|
+static void mvpp2_cls_c2_read(struct mvpp2 *priv, int index,
|
|
|
+ struct mvpp2_cls_c2_entry *c2)
|
|
|
+{
|
|
|
+ mvpp2_write(priv, MVPP22_CLS_C2_TCAM_IDX, index);
|
|
|
+
|
|
|
+ c2->index = index;
|
|
|
+
|
|
|
+ c2->tcam[0] = mvpp2_read(priv, MVPP22_CLS_C2_TCAM_DATA0);
|
|
|
+ c2->tcam[1] = mvpp2_read(priv, MVPP22_CLS_C2_TCAM_DATA1);
|
|
|
+ c2->tcam[2] = mvpp2_read(priv, MVPP22_CLS_C2_TCAM_DATA2);
|
|
|
+ c2->tcam[3] = mvpp2_read(priv, MVPP22_CLS_C2_TCAM_DATA3);
|
|
|
+ c2->tcam[4] = mvpp2_read(priv, MVPP22_CLS_C2_TCAM_DATA4);
|
|
|
+
|
|
|
+ c2->act = mvpp2_read(priv, MVPP22_CLS_C2_ACT);
|
|
|
+
|
|
|
+ c2->attr[0] = mvpp2_read(priv, MVPP22_CLS_C2_ATTR0);
|
|
|
+ c2->attr[1] = mvpp2_read(priv, MVPP22_CLS_C2_ATTR1);
|
|
|
+ c2->attr[2] = mvpp2_read(priv, MVPP22_CLS_C2_ATTR2);
|
|
|
+ c2->attr[3] = mvpp2_read(priv, MVPP22_CLS_C2_ATTR3);
|
|
|
+}
|
|
|
+
|
|
|
static void mvpp2_port_c2_cls_init(struct mvpp2_port *port)
|
|
|
{
|
|
|
struct mvpp2_cls_c2_entry c2;
|
|
|
@@ -580,6 +815,38 @@ void mvpp2_cls_port_config(struct mvpp2_port *port)
|
|
|
mvpp2_port_c2_cls_init(port);
|
|
|
}
|
|
|
|
|
|
+static void mvpp2_rss_port_c2_enable(struct mvpp2_port *port)
|
|
|
+{
|
|
|
+ struct mvpp2_cls_c2_entry c2;
|
|
|
+
|
|
|
+ mvpp2_cls_c2_read(port->priv, MVPP22_CLS_C2_RSS_ENTRY(port->id), &c2);
|
|
|
+
|
|
|
+ c2.attr[2] |= MVPP22_CLS_C2_ATTR2_RSS_EN;
|
|
|
+
|
|
|
+ mvpp2_cls_c2_write(port->priv, &c2);
|
|
|
+}
|
|
|
+
|
|
|
+static void mvpp2_rss_port_c2_disable(struct mvpp2_port *port)
|
|
|
+{
|
|
|
+ struct mvpp2_cls_c2_entry c2;
|
|
|
+
|
|
|
+ mvpp2_cls_c2_read(port->priv, MVPP22_CLS_C2_RSS_ENTRY(port->id), &c2);
|
|
|
+
|
|
|
+ c2.attr[2] &= ~MVPP22_CLS_C2_ATTR2_RSS_EN;
|
|
|
+
|
|
|
+ mvpp2_cls_c2_write(port->priv, &c2);
|
|
|
+}
|
|
|
+
|
|
|
+void mvpp22_rss_enable(struct mvpp2_port *port)
|
|
|
+{
|
|
|
+ mvpp2_rss_port_c2_enable(port);
|
|
|
+}
|
|
|
+
|
|
|
+void mvpp22_rss_disable(struct mvpp2_port *port)
|
|
|
+{
|
|
|
+ mvpp2_rss_port_c2_disable(port);
|
|
|
+}
|
|
|
+
|
|
|
/* Set CPU queue number for oversize packets */
|
|
|
void mvpp2_cls_oversize_rxq_set(struct mvpp2_port *port)
|
|
|
{
|
|
|
@@ -656,4 +923,12 @@ void mvpp22_rss_port_init(struct mvpp2_port *port)
|
|
|
port->indir[i] = ethtool_rxfh_indir_default(i, port->nrxqs);
|
|
|
|
|
|
mvpp22_rss_fill_table(port, port->id);
|
|
|
+
|
|
|
+ /* Configure default flows */
|
|
|
+ mvpp2_port_rss_hash_opts_set(port, IPV4_FLOW, MVPP22_CLS_HEK_IP4_2T);
|
|
|
+ mvpp2_port_rss_hash_opts_set(port, IPV6_FLOW, MVPP22_CLS_HEK_IP6_2T);
|
|
|
+ mvpp2_port_rss_hash_opts_set(port, TCP_V4_FLOW, MVPP22_CLS_HEK_IP4_5T);
|
|
|
+ mvpp2_port_rss_hash_opts_set(port, TCP_V6_FLOW, MVPP22_CLS_HEK_IP6_5T);
|
|
|
+ mvpp2_port_rss_hash_opts_set(port, UDP_V4_FLOW, MVPP22_CLS_HEK_IP4_5T);
|
|
|
+ mvpp2_port_rss_hash_opts_set(port, UDP_V6_FLOW, MVPP22_CLS_HEK_IP6_5T);
|
|
|
}
|