1 | /* |
2 | * This file is part of the Chelsio T4 Ethernet driver for Linux. |
3 | * |
4 | * Copyright (c) 2016 Chelsio Communications, Inc. All rights reserved. |
5 | * |
6 | * This software is available to you under a choice of one of two |
7 | * licenses. You may choose to be licensed under the terms of the GNU |
8 | * General Public License (GPL) Version 2, available from the file |
9 | * COPYING in the main directory of this source tree, or the |
10 | * OpenIB.org BSD license below: |
11 | * |
12 | * Redistribution and use in source and binary forms, with or |
13 | * without modification, are permitted provided that the following |
14 | * conditions are met: |
15 | * |
16 | * - Redistributions of source code must retain the above |
17 | * copyright notice, this list of conditions and the following |
18 | * disclaimer. |
19 | * |
20 | * - Redistributions in binary form must reproduce the above |
21 | * copyright notice, this list of conditions and the following |
22 | * disclaimer in the documentation and/or other materials |
23 | * provided with the distribution. |
24 | * |
25 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
26 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
27 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
28 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS |
29 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN |
30 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
31 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
32 | * SOFTWARE. |
33 | */ |
34 | |
35 | #include <linux/module.h> |
36 | #include <linux/netdevice.h> |
37 | |
38 | #include "cxgb4.h" |
39 | #include "sched.h" |
40 | |
41 | static int t4_sched_class_fw_cmd(struct port_info *pi, |
42 | struct ch_sched_params *p, |
43 | enum sched_fw_ops op) |
44 | { |
45 | struct adapter *adap = pi->adapter; |
46 | struct sched_table *s = pi->sched_tbl; |
47 | struct sched_class *e; |
48 | int err = 0; |
49 | |
50 | e = &s->tab[p->u.params.class]; |
51 | switch (op) { |
52 | case SCHED_FW_OP_ADD: |
53 | case SCHED_FW_OP_DEL: |
54 | err = t4_sched_params(adapter: adap, type: p->type, |
55 | level: p->u.params.level, mode: p->u.params.mode, |
56 | rateunit: p->u.params.rateunit, |
57 | ratemode: p->u.params.ratemode, |
58 | channel: p->u.params.channel, class: e->idx, |
59 | minrate: p->u.params.minrate, maxrate: p->u.params.maxrate, |
60 | weight: p->u.params.weight, pktsize: p->u.params.pktsize, |
61 | burstsize: p->u.params.burstsize); |
62 | break; |
63 | default: |
64 | err = -ENOTSUPP; |
65 | break; |
66 | } |
67 | |
68 | return err; |
69 | } |
70 | |
71 | static int t4_sched_bind_unbind_op(struct port_info *pi, void *arg, |
72 | enum sched_bind_type type, bool bind) |
73 | { |
74 | struct adapter *adap = pi->adapter; |
75 | u32 fw_mnem, fw_class, fw_param; |
76 | unsigned int pf = adap->pf; |
77 | unsigned int vf = 0; |
78 | int err = 0; |
79 | |
80 | switch (type) { |
81 | case SCHED_QUEUE: { |
82 | struct sched_queue_entry *qe; |
83 | |
84 | qe = (struct sched_queue_entry *)arg; |
85 | |
86 | /* Create a template for the FW_PARAMS_CMD mnemonic and |
87 | * value (TX Scheduling Class in this case). |
88 | */ |
89 | fw_mnem = (FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_DMAQ) | |
90 | FW_PARAMS_PARAM_X_V( |
91 | FW_PARAMS_PARAM_DMAQ_EQ_SCHEDCLASS_ETH)); |
92 | fw_class = bind ? qe->param.class : FW_SCHED_CLS_NONE; |
93 | fw_param = (fw_mnem | FW_PARAMS_PARAM_YZ_V(qe->cntxt_id)); |
94 | |
95 | pf = adap->pf; |
96 | vf = 0; |
97 | |
98 | err = t4_set_params(adap, mbox: adap->mbox, pf, vf, nparams: 1, |
99 | params: &fw_param, val: &fw_class); |
100 | break; |
101 | } |
102 | case SCHED_FLOWC: { |
103 | struct sched_flowc_entry *fe; |
104 | |
105 | fe = (struct sched_flowc_entry *)arg; |
106 | |
107 | fw_class = bind ? fe->param.class : FW_SCHED_CLS_NONE; |
108 | err = cxgb4_ethofld_send_flowc(dev: adap->port[pi->port_id], |
109 | eotid: fe->param.tid, tc: fw_class); |
110 | break; |
111 | } |
112 | default: |
113 | err = -ENOTSUPP; |
114 | break; |
115 | } |
116 | |
117 | return err; |
118 | } |
119 | |
120 | static void *t4_sched_entry_lookup(struct port_info *pi, |
121 | enum sched_bind_type type, |
122 | const u32 val) |
123 | { |
124 | struct sched_table *s = pi->sched_tbl; |
125 | struct sched_class *e, *end; |
126 | void *found = NULL; |
127 | |
128 | /* Look for an entry with matching @val */ |
129 | end = &s->tab[s->sched_size]; |
130 | for (e = &s->tab[0]; e != end; ++e) { |
131 | if (e->state == SCHED_STATE_UNUSED || |
132 | e->bind_type != type) |
133 | continue; |
134 | |
135 | switch (type) { |
136 | case SCHED_QUEUE: { |
137 | struct sched_queue_entry *qe; |
138 | |
139 | list_for_each_entry(qe, &e->entry_list, list) { |
140 | if (qe->cntxt_id == val) { |
141 | found = qe; |
142 | break; |
143 | } |
144 | } |
145 | break; |
146 | } |
147 | case SCHED_FLOWC: { |
148 | struct sched_flowc_entry *fe; |
149 | |
150 | list_for_each_entry(fe, &e->entry_list, list) { |
151 | if (fe->param.tid == val) { |
152 | found = fe; |
153 | break; |
154 | } |
155 | } |
156 | break; |
157 | } |
158 | default: |
159 | return NULL; |
160 | } |
161 | |
162 | if (found) |
163 | break; |
164 | } |
165 | |
166 | return found; |
167 | } |
168 | |
169 | struct sched_class *cxgb4_sched_queue_lookup(struct net_device *dev, |
170 | struct ch_sched_queue *p) |
171 | { |
172 | struct port_info *pi = netdev2pinfo(dev); |
173 | struct sched_queue_entry *qe = NULL; |
174 | struct adapter *adap = pi->adapter; |
175 | struct sge_eth_txq *txq; |
176 | |
177 | if (p->queue < 0 || p->queue >= pi->nqsets) |
178 | return NULL; |
179 | |
180 | txq = &adap->sge.ethtxq[pi->first_qset + p->queue]; |
181 | qe = t4_sched_entry_lookup(pi, type: SCHED_QUEUE, val: txq->q.cntxt_id); |
182 | return qe ? &pi->sched_tbl->tab[qe->param.class] : NULL; |
183 | } |
184 | |
185 | static int t4_sched_queue_unbind(struct port_info *pi, struct ch_sched_queue *p) |
186 | { |
187 | struct sched_queue_entry *qe = NULL; |
188 | struct adapter *adap = pi->adapter; |
189 | struct sge_eth_txq *txq; |
190 | struct sched_class *e; |
191 | int err = 0; |
192 | |
193 | if (p->queue < 0 || p->queue >= pi->nqsets) |
194 | return -ERANGE; |
195 | |
196 | txq = &adap->sge.ethtxq[pi->first_qset + p->queue]; |
197 | |
198 | /* Find the existing entry that the queue is bound to */ |
199 | qe = t4_sched_entry_lookup(pi, type: SCHED_QUEUE, val: txq->q.cntxt_id); |
200 | if (qe) { |
201 | err = t4_sched_bind_unbind_op(pi, arg: (void *)qe, type: SCHED_QUEUE, |
202 | bind: false); |
203 | if (err) |
204 | return err; |
205 | |
206 | e = &pi->sched_tbl->tab[qe->param.class]; |
207 | list_del(entry: &qe->list); |
208 | kvfree(addr: qe); |
209 | if (atomic_dec_and_test(v: &e->refcnt)) |
210 | cxgb4_sched_class_free(dev: adap->port[pi->port_id], classid: e->idx); |
211 | } |
212 | return err; |
213 | } |
214 | |
215 | static int t4_sched_queue_bind(struct port_info *pi, struct ch_sched_queue *p) |
216 | { |
217 | struct sched_table *s = pi->sched_tbl; |
218 | struct sched_queue_entry *qe = NULL; |
219 | struct adapter *adap = pi->adapter; |
220 | struct sge_eth_txq *txq; |
221 | struct sched_class *e; |
222 | unsigned int qid; |
223 | int err = 0; |
224 | |
225 | if (p->queue < 0 || p->queue >= pi->nqsets) |
226 | return -ERANGE; |
227 | |
228 | qe = kvzalloc(size: sizeof(struct sched_queue_entry), GFP_KERNEL); |
229 | if (!qe) |
230 | return -ENOMEM; |
231 | |
232 | txq = &adap->sge.ethtxq[pi->first_qset + p->queue]; |
233 | qid = txq->q.cntxt_id; |
234 | |
235 | /* Unbind queue from any existing class */ |
236 | err = t4_sched_queue_unbind(pi, p); |
237 | if (err) |
238 | goto out_err; |
239 | |
240 | /* Bind queue to specified class */ |
241 | qe->cntxt_id = qid; |
242 | memcpy(&qe->param, p, sizeof(qe->param)); |
243 | |
244 | e = &s->tab[qe->param.class]; |
245 | err = t4_sched_bind_unbind_op(pi, arg: (void *)qe, type: SCHED_QUEUE, bind: true); |
246 | if (err) |
247 | goto out_err; |
248 | |
249 | list_add_tail(new: &qe->list, head: &e->entry_list); |
250 | e->bind_type = SCHED_QUEUE; |
251 | atomic_inc(v: &e->refcnt); |
252 | return err; |
253 | |
254 | out_err: |
255 | kvfree(addr: qe); |
256 | return err; |
257 | } |
258 | |
259 | static int t4_sched_flowc_unbind(struct port_info *pi, struct ch_sched_flowc *p) |
260 | { |
261 | struct sched_flowc_entry *fe = NULL; |
262 | struct adapter *adap = pi->adapter; |
263 | struct sched_class *e; |
264 | int err = 0; |
265 | |
266 | if (p->tid < 0 || p->tid >= adap->tids.neotids) |
267 | return -ERANGE; |
268 | |
269 | /* Find the existing entry that the flowc is bound to */ |
270 | fe = t4_sched_entry_lookup(pi, type: SCHED_FLOWC, val: p->tid); |
271 | if (fe) { |
272 | err = t4_sched_bind_unbind_op(pi, arg: (void *)fe, type: SCHED_FLOWC, |
273 | bind: false); |
274 | if (err) |
275 | return err; |
276 | |
277 | e = &pi->sched_tbl->tab[fe->param.class]; |
278 | list_del(entry: &fe->list); |
279 | kvfree(addr: fe); |
280 | if (atomic_dec_and_test(v: &e->refcnt)) |
281 | cxgb4_sched_class_free(dev: adap->port[pi->port_id], classid: e->idx); |
282 | } |
283 | return err; |
284 | } |
285 | |
286 | static int t4_sched_flowc_bind(struct port_info *pi, struct ch_sched_flowc *p) |
287 | { |
288 | struct sched_table *s = pi->sched_tbl; |
289 | struct sched_flowc_entry *fe = NULL; |
290 | struct adapter *adap = pi->adapter; |
291 | struct sched_class *e; |
292 | int err = 0; |
293 | |
294 | if (p->tid < 0 || p->tid >= adap->tids.neotids) |
295 | return -ERANGE; |
296 | |
297 | fe = kvzalloc(size: sizeof(*fe), GFP_KERNEL); |
298 | if (!fe) |
299 | return -ENOMEM; |
300 | |
301 | /* Unbind flowc from any existing class */ |
302 | err = t4_sched_flowc_unbind(pi, p); |
303 | if (err) |
304 | goto out_err; |
305 | |
306 | /* Bind flowc to specified class */ |
307 | memcpy(&fe->param, p, sizeof(fe->param)); |
308 | |
309 | e = &s->tab[fe->param.class]; |
310 | err = t4_sched_bind_unbind_op(pi, arg: (void *)fe, type: SCHED_FLOWC, bind: true); |
311 | if (err) |
312 | goto out_err; |
313 | |
314 | list_add_tail(new: &fe->list, head: &e->entry_list); |
315 | e->bind_type = SCHED_FLOWC; |
316 | atomic_inc(v: &e->refcnt); |
317 | return err; |
318 | |
319 | out_err: |
320 | kvfree(addr: fe); |
321 | return err; |
322 | } |
323 | |
324 | static void t4_sched_class_unbind_all(struct port_info *pi, |
325 | struct sched_class *e, |
326 | enum sched_bind_type type) |
327 | { |
328 | if (!e) |
329 | return; |
330 | |
331 | switch (type) { |
332 | case SCHED_QUEUE: { |
333 | struct sched_queue_entry *qe; |
334 | |
335 | list_for_each_entry(qe, &e->entry_list, list) |
336 | t4_sched_queue_unbind(pi, p: &qe->param); |
337 | break; |
338 | } |
339 | case SCHED_FLOWC: { |
340 | struct sched_flowc_entry *fe; |
341 | |
342 | list_for_each_entry(fe, &e->entry_list, list) |
343 | t4_sched_flowc_unbind(pi, p: &fe->param); |
344 | break; |
345 | } |
346 | default: |
347 | break; |
348 | } |
349 | } |
350 | |
351 | static int t4_sched_class_bind_unbind_op(struct port_info *pi, void *arg, |
352 | enum sched_bind_type type, bool bind) |
353 | { |
354 | int err = 0; |
355 | |
356 | if (!arg) |
357 | return -EINVAL; |
358 | |
359 | switch (type) { |
360 | case SCHED_QUEUE: { |
361 | struct ch_sched_queue *qe = (struct ch_sched_queue *)arg; |
362 | |
363 | if (bind) |
364 | err = t4_sched_queue_bind(pi, p: qe); |
365 | else |
366 | err = t4_sched_queue_unbind(pi, p: qe); |
367 | break; |
368 | } |
369 | case SCHED_FLOWC: { |
370 | struct ch_sched_flowc *fe = (struct ch_sched_flowc *)arg; |
371 | |
372 | if (bind) |
373 | err = t4_sched_flowc_bind(pi, p: fe); |
374 | else |
375 | err = t4_sched_flowc_unbind(pi, p: fe); |
376 | break; |
377 | } |
378 | default: |
379 | err = -ENOTSUPP; |
380 | break; |
381 | } |
382 | |
383 | return err; |
384 | } |
385 | |
386 | /** |
387 | * cxgb4_sched_class_bind - Bind an entity to a scheduling class |
388 | * @dev: net_device pointer |
389 | * @arg: Entity opaque data |
390 | * @type: Entity type (Queue) |
391 | * |
392 | * Binds an entity (queue) to a scheduling class. If the entity |
393 | * is bound to another class, it will be unbound from the other class |
394 | * and bound to the class specified in @arg. |
395 | */ |
396 | int cxgb4_sched_class_bind(struct net_device *dev, void *arg, |
397 | enum sched_bind_type type) |
398 | { |
399 | struct port_info *pi = netdev2pinfo(dev); |
400 | u8 class_id; |
401 | |
402 | if (!can_sched(dev)) |
403 | return -ENOTSUPP; |
404 | |
405 | if (!arg) |
406 | return -EINVAL; |
407 | |
408 | switch (type) { |
409 | case SCHED_QUEUE: { |
410 | struct ch_sched_queue *qe = (struct ch_sched_queue *)arg; |
411 | |
412 | class_id = qe->class; |
413 | break; |
414 | } |
415 | case SCHED_FLOWC: { |
416 | struct ch_sched_flowc *fe = (struct ch_sched_flowc *)arg; |
417 | |
418 | class_id = fe->class; |
419 | break; |
420 | } |
421 | default: |
422 | return -ENOTSUPP; |
423 | } |
424 | |
425 | if (!valid_class_id(dev, class_id)) |
426 | return -EINVAL; |
427 | |
428 | if (class_id == SCHED_CLS_NONE) |
429 | return -ENOTSUPP; |
430 | |
431 | return t4_sched_class_bind_unbind_op(pi, arg, type, bind: true); |
432 | |
433 | } |
434 | |
435 | /** |
436 | * cxgb4_sched_class_unbind - Unbind an entity from a scheduling class |
437 | * @dev: net_device pointer |
438 | * @arg: Entity opaque data |
439 | * @type: Entity type (Queue) |
440 | * |
441 | * Unbinds an entity (queue) from a scheduling class. |
442 | */ |
443 | int cxgb4_sched_class_unbind(struct net_device *dev, void *arg, |
444 | enum sched_bind_type type) |
445 | { |
446 | struct port_info *pi = netdev2pinfo(dev); |
447 | u8 class_id; |
448 | |
449 | if (!can_sched(dev)) |
450 | return -ENOTSUPP; |
451 | |
452 | if (!arg) |
453 | return -EINVAL; |
454 | |
455 | switch (type) { |
456 | case SCHED_QUEUE: { |
457 | struct ch_sched_queue *qe = (struct ch_sched_queue *)arg; |
458 | |
459 | class_id = qe->class; |
460 | break; |
461 | } |
462 | case SCHED_FLOWC: { |
463 | struct ch_sched_flowc *fe = (struct ch_sched_flowc *)arg; |
464 | |
465 | class_id = fe->class; |
466 | break; |
467 | } |
468 | default: |
469 | return -ENOTSUPP; |
470 | } |
471 | |
472 | if (!valid_class_id(dev, class_id)) |
473 | return -EINVAL; |
474 | |
475 | return t4_sched_class_bind_unbind_op(pi, arg, type, bind: false); |
476 | } |
477 | |
478 | /* If @p is NULL, fetch any available unused class */ |
479 | static struct sched_class *t4_sched_class_lookup(struct port_info *pi, |
480 | const struct ch_sched_params *p) |
481 | { |
482 | struct sched_table *s = pi->sched_tbl; |
483 | struct sched_class *found = NULL; |
484 | struct sched_class *e, *end; |
485 | |
486 | if (!p) { |
487 | /* Get any available unused class */ |
488 | end = &s->tab[s->sched_size]; |
489 | for (e = &s->tab[0]; e != end; ++e) { |
490 | if (e->state == SCHED_STATE_UNUSED) { |
491 | found = e; |
492 | break; |
493 | } |
494 | } |
495 | } else { |
496 | /* Look for a class with matching scheduling parameters */ |
497 | struct ch_sched_params info; |
498 | struct ch_sched_params tp; |
499 | |
500 | memcpy(&tp, p, sizeof(tp)); |
501 | /* Don't try to match class parameter */ |
502 | tp.u.params.class = SCHED_CLS_NONE; |
503 | |
504 | end = &s->tab[s->sched_size]; |
505 | for (e = &s->tab[0]; e != end; ++e) { |
506 | if (e->state == SCHED_STATE_UNUSED) |
507 | continue; |
508 | |
509 | memcpy(&info, &e->info, sizeof(info)); |
510 | /* Don't try to match class parameter */ |
511 | info.u.params.class = SCHED_CLS_NONE; |
512 | |
513 | if ((info.type == tp.type) && |
514 | (!memcmp(p: &info.u.params, q: &tp.u.params, |
515 | size: sizeof(info.u.params)))) { |
516 | found = e; |
517 | break; |
518 | } |
519 | } |
520 | } |
521 | |
522 | return found; |
523 | } |
524 | |
525 | static struct sched_class *t4_sched_class_alloc(struct port_info *pi, |
526 | struct ch_sched_params *p) |
527 | { |
528 | struct sched_class *e = NULL; |
529 | u8 class_id; |
530 | int err; |
531 | |
532 | if (!p) |
533 | return NULL; |
534 | |
535 | class_id = p->u.params.class; |
536 | |
537 | /* Only accept search for existing class with matching params |
538 | * or allocation of new class with specified params |
539 | */ |
540 | if (class_id != SCHED_CLS_NONE) |
541 | return NULL; |
542 | |
543 | /* See if there's an exisiting class with same requested sched |
544 | * params. Classes can only be shared among FLOWC types. For |
545 | * other types, always request a new class. |
546 | */ |
547 | if (p->u.params.mode == SCHED_CLASS_MODE_FLOW) |
548 | e = t4_sched_class_lookup(pi, p); |
549 | |
550 | if (!e) { |
551 | struct ch_sched_params np; |
552 | |
553 | /* Fetch any available unused class */ |
554 | e = t4_sched_class_lookup(pi, NULL); |
555 | if (!e) |
556 | return NULL; |
557 | |
558 | memcpy(&np, p, sizeof(np)); |
559 | np.u.params.class = e->idx; |
560 | /* New class */ |
561 | err = t4_sched_class_fw_cmd(pi, p: &np, op: SCHED_FW_OP_ADD); |
562 | if (err) |
563 | return NULL; |
564 | memcpy(&e->info, &np, sizeof(e->info)); |
565 | atomic_set(v: &e->refcnt, i: 0); |
566 | e->state = SCHED_STATE_ACTIVE; |
567 | } |
568 | |
569 | return e; |
570 | } |
571 | |
572 | /** |
573 | * cxgb4_sched_class_alloc - allocate a scheduling class |
574 | * @dev: net_device pointer |
575 | * @p: new scheduling class to create. |
576 | * |
577 | * Returns pointer to the scheduling class created. If @p is NULL, then |
578 | * it allocates and returns any available unused scheduling class. If a |
579 | * scheduling class with matching @p is found, then the matching class is |
580 | * returned. |
581 | */ |
582 | struct sched_class *cxgb4_sched_class_alloc(struct net_device *dev, |
583 | struct ch_sched_params *p) |
584 | { |
585 | struct port_info *pi = netdev2pinfo(dev); |
586 | u8 class_id; |
587 | |
588 | if (!can_sched(dev)) |
589 | return NULL; |
590 | |
591 | class_id = p->u.params.class; |
592 | if (!valid_class_id(dev, class_id)) |
593 | return NULL; |
594 | |
595 | return t4_sched_class_alloc(pi, p); |
596 | } |
597 | |
598 | /** |
599 | * cxgb4_sched_class_free - free a scheduling class |
600 | * @dev: net_device pointer |
601 | * @classid: scheduling class id to free |
602 | * |
603 | * Frees a scheduling class if there are no users. |
604 | */ |
605 | void cxgb4_sched_class_free(struct net_device *dev, u8 classid) |
606 | { |
607 | struct port_info *pi = netdev2pinfo(dev); |
608 | struct sched_table *s = pi->sched_tbl; |
609 | struct ch_sched_params p; |
610 | struct sched_class *e; |
611 | u32 speed; |
612 | int ret; |
613 | |
614 | e = &s->tab[classid]; |
615 | if (!atomic_read(v: &e->refcnt) && e->state != SCHED_STATE_UNUSED) { |
616 | /* Port based rate limiting needs explicit reset back |
617 | * to max rate. But, we'll do explicit reset for all |
618 | * types, instead of just port based type, to be on |
619 | * the safer side. |
620 | */ |
621 | memcpy(&p, &e->info, sizeof(p)); |
622 | /* Always reset mode to 0. Otherwise, FLOWC mode will |
623 | * still be enabled even after resetting the traffic |
624 | * class. |
625 | */ |
626 | p.u.params.mode = 0; |
627 | p.u.params.minrate = 0; |
628 | p.u.params.pktsize = 0; |
629 | |
630 | ret = t4_get_link_params(pi, NULL, speedp: &speed, NULL); |
631 | if (!ret) |
632 | p.u.params.maxrate = speed * 1000; /* Mbps to Kbps */ |
633 | else |
634 | p.u.params.maxrate = SCHED_MAX_RATE_KBPS; |
635 | |
636 | t4_sched_class_fw_cmd(pi, p: &p, op: SCHED_FW_OP_DEL); |
637 | |
638 | e->state = SCHED_STATE_UNUSED; |
639 | memset(&e->info, 0, sizeof(e->info)); |
640 | } |
641 | } |
642 | |
643 | static void t4_sched_class_free(struct net_device *dev, struct sched_class *e) |
644 | { |
645 | struct port_info *pi = netdev2pinfo(dev); |
646 | |
647 | t4_sched_class_unbind_all(pi, e, type: e->bind_type); |
648 | cxgb4_sched_class_free(dev, classid: e->idx); |
649 | } |
650 | |
651 | struct sched_table *t4_init_sched(unsigned int sched_size) |
652 | { |
653 | struct sched_table *s; |
654 | unsigned int i; |
655 | |
656 | s = kvzalloc(struct_size(s, tab, sched_size), GFP_KERNEL); |
657 | if (!s) |
658 | return NULL; |
659 | |
660 | s->sched_size = sched_size; |
661 | |
662 | for (i = 0; i < s->sched_size; i++) { |
663 | memset(&s->tab[i], 0, sizeof(struct sched_class)); |
664 | s->tab[i].idx = i; |
665 | s->tab[i].state = SCHED_STATE_UNUSED; |
666 | INIT_LIST_HEAD(list: &s->tab[i].entry_list); |
667 | atomic_set(v: &s->tab[i].refcnt, i: 0); |
668 | } |
669 | return s; |
670 | } |
671 | |
672 | void t4_cleanup_sched(struct adapter *adap) |
673 | { |
674 | struct sched_table *s; |
675 | unsigned int j, i; |
676 | |
677 | for_each_port(adap, j) { |
678 | struct port_info *pi = netdev2pinfo(dev: adap->port[j]); |
679 | |
680 | s = pi->sched_tbl; |
681 | if (!s) |
682 | continue; |
683 | |
684 | for (i = 0; i < s->sched_size; i++) { |
685 | struct sched_class *e; |
686 | |
687 | e = &s->tab[i]; |
688 | if (e->state == SCHED_STATE_ACTIVE) |
689 | t4_sched_class_free(dev: adap->port[j], e); |
690 | } |
691 | kvfree(addr: s); |
692 | } |
693 | } |
694 | |