|
@@ -645,6 +645,241 @@ static int gfar_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
|
|
|
}
|
|
|
#endif
|
|
|
|
|
|
+static int gfar_ethflow_to_class(int flow_type, u64 *class)
|
|
|
+{
|
|
|
+ switch (flow_type) {
|
|
|
+ case TCP_V4_FLOW:
|
|
|
+ *class = CLASS_CODE_TCP_IPV4;
|
|
|
+ break;
|
|
|
+ case UDP_V4_FLOW:
|
|
|
+ *class = CLASS_CODE_UDP_IPV4;
|
|
|
+ break;
|
|
|
+ case AH_V4_FLOW:
|
|
|
+ case ESP_V4_FLOW:
|
|
|
+ *class = CLASS_CODE_AH_ESP_IPV4;
|
|
|
+ break;
|
|
|
+ case SCTP_V4_FLOW:
|
|
|
+ *class = CLASS_CODE_SCTP_IPV4;
|
|
|
+ break;
|
|
|
+ case TCP_V6_FLOW:
|
|
|
+ *class = CLASS_CODE_TCP_IPV6;
|
|
|
+ break;
|
|
|
+ case UDP_V6_FLOW:
|
|
|
+ *class = CLASS_CODE_UDP_IPV6;
|
|
|
+ break;
|
|
|
+ case AH_V6_FLOW:
|
|
|
+ case ESP_V6_FLOW:
|
|
|
+ *class = CLASS_CODE_AH_ESP_IPV6;
|
|
|
+ break;
|
|
|
+ case SCTP_V6_FLOW:
|
|
|
+ *class = CLASS_CODE_SCTP_IPV6;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 1;
|
|
|
+}
|
|
|
+
|
|
|
+static void ethflow_to_filer_rules (struct gfar_private *priv, u64 ethflow)
|
|
|
+{
|
|
|
+ u32 fcr = 0x0, fpr = FPR_FILER_MASK;
|
|
|
+
|
|
|
+ if (ethflow & RXH_L2DA) {
|
|
|
+ fcr = RQFCR_PID_DAH |RQFCR_CMP_NOMATCH |
|
|
|
+ RQFCR_HASH | RQFCR_AND | RQFCR_HASHTBL_0;
|
|
|
+ ftp_rqfpr[priv->cur_filer_idx] = fpr;
|
|
|
+ ftp_rqfcr[priv->cur_filer_idx] = fcr;
|
|
|
+ gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr);
|
|
|
+ priv->cur_filer_idx = priv->cur_filer_idx - 1;
|
|
|
+
|
|
|
+ fcr = RQFCR_PID_DAL | RQFCR_AND | RQFCR_CMP_NOMATCH |
|
|
|
+ RQFCR_HASH | RQFCR_AND | RQFCR_HASHTBL_0;
|
|
|
+ ftp_rqfpr[priv->cur_filer_idx] = fpr;
|
|
|
+ ftp_rqfcr[priv->cur_filer_idx] = fcr;
|
|
|
+ gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr);
|
|
|
+ priv->cur_filer_idx = priv->cur_filer_idx - 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (ethflow & RXH_VLAN) {
|
|
|
+ fcr = RQFCR_PID_VID | RQFCR_CMP_NOMATCH | RQFCR_HASH |
|
|
|
+ RQFCR_AND | RQFCR_HASHTBL_0;
|
|
|
+ gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr);
|
|
|
+ ftp_rqfpr[priv->cur_filer_idx] = fpr;
|
|
|
+ ftp_rqfcr[priv->cur_filer_idx] = fcr;
|
|
|
+ priv->cur_filer_idx = priv->cur_filer_idx - 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (ethflow & RXH_IP_SRC) {
|
|
|
+ fcr = RQFCR_PID_SIA | RQFCR_CMP_NOMATCH | RQFCR_HASH |
|
|
|
+ RQFCR_AND | RQFCR_HASHTBL_0;
|
|
|
+ ftp_rqfpr[priv->cur_filer_idx] = fpr;
|
|
|
+ ftp_rqfcr[priv->cur_filer_idx] = fcr;
|
|
|
+ gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr);
|
|
|
+ priv->cur_filer_idx = priv->cur_filer_idx - 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (ethflow & (RXH_IP_DST)) {
|
|
|
+ fcr = RQFCR_PID_DIA | RQFCR_CMP_NOMATCH | RQFCR_HASH |
|
|
|
+ RQFCR_AND | RQFCR_HASHTBL_0;
|
|
|
+ ftp_rqfpr[priv->cur_filer_idx] = fpr;
|
|
|
+ ftp_rqfcr[priv->cur_filer_idx] = fcr;
|
|
|
+ gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr);
|
|
|
+ priv->cur_filer_idx = priv->cur_filer_idx - 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (ethflow & RXH_L3_PROTO) {
|
|
|
+ fcr = RQFCR_PID_L4P | RQFCR_CMP_NOMATCH | RQFCR_HASH |
|
|
|
+ RQFCR_AND | RQFCR_HASHTBL_0;
|
|
|
+ ftp_rqfpr[priv->cur_filer_idx] = fpr;
|
|
|
+ ftp_rqfcr[priv->cur_filer_idx] = fcr;
|
|
|
+ gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr);
|
|
|
+ priv->cur_filer_idx = priv->cur_filer_idx - 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (ethflow & RXH_L4_B_0_1) {
|
|
|
+ fcr = RQFCR_PID_SPT | RQFCR_CMP_NOMATCH | RQFCR_HASH |
|
|
|
+ RQFCR_AND | RQFCR_HASHTBL_0;
|
|
|
+ ftp_rqfpr[priv->cur_filer_idx] = fpr;
|
|
|
+ ftp_rqfcr[priv->cur_filer_idx] = fcr;
|
|
|
+ gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr);
|
|
|
+ priv->cur_filer_idx = priv->cur_filer_idx - 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (ethflow & RXH_L4_B_2_3) {
|
|
|
+ fcr = RQFCR_PID_DPT | RQFCR_CMP_NOMATCH | RQFCR_HASH |
|
|
|
+ RQFCR_AND | RQFCR_HASHTBL_0;
|
|
|
+ ftp_rqfpr[priv->cur_filer_idx] = fpr;
|
|
|
+ ftp_rqfcr[priv->cur_filer_idx] = fcr;
|
|
|
+ gfar_write_filer(priv, priv->cur_filer_idx, fcr, fpr);
|
|
|
+ priv->cur_filer_idx = priv->cur_filer_idx - 1;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static int gfar_ethflow_to_filer_table(struct gfar_private *priv, u64 ethflow, u64 class)
|
|
|
+{
|
|
|
+ unsigned int last_rule_idx = priv->cur_filer_idx;
|
|
|
+ unsigned int cmp_rqfpr;
|
|
|
+ unsigned int local_rqfpr[MAX_FILER_IDX + 1];
|
|
|
+ unsigned int local_rqfcr[MAX_FILER_IDX + 1];
|
|
|
+ int i = 0x0, k = 0x0;
|
|
|
+ int j = MAX_FILER_IDX, l = 0x0;
|
|
|
+
|
|
|
+ switch (class) {
|
|
|
+ case TCP_V4_FLOW:
|
|
|
+ cmp_rqfpr = RQFPR_IPV4 |RQFPR_TCP;
|
|
|
+ break;
|
|
|
+ case UDP_V4_FLOW:
|
|
|
+ cmp_rqfpr = RQFPR_IPV4 |RQFPR_UDP;
|
|
|
+ break;
|
|
|
+ case TCP_V6_FLOW:
|
|
|
+ cmp_rqfpr = RQFPR_IPV6 |RQFPR_TCP;
|
|
|
+ break;
|
|
|
+ case UDP_V6_FLOW:
|
|
|
+ cmp_rqfpr = RQFPR_IPV6 |RQFPR_UDP;
|
|
|
+ break;
|
|
|
+ case IPV4_FLOW:
|
|
|
+ cmp_rqfpr = RQFPR_IPV4;
|
|
|
+ case IPV6_FLOW:
|
|
|
+ cmp_rqfpr = RQFPR_IPV6;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ printk(KERN_ERR "Right now this class is not supported\n");
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ for (i = 0; i < MAX_FILER_IDX + 1; i++) {
|
|
|
+ local_rqfpr[j] = ftp_rqfpr[i];
|
|
|
+ local_rqfcr[j] = ftp_rqfcr[i];
|
|
|
+ j--;
|
|
|
+ if ((ftp_rqfcr[i] == (RQFCR_PID_PARSE |
|
|
|
+ RQFCR_CLE |RQFCR_AND)) &&
|
|
|
+ (ftp_rqfpr[i] == cmp_rqfpr))
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (i == MAX_FILER_IDX + 1) {
|
|
|
+ printk(KERN_ERR "No parse rule found, ");
|
|
|
+ printk(KERN_ERR "can't create hash rules\n");
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* If a match was found, then it begins the starting of a cluster rule
|
|
|
+ * if it was already programmed, we need to overwrite these rules
|
|
|
+ */
|
|
|
+ for (l = i+1; l < MAX_FILER_IDX; l++) {
|
|
|
+ if ((ftp_rqfcr[l] & RQFCR_CLE) &&
|
|
|
+ !(ftp_rqfcr[l] & RQFCR_AND)) {
|
|
|
+ ftp_rqfcr[l] = RQFCR_CLE | RQFCR_CMP_EXACT |
|
|
|
+ RQFCR_HASHTBL_0 | RQFCR_PID_MASK;
|
|
|
+ ftp_rqfpr[l] = FPR_FILER_MASK;
|
|
|
+ gfar_write_filer(priv, l, ftp_rqfcr[l], ftp_rqfpr[l]);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!(ftp_rqfcr[l] & RQFCR_CLE) && (ftp_rqfcr[l] & RQFCR_AND))
|
|
|
+ continue;
|
|
|
+ else {
|
|
|
+ local_rqfpr[j] = ftp_rqfpr[l];
|
|
|
+ local_rqfcr[j] = ftp_rqfcr[l];
|
|
|
+ j--;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ priv->cur_filer_idx = l - 1;
|
|
|
+ last_rule_idx = l;
|
|
|
+
|
|
|
+ /* hash rules */
|
|
|
+ ethflow_to_filer_rules(priv, ethflow);
|
|
|
+
|
|
|
+ /* Write back the popped out rules again */
|
|
|
+ for (k = j+1; k < MAX_FILER_IDX; k++) {
|
|
|
+ ftp_rqfpr[priv->cur_filer_idx] = local_rqfpr[k];
|
|
|
+ ftp_rqfcr[priv->cur_filer_idx] = local_rqfcr[k];
|
|
|
+ gfar_write_filer(priv, priv->cur_filer_idx,
|
|
|
+ local_rqfcr[k], local_rqfpr[k]);
|
|
|
+ if (!priv->cur_filer_idx)
|
|
|
+ break;
|
|
|
+ priv->cur_filer_idx = priv->cur_filer_idx - 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 1;
|
|
|
+}
|
|
|
+
|
|
|
+static int gfar_set_hash_opts(struct gfar_private *priv, struct ethtool_rxnfc *cmd)
|
|
|
+{
|
|
|
+ u64 class;
|
|
|
+
|
|
|
+ if (!gfar_ethflow_to_class(cmd->flow_type, &class))
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ if (class < CLASS_CODE_USER_PROG1 ||
|
|
|
+ class > CLASS_CODE_SCTP_IPV6)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ /* write the filer rules here */
|
|
|
+ if (!gfar_ethflow_to_filer_table(priv, cmd->data, cmd->flow_type))
|
|
|
+ return -1;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int gfar_set_nfc(struct net_device *dev, struct ethtool_rxnfc *cmd)
|
|
|
+{
|
|
|
+ struct gfar_private *priv = netdev_priv(dev);
|
|
|
+ int ret = 0;
|
|
|
+
|
|
|
+ switch(cmd->cmd) {
|
|
|
+ case ETHTOOL_SRXFH:
|
|
|
+ ret = gfar_set_hash_opts(priv, cmd);
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ ret = -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
const struct ethtool_ops gfar_ethtool_ops = {
|
|
|
.get_settings = gfar_gsettings,
|
|
|
.set_settings = gfar_ssettings,
|
|
@@ -670,4 +905,5 @@ const struct ethtool_ops gfar_ethtool_ops = {
|
|
|
.get_wol = gfar_get_wol,
|
|
|
.set_wol = gfar_set_wol,
|
|
|
#endif
|
|
|
+ .set_rxnfc = gfar_set_nfc,
|
|
|
};
|