root/drivers/net/ethernet/chelsio/cxgb4/sched.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. t4_sched_class_fw_cmd
  2. t4_sched_bind_unbind_op
  3. t4_sched_queue_lookup
  4. t4_sched_queue_unbind
  5. t4_sched_queue_bind
  6. t4_sched_class_unbind_all
  7. t4_sched_class_bind_unbind_op
  8. cxgb4_sched_class_bind
  9. cxgb4_sched_class_unbind
  10. t4_sched_class_lookup
  11. t4_sched_class_alloc
  12. cxgb4_sched_class_alloc
  13. t4_sched_class_free
  14. t4_init_sched
  15. t4_cleanup_sched

   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                 err = t4_sched_params(adap, p->type,
  54                                       p->u.params.level, p->u.params.mode,
  55                                       p->u.params.rateunit,
  56                                       p->u.params.ratemode,
  57                                       p->u.params.channel, e->idx,
  58                                       p->u.params.minrate, p->u.params.maxrate,
  59                                       p->u.params.weight, p->u.params.pktsize);
  60                 break;
  61         default:
  62                 err = -ENOTSUPP;
  63                 break;
  64         }
  65 
  66         return err;
  67 }
  68 
  69 static int t4_sched_bind_unbind_op(struct port_info *pi, void *arg,
  70                                    enum sched_bind_type type, bool bind)
  71 {
  72         struct adapter *adap = pi->adapter;
  73         u32 fw_mnem, fw_class, fw_param;
  74         unsigned int pf = adap->pf;
  75         unsigned int vf = 0;
  76         int err = 0;
  77 
  78         switch (type) {
  79         case SCHED_QUEUE: {
  80                 struct sched_queue_entry *qe;
  81 
  82                 qe = (struct sched_queue_entry *)arg;
  83 
  84                 /* Create a template for the FW_PARAMS_CMD mnemonic and
  85                  * value (TX Scheduling Class in this case).
  86                  */
  87                 fw_mnem = (FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_DMAQ) |
  88                            FW_PARAMS_PARAM_X_V(
  89                                    FW_PARAMS_PARAM_DMAQ_EQ_SCHEDCLASS_ETH));
  90                 fw_class = bind ? qe->param.class : FW_SCHED_CLS_NONE;
  91                 fw_param = (fw_mnem | FW_PARAMS_PARAM_YZ_V(qe->cntxt_id));
  92 
  93                 pf = adap->pf;
  94                 vf = 0;
  95                 break;
  96         }
  97         default:
  98                 err = -ENOTSUPP;
  99                 goto out;
 100         }
 101 
 102         err = t4_set_params(adap, adap->mbox, pf, vf, 1, &fw_param, &fw_class);
 103 
 104 out:
 105         return err;
 106 }
 107 
 108 static struct sched_class *t4_sched_queue_lookup(struct port_info *pi,
 109                                                  const unsigned int qid,
 110                                                  int *index)
 111 {
 112         struct sched_table *s = pi->sched_tbl;
 113         struct sched_class *e, *end;
 114         struct sched_class *found = NULL;
 115         int i;
 116 
 117         /* Look for a class with matching bound queue parameters */
 118         end = &s->tab[s->sched_size];
 119         for (e = &s->tab[0]; e != end; ++e) {
 120                 struct sched_queue_entry *qe;
 121 
 122                 i = 0;
 123                 if (e->state == SCHED_STATE_UNUSED)
 124                         continue;
 125 
 126                 list_for_each_entry(qe, &e->queue_list, list) {
 127                         if (qe->cntxt_id == qid) {
 128                                 found = e;
 129                                 if (index)
 130                                         *index = i;
 131                                 break;
 132                         }
 133                         i++;
 134                 }
 135 
 136                 if (found)
 137                         break;
 138         }
 139 
 140         return found;
 141 }
 142 
 143 static int t4_sched_queue_unbind(struct port_info *pi, struct ch_sched_queue *p)
 144 {
 145         struct adapter *adap = pi->adapter;
 146         struct sched_class *e;
 147         struct sched_queue_entry *qe = NULL;
 148         struct sge_eth_txq *txq;
 149         unsigned int qid;
 150         int index = -1;
 151         int err = 0;
 152 
 153         if (p->queue < 0 || p->queue >= pi->nqsets)
 154                 return -ERANGE;
 155 
 156         txq = &adap->sge.ethtxq[pi->first_qset + p->queue];
 157         qid = txq->q.cntxt_id;
 158 
 159         /* Find the existing class that the queue is bound to */
 160         e = t4_sched_queue_lookup(pi, qid, &index);
 161         if (e && index >= 0) {
 162                 int i = 0;
 163 
 164                 list_for_each_entry(qe, &e->queue_list, list) {
 165                         if (i == index)
 166                                 break;
 167                         i++;
 168                 }
 169                 err = t4_sched_bind_unbind_op(pi, (void *)qe, SCHED_QUEUE,
 170                                               false);
 171                 if (err)
 172                         return err;
 173 
 174                 list_del(&qe->list);
 175                 kvfree(qe);
 176                 if (atomic_dec_and_test(&e->refcnt)) {
 177                         e->state = SCHED_STATE_UNUSED;
 178                         memset(&e->info, 0, sizeof(e->info));
 179                 }
 180         }
 181         return err;
 182 }
 183 
 184 static int t4_sched_queue_bind(struct port_info *pi, struct ch_sched_queue *p)
 185 {
 186         struct adapter *adap = pi->adapter;
 187         struct sched_table *s = pi->sched_tbl;
 188         struct sched_class *e;
 189         struct sched_queue_entry *qe = NULL;
 190         struct sge_eth_txq *txq;
 191         unsigned int qid;
 192         int err = 0;
 193 
 194         if (p->queue < 0 || p->queue >= pi->nqsets)
 195                 return -ERANGE;
 196 
 197         qe = kvzalloc(sizeof(struct sched_queue_entry), GFP_KERNEL);
 198         if (!qe)
 199                 return -ENOMEM;
 200 
 201         txq = &adap->sge.ethtxq[pi->first_qset + p->queue];
 202         qid = txq->q.cntxt_id;
 203 
 204         /* Unbind queue from any existing class */
 205         err = t4_sched_queue_unbind(pi, p);
 206         if (err)
 207                 goto out_err;
 208 
 209         /* Bind queue to specified class */
 210         qe->cntxt_id = qid;
 211         memcpy(&qe->param, p, sizeof(qe->param));
 212 
 213         e = &s->tab[qe->param.class];
 214         err = t4_sched_bind_unbind_op(pi, (void *)qe, SCHED_QUEUE, true);
 215         if (err)
 216                 goto out_err;
 217 
 218         list_add_tail(&qe->list, &e->queue_list);
 219         atomic_inc(&e->refcnt);
 220         return err;
 221 
 222 out_err:
 223         kvfree(qe);
 224         return err;
 225 }
 226 
 227 static void t4_sched_class_unbind_all(struct port_info *pi,
 228                                       struct sched_class *e,
 229                                       enum sched_bind_type type)
 230 {
 231         if (!e)
 232                 return;
 233 
 234         switch (type) {
 235         case SCHED_QUEUE: {
 236                 struct sched_queue_entry *qe;
 237 
 238                 list_for_each_entry(qe, &e->queue_list, list)
 239                         t4_sched_queue_unbind(pi, &qe->param);
 240                 break;
 241         }
 242         default:
 243                 break;
 244         }
 245 }
 246 
 247 static int t4_sched_class_bind_unbind_op(struct port_info *pi, void *arg,
 248                                          enum sched_bind_type type, bool bind)
 249 {
 250         int err = 0;
 251 
 252         if (!arg)
 253                 return -EINVAL;
 254 
 255         switch (type) {
 256         case SCHED_QUEUE: {
 257                 struct ch_sched_queue *qe = (struct ch_sched_queue *)arg;
 258 
 259                 if (bind)
 260                         err = t4_sched_queue_bind(pi, qe);
 261                 else
 262                         err = t4_sched_queue_unbind(pi, qe);
 263                 break;
 264         }
 265         default:
 266                 err = -ENOTSUPP;
 267                 break;
 268         }
 269 
 270         return err;
 271 }
 272 
 273 /**
 274  * cxgb4_sched_class_bind - Bind an entity to a scheduling class
 275  * @dev: net_device pointer
 276  * @arg: Entity opaque data
 277  * @type: Entity type (Queue)
 278  *
 279  * Binds an entity (queue) to a scheduling class.  If the entity
 280  * is bound to another class, it will be unbound from the other class
 281  * and bound to the class specified in @arg.
 282  */
 283 int cxgb4_sched_class_bind(struct net_device *dev, void *arg,
 284                            enum sched_bind_type type)
 285 {
 286         struct port_info *pi = netdev2pinfo(dev);
 287         u8 class_id;
 288 
 289         if (!can_sched(dev))
 290                 return -ENOTSUPP;
 291 
 292         if (!arg)
 293                 return -EINVAL;
 294 
 295         switch (type) {
 296         case SCHED_QUEUE: {
 297                 struct ch_sched_queue *qe = (struct ch_sched_queue *)arg;
 298 
 299                 class_id = qe->class;
 300                 break;
 301         }
 302         default:
 303                 return -ENOTSUPP;
 304         }
 305 
 306         if (!valid_class_id(dev, class_id))
 307                 return -EINVAL;
 308 
 309         if (class_id == SCHED_CLS_NONE)
 310                 return -ENOTSUPP;
 311 
 312         return t4_sched_class_bind_unbind_op(pi, arg, type, true);
 313 
 314 }
 315 
 316 /**
 317  * cxgb4_sched_class_unbind - Unbind an entity from a scheduling class
 318  * @dev: net_device pointer
 319  * @arg: Entity opaque data
 320  * @type: Entity type (Queue)
 321  *
 322  * Unbinds an entity (queue) from a scheduling class.
 323  */
 324 int cxgb4_sched_class_unbind(struct net_device *dev, void *arg,
 325                              enum sched_bind_type type)
 326 {
 327         struct port_info *pi = netdev2pinfo(dev);
 328         u8 class_id;
 329 
 330         if (!can_sched(dev))
 331                 return -ENOTSUPP;
 332 
 333         if (!arg)
 334                 return -EINVAL;
 335 
 336         switch (type) {
 337         case SCHED_QUEUE: {
 338                 struct ch_sched_queue *qe = (struct ch_sched_queue *)arg;
 339 
 340                 class_id = qe->class;
 341                 break;
 342         }
 343         default:
 344                 return -ENOTSUPP;
 345         }
 346 
 347         if (!valid_class_id(dev, class_id))
 348                 return -EINVAL;
 349 
 350         return t4_sched_class_bind_unbind_op(pi, arg, type, false);
 351 }
 352 
 353 /* If @p is NULL, fetch any available unused class */
 354 static struct sched_class *t4_sched_class_lookup(struct port_info *pi,
 355                                                 const struct ch_sched_params *p)
 356 {
 357         struct sched_table *s = pi->sched_tbl;
 358         struct sched_class *e, *end;
 359         struct sched_class *found = NULL;
 360 
 361         if (!p) {
 362                 /* Get any available unused class */
 363                 end = &s->tab[s->sched_size];
 364                 for (e = &s->tab[0]; e != end; ++e) {
 365                         if (e->state == SCHED_STATE_UNUSED) {
 366                                 found = e;
 367                                 break;
 368                         }
 369                 }
 370         } else {
 371                 /* Look for a class with matching scheduling parameters */
 372                 struct ch_sched_params info;
 373                 struct ch_sched_params tp;
 374 
 375                 memcpy(&tp, p, sizeof(tp));
 376                 /* Don't try to match class parameter */
 377                 tp.u.params.class = SCHED_CLS_NONE;
 378 
 379                 end = &s->tab[s->sched_size];
 380                 for (e = &s->tab[0]; e != end; ++e) {
 381                         if (e->state == SCHED_STATE_UNUSED)
 382                                 continue;
 383 
 384                         memcpy(&info, &e->info, sizeof(info));
 385                         /* Don't try to match class parameter */
 386                         info.u.params.class = SCHED_CLS_NONE;
 387 
 388                         if ((info.type == tp.type) &&
 389                             (!memcmp(&info.u.params, &tp.u.params,
 390                                      sizeof(info.u.params)))) {
 391                                 found = e;
 392                                 break;
 393                         }
 394                 }
 395         }
 396 
 397         return found;
 398 }
 399 
 400 static struct sched_class *t4_sched_class_alloc(struct port_info *pi,
 401                                                 struct ch_sched_params *p)
 402 {
 403         struct sched_class *e;
 404         u8 class_id;
 405         int err;
 406 
 407         if (!p)
 408                 return NULL;
 409 
 410         class_id = p->u.params.class;
 411 
 412         /* Only accept search for existing class with matching params
 413          * or allocation of new class with specified params
 414          */
 415         if (class_id != SCHED_CLS_NONE)
 416                 return NULL;
 417 
 418         /* See if there's an exisiting class with same
 419          * requested sched params
 420          */
 421         e = t4_sched_class_lookup(pi, p);
 422         if (!e) {
 423                 struct ch_sched_params np;
 424 
 425                 /* Fetch any available unused class */
 426                 e = t4_sched_class_lookup(pi, NULL);
 427                 if (!e)
 428                         return NULL;
 429 
 430                 memcpy(&np, p, sizeof(np));
 431                 np.u.params.class = e->idx;
 432                 /* New class */
 433                 err = t4_sched_class_fw_cmd(pi, &np, SCHED_FW_OP_ADD);
 434                 if (err)
 435                         return NULL;
 436                 memcpy(&e->info, &np, sizeof(e->info));
 437                 atomic_set(&e->refcnt, 0);
 438                 e->state = SCHED_STATE_ACTIVE;
 439         }
 440 
 441         return e;
 442 }
 443 
 444 /**
 445  * cxgb4_sched_class_alloc - allocate a scheduling class
 446  * @dev: net_device pointer
 447  * @p: new scheduling class to create.
 448  *
 449  * Returns pointer to the scheduling class created.  If @p is NULL, then
 450  * it allocates and returns any available unused scheduling class. If a
 451  * scheduling class with matching @p is found, then the matching class is
 452  * returned.
 453  */
 454 struct sched_class *cxgb4_sched_class_alloc(struct net_device *dev,
 455                                             struct ch_sched_params *p)
 456 {
 457         struct port_info *pi = netdev2pinfo(dev);
 458         u8 class_id;
 459 
 460         if (!can_sched(dev))
 461                 return NULL;
 462 
 463         class_id = p->u.params.class;
 464         if (!valid_class_id(dev, class_id))
 465                 return NULL;
 466 
 467         return t4_sched_class_alloc(pi, p);
 468 }
 469 
 470 static void t4_sched_class_free(struct port_info *pi, struct sched_class *e)
 471 {
 472         t4_sched_class_unbind_all(pi, e, SCHED_QUEUE);
 473 }
 474 
 475 struct sched_table *t4_init_sched(unsigned int sched_size)
 476 {
 477         struct sched_table *s;
 478         unsigned int i;
 479 
 480         s = kvzalloc(struct_size(s, tab, sched_size), GFP_KERNEL);
 481         if (!s)
 482                 return NULL;
 483 
 484         s->sched_size = sched_size;
 485 
 486         for (i = 0; i < s->sched_size; i++) {
 487                 memset(&s->tab[i], 0, sizeof(struct sched_class));
 488                 s->tab[i].idx = i;
 489                 s->tab[i].state = SCHED_STATE_UNUSED;
 490                 INIT_LIST_HEAD(&s->tab[i].queue_list);
 491                 atomic_set(&s->tab[i].refcnt, 0);
 492         }
 493         return s;
 494 }
 495 
 496 void t4_cleanup_sched(struct adapter *adap)
 497 {
 498         struct sched_table *s;
 499         unsigned int j, i;
 500 
 501         for_each_port(adap, j) {
 502                 struct port_info *pi = netdev2pinfo(adap->port[j]);
 503 
 504                 s = pi->sched_tbl;
 505                 if (!s)
 506                         continue;
 507 
 508                 for (i = 0; i < s->sched_size; i++) {
 509                         struct sched_class *e;
 510 
 511                         e = &s->tab[i];
 512                         if (e->state == SCHED_STATE_ACTIVE)
 513                                 t4_sched_class_free(pi, e);
 514                 }
 515                 kvfree(s);
 516         }
 517 }

/* [<][>][^][v][top][bottom][index][help] */