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 } 518