Merge git://git.kernel.org/pub/scm/linux/kernel/git/kvalo/wireless-drivers.git
[platform/kernel/linux-starfive.git] / drivers / net / ethernet / chelsio / cxgb4 / sched.c
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(adap, p->type,
55                                       p->u.params.level, p->u.params.mode,
56                                       p->u.params.rateunit,
57                                       p->u.params.ratemode,
58                                       p->u.params.channel, e->idx,
59                                       p->u.params.minrate, p->u.params.maxrate,
60                                       p->u.params.weight, p->u.params.pktsize,
61                                       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, adap->mbox, pf, vf, 1,
99                                     &fw_param, &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(adap->port[pi->port_id],
109                                                fe->param.tid, 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, SCHED_QUEUE, 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, SCHED_QUEUE, txq->q.cntxt_id);
200         if (qe) {
201                 err = t4_sched_bind_unbind_op(pi, (void *)qe, SCHED_QUEUE,
202                                               false);
203                 if (err)
204                         return err;
205
206                 e = &pi->sched_tbl->tab[qe->param.class];
207                 list_del(&qe->list);
208                 kvfree(qe);
209                 if (atomic_dec_and_test(&e->refcnt))
210                         cxgb4_sched_class_free(adap->port[pi->port_id], 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(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, (void *)qe, SCHED_QUEUE, true);
246         if (err)
247                 goto out_err;
248
249         list_add_tail(&qe->list, &e->entry_list);
250         e->bind_type = SCHED_QUEUE;
251         atomic_inc(&e->refcnt);
252         return err;
253
254 out_err:
255         kvfree(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, SCHED_FLOWC, p->tid);
271         if (fe) {
272                 err = t4_sched_bind_unbind_op(pi, (void *)fe, SCHED_FLOWC,
273                                               false);
274                 if (err)
275                         return err;
276
277                 e = &pi->sched_tbl->tab[fe->param.class];
278                 list_del(&fe->list);
279                 kvfree(fe);
280                 if (atomic_dec_and_test(&e->refcnt))
281                         cxgb4_sched_class_free(adap->port[pi->port_id], 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(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, (void *)fe, SCHED_FLOWC, true);
311         if (err)
312                 goto out_err;
313
314         list_add_tail(&fe->list, &e->entry_list);
315         e->bind_type = SCHED_FLOWC;
316         atomic_inc(&e->refcnt);
317         return err;
318
319 out_err:
320         kvfree(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, &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, &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, qe);
365                 else
366                         err = t4_sched_queue_unbind(pi, 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, fe);
374                 else
375                         err = t4_sched_flowc_unbind(pi, 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, 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, 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(&info.u.params, &tp.u.params,
515                                      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, &np, SCHED_FW_OP_ADD);
562                 if (err)
563                         return NULL;
564                 memcpy(&e->info, &np, sizeof(e->info));
565                 atomic_set(&e->refcnt, 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(&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, &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, 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, e->bind_type);
648         cxgb4_sched_class_free(dev, 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(&s->tab[i].entry_list);
667                 atomic_set(&s->tab[i].refcnt, 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(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(adap->port[j], e);
690                 }
691                 kvfree(s);
692         }
693 }