t4_sched.c revision 318850
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/11/sys/dev/cxgbe/t4_sched.c 318850 2017-05-25 01:40:40Z 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
47static int
48in_range(int val, int lo, int hi)
49{
50
51	return (val < 0 || (val <= hi && val >= lo));
52}
53
54static int
55set_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
71static int
72set_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	}
183done:
184	end_synchronized_op(sc, sleep_ok ? 0 : LOCK_HELD);
185
186	return (rc);
187}
188
189static void
190update_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
239int
240t4_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
255int
256t4_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;
321done:
322	end_synchronized_op(sc, 0);
323	return (rc);
324}
325
326int
327t4_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
378int
379t4_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
394void
395t4_update_tx_sched(struct adapter *sc)
396{
397
398	taskqueue_enqueue(taskqueue_thread, &sc->tc_task);
399}
400
401int
402t4_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	}
442done:
443	mtx_unlock(&sc->tc_lock);
444	return (rc);
445}
446
447void
448t4_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