sched.c (16418B)
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 41static 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 71static 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 120static 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 169struct 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 185static 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 215static 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 254out_err: 255 kvfree(qe); 256 return err; 257} 258 259static 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 286static 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 319out_err: 320 kvfree(fe); 321 return err; 322} 323 324static 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 351static 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 */ 396int 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 */ 443int 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 */ 479static 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 525static 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 */ 582struct 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 */ 605void 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 643static 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 651struct 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 672void 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}