18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * This file is part of the Chelsio T4 Ethernet driver for Linux. 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (c) 2016 Chelsio Communications, Inc. All rights reserved. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * This software is available to you under a choice of one of two 78c2ecf20Sopenharmony_ci * licenses. You may choose to be licensed under the terms of the GNU 88c2ecf20Sopenharmony_ci * General Public License (GPL) Version 2, available from the file 98c2ecf20Sopenharmony_ci * COPYING in the main directory of this source tree, or the 108c2ecf20Sopenharmony_ci * OpenIB.org BSD license below: 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * Redistribution and use in source and binary forms, with or 138c2ecf20Sopenharmony_ci * without modification, are permitted provided that the following 148c2ecf20Sopenharmony_ci * conditions are met: 158c2ecf20Sopenharmony_ci * 168c2ecf20Sopenharmony_ci * - Redistributions of source code must retain the above 178c2ecf20Sopenharmony_ci * copyright notice, this list of conditions and the following 188c2ecf20Sopenharmony_ci * disclaimer. 198c2ecf20Sopenharmony_ci * 208c2ecf20Sopenharmony_ci * - Redistributions in binary form must reproduce the above 218c2ecf20Sopenharmony_ci * copyright notice, this list of conditions and the following 228c2ecf20Sopenharmony_ci * disclaimer in the documentation and/or other materials 238c2ecf20Sopenharmony_ci * provided with the distribution. 248c2ecf20Sopenharmony_ci * 258c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 268c2ecf20Sopenharmony_ci * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 278c2ecf20Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 288c2ecf20Sopenharmony_ci * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 298c2ecf20Sopenharmony_ci * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 308c2ecf20Sopenharmony_ci * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 318c2ecf20Sopenharmony_ci * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 328c2ecf20Sopenharmony_ci * SOFTWARE. 338c2ecf20Sopenharmony_ci */ 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#include <linux/module.h> 368c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci#include "cxgb4.h" 398c2ecf20Sopenharmony_ci#include "sched.h" 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistatic int t4_sched_class_fw_cmd(struct port_info *pi, 428c2ecf20Sopenharmony_ci struct ch_sched_params *p, 438c2ecf20Sopenharmony_ci enum sched_fw_ops op) 448c2ecf20Sopenharmony_ci{ 458c2ecf20Sopenharmony_ci struct adapter *adap = pi->adapter; 468c2ecf20Sopenharmony_ci struct sched_table *s = pi->sched_tbl; 478c2ecf20Sopenharmony_ci struct sched_class *e; 488c2ecf20Sopenharmony_ci int err = 0; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci e = &s->tab[p->u.params.class]; 518c2ecf20Sopenharmony_ci switch (op) { 528c2ecf20Sopenharmony_ci case SCHED_FW_OP_ADD: 538c2ecf20Sopenharmony_ci case SCHED_FW_OP_DEL: 548c2ecf20Sopenharmony_ci err = t4_sched_params(adap, p->type, 558c2ecf20Sopenharmony_ci p->u.params.level, p->u.params.mode, 568c2ecf20Sopenharmony_ci p->u.params.rateunit, 578c2ecf20Sopenharmony_ci p->u.params.ratemode, 588c2ecf20Sopenharmony_ci p->u.params.channel, e->idx, 598c2ecf20Sopenharmony_ci p->u.params.minrate, p->u.params.maxrate, 608c2ecf20Sopenharmony_ci p->u.params.weight, p->u.params.pktsize, 618c2ecf20Sopenharmony_ci p->u.params.burstsize); 628c2ecf20Sopenharmony_ci break; 638c2ecf20Sopenharmony_ci default: 648c2ecf20Sopenharmony_ci err = -ENOTSUPP; 658c2ecf20Sopenharmony_ci break; 668c2ecf20Sopenharmony_ci } 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci return err; 698c2ecf20Sopenharmony_ci} 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistatic int t4_sched_bind_unbind_op(struct port_info *pi, void *arg, 728c2ecf20Sopenharmony_ci enum sched_bind_type type, bool bind) 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci struct adapter *adap = pi->adapter; 758c2ecf20Sopenharmony_ci u32 fw_mnem, fw_class, fw_param; 768c2ecf20Sopenharmony_ci unsigned int pf = adap->pf; 778c2ecf20Sopenharmony_ci unsigned int vf = 0; 788c2ecf20Sopenharmony_ci int err = 0; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci switch (type) { 818c2ecf20Sopenharmony_ci case SCHED_QUEUE: { 828c2ecf20Sopenharmony_ci struct sched_queue_entry *qe; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci qe = (struct sched_queue_entry *)arg; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci /* Create a template for the FW_PARAMS_CMD mnemonic and 878c2ecf20Sopenharmony_ci * value (TX Scheduling Class in this case). 888c2ecf20Sopenharmony_ci */ 898c2ecf20Sopenharmony_ci fw_mnem = (FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_DMAQ) | 908c2ecf20Sopenharmony_ci FW_PARAMS_PARAM_X_V( 918c2ecf20Sopenharmony_ci FW_PARAMS_PARAM_DMAQ_EQ_SCHEDCLASS_ETH)); 928c2ecf20Sopenharmony_ci fw_class = bind ? qe->param.class : FW_SCHED_CLS_NONE; 938c2ecf20Sopenharmony_ci fw_param = (fw_mnem | FW_PARAMS_PARAM_YZ_V(qe->cntxt_id)); 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci pf = adap->pf; 968c2ecf20Sopenharmony_ci vf = 0; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci err = t4_set_params(adap, adap->mbox, pf, vf, 1, 998c2ecf20Sopenharmony_ci &fw_param, &fw_class); 1008c2ecf20Sopenharmony_ci break; 1018c2ecf20Sopenharmony_ci } 1028c2ecf20Sopenharmony_ci case SCHED_FLOWC: { 1038c2ecf20Sopenharmony_ci struct sched_flowc_entry *fe; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci fe = (struct sched_flowc_entry *)arg; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci fw_class = bind ? fe->param.class : FW_SCHED_CLS_NONE; 1088c2ecf20Sopenharmony_ci err = cxgb4_ethofld_send_flowc(adap->port[pi->port_id], 1098c2ecf20Sopenharmony_ci fe->param.tid, fw_class); 1108c2ecf20Sopenharmony_ci break; 1118c2ecf20Sopenharmony_ci } 1128c2ecf20Sopenharmony_ci default: 1138c2ecf20Sopenharmony_ci err = -ENOTSUPP; 1148c2ecf20Sopenharmony_ci break; 1158c2ecf20Sopenharmony_ci } 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci return err; 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistatic void *t4_sched_entry_lookup(struct port_info *pi, 1218c2ecf20Sopenharmony_ci enum sched_bind_type type, 1228c2ecf20Sopenharmony_ci const u32 val) 1238c2ecf20Sopenharmony_ci{ 1248c2ecf20Sopenharmony_ci struct sched_table *s = pi->sched_tbl; 1258c2ecf20Sopenharmony_ci struct sched_class *e, *end; 1268c2ecf20Sopenharmony_ci void *found = NULL; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci /* Look for an entry with matching @val */ 1298c2ecf20Sopenharmony_ci end = &s->tab[s->sched_size]; 1308c2ecf20Sopenharmony_ci for (e = &s->tab[0]; e != end; ++e) { 1318c2ecf20Sopenharmony_ci if (e->state == SCHED_STATE_UNUSED || 1328c2ecf20Sopenharmony_ci e->bind_type != type) 1338c2ecf20Sopenharmony_ci continue; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci switch (type) { 1368c2ecf20Sopenharmony_ci case SCHED_QUEUE: { 1378c2ecf20Sopenharmony_ci struct sched_queue_entry *qe; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci list_for_each_entry(qe, &e->entry_list, list) { 1408c2ecf20Sopenharmony_ci if (qe->cntxt_id == val) { 1418c2ecf20Sopenharmony_ci found = qe; 1428c2ecf20Sopenharmony_ci break; 1438c2ecf20Sopenharmony_ci } 1448c2ecf20Sopenharmony_ci } 1458c2ecf20Sopenharmony_ci break; 1468c2ecf20Sopenharmony_ci } 1478c2ecf20Sopenharmony_ci case SCHED_FLOWC: { 1488c2ecf20Sopenharmony_ci struct sched_flowc_entry *fe; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci list_for_each_entry(fe, &e->entry_list, list) { 1518c2ecf20Sopenharmony_ci if (fe->param.tid == val) { 1528c2ecf20Sopenharmony_ci found = fe; 1538c2ecf20Sopenharmony_ci break; 1548c2ecf20Sopenharmony_ci } 1558c2ecf20Sopenharmony_ci } 1568c2ecf20Sopenharmony_ci break; 1578c2ecf20Sopenharmony_ci } 1588c2ecf20Sopenharmony_ci default: 1598c2ecf20Sopenharmony_ci return NULL; 1608c2ecf20Sopenharmony_ci } 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci if (found) 1638c2ecf20Sopenharmony_ci break; 1648c2ecf20Sopenharmony_ci } 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci return found; 1678c2ecf20Sopenharmony_ci} 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_cistruct sched_class *cxgb4_sched_queue_lookup(struct net_device *dev, 1708c2ecf20Sopenharmony_ci struct ch_sched_queue *p) 1718c2ecf20Sopenharmony_ci{ 1728c2ecf20Sopenharmony_ci struct port_info *pi = netdev2pinfo(dev); 1738c2ecf20Sopenharmony_ci struct sched_queue_entry *qe = NULL; 1748c2ecf20Sopenharmony_ci struct adapter *adap = pi->adapter; 1758c2ecf20Sopenharmony_ci struct sge_eth_txq *txq; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci if (p->queue < 0 || p->queue >= pi->nqsets) 1788c2ecf20Sopenharmony_ci return NULL; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci txq = &adap->sge.ethtxq[pi->first_qset + p->queue]; 1818c2ecf20Sopenharmony_ci qe = t4_sched_entry_lookup(pi, SCHED_QUEUE, txq->q.cntxt_id); 1828c2ecf20Sopenharmony_ci return qe ? &pi->sched_tbl->tab[qe->param.class] : NULL; 1838c2ecf20Sopenharmony_ci} 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_cistatic int t4_sched_queue_unbind(struct port_info *pi, struct ch_sched_queue *p) 1868c2ecf20Sopenharmony_ci{ 1878c2ecf20Sopenharmony_ci struct sched_queue_entry *qe = NULL; 1888c2ecf20Sopenharmony_ci struct adapter *adap = pi->adapter; 1898c2ecf20Sopenharmony_ci struct sge_eth_txq *txq; 1908c2ecf20Sopenharmony_ci struct sched_class *e; 1918c2ecf20Sopenharmony_ci int err = 0; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci if (p->queue < 0 || p->queue >= pi->nqsets) 1948c2ecf20Sopenharmony_ci return -ERANGE; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci txq = &adap->sge.ethtxq[pi->first_qset + p->queue]; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci /* Find the existing entry that the queue is bound to */ 1998c2ecf20Sopenharmony_ci qe = t4_sched_entry_lookup(pi, SCHED_QUEUE, txq->q.cntxt_id); 2008c2ecf20Sopenharmony_ci if (qe) { 2018c2ecf20Sopenharmony_ci err = t4_sched_bind_unbind_op(pi, (void *)qe, SCHED_QUEUE, 2028c2ecf20Sopenharmony_ci false); 2038c2ecf20Sopenharmony_ci if (err) 2048c2ecf20Sopenharmony_ci return err; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci e = &pi->sched_tbl->tab[qe->param.class]; 2078c2ecf20Sopenharmony_ci list_del(&qe->list); 2088c2ecf20Sopenharmony_ci kvfree(qe); 2098c2ecf20Sopenharmony_ci if (atomic_dec_and_test(&e->refcnt)) 2108c2ecf20Sopenharmony_ci cxgb4_sched_class_free(adap->port[pi->port_id], e->idx); 2118c2ecf20Sopenharmony_ci } 2128c2ecf20Sopenharmony_ci return err; 2138c2ecf20Sopenharmony_ci} 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_cistatic int t4_sched_queue_bind(struct port_info *pi, struct ch_sched_queue *p) 2168c2ecf20Sopenharmony_ci{ 2178c2ecf20Sopenharmony_ci struct sched_table *s = pi->sched_tbl; 2188c2ecf20Sopenharmony_ci struct sched_queue_entry *qe = NULL; 2198c2ecf20Sopenharmony_ci struct adapter *adap = pi->adapter; 2208c2ecf20Sopenharmony_ci struct sge_eth_txq *txq; 2218c2ecf20Sopenharmony_ci struct sched_class *e; 2228c2ecf20Sopenharmony_ci unsigned int qid; 2238c2ecf20Sopenharmony_ci int err = 0; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci if (p->queue < 0 || p->queue >= pi->nqsets) 2268c2ecf20Sopenharmony_ci return -ERANGE; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci qe = kvzalloc(sizeof(struct sched_queue_entry), GFP_KERNEL); 2298c2ecf20Sopenharmony_ci if (!qe) 2308c2ecf20Sopenharmony_ci return -ENOMEM; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci txq = &adap->sge.ethtxq[pi->first_qset + p->queue]; 2338c2ecf20Sopenharmony_ci qid = txq->q.cntxt_id; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci /* Unbind queue from any existing class */ 2368c2ecf20Sopenharmony_ci err = t4_sched_queue_unbind(pi, p); 2378c2ecf20Sopenharmony_ci if (err) 2388c2ecf20Sopenharmony_ci goto out_err; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci /* Bind queue to specified class */ 2418c2ecf20Sopenharmony_ci qe->cntxt_id = qid; 2428c2ecf20Sopenharmony_ci memcpy(&qe->param, p, sizeof(qe->param)); 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci e = &s->tab[qe->param.class]; 2458c2ecf20Sopenharmony_ci err = t4_sched_bind_unbind_op(pi, (void *)qe, SCHED_QUEUE, true); 2468c2ecf20Sopenharmony_ci if (err) 2478c2ecf20Sopenharmony_ci goto out_err; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci list_add_tail(&qe->list, &e->entry_list); 2508c2ecf20Sopenharmony_ci e->bind_type = SCHED_QUEUE; 2518c2ecf20Sopenharmony_ci atomic_inc(&e->refcnt); 2528c2ecf20Sopenharmony_ci return err; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ciout_err: 2558c2ecf20Sopenharmony_ci kvfree(qe); 2568c2ecf20Sopenharmony_ci return err; 2578c2ecf20Sopenharmony_ci} 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_cistatic int t4_sched_flowc_unbind(struct port_info *pi, struct ch_sched_flowc *p) 2608c2ecf20Sopenharmony_ci{ 2618c2ecf20Sopenharmony_ci struct sched_flowc_entry *fe = NULL; 2628c2ecf20Sopenharmony_ci struct adapter *adap = pi->adapter; 2638c2ecf20Sopenharmony_ci struct sched_class *e; 2648c2ecf20Sopenharmony_ci int err = 0; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci if (p->tid < 0 || p->tid >= adap->tids.neotids) 2678c2ecf20Sopenharmony_ci return -ERANGE; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci /* Find the existing entry that the flowc is bound to */ 2708c2ecf20Sopenharmony_ci fe = t4_sched_entry_lookup(pi, SCHED_FLOWC, p->tid); 2718c2ecf20Sopenharmony_ci if (fe) { 2728c2ecf20Sopenharmony_ci err = t4_sched_bind_unbind_op(pi, (void *)fe, SCHED_FLOWC, 2738c2ecf20Sopenharmony_ci false); 2748c2ecf20Sopenharmony_ci if (err) 2758c2ecf20Sopenharmony_ci return err; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci e = &pi->sched_tbl->tab[fe->param.class]; 2788c2ecf20Sopenharmony_ci list_del(&fe->list); 2798c2ecf20Sopenharmony_ci kvfree(fe); 2808c2ecf20Sopenharmony_ci if (atomic_dec_and_test(&e->refcnt)) 2818c2ecf20Sopenharmony_ci cxgb4_sched_class_free(adap->port[pi->port_id], e->idx); 2828c2ecf20Sopenharmony_ci } 2838c2ecf20Sopenharmony_ci return err; 2848c2ecf20Sopenharmony_ci} 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_cistatic int t4_sched_flowc_bind(struct port_info *pi, struct ch_sched_flowc *p) 2878c2ecf20Sopenharmony_ci{ 2888c2ecf20Sopenharmony_ci struct sched_table *s = pi->sched_tbl; 2898c2ecf20Sopenharmony_ci struct sched_flowc_entry *fe = NULL; 2908c2ecf20Sopenharmony_ci struct adapter *adap = pi->adapter; 2918c2ecf20Sopenharmony_ci struct sched_class *e; 2928c2ecf20Sopenharmony_ci int err = 0; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci if (p->tid < 0 || p->tid >= adap->tids.neotids) 2958c2ecf20Sopenharmony_ci return -ERANGE; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci fe = kvzalloc(sizeof(*fe), GFP_KERNEL); 2988c2ecf20Sopenharmony_ci if (!fe) 2998c2ecf20Sopenharmony_ci return -ENOMEM; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci /* Unbind flowc from any existing class */ 3028c2ecf20Sopenharmony_ci err = t4_sched_flowc_unbind(pi, p); 3038c2ecf20Sopenharmony_ci if (err) 3048c2ecf20Sopenharmony_ci goto out_err; 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci /* Bind flowc to specified class */ 3078c2ecf20Sopenharmony_ci memcpy(&fe->param, p, sizeof(fe->param)); 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci e = &s->tab[fe->param.class]; 3108c2ecf20Sopenharmony_ci err = t4_sched_bind_unbind_op(pi, (void *)fe, SCHED_FLOWC, true); 3118c2ecf20Sopenharmony_ci if (err) 3128c2ecf20Sopenharmony_ci goto out_err; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci list_add_tail(&fe->list, &e->entry_list); 3158c2ecf20Sopenharmony_ci e->bind_type = SCHED_FLOWC; 3168c2ecf20Sopenharmony_ci atomic_inc(&e->refcnt); 3178c2ecf20Sopenharmony_ci return err; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ciout_err: 3208c2ecf20Sopenharmony_ci kvfree(fe); 3218c2ecf20Sopenharmony_ci return err; 3228c2ecf20Sopenharmony_ci} 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_cistatic void t4_sched_class_unbind_all(struct port_info *pi, 3258c2ecf20Sopenharmony_ci struct sched_class *e, 3268c2ecf20Sopenharmony_ci enum sched_bind_type type) 3278c2ecf20Sopenharmony_ci{ 3288c2ecf20Sopenharmony_ci if (!e) 3298c2ecf20Sopenharmony_ci return; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci switch (type) { 3328c2ecf20Sopenharmony_ci case SCHED_QUEUE: { 3338c2ecf20Sopenharmony_ci struct sched_queue_entry *qe; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci list_for_each_entry(qe, &e->entry_list, list) 3368c2ecf20Sopenharmony_ci t4_sched_queue_unbind(pi, &qe->param); 3378c2ecf20Sopenharmony_ci break; 3388c2ecf20Sopenharmony_ci } 3398c2ecf20Sopenharmony_ci case SCHED_FLOWC: { 3408c2ecf20Sopenharmony_ci struct sched_flowc_entry *fe; 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci list_for_each_entry(fe, &e->entry_list, list) 3438c2ecf20Sopenharmony_ci t4_sched_flowc_unbind(pi, &fe->param); 3448c2ecf20Sopenharmony_ci break; 3458c2ecf20Sopenharmony_ci } 3468c2ecf20Sopenharmony_ci default: 3478c2ecf20Sopenharmony_ci break; 3488c2ecf20Sopenharmony_ci } 3498c2ecf20Sopenharmony_ci} 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_cistatic int t4_sched_class_bind_unbind_op(struct port_info *pi, void *arg, 3528c2ecf20Sopenharmony_ci enum sched_bind_type type, bool bind) 3538c2ecf20Sopenharmony_ci{ 3548c2ecf20Sopenharmony_ci int err = 0; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci if (!arg) 3578c2ecf20Sopenharmony_ci return -EINVAL; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci switch (type) { 3608c2ecf20Sopenharmony_ci case SCHED_QUEUE: { 3618c2ecf20Sopenharmony_ci struct ch_sched_queue *qe = (struct ch_sched_queue *)arg; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci if (bind) 3648c2ecf20Sopenharmony_ci err = t4_sched_queue_bind(pi, qe); 3658c2ecf20Sopenharmony_ci else 3668c2ecf20Sopenharmony_ci err = t4_sched_queue_unbind(pi, qe); 3678c2ecf20Sopenharmony_ci break; 3688c2ecf20Sopenharmony_ci } 3698c2ecf20Sopenharmony_ci case SCHED_FLOWC: { 3708c2ecf20Sopenharmony_ci struct ch_sched_flowc *fe = (struct ch_sched_flowc *)arg; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci if (bind) 3738c2ecf20Sopenharmony_ci err = t4_sched_flowc_bind(pi, fe); 3748c2ecf20Sopenharmony_ci else 3758c2ecf20Sopenharmony_ci err = t4_sched_flowc_unbind(pi, fe); 3768c2ecf20Sopenharmony_ci break; 3778c2ecf20Sopenharmony_ci } 3788c2ecf20Sopenharmony_ci default: 3798c2ecf20Sopenharmony_ci err = -ENOTSUPP; 3808c2ecf20Sopenharmony_ci break; 3818c2ecf20Sopenharmony_ci } 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci return err; 3848c2ecf20Sopenharmony_ci} 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci/** 3878c2ecf20Sopenharmony_ci * cxgb4_sched_class_bind - Bind an entity to a scheduling class 3888c2ecf20Sopenharmony_ci * @dev: net_device pointer 3898c2ecf20Sopenharmony_ci * @arg: Entity opaque data 3908c2ecf20Sopenharmony_ci * @type: Entity type (Queue) 3918c2ecf20Sopenharmony_ci * 3928c2ecf20Sopenharmony_ci * Binds an entity (queue) to a scheduling class. If the entity 3938c2ecf20Sopenharmony_ci * is bound to another class, it will be unbound from the other class 3948c2ecf20Sopenharmony_ci * and bound to the class specified in @arg. 3958c2ecf20Sopenharmony_ci */ 3968c2ecf20Sopenharmony_ciint cxgb4_sched_class_bind(struct net_device *dev, void *arg, 3978c2ecf20Sopenharmony_ci enum sched_bind_type type) 3988c2ecf20Sopenharmony_ci{ 3998c2ecf20Sopenharmony_ci struct port_info *pi = netdev2pinfo(dev); 4008c2ecf20Sopenharmony_ci u8 class_id; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci if (!can_sched(dev)) 4038c2ecf20Sopenharmony_ci return -ENOTSUPP; 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci if (!arg) 4068c2ecf20Sopenharmony_ci return -EINVAL; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci switch (type) { 4098c2ecf20Sopenharmony_ci case SCHED_QUEUE: { 4108c2ecf20Sopenharmony_ci struct ch_sched_queue *qe = (struct ch_sched_queue *)arg; 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci class_id = qe->class; 4138c2ecf20Sopenharmony_ci break; 4148c2ecf20Sopenharmony_ci } 4158c2ecf20Sopenharmony_ci case SCHED_FLOWC: { 4168c2ecf20Sopenharmony_ci struct ch_sched_flowc *fe = (struct ch_sched_flowc *)arg; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci class_id = fe->class; 4198c2ecf20Sopenharmony_ci break; 4208c2ecf20Sopenharmony_ci } 4218c2ecf20Sopenharmony_ci default: 4228c2ecf20Sopenharmony_ci return -ENOTSUPP; 4238c2ecf20Sopenharmony_ci } 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci if (!valid_class_id(dev, class_id)) 4268c2ecf20Sopenharmony_ci return -EINVAL; 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci if (class_id == SCHED_CLS_NONE) 4298c2ecf20Sopenharmony_ci return -ENOTSUPP; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci return t4_sched_class_bind_unbind_op(pi, arg, type, true); 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci} 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci/** 4368c2ecf20Sopenharmony_ci * cxgb4_sched_class_unbind - Unbind an entity from a scheduling class 4378c2ecf20Sopenharmony_ci * @dev: net_device pointer 4388c2ecf20Sopenharmony_ci * @arg: Entity opaque data 4398c2ecf20Sopenharmony_ci * @type: Entity type (Queue) 4408c2ecf20Sopenharmony_ci * 4418c2ecf20Sopenharmony_ci * Unbinds an entity (queue) from a scheduling class. 4428c2ecf20Sopenharmony_ci */ 4438c2ecf20Sopenharmony_ciint cxgb4_sched_class_unbind(struct net_device *dev, void *arg, 4448c2ecf20Sopenharmony_ci enum sched_bind_type type) 4458c2ecf20Sopenharmony_ci{ 4468c2ecf20Sopenharmony_ci struct port_info *pi = netdev2pinfo(dev); 4478c2ecf20Sopenharmony_ci u8 class_id; 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci if (!can_sched(dev)) 4508c2ecf20Sopenharmony_ci return -ENOTSUPP; 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci if (!arg) 4538c2ecf20Sopenharmony_ci return -EINVAL; 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci switch (type) { 4568c2ecf20Sopenharmony_ci case SCHED_QUEUE: { 4578c2ecf20Sopenharmony_ci struct ch_sched_queue *qe = (struct ch_sched_queue *)arg; 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci class_id = qe->class; 4608c2ecf20Sopenharmony_ci break; 4618c2ecf20Sopenharmony_ci } 4628c2ecf20Sopenharmony_ci case SCHED_FLOWC: { 4638c2ecf20Sopenharmony_ci struct ch_sched_flowc *fe = (struct ch_sched_flowc *)arg; 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci class_id = fe->class; 4668c2ecf20Sopenharmony_ci break; 4678c2ecf20Sopenharmony_ci } 4688c2ecf20Sopenharmony_ci default: 4698c2ecf20Sopenharmony_ci return -ENOTSUPP; 4708c2ecf20Sopenharmony_ci } 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci if (!valid_class_id(dev, class_id)) 4738c2ecf20Sopenharmony_ci return -EINVAL; 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci return t4_sched_class_bind_unbind_op(pi, arg, type, false); 4768c2ecf20Sopenharmony_ci} 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci/* If @p is NULL, fetch any available unused class */ 4798c2ecf20Sopenharmony_cistatic struct sched_class *t4_sched_class_lookup(struct port_info *pi, 4808c2ecf20Sopenharmony_ci const struct ch_sched_params *p) 4818c2ecf20Sopenharmony_ci{ 4828c2ecf20Sopenharmony_ci struct sched_table *s = pi->sched_tbl; 4838c2ecf20Sopenharmony_ci struct sched_class *found = NULL; 4848c2ecf20Sopenharmony_ci struct sched_class *e, *end; 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci if (!p) { 4878c2ecf20Sopenharmony_ci /* Get any available unused class */ 4888c2ecf20Sopenharmony_ci end = &s->tab[s->sched_size]; 4898c2ecf20Sopenharmony_ci for (e = &s->tab[0]; e != end; ++e) { 4908c2ecf20Sopenharmony_ci if (e->state == SCHED_STATE_UNUSED) { 4918c2ecf20Sopenharmony_ci found = e; 4928c2ecf20Sopenharmony_ci break; 4938c2ecf20Sopenharmony_ci } 4948c2ecf20Sopenharmony_ci } 4958c2ecf20Sopenharmony_ci } else { 4968c2ecf20Sopenharmony_ci /* Look for a class with matching scheduling parameters */ 4978c2ecf20Sopenharmony_ci struct ch_sched_params info; 4988c2ecf20Sopenharmony_ci struct ch_sched_params tp; 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci memcpy(&tp, p, sizeof(tp)); 5018c2ecf20Sopenharmony_ci /* Don't try to match class parameter */ 5028c2ecf20Sopenharmony_ci tp.u.params.class = SCHED_CLS_NONE; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci end = &s->tab[s->sched_size]; 5058c2ecf20Sopenharmony_ci for (e = &s->tab[0]; e != end; ++e) { 5068c2ecf20Sopenharmony_ci if (e->state == SCHED_STATE_UNUSED) 5078c2ecf20Sopenharmony_ci continue; 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci memcpy(&info, &e->info, sizeof(info)); 5108c2ecf20Sopenharmony_ci /* Don't try to match class parameter */ 5118c2ecf20Sopenharmony_ci info.u.params.class = SCHED_CLS_NONE; 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci if ((info.type == tp.type) && 5148c2ecf20Sopenharmony_ci (!memcmp(&info.u.params, &tp.u.params, 5158c2ecf20Sopenharmony_ci sizeof(info.u.params)))) { 5168c2ecf20Sopenharmony_ci found = e; 5178c2ecf20Sopenharmony_ci break; 5188c2ecf20Sopenharmony_ci } 5198c2ecf20Sopenharmony_ci } 5208c2ecf20Sopenharmony_ci } 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci return found; 5238c2ecf20Sopenharmony_ci} 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_cistatic struct sched_class *t4_sched_class_alloc(struct port_info *pi, 5268c2ecf20Sopenharmony_ci struct ch_sched_params *p) 5278c2ecf20Sopenharmony_ci{ 5288c2ecf20Sopenharmony_ci struct sched_class *e = NULL; 5298c2ecf20Sopenharmony_ci u8 class_id; 5308c2ecf20Sopenharmony_ci int err; 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci if (!p) 5338c2ecf20Sopenharmony_ci return NULL; 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci class_id = p->u.params.class; 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci /* Only accept search for existing class with matching params 5388c2ecf20Sopenharmony_ci * or allocation of new class with specified params 5398c2ecf20Sopenharmony_ci */ 5408c2ecf20Sopenharmony_ci if (class_id != SCHED_CLS_NONE) 5418c2ecf20Sopenharmony_ci return NULL; 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci /* See if there's an exisiting class with same requested sched 5448c2ecf20Sopenharmony_ci * params. Classes can only be shared among FLOWC types. For 5458c2ecf20Sopenharmony_ci * other types, always request a new class. 5468c2ecf20Sopenharmony_ci */ 5478c2ecf20Sopenharmony_ci if (p->u.params.mode == SCHED_CLASS_MODE_FLOW) 5488c2ecf20Sopenharmony_ci e = t4_sched_class_lookup(pi, p); 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci if (!e) { 5518c2ecf20Sopenharmony_ci struct ch_sched_params np; 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci /* Fetch any available unused class */ 5548c2ecf20Sopenharmony_ci e = t4_sched_class_lookup(pi, NULL); 5558c2ecf20Sopenharmony_ci if (!e) 5568c2ecf20Sopenharmony_ci return NULL; 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci memcpy(&np, p, sizeof(np)); 5598c2ecf20Sopenharmony_ci np.u.params.class = e->idx; 5608c2ecf20Sopenharmony_ci /* New class */ 5618c2ecf20Sopenharmony_ci err = t4_sched_class_fw_cmd(pi, &np, SCHED_FW_OP_ADD); 5628c2ecf20Sopenharmony_ci if (err) 5638c2ecf20Sopenharmony_ci return NULL; 5648c2ecf20Sopenharmony_ci memcpy(&e->info, &np, sizeof(e->info)); 5658c2ecf20Sopenharmony_ci atomic_set(&e->refcnt, 0); 5668c2ecf20Sopenharmony_ci e->state = SCHED_STATE_ACTIVE; 5678c2ecf20Sopenharmony_ci } 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci return e; 5708c2ecf20Sopenharmony_ci} 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci/** 5738c2ecf20Sopenharmony_ci * cxgb4_sched_class_alloc - allocate a scheduling class 5748c2ecf20Sopenharmony_ci * @dev: net_device pointer 5758c2ecf20Sopenharmony_ci * @p: new scheduling class to create. 5768c2ecf20Sopenharmony_ci * 5778c2ecf20Sopenharmony_ci * Returns pointer to the scheduling class created. If @p is NULL, then 5788c2ecf20Sopenharmony_ci * it allocates and returns any available unused scheduling class. If a 5798c2ecf20Sopenharmony_ci * scheduling class with matching @p is found, then the matching class is 5808c2ecf20Sopenharmony_ci * returned. 5818c2ecf20Sopenharmony_ci */ 5828c2ecf20Sopenharmony_cistruct sched_class *cxgb4_sched_class_alloc(struct net_device *dev, 5838c2ecf20Sopenharmony_ci struct ch_sched_params *p) 5848c2ecf20Sopenharmony_ci{ 5858c2ecf20Sopenharmony_ci struct port_info *pi = netdev2pinfo(dev); 5868c2ecf20Sopenharmony_ci u8 class_id; 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci if (!can_sched(dev)) 5898c2ecf20Sopenharmony_ci return NULL; 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci class_id = p->u.params.class; 5928c2ecf20Sopenharmony_ci if (!valid_class_id(dev, class_id)) 5938c2ecf20Sopenharmony_ci return NULL; 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci return t4_sched_class_alloc(pi, p); 5968c2ecf20Sopenharmony_ci} 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci/** 5998c2ecf20Sopenharmony_ci * cxgb4_sched_class_free - free a scheduling class 6008c2ecf20Sopenharmony_ci * @dev: net_device pointer 6018c2ecf20Sopenharmony_ci * @classid: scheduling class id to free 6028c2ecf20Sopenharmony_ci * 6038c2ecf20Sopenharmony_ci * Frees a scheduling class if there are no users. 6048c2ecf20Sopenharmony_ci */ 6058c2ecf20Sopenharmony_civoid cxgb4_sched_class_free(struct net_device *dev, u8 classid) 6068c2ecf20Sopenharmony_ci{ 6078c2ecf20Sopenharmony_ci struct port_info *pi = netdev2pinfo(dev); 6088c2ecf20Sopenharmony_ci struct sched_table *s = pi->sched_tbl; 6098c2ecf20Sopenharmony_ci struct ch_sched_params p; 6108c2ecf20Sopenharmony_ci struct sched_class *e; 6118c2ecf20Sopenharmony_ci u32 speed; 6128c2ecf20Sopenharmony_ci int ret; 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci e = &s->tab[classid]; 6158c2ecf20Sopenharmony_ci if (!atomic_read(&e->refcnt) && e->state != SCHED_STATE_UNUSED) { 6168c2ecf20Sopenharmony_ci /* Port based rate limiting needs explicit reset back 6178c2ecf20Sopenharmony_ci * to max rate. But, we'll do explicit reset for all 6188c2ecf20Sopenharmony_ci * types, instead of just port based type, to be on 6198c2ecf20Sopenharmony_ci * the safer side. 6208c2ecf20Sopenharmony_ci */ 6218c2ecf20Sopenharmony_ci memcpy(&p, &e->info, sizeof(p)); 6228c2ecf20Sopenharmony_ci /* Always reset mode to 0. Otherwise, FLOWC mode will 6238c2ecf20Sopenharmony_ci * still be enabled even after resetting the traffic 6248c2ecf20Sopenharmony_ci * class. 6258c2ecf20Sopenharmony_ci */ 6268c2ecf20Sopenharmony_ci p.u.params.mode = 0; 6278c2ecf20Sopenharmony_ci p.u.params.minrate = 0; 6288c2ecf20Sopenharmony_ci p.u.params.pktsize = 0; 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci ret = t4_get_link_params(pi, NULL, &speed, NULL); 6318c2ecf20Sopenharmony_ci if (!ret) 6328c2ecf20Sopenharmony_ci p.u.params.maxrate = speed * 1000; /* Mbps to Kbps */ 6338c2ecf20Sopenharmony_ci else 6348c2ecf20Sopenharmony_ci p.u.params.maxrate = SCHED_MAX_RATE_KBPS; 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci t4_sched_class_fw_cmd(pi, &p, SCHED_FW_OP_DEL); 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci e->state = SCHED_STATE_UNUSED; 6398c2ecf20Sopenharmony_ci memset(&e->info, 0, sizeof(e->info)); 6408c2ecf20Sopenharmony_ci } 6418c2ecf20Sopenharmony_ci} 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_cistatic void t4_sched_class_free(struct net_device *dev, struct sched_class *e) 6448c2ecf20Sopenharmony_ci{ 6458c2ecf20Sopenharmony_ci struct port_info *pi = netdev2pinfo(dev); 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci t4_sched_class_unbind_all(pi, e, e->bind_type); 6488c2ecf20Sopenharmony_ci cxgb4_sched_class_free(dev, e->idx); 6498c2ecf20Sopenharmony_ci} 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_cistruct sched_table *t4_init_sched(unsigned int sched_size) 6528c2ecf20Sopenharmony_ci{ 6538c2ecf20Sopenharmony_ci struct sched_table *s; 6548c2ecf20Sopenharmony_ci unsigned int i; 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci s = kvzalloc(struct_size(s, tab, sched_size), GFP_KERNEL); 6578c2ecf20Sopenharmony_ci if (!s) 6588c2ecf20Sopenharmony_ci return NULL; 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci s->sched_size = sched_size; 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci for (i = 0; i < s->sched_size; i++) { 6638c2ecf20Sopenharmony_ci memset(&s->tab[i], 0, sizeof(struct sched_class)); 6648c2ecf20Sopenharmony_ci s->tab[i].idx = i; 6658c2ecf20Sopenharmony_ci s->tab[i].state = SCHED_STATE_UNUSED; 6668c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&s->tab[i].entry_list); 6678c2ecf20Sopenharmony_ci atomic_set(&s->tab[i].refcnt, 0); 6688c2ecf20Sopenharmony_ci } 6698c2ecf20Sopenharmony_ci return s; 6708c2ecf20Sopenharmony_ci} 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_civoid t4_cleanup_sched(struct adapter *adap) 6738c2ecf20Sopenharmony_ci{ 6748c2ecf20Sopenharmony_ci struct sched_table *s; 6758c2ecf20Sopenharmony_ci unsigned int j, i; 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci for_each_port(adap, j) { 6788c2ecf20Sopenharmony_ci struct port_info *pi = netdev2pinfo(adap->port[j]); 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci s = pi->sched_tbl; 6818c2ecf20Sopenharmony_ci if (!s) 6828c2ecf20Sopenharmony_ci continue; 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci for (i = 0; i < s->sched_size; i++) { 6858c2ecf20Sopenharmony_ci struct sched_class *e; 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci e = &s->tab[i]; 6888c2ecf20Sopenharmony_ci if (e->state == SCHED_STATE_ACTIVE) 6898c2ecf20Sopenharmony_ci t4_sched_class_free(adap->port[j], e); 6908c2ecf20Sopenharmony_ci } 6918c2ecf20Sopenharmony_ci kvfree(s); 6928c2ecf20Sopenharmony_ci } 6938c2ecf20Sopenharmony_ci} 694