1 /*-
2 * Copyright (c) 2017 Chelsio Communications, Inc.
3 * All rights reserved.
4 * Written by: Navdeep Parhar <np@FreeBSD.org>
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD: stable/10/sys/dev/cxgbe/t4_sched.c 318851 2017-05-25 01:43:28Z np $");
30
31 #include "opt_inet.h"
32 #include "opt_inet6.h"
33
34 #include <sys/types.h>
35 #include <sys/malloc.h>
36 #include <sys/queue.h>
37 #include <sys/sbuf.h>
38 #include <sys/taskqueue.h>
39 #include <sys/sysctl.h>
40
41 #include "common/common.h"
42 #include "common/t4_regs.h"
43 #include "common/t4_regs_values.h"
44 #include "common/t4_msg.h"
45
46
47 static int
in_range(int val,int lo,int hi)48 in_range(int val, int lo, int hi)
49 {
50
51 return (val < 0 || (val <= hi && val >= lo));
52 }
53
54 static int
set_sched_class_config(struct adapter * sc,int minmax)55 set_sched_class_config(struct adapter *sc, int minmax)
56 {
57 int rc;
58
59 if (minmax < 0)
60 return (EINVAL);
61
62 rc = begin_synchronized_op(sc, NULL, SLEEP_OK | INTR_OK, "t4sscc");
63 if (rc)
64 return (rc);
65 rc = -t4_sched_config(sc, FW_SCHED_TYPE_PKTSCHED, minmax, 1);
66 end_synchronized_op(sc, 0);
67
68 return (rc);
69 }
70
71 static int
set_sched_class_params(struct adapter * sc,struct t4_sched_class_params * p,int sleep_ok)72 set_sched_class_params(struct adapter *sc, struct t4_sched_class_params *p,
73 int sleep_ok)
74 {
75 int rc, top_speed, fw_level, fw_mode, fw_rateunit, fw_ratemode;
76 struct port_info *pi;
77 struct tx_cl_rl_params *tc;
78
79 if (p->level == SCHED_CLASS_LEVEL_CL_RL)
80 fw_level = FW_SCHED_PARAMS_LEVEL_CL_RL;
81 else if (p->level == SCHED_CLASS_LEVEL_CL_WRR)
82 fw_level = FW_SCHED_PARAMS_LEVEL_CL_WRR;
83 else if (p->level == SCHED_CLASS_LEVEL_CH_RL)
84 fw_level = FW_SCHED_PARAMS_LEVEL_CH_RL;
85 else
86 return (EINVAL);
87
88 if (p->mode == SCHED_CLASS_MODE_CLASS)
89 fw_mode = FW_SCHED_PARAMS_MODE_CLASS;
90 else if (p->mode == SCHED_CLASS_MODE_FLOW)
91 fw_mode = FW_SCHED_PARAMS_MODE_FLOW;
92 else
93 return (EINVAL);
94
95 if (p->rateunit == SCHED_CLASS_RATEUNIT_BITS)
96 fw_rateunit = FW_SCHED_PARAMS_UNIT_BITRATE;
97 else if (p->rateunit == SCHED_CLASS_RATEUNIT_PKTS)
98 fw_rateunit = FW_SCHED_PARAMS_UNIT_PKTRATE;
99 else
100 return (EINVAL);
101
102 if (p->ratemode == SCHED_CLASS_RATEMODE_REL)
103 fw_ratemode = FW_SCHED_PARAMS_RATE_REL;
104 else if (p->ratemode == SCHED_CLASS_RATEMODE_ABS)
105 fw_ratemode = FW_SCHED_PARAMS_RATE_ABS;
106 else
107 return (EINVAL);
108
109 /* Vet our parameters ... */
110 if (!in_range(p->channel, 0, sc->chip_params->nchan - 1))
111 return (ERANGE);
112
113 pi = sc->port[sc->chan_map[p->channel]];
114 if (pi == NULL)
115 return (ENXIO);
116 MPASS(pi->tx_chan == p->channel);
117 top_speed = port_top_speed(pi) * 1000000; /* Gbps -> Kbps */
118
119 if (!in_range(p->cl, 0, sc->chip_params->nsched_cls) ||
120 !in_range(p->minrate, 0, top_speed) ||
121 !in_range(p->maxrate, 0, top_speed) ||
122 !in_range(p->weight, 0, 100))
123 return (ERANGE);
124
125 /*
126 * Translate any unset parameters into the firmware's
127 * nomenclature and/or fail the call if the parameters
128 * are required ...
129 */
130 if (p->rateunit < 0 || p->ratemode < 0 || p->channel < 0 || p->cl < 0)
131 return (EINVAL);
132
133 if (p->minrate < 0)
134 p->minrate = 0;
135 if (p->maxrate < 0) {
136 if (p->level == SCHED_CLASS_LEVEL_CL_RL ||
137 p->level == SCHED_CLASS_LEVEL_CH_RL)
138 return (EINVAL);
139 else
140 p->maxrate = 0;
141 }
142 if (p->weight < 0) {
143 if (p->level == SCHED_CLASS_LEVEL_CL_WRR)
144 return (EINVAL);
145 else
146 p->weight = 0;
147 }
148 if (p->pktsize < 0) {
149 if (p->level == SCHED_CLASS_LEVEL_CL_RL ||
150 p->level == SCHED_CLASS_LEVEL_CH_RL)
151 return (EINVAL);
152 else
153 p->pktsize = 0;
154 }
155
156 rc = begin_synchronized_op(sc, NULL,
157 sleep_ok ? (SLEEP_OK | INTR_OK) : HOLD_LOCK, "t4sscp");
158 if (rc)
159 return (rc);
160 if (p->level == SCHED_CLASS_LEVEL_CL_RL) {
161 tc = &pi->sched_params->cl_rl[p->cl];
162 if (tc->refcount > 0) {
163 rc = EBUSY;
164 goto done;
165 } else {
166 tc->ratemode = fw_ratemode;
167 tc->rateunit = fw_rateunit;
168 tc->mode = fw_mode;
169 tc->maxrate = p->maxrate;
170 tc->pktsize = p->pktsize;
171 }
172 }
173 rc = -t4_sched_params(sc, FW_SCHED_TYPE_PKTSCHED, fw_level, fw_mode,
174 fw_rateunit, fw_ratemode, p->channel, p->cl, p->minrate, p->maxrate,
175 p->weight, p->pktsize, sleep_ok);
176 if (p->level == SCHED_CLASS_LEVEL_CL_RL && rc != 0) {
177 /*
178 * Unknown state at this point, see parameters in tc for what
179 * was attempted.
180 */
181 tc->flags |= TX_CLRL_ERROR;
182 }
183 done:
184 end_synchronized_op(sc, sleep_ok ? 0 : LOCK_HELD);
185
186 return (rc);
187 }
188
189 static void
update_tx_sched(void * context,int pending)190 update_tx_sched(void *context, int pending)
191 {
192 int i, j, mode, rateunit, ratemode, maxrate, pktsize, rc;
193 struct port_info *pi;
194 struct tx_cl_rl_params *tc;
195 struct adapter *sc = context;
196 const int n = sc->chip_params->nsched_cls;
197
198 mtx_lock(&sc->tc_lock);
199 for_each_port(sc, i) {
200 pi = sc->port[i];
201 tc = &pi->sched_params->cl_rl[0];
202 for (j = 0; j < n; j++, tc++) {
203 MPASS(mtx_owned(&sc->tc_lock));
204 if ((tc->flags & TX_CLRL_REFRESH) == 0)
205 continue;
206
207 mode = tc->mode;
208 rateunit = tc->rateunit;
209 ratemode = tc->ratemode;
210 maxrate = tc->maxrate;
211 pktsize = tc->pktsize;
212 mtx_unlock(&sc->tc_lock);
213
214 if (begin_synchronized_op(sc, NULL, SLEEP_OK | INTR_OK,
215 "t4utxs") != 0) {
216 mtx_lock(&sc->tc_lock);
217 continue;
218 }
219 rc = t4_sched_params(sc, FW_SCHED_TYPE_PKTSCHED,
220 FW_SCHED_PARAMS_LEVEL_CL_RL, mode, rateunit,
221 ratemode, pi->tx_chan, j, 0, maxrate, 0, pktsize,
222 1);
223 end_synchronized_op(sc, 0);
224
225 mtx_lock(&sc->tc_lock);
226 if (rc != 0) {
227 tc->flags |= TX_CLRL_ERROR;
228 } else if (tc->mode == mode &&
229 tc->rateunit == rateunit &&
230 tc->maxrate == maxrate &&
231 tc->pktsize == tc->pktsize) {
232 tc->flags &= ~(TX_CLRL_REFRESH | TX_CLRL_ERROR);
233 }
234 }
235 }
236 mtx_unlock(&sc->tc_lock);
237 }
238
239 int
t4_set_sched_class(struct adapter * sc,struct t4_sched_params * p)240 t4_set_sched_class(struct adapter *sc, struct t4_sched_params *p)
241 {
242
243 if (p->type != SCHED_CLASS_TYPE_PACKET)
244 return (EINVAL);
245
246 if (p->subcmd == SCHED_CLASS_SUBCMD_CONFIG)
247 return (set_sched_class_config(sc, p->u.config.minmax));
248
249 if (p->subcmd == SCHED_CLASS_SUBCMD_PARAMS)
250 return (set_sched_class_params(sc, &p->u.params, 1));
251
252 return (EINVAL);
253 }
254
255 int
t4_set_sched_queue(struct adapter * sc,struct t4_sched_queue * p)256 t4_set_sched_queue(struct adapter *sc, struct t4_sched_queue *p)
257 {
258 struct port_info *pi = NULL;
259 struct vi_info *vi;
260 struct sge_txq *txq;
261 uint32_t fw_mnem, fw_queue, fw_class;
262 int i, rc;
263
264 rc = begin_synchronized_op(sc, NULL, SLEEP_OK | INTR_OK, "t4setsq");
265 if (rc)
266 return (rc);
267
268 if (p->port >= sc->params.nports) {
269 rc = EINVAL;
270 goto done;
271 }
272
273 /* XXX: Only supported for the main VI. */
274 pi = sc->port[p->port];
275 vi = &pi->vi[0];
276 if (!(vi->flags & VI_INIT_DONE)) {
277 /* tx queues not set up yet */
278 rc = EAGAIN;
279 goto done;
280 }
281
282 if (!in_range(p->queue, 0, vi->ntxq - 1) ||
283 !in_range(p->cl, 0, sc->chip_params->nsched_cls - 1)) {
284 rc = EINVAL;
285 goto done;
286 }
287
288 /*
289 * Create a template for the FW_PARAMS_CMD mnemonic and value (TX
290 * Scheduling Class in this case).
291 */
292 fw_mnem = (V_FW_PARAMS_MNEM(FW_PARAMS_MNEM_DMAQ) |
293 V_FW_PARAMS_PARAM_X(FW_PARAMS_PARAM_DMAQ_EQ_SCHEDCLASS_ETH));
294 fw_class = p->cl < 0 ? 0xffffffff : p->cl;
295
296 /*
297 * If op.queue is non-negative, then we're only changing the scheduling
298 * on a single specified TX queue.
299 */
300 if (p->queue >= 0) {
301 txq = &sc->sge.txq[vi->first_txq + p->queue];
302 fw_queue = (fw_mnem | V_FW_PARAMS_PARAM_YZ(txq->eq.cntxt_id));
303 rc = -t4_set_params(sc, sc->mbox, sc->pf, 0, 1, &fw_queue,
304 &fw_class);
305 goto done;
306 }
307
308 /*
309 * Change the scheduling on all the TX queues for the
310 * interface.
311 */
312 for_each_txq(vi, i, txq) {
313 fw_queue = (fw_mnem | V_FW_PARAMS_PARAM_YZ(txq->eq.cntxt_id));
314 rc = -t4_set_params(sc, sc->mbox, sc->pf, 0, 1, &fw_queue,
315 &fw_class);
316 if (rc)
317 goto done;
318 }
319
320 rc = 0;
321 done:
322 end_synchronized_op(sc, 0);
323 return (rc);
324 }
325
326 int
t4_init_tx_sched(struct adapter * sc)327 t4_init_tx_sched(struct adapter *sc)
328 {
329 int i, j;
330 const int n = sc->chip_params->nsched_cls;
331 struct port_info *pi;
332 struct tx_cl_rl_params *tc;
333 static const uint32_t init_kbps[] = {
334 100 * 1000,
335 200 * 1000,
336 400 * 1000,
337 500 * 1000,
338 800 * 1000,
339 1000 * 1000,
340 1200 * 1000,
341 1500 * 1000,
342 1800 * 1000,
343 2000 * 1000,
344 2500 * 1000,
345 3000 * 1000,
346 3500 * 1000,
347 4000 * 1000,
348 5000 * 1000,
349 10000 * 1000
350 };
351
352 mtx_init(&sc->tc_lock, "tx_sched lock", NULL, MTX_DEF);
353 TASK_INIT(&sc->tc_task, 0, update_tx_sched, sc);
354 for_each_port(sc, i) {
355 pi = sc->port[i];
356 pi->sched_params = malloc(sizeof(*pi->sched_params) +
357 n * sizeof(*tc), M_CXGBE, M_ZERO | M_WAITOK);
358 tc = &pi->sched_params->cl_rl[0];
359 for (j = 0; j < n; j++, tc++) {
360 tc->refcount = 0;
361 tc->ratemode = FW_SCHED_PARAMS_RATE_ABS;
362 tc->rateunit = FW_SCHED_PARAMS_UNIT_BITRATE;
363 tc->mode = FW_SCHED_PARAMS_MODE_FLOW;
364 tc->maxrate = init_kbps[min(j, nitems(init_kbps) - 1)];
365 tc->pktsize = ETHERMTU; /* XXX */
366
367 if (t4_sched_params_cl_rl_kbps(sc, pi->tx_chan, j,
368 tc->mode, tc->maxrate, tc->pktsize, 1) == 0)
369 tc->flags = 0;
370 else
371 tc->flags = TX_CLRL_ERROR;
372 }
373 }
374
375 return (0);
376 }
377
378 int
t4_free_tx_sched(struct adapter * sc)379 t4_free_tx_sched(struct adapter *sc)
380 {
381 int i;
382
383 taskqueue_drain(taskqueue_thread, &sc->tc_task);
384
385 for_each_port(sc, i)
386 free(sc->port[i]->sched_params, M_CXGBE);
387
388 if (mtx_initialized(&sc->tc_lock))
389 mtx_destroy(&sc->tc_lock);
390
391 return (0);
392 }
393
394 void
t4_update_tx_sched(struct adapter * sc)395 t4_update_tx_sched(struct adapter *sc)
396 {
397
398 taskqueue_enqueue(taskqueue_thread, &sc->tc_task);
399 }
400
401 int
t4_reserve_cl_rl_kbps(struct adapter * sc,int port_id,u_int maxrate,int * tc_idx)402 t4_reserve_cl_rl_kbps(struct adapter *sc, int port_id, u_int maxrate,
403 int *tc_idx)
404 {
405 int rc = 0, fa = -1, i;
406 struct tx_cl_rl_params *tc;
407
408 MPASS(port_id >= 0 && port_id < sc->params.nports);
409
410 tc = &sc->port[port_id]->sched_params->cl_rl[0];
411 mtx_lock(&sc->tc_lock);
412 for (i = 0; i < sc->chip_params->nsched_cls; i++, tc++) {
413 if (fa < 0 && tc->refcount == 0)
414 fa = i;
415
416 if (tc->ratemode == FW_SCHED_PARAMS_RATE_ABS &&
417 tc->rateunit == FW_SCHED_PARAMS_UNIT_BITRATE &&
418 tc->mode == FW_SCHED_PARAMS_MODE_FLOW &&
419 tc->maxrate == maxrate) {
420 tc->refcount++;
421 *tc_idx = i;
422 goto done;
423 }
424 }
425 /* Not found */
426 MPASS(i == sc->chip_params->nsched_cls);
427 if (fa != -1) {
428 tc = &sc->port[port_id]->sched_params->cl_rl[fa];
429 tc->flags = TX_CLRL_REFRESH;
430 tc->refcount = 1;
431 tc->ratemode = FW_SCHED_PARAMS_RATE_ABS;
432 tc->rateunit = FW_SCHED_PARAMS_UNIT_BITRATE;
433 tc->mode = FW_SCHED_PARAMS_MODE_FLOW;
434 tc->maxrate = maxrate;
435 tc->pktsize = ETHERMTU; /* XXX */
436 *tc_idx = fa;
437 t4_update_tx_sched(sc);
438 } else {
439 *tc_idx = -1;
440 rc = ENOSPC;
441 }
442 done:
443 mtx_unlock(&sc->tc_lock);
444 return (rc);
445 }
446
447 void
t4_release_cl_rl_kbps(struct adapter * sc,int port_id,int tc_idx)448 t4_release_cl_rl_kbps(struct adapter *sc, int port_id, int tc_idx)
449 {
450 struct tx_cl_rl_params *tc;
451
452 MPASS(port_id >= 0 && port_id < sc->params.nports);
453 MPASS(tc_idx >= 0 && tc_idx < sc->chip_params->nsched_cls);
454
455 mtx_lock(&sc->tc_lock);
456 tc = &sc->port[port_id]->sched_params->cl_rl[tc_idx];
457 MPASS(tc->refcount > 0);
458 MPASS(tc->ratemode == FW_SCHED_PARAMS_RATE_ABS);
459 MPASS(tc->rateunit == FW_SCHED_PARAMS_UNIT_BITRATE);
460 MPASS(tc->mode == FW_SCHED_PARAMS_MODE_FLOW);
461 tc->refcount--;
462 mtx_unlock(&sc->tc_lock);
463 }
464