| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284 |
- // SPDX-License-Identifier: GPL-2.0
- // Copyright (c) 2023 Pengutronix, Oleksij Rempel <kernel@pengutronix.de>
- #include "ksz9477.h"
- #include "ksz9477_reg.h"
- #include "ksz_common.h"
- #define ETHER_TYPE_FULL_MASK cpu_to_be16(~0)
- #define KSZ9477_MAX_TC 7
- /**
- * ksz9477_flower_parse_key_l2 - Parse Layer 2 key from flow rule and configure
- * ACL entries accordingly.
- * @dev: Pointer to the ksz_device.
- * @port: Port number.
- * @extack: Pointer to the netlink_ext_ack.
- * @rule: Pointer to the flow_rule.
- * @cookie: The cookie to associate with the entry.
- * @prio: The priority of the entry.
- *
- * This function parses the Layer 2 key from the flow rule and configures
- * the corresponding ACL entries. It checks for unsupported offloads and
- * available entries before proceeding with the configuration.
- *
- * Returns: 0 on success or a negative error code on failure.
- */
- static int ksz9477_flower_parse_key_l2(struct ksz_device *dev, int port,
- struct netlink_ext_ack *extack,
- struct flow_rule *rule,
- unsigned long cookie, u32 prio)
- {
- struct ksz9477_acl_priv *acl = dev->ports[port].acl_priv;
- struct flow_match_eth_addrs ematch;
- struct ksz9477_acl_entries *acles;
- int required_entries;
- u8 *src_mac = NULL;
- u8 *dst_mac = NULL;
- u16 ethtype = 0;
- if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC)) {
- struct flow_match_basic match;
- flow_rule_match_basic(rule, &match);
- if (match.key->n_proto) {
- if (match.mask->n_proto != ETHER_TYPE_FULL_MASK) {
- NL_SET_ERR_MSG_MOD(extack,
- "ethernet type mask must be a full mask");
- return -EINVAL;
- }
- ethtype = be16_to_cpu(match.key->n_proto);
- }
- }
- if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
- flow_rule_match_eth_addrs(rule, &ematch);
- if (!is_zero_ether_addr(ematch.key->src)) {
- if (!is_broadcast_ether_addr(ematch.mask->src))
- goto not_full_mask_err;
- src_mac = ematch.key->src;
- }
- if (!is_zero_ether_addr(ematch.key->dst)) {
- if (!is_broadcast_ether_addr(ematch.mask->dst))
- goto not_full_mask_err;
- dst_mac = ematch.key->dst;
- }
- }
- acles = &acl->acles;
- /* ACL supports only one MAC per entry */
- required_entries = src_mac && dst_mac ? 2 : 1;
- /* Check if there are enough available entries */
- if (acles->entries_count + required_entries > KSZ9477_ACL_MAX_ENTRIES) {
- NL_SET_ERR_MSG_MOD(extack, "ACL entry limit reached");
- return -EOPNOTSUPP;
- }
- ksz9477_acl_match_process_l2(dev, port, ethtype, src_mac, dst_mac,
- cookie, prio);
- return 0;
- not_full_mask_err:
- NL_SET_ERR_MSG_MOD(extack, "MAC address mask must be a full mask");
- return -EOPNOTSUPP;
- }
- /**
- * ksz9477_flower_parse_key - Parse flow rule keys for a specified port on a
- * ksz_device.
- * @dev: The ksz_device instance.
- * @port: The port number to parse the flow rule keys for.
- * @extack: The netlink extended ACK for reporting errors.
- * @rule: The flow_rule to parse.
- * @cookie: The cookie to associate with the entry.
- * @prio: The priority of the entry.
- *
- * This function checks if the used keys in the flow rule are supported by
- * the device and parses the L2 keys if they match. If unsupported keys are
- * used, an error message is set in the extended ACK.
- *
- * Returns: 0 on success or a negative error code on failure.
- */
- static int ksz9477_flower_parse_key(struct ksz_device *dev, int port,
- struct netlink_ext_ack *extack,
- struct flow_rule *rule,
- unsigned long cookie, u32 prio)
- {
- struct flow_dissector *dissector = rule->match.dissector;
- int ret;
- if (dissector->used_keys &
- ~(BIT_ULL(FLOW_DISSECTOR_KEY_BASIC) |
- BIT_ULL(FLOW_DISSECTOR_KEY_ETH_ADDRS) |
- BIT_ULL(FLOW_DISSECTOR_KEY_CONTROL))) {
- NL_SET_ERR_MSG_MOD(extack,
- "Unsupported keys used");
- return -EOPNOTSUPP;
- }
- if (flow_rule_match_has_control_flags(rule, extack))
- return -EOPNOTSUPP;
- if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC) ||
- flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
- ret = ksz9477_flower_parse_key_l2(dev, port, extack, rule,
- cookie, prio);
- if (ret)
- return ret;
- }
- return 0;
- }
- /**
- * ksz9477_flower_parse_action - Parse flow rule actions for a specified port
- * on a ksz_device.
- * @dev: The ksz_device instance.
- * @port: The port number to parse the flow rule actions for.
- * @extack: The netlink extended ACK for reporting errors.
- * @cls: The flow_cls_offload instance containing the flow rule.
- * @entry_idx: The index of the ACL entry to store the action.
- *
- * This function checks if the actions in the flow rule are supported by
- * the device. Currently, only actions that change priorities are supported.
- * If unsupported actions are encountered, an error message is set in the
- * extended ACK.
- *
- * Returns: 0 on success or a negative error code on failure.
- */
- static int ksz9477_flower_parse_action(struct ksz_device *dev, int port,
- struct netlink_ext_ack *extack,
- struct flow_cls_offload *cls,
- int entry_idx)
- {
- struct flow_rule *rule = flow_cls_offload_flow_rule(cls);
- struct ksz9477_acl_priv *acl = dev->ports[port].acl_priv;
- const struct flow_action_entry *act;
- struct ksz9477_acl_entry *entry;
- bool prio_force = false;
- u8 prio_val = 0;
- int i;
- if (TC_H_MIN(cls->classid)) {
- NL_SET_ERR_MSG_MOD(extack, "hw_tc is not supported. Use: action skbedit prio");
- return -EOPNOTSUPP;
- }
- flow_action_for_each(i, act, &rule->action) {
- switch (act->id) {
- case FLOW_ACTION_PRIORITY:
- if (act->priority > KSZ9477_MAX_TC) {
- NL_SET_ERR_MSG_MOD(extack, "Priority value is too high");
- return -EOPNOTSUPP;
- }
- prio_force = true;
- prio_val = act->priority;
- break;
- default:
- NL_SET_ERR_MSG_MOD(extack, "action not supported");
- return -EOPNOTSUPP;
- }
- }
- /* pick entry to store action */
- entry = &acl->acles.entries[entry_idx];
- ksz9477_acl_action_rule_cfg(entry->entry, prio_force, prio_val);
- ksz9477_acl_processing_rule_set_action(entry->entry, entry_idx);
- return 0;
- }
- /**
- * ksz9477_cls_flower_add - Add a flow classification rule for a specified port
- * on a ksz_device.
- * @ds: The DSA switch instance.
- * @port: The port number to add the flow classification rule to.
- * @cls: The flow_cls_offload instance containing the flow rule.
- * @ingress: A flag indicating if the rule is applied on the ingress path.
- *
- * This function adds a flow classification rule for a specified port on a
- * ksz_device. It checks if the ACL offloading is supported and parses the flow
- * keys and actions. If the ACL is not supported, it returns an error. If there
- * are unprocessed entries, it parses the action for the rule.
- *
- * Returns: 0 on success or a negative error code on failure.
- */
- int ksz9477_cls_flower_add(struct dsa_switch *ds, int port,
- struct flow_cls_offload *cls, bool ingress)
- {
- struct flow_rule *rule = flow_cls_offload_flow_rule(cls);
- struct netlink_ext_ack *extack = cls->common.extack;
- struct ksz_device *dev = ds->priv;
- struct ksz9477_acl_priv *acl;
- int action_entry_idx;
- int ret;
- acl = dev->ports[port].acl_priv;
- if (!acl) {
- NL_SET_ERR_MSG_MOD(extack, "ACL offloading is not supported");
- return -EOPNOTSUPP;
- }
- /* A complex rule set can take multiple entries. Use first entry
- * to store the action.
- */
- action_entry_idx = acl->acles.entries_count;
- ret = ksz9477_flower_parse_key(dev, port, extack, rule, cls->cookie,
- cls->common.prio);
- if (ret)
- return ret;
- ret = ksz9477_flower_parse_action(dev, port, extack, cls,
- action_entry_idx);
- if (ret)
- return ret;
- ret = ksz9477_sort_acl_entries(dev, port);
- if (ret)
- return ret;
- return ksz9477_acl_write_list(dev, port);
- }
- /**
- * ksz9477_cls_flower_del - Remove a flow classification rule for a specified
- * port on a ksz_device.
- * @ds: The DSA switch instance.
- * @port: The port number to remove the flow classification rule from.
- * @cls: The flow_cls_offload instance containing the flow rule.
- * @ingress: A flag indicating if the rule is applied on the ingress path.
- *
- * This function removes a flow classification rule for a specified port on a
- * ksz_device. It checks if the ACL is initialized, and if not, returns an
- * error. If the ACL is initialized, it removes entries with the specified
- * cookie and rewrites the ACL list.
- *
- * Returns: 0 on success or a negative error code on failure.
- */
- int ksz9477_cls_flower_del(struct dsa_switch *ds, int port,
- struct flow_cls_offload *cls, bool ingress)
- {
- unsigned long cookie = cls->cookie;
- struct ksz_device *dev = ds->priv;
- struct ksz9477_acl_priv *acl;
- acl = dev->ports[port].acl_priv;
- if (!acl)
- return -EOPNOTSUPP;
- ksz9477_acl_remove_entries(dev, port, &acl->acles, cookie);
- return ksz9477_acl_write_list(dev, port);
- }
|