123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198 |
- // SPDX-License-Identifier: (GPL-2.0 OR MIT)
- /* Microsemi Ocelot Switch TC driver
- *
- * Copyright (c) 2019 Microsemi Corporation
- */
- #include "ocelot_tc.h"
- #include "ocelot_police.h"
- #include "ocelot_ace.h"
- #include <net/pkt_cls.h>
- static int ocelot_setup_tc_cls_matchall(struct ocelot_port *port,
- struct tc_cls_matchall_offload *f,
- bool ingress)
- {
- struct netlink_ext_ack *extack = f->common.extack;
- struct ocelot_policer pol = { 0 };
- struct flow_action_entry *action;
- int err;
- netdev_dbg(port->dev, "%s: port %u command %d cookie %lu\n",
- __func__, port->chip_port, f->command, f->cookie);
- if (!ingress) {
- NL_SET_ERR_MSG_MOD(extack, "Only ingress is supported");
- return -EOPNOTSUPP;
- }
- switch (f->command) {
- case TC_CLSMATCHALL_REPLACE:
- if (!flow_offload_has_one_action(&f->rule->action)) {
- NL_SET_ERR_MSG_MOD(extack,
- "Only one action is supported");
- return -EOPNOTSUPP;
- }
- if (port->tc.block_shared) {
- NL_SET_ERR_MSG_MOD(extack,
- "Rate limit is not supported on shared blocks");
- return -EOPNOTSUPP;
- }
- action = &f->rule->action.entries[0];
- if (action->id != FLOW_ACTION_POLICE) {
- NL_SET_ERR_MSG_MOD(extack, "Unsupported action");
- return -EOPNOTSUPP;
- }
- if (port->tc.police_id && port->tc.police_id != f->cookie) {
- NL_SET_ERR_MSG_MOD(extack,
- "Only one policer per port is supported\n");
- return -EEXIST;
- }
- pol.rate = (u32)div_u64(action->police.rate_bytes_ps, 1000) * 8;
- pol.burst = (u32)div_u64(action->police.rate_bytes_ps *
- PSCHED_NS2TICKS(action->police.burst),
- PSCHED_TICKS_PER_SEC);
- err = ocelot_port_policer_add(port, &pol);
- if (err) {
- NL_SET_ERR_MSG_MOD(extack, "Could not add policer\n");
- return err;
- }
- port->tc.police_id = f->cookie;
- port->tc.offload_cnt++;
- return 0;
- case TC_CLSMATCHALL_DESTROY:
- if (port->tc.police_id != f->cookie)
- return -ENOENT;
- err = ocelot_port_policer_del(port);
- if (err) {
- NL_SET_ERR_MSG_MOD(extack,
- "Could not delete policer\n");
- return err;
- }
- port->tc.police_id = 0;
- port->tc.offload_cnt--;
- return 0;
- case TC_CLSMATCHALL_STATS: /* fall through */
- default:
- return -EOPNOTSUPP;
- }
- }
- static int ocelot_setup_tc_block_cb(enum tc_setup_type type,
- void *type_data,
- void *cb_priv, bool ingress)
- {
- struct ocelot_port *port = cb_priv;
- if (!tc_cls_can_offload_and_chain0(port->dev, type_data))
- return -EOPNOTSUPP;
- switch (type) {
- case TC_SETUP_CLSMATCHALL:
- netdev_dbg(port->dev, "tc_block_cb: TC_SETUP_CLSMATCHALL %s\n",
- ingress ? "ingress" : "egress");
- return ocelot_setup_tc_cls_matchall(port, type_data, ingress);
- case TC_SETUP_CLSFLOWER:
- return 0;
- default:
- netdev_dbg(port->dev, "tc_block_cb: type %d %s\n",
- type,
- ingress ? "ingress" : "egress");
- return -EOPNOTSUPP;
- }
- }
- static int ocelot_setup_tc_block_cb_ig(enum tc_setup_type type,
- void *type_data,
- void *cb_priv)
- {
- return ocelot_setup_tc_block_cb(type, type_data,
- cb_priv, true);
- }
- static int ocelot_setup_tc_block_cb_eg(enum tc_setup_type type,
- void *type_data,
- void *cb_priv)
- {
- return ocelot_setup_tc_block_cb(type, type_data,
- cb_priv, false);
- }
- static LIST_HEAD(ocelot_block_cb_list);
- static int ocelot_setup_tc_block(struct ocelot_port *port,
- struct flow_block_offload *f)
- {
- struct flow_block_cb *block_cb;
- flow_setup_cb_t *cb;
- int err;
- netdev_dbg(port->dev, "tc_block command %d, binder_type %d\n",
- f->command, f->binder_type);
- if (f->binder_type == FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS) {
- cb = ocelot_setup_tc_block_cb_ig;
- port->tc.block_shared = f->block_shared;
- } else if (f->binder_type == FLOW_BLOCK_BINDER_TYPE_CLSACT_EGRESS) {
- cb = ocelot_setup_tc_block_cb_eg;
- } else {
- return -EOPNOTSUPP;
- }
- f->driver_block_list = &ocelot_block_cb_list;
- switch (f->command) {
- case FLOW_BLOCK_BIND:
- if (flow_block_cb_is_busy(cb, port, &ocelot_block_cb_list))
- return -EBUSY;
- block_cb = flow_block_cb_alloc(cb, port, port, NULL);
- if (IS_ERR(block_cb))
- return PTR_ERR(block_cb);
- err = ocelot_setup_tc_block_flower_bind(port, f);
- if (err < 0) {
- flow_block_cb_free(block_cb);
- return err;
- }
- flow_block_cb_add(block_cb, f);
- list_add_tail(&block_cb->driver_list, f->driver_block_list);
- return 0;
- case FLOW_BLOCK_UNBIND:
- block_cb = flow_block_cb_lookup(f->block, cb, port);
- if (!block_cb)
- return -ENOENT;
- ocelot_setup_tc_block_flower_unbind(port, f);
- flow_block_cb_remove(block_cb, f);
- list_del(&block_cb->driver_list);
- return 0;
- default:
- return -EOPNOTSUPP;
- }
- }
- int ocelot_setup_tc(struct net_device *dev, enum tc_setup_type type,
- void *type_data)
- {
- struct ocelot_port *port = netdev_priv(dev);
- switch (type) {
- case TC_SETUP_BLOCK:
- return ocelot_setup_tc_block(port, type_data);
- default:
- return -EOPNOTSUPP;
- }
- return 0;
- }
|