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 memset(qe, 0, sizeof(*qe)); 211 qe->cntxt_id = qid; 212 memcpy(&qe->param, p, sizeof(qe->param)); 213 214 e = &s->tab[qe->param.class]; 215 err = t4_sched_bind_unbind_op(pi, (void *)qe, SCHED_QUEUE, true); 216 if (err) 217 goto out_err; 218 219 list_add_tail(&qe->list, &e->queue_list); 220 atomic_inc(&e->refcnt); 221 return err; 222 223 out_err: 224 kvfree(qe); 225 return err; 226 } 227 228 static void t4_sched_class_unbind_all(struct port_info *pi, 229 struct sched_class *e, 230 enum sched_bind_type type) 231 { 232 if (!e) 233 return; 234 235 switch (type) { 236 case SCHED_QUEUE: { 237 struct sched_queue_entry *qe; 238 239 list_for_each_entry(qe, &e->queue_list, list) 240 t4_sched_queue_unbind(pi, &qe->param); 241 break; 242 } 243 default: 244 break; 245 } 246 } 247 248 static int t4_sched_class_bind_unbind_op(struct port_info *pi, void *arg, 249 enum sched_bind_type type, bool bind) 250 { 251 int err = 0; 252 253 if (!arg) 254 return -EINVAL; 255 256 switch (type) { 257 case SCHED_QUEUE: { 258 struct ch_sched_queue *qe = (struct ch_sched_queue *)arg; 259 260 if (bind) 261 err = t4_sched_queue_bind(pi, qe); 262 else 263 err = t4_sched_queue_unbind(pi, qe); 264 break; 265 } 266 default: 267 err = -ENOTSUPP; 268 break; 269 } 270 271 return err; 272 } 273 274 /** 275 * cxgb4_sched_class_bind - Bind an entity to a scheduling class 276 * @dev: net_device pointer 277 * @arg: Entity opaque data 278 * @type: Entity type (Queue) 279 * 280 * Binds an entity (queue) to a scheduling class. If the entity 281 * is bound to another class, it will be unbound from the other class 282 * and bound to the class specified in @arg. 283 */ 284 int cxgb4_sched_class_bind(struct net_device *dev, void *arg, 285 enum sched_bind_type type) 286 { 287 struct port_info *pi = netdev2pinfo(dev); 288 u8 class_id; 289 290 if (!can_sched(dev)) 291 return -ENOTSUPP; 292 293 if (!arg) 294 return -EINVAL; 295 296 switch (type) { 297 case SCHED_QUEUE: { 298 struct ch_sched_queue *qe = (struct ch_sched_queue *)arg; 299 300 class_id = qe->class; 301 break; 302 } 303 default: 304 return -ENOTSUPP; 305 } 306 307 if (!valid_class_id(dev, class_id)) 308 return -EINVAL; 309 310 if (class_id == SCHED_CLS_NONE) 311 return -ENOTSUPP; 312 313 return t4_sched_class_bind_unbind_op(pi, arg, type, true); 314 315 } 316 317 /** 318 * cxgb4_sched_class_unbind - Unbind an entity from a scheduling class 319 * @dev: net_device pointer 320 * @arg: Entity opaque data 321 * @type: Entity type (Queue) 322 * 323 * Unbinds an entity (queue) from a scheduling class. 324 */ 325 int cxgb4_sched_class_unbind(struct net_device *dev, void *arg, 326 enum sched_bind_type type) 327 { 328 struct port_info *pi = netdev2pinfo(dev); 329 u8 class_id; 330 331 if (!can_sched(dev)) 332 return -ENOTSUPP; 333 334 if (!arg) 335 return -EINVAL; 336 337 switch (type) { 338 case SCHED_QUEUE: { 339 struct ch_sched_queue *qe = (struct ch_sched_queue *)arg; 340 341 class_id = qe->class; 342 break; 343 } 344 default: 345 return -ENOTSUPP; 346 } 347 348 if (!valid_class_id(dev, class_id)) 349 return -EINVAL; 350 351 return t4_sched_class_bind_unbind_op(pi, arg, type, false); 352 } 353 354 /* If @p is NULL, fetch any available unused class */ 355 static struct sched_class *t4_sched_class_lookup(struct port_info *pi, 356 const struct ch_sched_params *p) 357 { 358 struct sched_table *s = pi->sched_tbl; 359 struct sched_class *e, *end; 360 struct sched_class *found = NULL; 361 362 if (!p) { 363 /* Get any available unused class */ 364 end = &s->tab[s->sched_size]; 365 for (e = &s->tab[0]; e != end; ++e) { 366 if (e->state == SCHED_STATE_UNUSED) { 367 found = e; 368 break; 369 } 370 } 371 } else { 372 /* Look for a class with matching scheduling parameters */ 373 struct ch_sched_params info; 374 struct ch_sched_params tp; 375 376 memcpy(&tp, p, sizeof(tp)); 377 /* Don't try to match class parameter */ 378 tp.u.params.class = SCHED_CLS_NONE; 379 380 end = &s->tab[s->sched_size]; 381 for (e = &s->tab[0]; e != end; ++e) { 382 if (e->state == SCHED_STATE_UNUSED) 383 continue; 384 385 memcpy(&info, &e->info, sizeof(info)); 386 /* Don't try to match class parameter */ 387 info.u.params.class = SCHED_CLS_NONE; 388 389 if ((info.type == tp.type) && 390 (!memcmp(&info.u.params, &tp.u.params, 391 sizeof(info.u.params)))) { 392 found = e; 393 break; 394 } 395 } 396 } 397 398 return found; 399 } 400 401 static struct sched_class *t4_sched_class_alloc(struct port_info *pi, 402 struct ch_sched_params *p) 403 { 404 struct sched_class *e; 405 u8 class_id; 406 int err; 407 408 if (!p) 409 return NULL; 410 411 class_id = p->u.params.class; 412 413 /* Only accept search for existing class with matching params 414 * or allocation of new class with specified params 415 */ 416 if (class_id != SCHED_CLS_NONE) 417 return NULL; 418 419 /* See if there's an exisiting class with same 420 * requested sched params 421 */ 422 e = t4_sched_class_lookup(pi, p); 423 if (!e) { 424 struct ch_sched_params np; 425 426 /* Fetch any available unused class */ 427 e = t4_sched_class_lookup(pi, NULL); 428 if (!e) 429 return NULL; 430 431 memcpy(&np, p, sizeof(np)); 432 np.u.params.class = e->idx; 433 /* New class */ 434 err = t4_sched_class_fw_cmd(pi, &np, SCHED_FW_OP_ADD); 435 if (err) 436 return NULL; 437 memcpy(&e->info, &np, sizeof(e->info)); 438 atomic_set(&e->refcnt, 0); 439 e->state = SCHED_STATE_ACTIVE; 440 } 441 442 return e; 443 } 444 445 /** 446 * cxgb4_sched_class_alloc - allocate a scheduling class 447 * @dev: net_device pointer 448 * @p: new scheduling class to create. 449 * 450 * Returns pointer to the scheduling class created. If @p is NULL, then 451 * it allocates and returns any available unused scheduling class. If a 452 * scheduling class with matching @p is found, then the matching class is 453 * returned. 454 */ 455 struct sched_class *cxgb4_sched_class_alloc(struct net_device *dev, 456 struct ch_sched_params *p) 457 { 458 struct port_info *pi = netdev2pinfo(dev); 459 u8 class_id; 460 461 if (!can_sched(dev)) 462 return NULL; 463 464 class_id = p->u.params.class; 465 if (!valid_class_id(dev, class_id)) 466 return NULL; 467 468 return t4_sched_class_alloc(pi, p); 469 } 470 471 static void t4_sched_class_free(struct port_info *pi, struct sched_class *e) 472 { 473 t4_sched_class_unbind_all(pi, e, SCHED_QUEUE); 474 } 475 476 struct sched_table *t4_init_sched(unsigned int sched_size) 477 { 478 struct sched_table *s; 479 unsigned int i; 480 481 s = kvzalloc(sizeof(*s) + sched_size * sizeof(struct sched_class), GFP_KERNEL); 482 if (!s) 483 return NULL; 484 485 s->sched_size = sched_size; 486 487 for (i = 0; i < s->sched_size; i++) { 488 memset(&s->tab[i], 0, sizeof(struct sched_class)); 489 s->tab[i].idx = i; 490 s->tab[i].state = SCHED_STATE_UNUSED; 491 INIT_LIST_HEAD(&s->tab[i].queue_list); 492 atomic_set(&s->tab[i].refcnt, 0); 493 } 494 return s; 495 } 496 497 void t4_cleanup_sched(struct adapter *adap) 498 { 499 struct sched_table *s; 500 unsigned int j, i; 501 502 for_each_port(adap, j) { 503 struct port_info *pi = netdev2pinfo(adap->port[j]); 504 505 s = pi->sched_tbl; 506 if (!s) 507 continue; 508 509 for (i = 0; i < s->sched_size; i++) { 510 struct sched_class *e; 511 512 e = &s->tab[i]; 513 if (e->state == SCHED_STATE_ACTIVE) 514 t4_sched_class_free(pi, e); 515 } 516 kvfree(s); 517 } 518 } 519