aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2018-11-08 23:48:01 -0500
committerDavid S. Miller <davem@davemloft.net>2018-11-08 23:48:01 -0500
commitdb8ba1e8c39a0e4a012949b5b8ac68386bd39b28 (patch)
tree43dff3f5fe65ac9a1d3ffb56b3f71aaa2bb07364
parent1106a5ade15fa2effdbfb3b3a1ba560a536dbcfe (diff)
parent6e5a716f424b1020fc8cf52b0ab4a1551e952733 (diff)
Merge branch 'nfp-abm-move-code-and-improve-parameter-validation'
Jakub Kicinski says: ==================== nfp: abm: move code and improve parameter validation This set starts by separating Qdisc handling code into a new file. Next two patches allow early access to TLV-based capabilities during probe, previously the capabilities were parsed just before netdevs were registered, but its cleaner to do some basic validation earlier and avoid cleanup work. Next three patches improve RED's parameter validation. First we provide a more precise message about why offload failed (and move the parameter validation to a helper). Next we make sure we don't set the top bit in the 32 bit max RED threshold value. Because FW is treating the value as signed it reportedly causes slow downs (unnecessary queuing and marking) when top bit is set with recent firmwares. Last (and perhaps least importantly) we offload the harddrop parameter of the Qdisc. We don't plan to offload harddrop RED, but it seems prudent to make sure user didn't set that flag as device behaviour would have differed. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--drivers/net/ethernet/netronome/nfp/Makefile1
-rw-r--r--drivers/net/ethernet/netronome/nfp/abm/main.c266
-rw-r--r--drivers/net/ethernet/netronome/nfp/abm/main.h9
-rw-r--r--drivers/net/ethernet/netronome/nfp/abm/qdisc.c301
-rw-r--r--drivers/net/ethernet/netronome/nfp/nfp_net.h2
-rw-r--r--drivers/net/ethernet/netronome/nfp/nfp_net_common.c26
-rw-r--r--drivers/net/ethernet/netronome/nfp/nfp_net_main.c4
-rw-r--r--drivers/net/ethernet/netronome/nfp/nfp_netvf_main.c3
-rw-r--r--include/net/pkt_cls.h1
-rw-r--r--net/sched/sch_red.c1
10 files changed, 335 insertions, 279 deletions
diff --git a/drivers/net/ethernet/netronome/nfp/Makefile b/drivers/net/ethernet/netronome/nfp/Makefile
index 4afb10375397..190e8b56a41f 100644
--- a/drivers/net/ethernet/netronome/nfp/Makefile
+++ b/drivers/net/ethernet/netronome/nfp/Makefile
@@ -57,6 +57,7 @@ endif
57ifeq ($(CONFIG_NFP_APP_ABM_NIC),y) 57ifeq ($(CONFIG_NFP_APP_ABM_NIC),y)
58nfp-objs += \ 58nfp-objs += \
59 abm/ctrl.o \ 59 abm/ctrl.o \
60 abm/qdisc.o \
60 abm/main.o 61 abm/main.o
61endif 62endif
62 63
diff --git a/drivers/net/ethernet/netronome/nfp/abm/main.c b/drivers/net/ethernet/netronome/nfp/abm/main.c
index c0830c0c2c3f..3d15de0ae271 100644
--- a/drivers/net/ethernet/netronome/nfp/abm/main.c
+++ b/drivers/net/ethernet/netronome/nfp/abm/main.c
@@ -7,9 +7,6 @@
7#include <linux/netdevice.h> 7#include <linux/netdevice.h>
8#include <linux/rcupdate.h> 8#include <linux/rcupdate.h>
9#include <linux/slab.h> 9#include <linux/slab.h>
10#include <net/pkt_cls.h>
11#include <net/pkt_sched.h>
12#include <net/red.h>
13 10
14#include "../nfpcore/nfp.h" 11#include "../nfpcore/nfp.h"
15#include "../nfpcore/nfp_cpp.h" 12#include "../nfpcore/nfp_cpp.h"
@@ -28,269 +25,6 @@ static u32 nfp_abm_portid(enum nfp_repr_type rtype, unsigned int id)
28} 25}
29 26
30static int 27static int
31__nfp_abm_reset_root(struct net_device *netdev, struct nfp_abm_link *alink,
32 u32 handle, unsigned int qs, u32 init_val)
33{
34 struct nfp_port *port = nfp_port_from_netdev(netdev);
35 int ret;
36
37 ret = nfp_abm_ctrl_set_all_q_lvls(alink, init_val);
38 memset(alink->qdiscs, 0, sizeof(*alink->qdiscs) * alink->num_qdiscs);
39
40 alink->parent = handle;
41 alink->num_qdiscs = qs;
42 port->tc_offload_cnt = qs;
43
44 return ret;
45}
46
47static void
48nfp_abm_reset_root(struct net_device *netdev, struct nfp_abm_link *alink,
49 u32 handle, unsigned int qs)
50{
51 __nfp_abm_reset_root(netdev, alink, handle, qs, ~0);
52}
53
54static int
55nfp_abm_red_find(struct nfp_abm_link *alink, struct tc_red_qopt_offload *opt)
56{
57 unsigned int i = TC_H_MIN(opt->parent) - 1;
58
59 if (opt->parent == TC_H_ROOT)
60 i = 0;
61 else if (TC_H_MAJ(alink->parent) == TC_H_MAJ(opt->parent))
62 i = TC_H_MIN(opt->parent) - 1;
63 else
64 return -EOPNOTSUPP;
65
66 if (i >= alink->num_qdiscs || opt->handle != alink->qdiscs[i].handle)
67 return -EOPNOTSUPP;
68
69 return i;
70}
71
72static void
73nfp_abm_red_destroy(struct net_device *netdev, struct nfp_abm_link *alink,
74 u32 handle)
75{
76 unsigned int i;
77
78 for (i = 0; i < alink->num_qdiscs; i++)
79 if (handle == alink->qdiscs[i].handle)
80 break;
81 if (i == alink->num_qdiscs)
82 return;
83
84 if (alink->parent == TC_H_ROOT) {
85 nfp_abm_reset_root(netdev, alink, TC_H_ROOT, 0);
86 } else {
87 nfp_abm_ctrl_set_q_lvl(alink, i, ~0);
88 memset(&alink->qdiscs[i], 0, sizeof(*alink->qdiscs));
89 }
90}
91
92static int
93nfp_abm_red_replace(struct net_device *netdev, struct nfp_abm_link *alink,
94 struct tc_red_qopt_offload *opt)
95{
96 bool existing;
97 int i, err;
98
99 i = nfp_abm_red_find(alink, opt);
100 existing = i >= 0;
101
102 if (opt->set.min != opt->set.max || !opt->set.is_ecn) {
103 nfp_warn(alink->abm->app->cpp,
104 "RED offload failed - unsupported parameters\n");
105 err = -EINVAL;
106 goto err_destroy;
107 }
108
109 if (existing) {
110 if (alink->parent == TC_H_ROOT)
111 err = nfp_abm_ctrl_set_all_q_lvls(alink, opt->set.min);
112 else
113 err = nfp_abm_ctrl_set_q_lvl(alink, i, opt->set.min);
114 if (err)
115 goto err_destroy;
116 return 0;
117 }
118
119 if (opt->parent == TC_H_ROOT) {
120 i = 0;
121 err = __nfp_abm_reset_root(netdev, alink, TC_H_ROOT, 1,
122 opt->set.min);
123 } else if (TC_H_MAJ(alink->parent) == TC_H_MAJ(opt->parent)) {
124 i = TC_H_MIN(opt->parent) - 1;
125 err = nfp_abm_ctrl_set_q_lvl(alink, i, opt->set.min);
126 } else {
127 return -EINVAL;
128 }
129 /* Set the handle to try full clean up, in case IO failed */
130 alink->qdiscs[i].handle = opt->handle;
131 if (err)
132 goto err_destroy;
133
134 if (opt->parent == TC_H_ROOT)
135 err = nfp_abm_ctrl_read_stats(alink, &alink->qdiscs[i].stats);
136 else
137 err = nfp_abm_ctrl_read_q_stats(alink, i,
138 &alink->qdiscs[i].stats);
139 if (err)
140 goto err_destroy;
141
142 if (opt->parent == TC_H_ROOT)
143 err = nfp_abm_ctrl_read_xstats(alink,
144 &alink->qdiscs[i].xstats);
145 else
146 err = nfp_abm_ctrl_read_q_xstats(alink, i,
147 &alink->qdiscs[i].xstats);
148 if (err)
149 goto err_destroy;
150
151 alink->qdiscs[i].stats.backlog_pkts = 0;
152 alink->qdiscs[i].stats.backlog_bytes = 0;
153
154 return 0;
155err_destroy:
156 /* If the qdisc keeps on living, but we can't offload undo changes */
157 if (existing) {
158 opt->set.qstats->qlen -= alink->qdiscs[i].stats.backlog_pkts;
159 opt->set.qstats->backlog -=
160 alink->qdiscs[i].stats.backlog_bytes;
161 }
162 nfp_abm_red_destroy(netdev, alink, opt->handle);
163
164 return err;
165}
166
167static void
168nfp_abm_update_stats(struct nfp_alink_stats *new, struct nfp_alink_stats *old,
169 struct tc_qopt_offload_stats *stats)
170{
171 _bstats_update(stats->bstats, new->tx_bytes - old->tx_bytes,
172 new->tx_pkts - old->tx_pkts);
173 stats->qstats->qlen += new->backlog_pkts - old->backlog_pkts;
174 stats->qstats->backlog += new->backlog_bytes - old->backlog_bytes;
175 stats->qstats->overlimits += new->overlimits - old->overlimits;
176 stats->qstats->drops += new->drops - old->drops;
177}
178
179static int
180nfp_abm_red_stats(struct nfp_abm_link *alink, struct tc_red_qopt_offload *opt)
181{
182 struct nfp_alink_stats *prev_stats;
183 struct nfp_alink_stats stats;
184 int i, err;
185
186 i = nfp_abm_red_find(alink, opt);
187 if (i < 0)
188 return i;
189 prev_stats = &alink->qdiscs[i].stats;
190
191 if (alink->parent == TC_H_ROOT)
192 err = nfp_abm_ctrl_read_stats(alink, &stats);
193 else
194 err = nfp_abm_ctrl_read_q_stats(alink, i, &stats);
195 if (err)
196 return err;
197
198 nfp_abm_update_stats(&stats, prev_stats, &opt->stats);
199
200 *prev_stats = stats;
201
202 return 0;
203}
204
205static int
206nfp_abm_red_xstats(struct nfp_abm_link *alink, struct tc_red_qopt_offload *opt)
207{
208 struct nfp_alink_xstats *prev_xstats;
209 struct nfp_alink_xstats xstats;
210 int i, err;
211
212 i = nfp_abm_red_find(alink, opt);
213 if (i < 0)
214 return i;
215 prev_xstats = &alink->qdiscs[i].xstats;
216
217 if (alink->parent == TC_H_ROOT)
218 err = nfp_abm_ctrl_read_xstats(alink, &xstats);
219 else
220 err = nfp_abm_ctrl_read_q_xstats(alink, i, &xstats);
221 if (err)
222 return err;
223
224 opt->xstats->forced_mark += xstats.ecn_marked - prev_xstats->ecn_marked;
225 opt->xstats->pdrop += xstats.pdrop - prev_xstats->pdrop;
226
227 *prev_xstats = xstats;
228
229 return 0;
230}
231
232static int
233nfp_abm_setup_tc_red(struct net_device *netdev, struct nfp_abm_link *alink,
234 struct tc_red_qopt_offload *opt)
235{
236 switch (opt->command) {
237 case TC_RED_REPLACE:
238 return nfp_abm_red_replace(netdev, alink, opt);
239 case TC_RED_DESTROY:
240 nfp_abm_red_destroy(netdev, alink, opt->handle);
241 return 0;
242 case TC_RED_STATS:
243 return nfp_abm_red_stats(alink, opt);
244 case TC_RED_XSTATS:
245 return nfp_abm_red_xstats(alink, opt);
246 default:
247 return -EOPNOTSUPP;
248 }
249}
250
251static int
252nfp_abm_mq_stats(struct nfp_abm_link *alink, struct tc_mq_qopt_offload *opt)
253{
254 struct nfp_alink_stats stats;
255 unsigned int i;
256 int err;
257
258 for (i = 0; i < alink->num_qdiscs; i++) {
259 if (alink->qdiscs[i].handle == TC_H_UNSPEC)
260 continue;
261
262 err = nfp_abm_ctrl_read_q_stats(alink, i, &stats);
263 if (err)
264 return err;
265
266 nfp_abm_update_stats(&stats, &alink->qdiscs[i].stats,
267 &opt->stats);
268 }
269
270 return 0;
271}
272
273static int
274nfp_abm_setup_tc_mq(struct net_device *netdev, struct nfp_abm_link *alink,
275 struct tc_mq_qopt_offload *opt)
276{
277 switch (opt->command) {
278 case TC_MQ_CREATE:
279 nfp_abm_reset_root(netdev, alink, opt->handle,
280 alink->total_queues);
281 return 0;
282 case TC_MQ_DESTROY:
283 if (opt->handle == alink->parent)
284 nfp_abm_reset_root(netdev, alink, TC_H_ROOT, 0);
285 return 0;
286 case TC_MQ_STATS:
287 return nfp_abm_mq_stats(alink, opt);
288 default:
289 return -EOPNOTSUPP;
290 }
291}
292
293static int
294nfp_abm_setup_tc(struct nfp_app *app, struct net_device *netdev, 28nfp_abm_setup_tc(struct nfp_app *app, struct net_device *netdev,
295 enum tc_setup_type type, void *type_data) 29 enum tc_setup_type type, void *type_data)
296{ 30{
diff --git a/drivers/net/ethernet/netronome/nfp/abm/main.h b/drivers/net/ethernet/netronome/nfp/abm/main.h
index f907b7d98917..3774c063e419 100644
--- a/drivers/net/ethernet/netronome/nfp/abm/main.h
+++ b/drivers/net/ethernet/netronome/nfp/abm/main.h
@@ -4,7 +4,11 @@
4#ifndef __NFP_ABM_H__ 4#ifndef __NFP_ABM_H__
5#define __NFP_ABM_H__ 1 5#define __NFP_ABM_H__ 1
6 6
7#include <linux/bits.h>
7#include <net/devlink.h> 8#include <net/devlink.h>
9#include <net/pkt_cls.h>
10
11#define NFP_ABM_LVL_INFINITY S32_MAX
8 12
9struct nfp_app; 13struct nfp_app;
10struct nfp_net; 14struct nfp_net;
@@ -91,6 +95,11 @@ struct nfp_abm_link {
91 struct nfp_red_qdisc *qdiscs; 95 struct nfp_red_qdisc *qdiscs;
92}; 96};
93 97
98int nfp_abm_setup_tc_red(struct net_device *netdev, struct nfp_abm_link *alink,
99 struct tc_red_qopt_offload *opt);
100int nfp_abm_setup_tc_mq(struct net_device *netdev, struct nfp_abm_link *alink,
101 struct tc_mq_qopt_offload *opt);
102
94void nfp_abm_ctrl_read_params(struct nfp_abm_link *alink); 103void nfp_abm_ctrl_read_params(struct nfp_abm_link *alink);
95int nfp_abm_ctrl_find_addrs(struct nfp_abm *abm); 104int nfp_abm_ctrl_find_addrs(struct nfp_abm *abm);
96int nfp_abm_ctrl_set_all_q_lvls(struct nfp_abm_link *alink, u32 val); 105int nfp_abm_ctrl_set_all_q_lvls(struct nfp_abm_link *alink, u32 val);
diff --git a/drivers/net/ethernet/netronome/nfp/abm/qdisc.c b/drivers/net/ethernet/netronome/nfp/abm/qdisc.c
new file mode 100644
index 000000000000..bb05f9ee0401
--- /dev/null
+++ b/drivers/net/ethernet/netronome/nfp/abm/qdisc.c
@@ -0,0 +1,301 @@
1// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
2/* Copyright (C) 2018 Netronome Systems, Inc. */
3
4#include <net/pkt_cls.h>
5#include <net/pkt_sched.h>
6#include <net/red.h>
7
8#include "../nfpcore/nfp_cpp.h"
9#include "../nfp_app.h"
10#include "../nfp_port.h"
11#include "main.h"
12
13static int
14__nfp_abm_reset_root(struct net_device *netdev, struct nfp_abm_link *alink,
15 u32 handle, unsigned int qs, u32 init_val)
16{
17 struct nfp_port *port = nfp_port_from_netdev(netdev);
18 int ret;
19
20 ret = nfp_abm_ctrl_set_all_q_lvls(alink, init_val);
21 memset(alink->qdiscs, 0, sizeof(*alink->qdiscs) * alink->num_qdiscs);
22
23 alink->parent = handle;
24 alink->num_qdiscs = qs;
25 port->tc_offload_cnt = qs;
26
27 return ret;
28}
29
30static void
31nfp_abm_reset_root(struct net_device *netdev, struct nfp_abm_link *alink,
32 u32 handle, unsigned int qs)
33{
34 __nfp_abm_reset_root(netdev, alink, handle, qs, NFP_ABM_LVL_INFINITY);
35}
36
37static int
38nfp_abm_red_find(struct nfp_abm_link *alink, struct tc_red_qopt_offload *opt)
39{
40 unsigned int i = TC_H_MIN(opt->parent) - 1;
41
42 if (opt->parent == TC_H_ROOT)
43 i = 0;
44 else if (TC_H_MAJ(alink->parent) == TC_H_MAJ(opt->parent))
45 i = TC_H_MIN(opt->parent) - 1;
46 else
47 return -EOPNOTSUPP;
48
49 if (i >= alink->num_qdiscs || opt->handle != alink->qdiscs[i].handle)
50 return -EOPNOTSUPP;
51
52 return i;
53}
54
55static void
56nfp_abm_red_destroy(struct net_device *netdev, struct nfp_abm_link *alink,
57 u32 handle)
58{
59 unsigned int i;
60
61 for (i = 0; i < alink->num_qdiscs; i++)
62 if (handle == alink->qdiscs[i].handle)
63 break;
64 if (i == alink->num_qdiscs)
65 return;
66
67 if (alink->parent == TC_H_ROOT) {
68 nfp_abm_reset_root(netdev, alink, TC_H_ROOT, 0);
69 } else {
70 nfp_abm_ctrl_set_q_lvl(alink, i, NFP_ABM_LVL_INFINITY);
71 memset(&alink->qdiscs[i], 0, sizeof(*alink->qdiscs));
72 }
73}
74
75static bool
76nfp_abm_red_check_params(struct nfp_abm_link *alink,
77 struct tc_red_qopt_offload *opt)
78{
79 struct nfp_cpp *cpp = alink->abm->app->cpp;
80
81 if (!opt->set.is_ecn) {
82 nfp_warn(cpp, "RED offload failed - drop is not supported (ECN option required) (p:%08x h:%08x)\n",
83 opt->parent, opt->handle);
84 return false;
85 }
86 if (opt->set.is_harddrop) {
87 nfp_warn(cpp, "RED offload failed - harddrop is not supported (p:%08x h:%08x)\n",
88 opt->parent, opt->handle);
89 return false;
90 }
91 if (opt->set.min != opt->set.max) {
92 nfp_warn(cpp, "RED offload failed - unsupported min/max parameters (p:%08x h:%08x)\n",
93 opt->parent, opt->handle);
94 return false;
95 }
96 if (opt->set.min > NFP_ABM_LVL_INFINITY) {
97 nfp_warn(cpp, "RED offload failed - threshold too large %d > %d (p:%08x h:%08x)\n",
98 opt->set.min, NFP_ABM_LVL_INFINITY, opt->parent,
99 opt->handle);
100 return false;
101 }
102
103 return true;
104}
105
106static int
107nfp_abm_red_replace(struct net_device *netdev, struct nfp_abm_link *alink,
108 struct tc_red_qopt_offload *opt)
109{
110 bool existing;
111 int i, err;
112
113 i = nfp_abm_red_find(alink, opt);
114 existing = i >= 0;
115
116 if (!nfp_abm_red_check_params(alink, opt)) {
117 err = -EINVAL;
118 goto err_destroy;
119 }
120
121 if (existing) {
122 if (alink->parent == TC_H_ROOT)
123 err = nfp_abm_ctrl_set_all_q_lvls(alink, opt->set.min);
124 else
125 err = nfp_abm_ctrl_set_q_lvl(alink, i, opt->set.min);
126 if (err)
127 goto err_destroy;
128 return 0;
129 }
130
131 if (opt->parent == TC_H_ROOT) {
132 i = 0;
133 err = __nfp_abm_reset_root(netdev, alink, TC_H_ROOT, 1,
134 opt->set.min);
135 } else if (TC_H_MAJ(alink->parent) == TC_H_MAJ(opt->parent)) {
136 i = TC_H_MIN(opt->parent) - 1;
137 err = nfp_abm_ctrl_set_q_lvl(alink, i, opt->set.min);
138 } else {
139 return -EINVAL;
140 }
141 /* Set the handle to try full clean up, in case IO failed */
142 alink->qdiscs[i].handle = opt->handle;
143 if (err)
144 goto err_destroy;
145
146 if (opt->parent == TC_H_ROOT)
147 err = nfp_abm_ctrl_read_stats(alink, &alink->qdiscs[i].stats);
148 else
149 err = nfp_abm_ctrl_read_q_stats(alink, i,
150 &alink->qdiscs[i].stats);
151 if (err)
152 goto err_destroy;
153
154 if (opt->parent == TC_H_ROOT)
155 err = nfp_abm_ctrl_read_xstats(alink,
156 &alink->qdiscs[i].xstats);
157 else
158 err = nfp_abm_ctrl_read_q_xstats(alink, i,
159 &alink->qdiscs[i].xstats);
160 if (err)
161 goto err_destroy;
162
163 alink->qdiscs[i].stats.backlog_pkts = 0;
164 alink->qdiscs[i].stats.backlog_bytes = 0;
165
166 return 0;
167err_destroy:
168 /* If the qdisc keeps on living, but we can't offload undo changes */
169 if (existing) {
170 opt->set.qstats->qlen -= alink->qdiscs[i].stats.backlog_pkts;
171 opt->set.qstats->backlog -=
172 alink->qdiscs[i].stats.backlog_bytes;
173 }
174 nfp_abm_red_destroy(netdev, alink, opt->handle);
175
176 return err;
177}
178
179static void
180nfp_abm_update_stats(struct nfp_alink_stats *new, struct nfp_alink_stats *old,
181 struct tc_qopt_offload_stats *stats)
182{
183 _bstats_update(stats->bstats, new->tx_bytes - old->tx_bytes,
184 new->tx_pkts - old->tx_pkts);
185 stats->qstats->qlen += new->backlog_pkts - old->backlog_pkts;
186 stats->qstats->backlog += new->backlog_bytes - old->backlog_bytes;
187 stats->qstats->overlimits += new->overlimits - old->overlimits;
188 stats->qstats->drops += new->drops - old->drops;
189}
190
191static int
192nfp_abm_red_stats(struct nfp_abm_link *alink, struct tc_red_qopt_offload *opt)
193{
194 struct nfp_alink_stats *prev_stats;
195 struct nfp_alink_stats stats;
196 int i, err;
197
198 i = nfp_abm_red_find(alink, opt);
199 if (i < 0)
200 return i;
201 prev_stats = &alink->qdiscs[i].stats;
202
203 if (alink->parent == TC_H_ROOT)
204 err = nfp_abm_ctrl_read_stats(alink, &stats);
205 else
206 err = nfp_abm_ctrl_read_q_stats(alink, i, &stats);
207 if (err)
208 return err;
209
210 nfp_abm_update_stats(&stats, prev_stats, &opt->stats);
211
212 *prev_stats = stats;
213
214 return 0;
215}
216
217static int
218nfp_abm_red_xstats(struct nfp_abm_link *alink, struct tc_red_qopt_offload *opt)
219{
220 struct nfp_alink_xstats *prev_xstats;
221 struct nfp_alink_xstats xstats;
222 int i, err;
223
224 i = nfp_abm_red_find(alink, opt);
225 if (i < 0)
226 return i;
227 prev_xstats = &alink->qdiscs[i].xstats;
228
229 if (alink->parent == TC_H_ROOT)
230 err = nfp_abm_ctrl_read_xstats(alink, &xstats);
231 else
232 err = nfp_abm_ctrl_read_q_xstats(alink, i, &xstats);
233 if (err)
234 return err;
235
236 opt->xstats->forced_mark += xstats.ecn_marked - prev_xstats->ecn_marked;
237 opt->xstats->pdrop += xstats.pdrop - prev_xstats->pdrop;
238
239 *prev_xstats = xstats;
240
241 return 0;
242}
243
244int nfp_abm_setup_tc_red(struct net_device *netdev, struct nfp_abm_link *alink,
245 struct tc_red_qopt_offload *opt)
246{
247 switch (opt->command) {
248 case TC_RED_REPLACE:
249 return nfp_abm_red_replace(netdev, alink, opt);
250 case TC_RED_DESTROY:
251 nfp_abm_red_destroy(netdev, alink, opt->handle);
252 return 0;
253 case TC_RED_STATS:
254 return nfp_abm_red_stats(alink, opt);
255 case TC_RED_XSTATS:
256 return nfp_abm_red_xstats(alink, opt);
257 default:
258 return -EOPNOTSUPP;
259 }
260}
261
262static int
263nfp_abm_mq_stats(struct nfp_abm_link *alink, struct tc_mq_qopt_offload *opt)
264{
265 struct nfp_alink_stats stats;
266 unsigned int i;
267 int err;
268
269 for (i = 0; i < alink->num_qdiscs; i++) {
270 if (alink->qdiscs[i].handle == TC_H_UNSPEC)
271 continue;
272
273 err = nfp_abm_ctrl_read_q_stats(alink, i, &stats);
274 if (err)
275 return err;
276
277 nfp_abm_update_stats(&stats, &alink->qdiscs[i].stats,
278 &opt->stats);
279 }
280
281 return 0;
282}
283
284int nfp_abm_setup_tc_mq(struct net_device *netdev, struct nfp_abm_link *alink,
285 struct tc_mq_qopt_offload *opt)
286{
287 switch (opt->command) {
288 case TC_MQ_CREATE:
289 nfp_abm_reset_root(netdev, alink, opt->handle,
290 alink->total_queues);
291 return 0;
292 case TC_MQ_DESTROY:
293 if (opt->handle == alink->parent)
294 nfp_abm_reset_root(netdev, alink, TC_H_ROOT, 0);
295 return 0;
296 case TC_MQ_STATS:
297 return nfp_abm_mq_stats(alink, opt);
298 default:
299 return -EOPNOTSUPP;
300 }
301}
diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net.h b/drivers/net/ethernet/netronome/nfp/nfp_net.h
index 6f0c37d09256..dda02fefc806 100644
--- a/drivers/net/ethernet/netronome/nfp/nfp_net.h
+++ b/drivers/net/ethernet/netronome/nfp/nfp_net.h
@@ -851,7 +851,7 @@ void nfp_net_get_fw_version(struct nfp_net_fw_version *fw_ver,
851 void __iomem *ctrl_bar); 851 void __iomem *ctrl_bar);
852 852
853struct nfp_net * 853struct nfp_net *
854nfp_net_alloc(struct pci_dev *pdev, bool needs_netdev, 854nfp_net_alloc(struct pci_dev *pdev, void __iomem *ctrl_bar, bool needs_netdev,
855 unsigned int max_tx_rings, unsigned int max_rx_rings); 855 unsigned int max_tx_rings, unsigned int max_rx_rings);
856void nfp_net_free(struct nfp_net *nn); 856void nfp_net_free(struct nfp_net *nn);
857 857
diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c
index 6bddfcfdec34..e00d5a2a41ee 100644
--- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c
+++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c
@@ -3560,6 +3560,7 @@ void nfp_net_info(struct nfp_net *nn)
3560/** 3560/**
3561 * nfp_net_alloc() - Allocate netdev and related structure 3561 * nfp_net_alloc() - Allocate netdev and related structure
3562 * @pdev: PCI device 3562 * @pdev: PCI device
3563 * @ctrl_bar: PCI IOMEM with vNIC config memory
3563 * @needs_netdev: Whether to allocate a netdev for this vNIC 3564 * @needs_netdev: Whether to allocate a netdev for this vNIC
3564 * @max_tx_rings: Maximum number of TX rings supported by device 3565 * @max_tx_rings: Maximum number of TX rings supported by device
3565 * @max_rx_rings: Maximum number of RX rings supported by device 3566 * @max_rx_rings: Maximum number of RX rings supported by device
@@ -3570,11 +3571,12 @@ void nfp_net_info(struct nfp_net *nn)
3570 * 3571 *
3571 * Return: NFP Net device structure, or ERR_PTR on error. 3572 * Return: NFP Net device structure, or ERR_PTR on error.
3572 */ 3573 */
3573struct nfp_net *nfp_net_alloc(struct pci_dev *pdev, bool needs_netdev, 3574struct nfp_net *
3574 unsigned int max_tx_rings, 3575nfp_net_alloc(struct pci_dev *pdev, void __iomem *ctrl_bar, bool needs_netdev,
3575 unsigned int max_rx_rings) 3576 unsigned int max_tx_rings, unsigned int max_rx_rings)
3576{ 3577{
3577 struct nfp_net *nn; 3578 struct nfp_net *nn;
3579 int err;
3578 3580
3579 if (needs_netdev) { 3581 if (needs_netdev) {
3580 struct net_device *netdev; 3582 struct net_device *netdev;
@@ -3594,6 +3596,7 @@ struct nfp_net *nfp_net_alloc(struct pci_dev *pdev, bool needs_netdev,
3594 } 3596 }
3595 3597
3596 nn->dp.dev = &pdev->dev; 3598 nn->dp.dev = &pdev->dev;
3599 nn->dp.ctrl_bar = ctrl_bar;
3597 nn->pdev = pdev; 3600 nn->pdev = pdev;
3598 3601
3599 nn->max_tx_rings = max_tx_rings; 3602 nn->max_tx_rings = max_tx_rings;
@@ -3616,7 +3619,19 @@ struct nfp_net *nfp_net_alloc(struct pci_dev *pdev, bool needs_netdev,
3616 3619
3617 timer_setup(&nn->reconfig_timer, nfp_net_reconfig_timer, 0); 3620 timer_setup(&nn->reconfig_timer, nfp_net_reconfig_timer, 0);
3618 3621
3622 err = nfp_net_tlv_caps_parse(&nn->pdev->dev, nn->dp.ctrl_bar,
3623 &nn->tlv_caps);
3624 if (err)
3625 goto err_free_nn;
3626
3619 return nn; 3627 return nn;
3628
3629err_free_nn:
3630 if (nn->dp.netdev)
3631 free_netdev(nn->dp.netdev);
3632 else
3633 vfree(nn);
3634 return ERR_PTR(err);
3620} 3635}
3621 3636
3622/** 3637/**
@@ -3889,11 +3904,6 @@ int nfp_net_init(struct nfp_net *nn)
3889 nn->dp.ctrl |= NFP_NET_CFG_CTRL_IRQMOD; 3904 nn->dp.ctrl |= NFP_NET_CFG_CTRL_IRQMOD;
3890 } 3905 }
3891 3906
3892 err = nfp_net_tlv_caps_parse(&nn->pdev->dev, nn->dp.ctrl_bar,
3893 &nn->tlv_caps);
3894 if (err)
3895 return err;
3896
3897 if (nn->dp.netdev) 3907 if (nn->dp.netdev)
3898 nfp_net_netdev_init(nn); 3908 nfp_net_netdev_init(nn);
3899 3909
diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_main.c b/drivers/net/ethernet/netronome/nfp/nfp_net_main.c
index 1e7d20468a34..08f5fdbd8e41 100644
--- a/drivers/net/ethernet/netronome/nfp/nfp_net_main.c
+++ b/drivers/net/ethernet/netronome/nfp/nfp_net_main.c
@@ -116,13 +116,13 @@ nfp_net_pf_alloc_vnic(struct nfp_pf *pf, bool needs_netdev,
116 n_rx_rings = readl(ctrl_bar + NFP_NET_CFG_MAX_RXRINGS); 116 n_rx_rings = readl(ctrl_bar + NFP_NET_CFG_MAX_RXRINGS);
117 117
118 /* Allocate and initialise the vNIC */ 118 /* Allocate and initialise the vNIC */
119 nn = nfp_net_alloc(pf->pdev, needs_netdev, n_tx_rings, n_rx_rings); 119 nn = nfp_net_alloc(pf->pdev, ctrl_bar, needs_netdev,
120 n_tx_rings, n_rx_rings);
120 if (IS_ERR(nn)) 121 if (IS_ERR(nn))
121 return nn; 122 return nn;
122 123
123 nn->app = pf->app; 124 nn->app = pf->app;
124 nfp_net_get_fw_version(&nn->fw_ver, ctrl_bar); 125 nfp_net_get_fw_version(&nn->fw_ver, ctrl_bar);
125 nn->dp.ctrl_bar = ctrl_bar;
126 nn->tx_bar = qc_bar + tx_base * NFP_QCP_QUEUE_ADDR_SZ; 126 nn->tx_bar = qc_bar + tx_base * NFP_QCP_QUEUE_ADDR_SZ;
127 nn->rx_bar = qc_bar + rx_base * NFP_QCP_QUEUE_ADDR_SZ; 127 nn->rx_bar = qc_bar + rx_base * NFP_QCP_QUEUE_ADDR_SZ;
128 nn->dp.is_vf = 0; 128 nn->dp.is_vf = 0;
diff --git a/drivers/net/ethernet/netronome/nfp/nfp_netvf_main.c b/drivers/net/ethernet/netronome/nfp/nfp_netvf_main.c
index d2c1e9ea5668..1145849ca7ba 100644
--- a/drivers/net/ethernet/netronome/nfp/nfp_netvf_main.c
+++ b/drivers/net/ethernet/netronome/nfp/nfp_netvf_main.c
@@ -172,7 +172,7 @@ static int nfp_netvf_pci_probe(struct pci_dev *pdev,
172 rx_bar_off = NFP_PCIE_QUEUE(startq); 172 rx_bar_off = NFP_PCIE_QUEUE(startq);
173 173
174 /* Allocate and initialise the netdev */ 174 /* Allocate and initialise the netdev */
175 nn = nfp_net_alloc(pdev, true, max_tx_rings, max_rx_rings); 175 nn = nfp_net_alloc(pdev, ctrl_bar, true, max_tx_rings, max_rx_rings);
176 if (IS_ERR(nn)) { 176 if (IS_ERR(nn)) {
177 err = PTR_ERR(nn); 177 err = PTR_ERR(nn);
178 goto err_ctrl_unmap; 178 goto err_ctrl_unmap;
@@ -180,7 +180,6 @@ static int nfp_netvf_pci_probe(struct pci_dev *pdev,
180 vf->nn = nn; 180 vf->nn = nn;
181 181
182 nn->fw_ver = fw_ver; 182 nn->fw_ver = fw_ver;
183 nn->dp.ctrl_bar = ctrl_bar;
184 nn->dp.is_vf = 1; 183 nn->dp.is_vf = 1;
185 nn->stride_tx = stride; 184 nn->stride_tx = stride;
186 nn->stride_rx = stride; 185 nn->stride_rx = stride;
diff --git a/include/net/pkt_cls.h b/include/net/pkt_cls.h
index 72ffb3120ced..00f71644fbcd 100644
--- a/include/net/pkt_cls.h
+++ b/include/net/pkt_cls.h
@@ -807,6 +807,7 @@ struct tc_red_qopt_offload_params {
807 u32 max; 807 u32 max;
808 u32 probability; 808 u32 probability;
809 bool is_ecn; 809 bool is_ecn;
810 bool is_harddrop;
810 struct gnet_stats_queue *qstats; 811 struct gnet_stats_queue *qstats;
811}; 812};
812 813
diff --git a/net/sched/sch_red.c b/net/sched/sch_red.c
index 7682f7a618a1..a1d08bdd9357 100644
--- a/net/sched/sch_red.c
+++ b/net/sched/sch_red.c
@@ -167,6 +167,7 @@ static int red_offload(struct Qdisc *sch, bool enable)
167 opt.set.max = q->parms.qth_max >> q->parms.Wlog; 167 opt.set.max = q->parms.qth_max >> q->parms.Wlog;
168 opt.set.probability = q->parms.max_P; 168 opt.set.probability = q->parms.max_P;
169 opt.set.is_ecn = red_use_ecn(q); 169 opt.set.is_ecn = red_use_ecn(q);
170 opt.set.is_harddrop = red_use_harddrop(q);
170 opt.set.qstats = &sch->qstats; 171 opt.set.qstats = &sch->qstats;
171 } else { 172 } else {
172 opt.command = TC_RED_DESTROY; 173 opt.command = TC_RED_DESTROY;