]> asedeno.scripts.mit.edu Git - linux.git/blob - drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_matchall.c
cxgb4: add TC-MATCHALL classifier egress offload
[linux.git] / drivers / net / ethernet / chelsio / cxgb4 / cxgb4_tc_matchall.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /* Copyright (C) 2019 Chelsio Communications.  All rights reserved. */
3
4 #include "cxgb4.h"
5 #include "cxgb4_tc_matchall.h"
6 #include "sched.h"
7
8 static int cxgb4_matchall_egress_validate(struct net_device *dev,
9                                           struct tc_cls_matchall_offload *cls)
10 {
11         struct netlink_ext_ack *extack = cls->common.extack;
12         struct flow_action *actions = &cls->rule->action;
13         struct port_info *pi = netdev2pinfo(dev);
14         struct flow_action_entry *entry;
15         u64 max_link_rate;
16         u32 i, speed;
17         int ret;
18
19         if (!flow_action_has_entries(actions)) {
20                 NL_SET_ERR_MSG_MOD(extack,
21                                    "Egress MATCHALL offload needs at least 1 policing action");
22                 return -EINVAL;
23         } else if (!flow_offload_has_one_action(actions)) {
24                 NL_SET_ERR_MSG_MOD(extack,
25                                    "Egress MATCHALL offload only supports 1 policing action");
26                 return -EINVAL;
27         } else if (pi->tc_block_shared) {
28                 NL_SET_ERR_MSG_MOD(extack,
29                                    "Egress MATCHALL offload not supported with shared blocks");
30                 return -EINVAL;
31         }
32
33         ret = t4_get_link_params(pi, NULL, &speed, NULL);
34         if (ret) {
35                 NL_SET_ERR_MSG_MOD(extack,
36                                    "Failed to get max speed supported by the link");
37                 return -EINVAL;
38         }
39
40         /* Convert from Mbps to bps */
41         max_link_rate = (u64)speed * 1000 * 1000;
42
43         flow_action_for_each(i, entry, actions) {
44                 switch (entry->id) {
45                 case FLOW_ACTION_POLICE:
46                         /* Convert bytes per second to bits per second */
47                         if (entry->police.rate_bytes_ps * 8 > max_link_rate) {
48                                 NL_SET_ERR_MSG_MOD(extack,
49                                                    "Specified policing max rate is larger than underlying link speed");
50                                 return -ERANGE;
51                         }
52                         break;
53                 default:
54                         NL_SET_ERR_MSG_MOD(extack,
55                                            "Only policing action supported with Egress MATCHALL offload");
56                         return -EOPNOTSUPP;
57                 }
58         }
59
60         return 0;
61 }
62
63 static int cxgb4_matchall_alloc_tc(struct net_device *dev,
64                                    struct tc_cls_matchall_offload *cls)
65 {
66         struct ch_sched_params p = {
67                 .type = SCHED_CLASS_TYPE_PACKET,
68                 .u.params.level = SCHED_CLASS_LEVEL_CH_RL,
69                 .u.params.mode = SCHED_CLASS_MODE_CLASS,
70                 .u.params.rateunit = SCHED_CLASS_RATEUNIT_BITS,
71                 .u.params.ratemode = SCHED_CLASS_RATEMODE_ABS,
72                 .u.params.class = SCHED_CLS_NONE,
73                 .u.params.minrate = 0,
74                 .u.params.weight = 0,
75                 .u.params.pktsize = dev->mtu,
76         };
77         struct netlink_ext_ack *extack = cls->common.extack;
78         struct cxgb4_tc_port_matchall *tc_port_matchall;
79         struct port_info *pi = netdev2pinfo(dev);
80         struct adapter *adap = netdev2adap(dev);
81         struct flow_action_entry *entry;
82         struct sched_class *e;
83         u32 i;
84
85         tc_port_matchall = &adap->tc_matchall->port_matchall[pi->port_id];
86
87         flow_action_for_each(i, entry, &cls->rule->action)
88                 if (entry->id == FLOW_ACTION_POLICE)
89                         break;
90
91         /* Convert from bytes per second to Kbps */
92         p.u.params.maxrate = div_u64(entry->police.rate_bytes_ps * 8, 1000);
93         p.u.params.channel = pi->tx_chan;
94         e = cxgb4_sched_class_alloc(dev, &p);
95         if (!e) {
96                 NL_SET_ERR_MSG_MOD(extack,
97                                    "No free traffic class available for policing action");
98                 return -ENOMEM;
99         }
100
101         tc_port_matchall->egress.hwtc = e->idx;
102         tc_port_matchall->egress.cookie = cls->cookie;
103         tc_port_matchall->egress.state = CXGB4_MATCHALL_STATE_ENABLED;
104         return 0;
105 }
106
107 static void cxgb4_matchall_free_tc(struct net_device *dev)
108 {
109         struct cxgb4_tc_port_matchall *tc_port_matchall;
110         struct port_info *pi = netdev2pinfo(dev);
111         struct adapter *adap = netdev2adap(dev);
112
113         tc_port_matchall = &adap->tc_matchall->port_matchall[pi->port_id];
114         cxgb4_sched_class_free(dev, tc_port_matchall->egress.hwtc);
115
116         tc_port_matchall->egress.hwtc = SCHED_CLS_NONE;
117         tc_port_matchall->egress.cookie = 0;
118         tc_port_matchall->egress.state = CXGB4_MATCHALL_STATE_DISABLED;
119 }
120
121 int cxgb4_tc_matchall_replace(struct net_device *dev,
122                               struct tc_cls_matchall_offload *cls_matchall)
123 {
124         struct netlink_ext_ack *extack = cls_matchall->common.extack;
125         struct cxgb4_tc_port_matchall *tc_port_matchall;
126         struct port_info *pi = netdev2pinfo(dev);
127         struct adapter *adap = netdev2adap(dev);
128         int ret;
129
130         tc_port_matchall = &adap->tc_matchall->port_matchall[pi->port_id];
131         if (tc_port_matchall->egress.state == CXGB4_MATCHALL_STATE_ENABLED) {
132                 NL_SET_ERR_MSG_MOD(extack,
133                                    "Only 1 Egress MATCHALL can be offloaded");
134                 return -ENOMEM;
135         }
136
137         ret = cxgb4_matchall_egress_validate(dev, cls_matchall);
138         if (ret)
139                 return ret;
140
141         return cxgb4_matchall_alloc_tc(dev, cls_matchall);
142 }
143
144 int cxgb4_tc_matchall_destroy(struct net_device *dev,
145                               struct tc_cls_matchall_offload *cls_matchall)
146 {
147         struct cxgb4_tc_port_matchall *tc_port_matchall;
148         struct port_info *pi = netdev2pinfo(dev);
149         struct adapter *adap = netdev2adap(dev);
150
151         tc_port_matchall = &adap->tc_matchall->port_matchall[pi->port_id];
152         if (cls_matchall->cookie != tc_port_matchall->egress.cookie)
153                 return -ENOENT;
154
155         cxgb4_matchall_free_tc(dev);
156         return 0;
157 }
158
159 static void cxgb4_matchall_disable_offload(struct net_device *dev)
160 {
161         struct cxgb4_tc_port_matchall *tc_port_matchall;
162         struct port_info *pi = netdev2pinfo(dev);
163         struct adapter *adap = netdev2adap(dev);
164
165         tc_port_matchall = &adap->tc_matchall->port_matchall[pi->port_id];
166         if (tc_port_matchall->egress.state == CXGB4_MATCHALL_STATE_ENABLED)
167                 cxgb4_matchall_free_tc(dev);
168 }
169
170 int cxgb4_init_tc_matchall(struct adapter *adap)
171 {
172         struct cxgb4_tc_port_matchall *tc_port_matchall;
173         struct cxgb4_tc_matchall *tc_matchall;
174         int ret;
175
176         tc_matchall = kzalloc(sizeof(*tc_matchall), GFP_KERNEL);
177         if (!tc_matchall)
178                 return -ENOMEM;
179
180         tc_port_matchall = kcalloc(adap->params.nports,
181                                    sizeof(*tc_port_matchall),
182                                    GFP_KERNEL);
183         if (!tc_port_matchall) {
184                 ret = -ENOMEM;
185                 goto out_free_matchall;
186         }
187
188         tc_matchall->port_matchall = tc_port_matchall;
189         adap->tc_matchall = tc_matchall;
190         return 0;
191
192 out_free_matchall:
193         kfree(tc_matchall);
194         return ret;
195 }
196
197 void cxgb4_cleanup_tc_matchall(struct adapter *adap)
198 {
199         u8 i;
200
201         if (adap->tc_matchall) {
202                 if (adap->tc_matchall->port_matchall) {
203                         for (i = 0; i < adap->params.nports; i++) {
204                                 struct net_device *dev = adap->port[i];
205
206                                 if (dev)
207                                         cxgb4_matchall_disable_offload(dev);
208                         }
209                         kfree(adap->tc_matchall->port_matchall);
210                 }
211                 kfree(adap->tc_matchall);
212         }
213 }