Linux Audio

Check our new training course

Linux kernel drivers training

Mar 31-Apr 9, 2025, special US time zones
Register
Loading...
Note: File does not exist in v6.8.
  1// SPDX-License-Identifier: (GPL-2.0 OR MIT)
  2/* Microsemi Ocelot Switch TC driver
  3 *
  4 * Copyright (c) 2019 Microsemi Corporation
  5 */
  6
  7#include "ocelot_tc.h"
  8#include "ocelot_police.h"
  9#include "ocelot_ace.h"
 10#include <net/pkt_cls.h>
 11
 12static int ocelot_setup_tc_cls_matchall(struct ocelot_port *port,
 13					struct tc_cls_matchall_offload *f,
 14					bool ingress)
 15{
 16	struct netlink_ext_ack *extack = f->common.extack;
 17	struct ocelot_policer pol = { 0 };
 18	struct flow_action_entry *action;
 19	int err;
 20
 21	netdev_dbg(port->dev, "%s: port %u command %d cookie %lu\n",
 22		   __func__, port->chip_port, f->command, f->cookie);
 23
 24	if (!ingress) {
 25		NL_SET_ERR_MSG_MOD(extack, "Only ingress is supported");
 26		return -EOPNOTSUPP;
 27	}
 28
 29	switch (f->command) {
 30	case TC_CLSMATCHALL_REPLACE:
 31		if (!flow_offload_has_one_action(&f->rule->action)) {
 32			NL_SET_ERR_MSG_MOD(extack,
 33					   "Only one action is supported");
 34			return -EOPNOTSUPP;
 35		}
 36
 37		if (port->tc.block_shared) {
 38			NL_SET_ERR_MSG_MOD(extack,
 39					   "Rate limit is not supported on shared blocks");
 40			return -EOPNOTSUPP;
 41		}
 42
 43		action = &f->rule->action.entries[0];
 44
 45		if (action->id != FLOW_ACTION_POLICE) {
 46			NL_SET_ERR_MSG_MOD(extack, "Unsupported action");
 47			return -EOPNOTSUPP;
 48		}
 49
 50		if (port->tc.police_id && port->tc.police_id != f->cookie) {
 51			NL_SET_ERR_MSG_MOD(extack,
 52					   "Only one policer per port is supported\n");
 53			return -EEXIST;
 54		}
 55
 56		pol.rate = (u32)div_u64(action->police.rate_bytes_ps, 1000) * 8;
 57		pol.burst = (u32)div_u64(action->police.rate_bytes_ps *
 58					 PSCHED_NS2TICKS(action->police.burst),
 59					 PSCHED_TICKS_PER_SEC);
 60
 61		err = ocelot_port_policer_add(port, &pol);
 62		if (err) {
 63			NL_SET_ERR_MSG_MOD(extack, "Could not add policer\n");
 64			return err;
 65		}
 66
 67		port->tc.police_id = f->cookie;
 68		port->tc.offload_cnt++;
 69		return 0;
 70	case TC_CLSMATCHALL_DESTROY:
 71		if (port->tc.police_id != f->cookie)
 72			return -ENOENT;
 73
 74		err = ocelot_port_policer_del(port);
 75		if (err) {
 76			NL_SET_ERR_MSG_MOD(extack,
 77					   "Could not delete policer\n");
 78			return err;
 79		}
 80		port->tc.police_id = 0;
 81		port->tc.offload_cnt--;
 82		return 0;
 83	case TC_CLSMATCHALL_STATS: /* fall through */
 84	default:
 85		return -EOPNOTSUPP;
 86	}
 87}
 88
 89static int ocelot_setup_tc_block_cb(enum tc_setup_type type,
 90				    void *type_data,
 91				    void *cb_priv, bool ingress)
 92{
 93	struct ocelot_port *port = cb_priv;
 94
 95	if (!tc_cls_can_offload_and_chain0(port->dev, type_data))
 96		return -EOPNOTSUPP;
 97
 98	switch (type) {
 99	case TC_SETUP_CLSMATCHALL:
100		netdev_dbg(port->dev, "tc_block_cb: TC_SETUP_CLSMATCHALL %s\n",
101			   ingress ? "ingress" : "egress");
102
103		return ocelot_setup_tc_cls_matchall(port, type_data, ingress);
104	case TC_SETUP_CLSFLOWER:
105		return 0;
106	default:
107		netdev_dbg(port->dev, "tc_block_cb: type %d %s\n",
108			   type,
109			   ingress ? "ingress" : "egress");
110
111		return -EOPNOTSUPP;
112	}
113}
114
115static int ocelot_setup_tc_block_cb_ig(enum tc_setup_type type,
116				       void *type_data,
117				       void *cb_priv)
118{
119	return ocelot_setup_tc_block_cb(type, type_data,
120					cb_priv, true);
121}
122
123static int ocelot_setup_tc_block_cb_eg(enum tc_setup_type type,
124				       void *type_data,
125				       void *cb_priv)
126{
127	return ocelot_setup_tc_block_cb(type, type_data,
128					cb_priv, false);
129}
130
131static LIST_HEAD(ocelot_block_cb_list);
132
133static int ocelot_setup_tc_block(struct ocelot_port *port,
134				 struct flow_block_offload *f)
135{
136	struct flow_block_cb *block_cb;
137	flow_setup_cb_t *cb;
138	int err;
139
140	netdev_dbg(port->dev, "tc_block command %d, binder_type %d\n",
141		   f->command, f->binder_type);
142
143	if (f->binder_type == FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS) {
144		cb = ocelot_setup_tc_block_cb_ig;
145		port->tc.block_shared = f->block_shared;
146	} else if (f->binder_type == FLOW_BLOCK_BINDER_TYPE_CLSACT_EGRESS) {
147		cb = ocelot_setup_tc_block_cb_eg;
148	} else {
149		return -EOPNOTSUPP;
150	}
151
152	f->driver_block_list = &ocelot_block_cb_list;
153
154	switch (f->command) {
155	case FLOW_BLOCK_BIND:
156		if (flow_block_cb_is_busy(cb, port, &ocelot_block_cb_list))
157			return -EBUSY;
158
159		block_cb = flow_block_cb_alloc(cb, port, port, NULL);
160		if (IS_ERR(block_cb))
161			return PTR_ERR(block_cb);
162
163		err = ocelot_setup_tc_block_flower_bind(port, f);
164		if (err < 0) {
165			flow_block_cb_free(block_cb);
166			return err;
167		}
168		flow_block_cb_add(block_cb, f);
169		list_add_tail(&block_cb->driver_list, f->driver_block_list);
170		return 0;
171	case FLOW_BLOCK_UNBIND:
172		block_cb = flow_block_cb_lookup(f->block, cb, port);
173		if (!block_cb)
174			return -ENOENT;
175
176		ocelot_setup_tc_block_flower_unbind(port, f);
177		flow_block_cb_remove(block_cb, f);
178		list_del(&block_cb->driver_list);
179		return 0;
180	default:
181		return -EOPNOTSUPP;
182	}
183}
184
185int ocelot_setup_tc(struct net_device *dev, enum tc_setup_type type,
186		    void *type_data)
187{
188	struct ocelot_port *port = netdev_priv(dev);
189
190	switch (type) {
191	case TC_SETUP_BLOCK:
192		return ocelot_setup_tc_block(port, type_data);
193	default:
194		return -EOPNOTSUPP;
195	}
196	return 0;
197}