123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307 |
- // SPDX-License-Identifier: GPL-2.0
- /* Copyright (C) 2022 Gerhard Engleder <[email protected]> */
- #include "tsnep.h"
- #define ETHER_TYPE_FULL_MASK ((__force __be16)~0)
- static void tsnep_enable_rule(struct tsnep_adapter *adapter,
- struct tsnep_rxnfc_rule *rule)
- {
- u8 rx_assign;
- void __iomem *addr;
- rx_assign = TSNEP_RX_ASSIGN_ACTIVE;
- rx_assign |= (rule->queue_index << TSNEP_RX_ASSIGN_QUEUE_SHIFT) &
- TSNEP_RX_ASSIGN_QUEUE_MASK;
- addr = adapter->addr + TSNEP_RX_ASSIGN_ETHER_TYPE +
- TSNEP_RX_ASSIGN_ETHER_TYPE_OFFSET * rule->location;
- iowrite16(rule->filter.ether_type, addr);
- /* enable rule after all settings are done */
- addr = adapter->addr + TSNEP_RX_ASSIGN +
- TSNEP_RX_ASSIGN_OFFSET * rule->location;
- iowrite8(rx_assign, addr);
- }
- static void tsnep_disable_rule(struct tsnep_adapter *adapter,
- struct tsnep_rxnfc_rule *rule)
- {
- void __iomem *addr;
- addr = adapter->addr + TSNEP_RX_ASSIGN +
- TSNEP_RX_ASSIGN_OFFSET * rule->location;
- iowrite8(0, addr);
- }
- static struct tsnep_rxnfc_rule *tsnep_get_rule(struct tsnep_adapter *adapter,
- int location)
- {
- struct tsnep_rxnfc_rule *rule;
- list_for_each_entry(rule, &adapter->rxnfc_rules, list) {
- if (rule->location == location)
- return rule;
- if (rule->location > location)
- break;
- }
- return NULL;
- }
- static void tsnep_add_rule(struct tsnep_adapter *adapter,
- struct tsnep_rxnfc_rule *rule)
- {
- struct tsnep_rxnfc_rule *pred, *cur;
- tsnep_enable_rule(adapter, rule);
- pred = NULL;
- list_for_each_entry(cur, &adapter->rxnfc_rules, list) {
- if (cur->location >= rule->location)
- break;
- pred = cur;
- }
- list_add(&rule->list, pred ? &pred->list : &adapter->rxnfc_rules);
- adapter->rxnfc_count++;
- }
- static void tsnep_delete_rule(struct tsnep_adapter *adapter,
- struct tsnep_rxnfc_rule *rule)
- {
- tsnep_disable_rule(adapter, rule);
- list_del(&rule->list);
- adapter->rxnfc_count--;
- kfree(rule);
- }
- static void tsnep_flush_rules(struct tsnep_adapter *adapter)
- {
- struct tsnep_rxnfc_rule *rule, *tmp;
- mutex_lock(&adapter->rxnfc_lock);
- list_for_each_entry_safe(rule, tmp, &adapter->rxnfc_rules, list)
- tsnep_delete_rule(adapter, rule);
- mutex_unlock(&adapter->rxnfc_lock);
- }
- int tsnep_rxnfc_get_rule(struct tsnep_adapter *adapter,
- struct ethtool_rxnfc *cmd)
- {
- struct ethtool_rx_flow_spec *fsp = &cmd->fs;
- struct tsnep_rxnfc_rule *rule = NULL;
- cmd->data = adapter->rxnfc_max;
- mutex_lock(&adapter->rxnfc_lock);
- rule = tsnep_get_rule(adapter, fsp->location);
- if (!rule) {
- mutex_unlock(&adapter->rxnfc_lock);
- return -ENOENT;
- }
- fsp->flow_type = ETHER_FLOW;
- fsp->ring_cookie = rule->queue_index;
- if (rule->filter.type == TSNEP_RXNFC_ETHER_TYPE) {
- fsp->h_u.ether_spec.h_proto = htons(rule->filter.ether_type);
- fsp->m_u.ether_spec.h_proto = ETHER_TYPE_FULL_MASK;
- }
- mutex_unlock(&adapter->rxnfc_lock);
- return 0;
- }
- int tsnep_rxnfc_get_all(struct tsnep_adapter *adapter,
- struct ethtool_rxnfc *cmd,
- u32 *rule_locs)
- {
- struct tsnep_rxnfc_rule *rule;
- int count = 0;
- cmd->data = adapter->rxnfc_max;
- mutex_lock(&adapter->rxnfc_lock);
- list_for_each_entry(rule, &adapter->rxnfc_rules, list) {
- if (count == cmd->rule_cnt) {
- mutex_unlock(&adapter->rxnfc_lock);
- return -EMSGSIZE;
- }
- rule_locs[count] = rule->location;
- count++;
- }
- mutex_unlock(&adapter->rxnfc_lock);
- cmd->rule_cnt = count;
- return 0;
- }
- static int tsnep_rxnfc_find_location(struct tsnep_adapter *adapter)
- {
- struct tsnep_rxnfc_rule *tmp;
- int location = 0;
- list_for_each_entry(tmp, &adapter->rxnfc_rules, list) {
- if (tmp->location == location)
- location++;
- else
- return location;
- }
- if (location >= adapter->rxnfc_max)
- return -ENOSPC;
- return location;
- }
- static void tsnep_rxnfc_init_rule(struct tsnep_rxnfc_rule *rule,
- const struct ethtool_rx_flow_spec *fsp)
- {
- INIT_LIST_HEAD(&rule->list);
- rule->queue_index = fsp->ring_cookie;
- rule->location = fsp->location;
- rule->filter.type = TSNEP_RXNFC_ETHER_TYPE;
- rule->filter.ether_type = ntohs(fsp->h_u.ether_spec.h_proto);
- }
- static int tsnep_rxnfc_check_rule(struct tsnep_adapter *adapter,
- struct tsnep_rxnfc_rule *rule)
- {
- struct net_device *dev = adapter->netdev;
- struct tsnep_rxnfc_rule *tmp;
- list_for_each_entry(tmp, &adapter->rxnfc_rules, list) {
- if (!memcmp(&rule->filter, &tmp->filter, sizeof(rule->filter)) &&
- tmp->location != rule->location) {
- netdev_dbg(dev, "rule already exists\n");
- return -EEXIST;
- }
- }
- return 0;
- }
- int tsnep_rxnfc_add_rule(struct tsnep_adapter *adapter,
- struct ethtool_rxnfc *cmd)
- {
- struct net_device *netdev = adapter->netdev;
- struct ethtool_rx_flow_spec *fsp =
- (struct ethtool_rx_flow_spec *)&cmd->fs;
- struct tsnep_rxnfc_rule *rule, *old_rule;
- int retval;
- /* only EtherType is supported */
- if (fsp->flow_type != ETHER_FLOW ||
- !is_zero_ether_addr(fsp->m_u.ether_spec.h_dest) ||
- !is_zero_ether_addr(fsp->m_u.ether_spec.h_source) ||
- fsp->m_u.ether_spec.h_proto != ETHER_TYPE_FULL_MASK) {
- netdev_dbg(netdev, "only ethernet protocol is supported\n");
- return -EOPNOTSUPP;
- }
- if (fsp->ring_cookie >
- (TSNEP_RX_ASSIGN_QUEUE_MASK >> TSNEP_RX_ASSIGN_QUEUE_SHIFT)) {
- netdev_dbg(netdev, "invalid action\n");
- return -EINVAL;
- }
- if (fsp->location != RX_CLS_LOC_ANY &&
- fsp->location >= adapter->rxnfc_max) {
- netdev_dbg(netdev, "invalid location\n");
- return -EINVAL;
- }
- rule = kzalloc(sizeof(*rule), GFP_KERNEL);
- if (!rule)
- return -ENOMEM;
- mutex_lock(&adapter->rxnfc_lock);
- if (fsp->location == RX_CLS_LOC_ANY) {
- retval = tsnep_rxnfc_find_location(adapter);
- if (retval < 0)
- goto failed;
- fsp->location = retval;
- }
- tsnep_rxnfc_init_rule(rule, fsp);
- retval = tsnep_rxnfc_check_rule(adapter, rule);
- if (retval)
- goto failed;
- old_rule = tsnep_get_rule(adapter, fsp->location);
- if (old_rule)
- tsnep_delete_rule(adapter, old_rule);
- tsnep_add_rule(adapter, rule);
- mutex_unlock(&adapter->rxnfc_lock);
- return 0;
- failed:
- mutex_unlock(&adapter->rxnfc_lock);
- kfree(rule);
- return retval;
- }
- int tsnep_rxnfc_del_rule(struct tsnep_adapter *adapter,
- struct ethtool_rxnfc *cmd)
- {
- struct ethtool_rx_flow_spec *fsp =
- (struct ethtool_rx_flow_spec *)&cmd->fs;
- struct tsnep_rxnfc_rule *rule;
- mutex_lock(&adapter->rxnfc_lock);
- rule = tsnep_get_rule(adapter, fsp->location);
- if (!rule) {
- mutex_unlock(&adapter->rxnfc_lock);
- return -ENOENT;
- }
- tsnep_delete_rule(adapter, rule);
- mutex_unlock(&adapter->rxnfc_lock);
- return 0;
- }
- int tsnep_rxnfc_init(struct tsnep_adapter *adapter)
- {
- int i;
- /* disable all rules */
- for (i = 0; i < adapter->rxnfc_max;
- i += sizeof(u32) / TSNEP_RX_ASSIGN_OFFSET)
- iowrite32(0, adapter->addr + TSNEP_RX_ASSIGN + i);
- return 0;
- }
- void tsnep_rxnfc_cleanup(struct tsnep_adapter *adapter)
- {
- tsnep_flush_rules(adapter);
- }
|