162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * This file is part of the Chelsio T4 Ethernet driver for Linux. 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (c) 2016 Chelsio Communications, Inc. All rights reserved. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * This software is available to you under a choice of one of two 762306a36Sopenharmony_ci * licenses. You may choose to be licensed under the terms of the GNU 862306a36Sopenharmony_ci * General Public License (GPL) Version 2, available from the file 962306a36Sopenharmony_ci * COPYING in the main directory of this source tree, or the 1062306a36Sopenharmony_ci * OpenIB.org BSD license below: 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * Redistribution and use in source and binary forms, with or 1362306a36Sopenharmony_ci * without modification, are permitted provided that the following 1462306a36Sopenharmony_ci * conditions are met: 1562306a36Sopenharmony_ci * 1662306a36Sopenharmony_ci * - Redistributions of source code must retain the above 1762306a36Sopenharmony_ci * copyright notice, this list of conditions and the following 1862306a36Sopenharmony_ci * disclaimer. 1962306a36Sopenharmony_ci * 2062306a36Sopenharmony_ci * - Redistributions in binary form must reproduce the above 2162306a36Sopenharmony_ci * copyright notice, this list of conditions and the following 2262306a36Sopenharmony_ci * disclaimer in the documentation and/or other materials 2362306a36Sopenharmony_ci * provided with the distribution. 2462306a36Sopenharmony_ci * 2562306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 2662306a36Sopenharmony_ci * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 2762306a36Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 2862306a36Sopenharmony_ci * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 2962306a36Sopenharmony_ci * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 3062306a36Sopenharmony_ci * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 3162306a36Sopenharmony_ci * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 3262306a36Sopenharmony_ci * SOFTWARE. 3362306a36Sopenharmony_ci */ 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#include <linux/module.h> 3662306a36Sopenharmony_ci#include <linux/netdevice.h> 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#include "cxgb4.h" 3962306a36Sopenharmony_ci#include "sched.h" 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistatic int t4_sched_class_fw_cmd(struct port_info *pi, 4262306a36Sopenharmony_ci struct ch_sched_params *p, 4362306a36Sopenharmony_ci enum sched_fw_ops op) 4462306a36Sopenharmony_ci{ 4562306a36Sopenharmony_ci struct adapter *adap = pi->adapter; 4662306a36Sopenharmony_ci struct sched_table *s = pi->sched_tbl; 4762306a36Sopenharmony_ci struct sched_class *e; 4862306a36Sopenharmony_ci int err = 0; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci e = &s->tab[p->u.params.class]; 5162306a36Sopenharmony_ci switch (op) { 5262306a36Sopenharmony_ci case SCHED_FW_OP_ADD: 5362306a36Sopenharmony_ci case SCHED_FW_OP_DEL: 5462306a36Sopenharmony_ci err = t4_sched_params(adap, p->type, 5562306a36Sopenharmony_ci p->u.params.level, p->u.params.mode, 5662306a36Sopenharmony_ci p->u.params.rateunit, 5762306a36Sopenharmony_ci p->u.params.ratemode, 5862306a36Sopenharmony_ci p->u.params.channel, e->idx, 5962306a36Sopenharmony_ci p->u.params.minrate, p->u.params.maxrate, 6062306a36Sopenharmony_ci p->u.params.weight, p->u.params.pktsize, 6162306a36Sopenharmony_ci p->u.params.burstsize); 6262306a36Sopenharmony_ci break; 6362306a36Sopenharmony_ci default: 6462306a36Sopenharmony_ci err = -ENOTSUPP; 6562306a36Sopenharmony_ci break; 6662306a36Sopenharmony_ci } 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci return err; 6962306a36Sopenharmony_ci} 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cistatic int t4_sched_bind_unbind_op(struct port_info *pi, void *arg, 7262306a36Sopenharmony_ci enum sched_bind_type type, bool bind) 7362306a36Sopenharmony_ci{ 7462306a36Sopenharmony_ci struct adapter *adap = pi->adapter; 7562306a36Sopenharmony_ci u32 fw_mnem, fw_class, fw_param; 7662306a36Sopenharmony_ci unsigned int pf = adap->pf; 7762306a36Sopenharmony_ci unsigned int vf = 0; 7862306a36Sopenharmony_ci int err = 0; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci switch (type) { 8162306a36Sopenharmony_ci case SCHED_QUEUE: { 8262306a36Sopenharmony_ci struct sched_queue_entry *qe; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci qe = (struct sched_queue_entry *)arg; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci /* Create a template for the FW_PARAMS_CMD mnemonic and 8762306a36Sopenharmony_ci * value (TX Scheduling Class in this case). 8862306a36Sopenharmony_ci */ 8962306a36Sopenharmony_ci fw_mnem = (FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_DMAQ) | 9062306a36Sopenharmony_ci FW_PARAMS_PARAM_X_V( 9162306a36Sopenharmony_ci FW_PARAMS_PARAM_DMAQ_EQ_SCHEDCLASS_ETH)); 9262306a36Sopenharmony_ci fw_class = bind ? qe->param.class : FW_SCHED_CLS_NONE; 9362306a36Sopenharmony_ci fw_param = (fw_mnem | FW_PARAMS_PARAM_YZ_V(qe->cntxt_id)); 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci pf = adap->pf; 9662306a36Sopenharmony_ci vf = 0; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci err = t4_set_params(adap, adap->mbox, pf, vf, 1, 9962306a36Sopenharmony_ci &fw_param, &fw_class); 10062306a36Sopenharmony_ci break; 10162306a36Sopenharmony_ci } 10262306a36Sopenharmony_ci case SCHED_FLOWC: { 10362306a36Sopenharmony_ci struct sched_flowc_entry *fe; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci fe = (struct sched_flowc_entry *)arg; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci fw_class = bind ? fe->param.class : FW_SCHED_CLS_NONE; 10862306a36Sopenharmony_ci err = cxgb4_ethofld_send_flowc(adap->port[pi->port_id], 10962306a36Sopenharmony_ci fe->param.tid, fw_class); 11062306a36Sopenharmony_ci break; 11162306a36Sopenharmony_ci } 11262306a36Sopenharmony_ci default: 11362306a36Sopenharmony_ci err = -ENOTSUPP; 11462306a36Sopenharmony_ci break; 11562306a36Sopenharmony_ci } 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci return err; 11862306a36Sopenharmony_ci} 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_cistatic void *t4_sched_entry_lookup(struct port_info *pi, 12162306a36Sopenharmony_ci enum sched_bind_type type, 12262306a36Sopenharmony_ci const u32 val) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci struct sched_table *s = pi->sched_tbl; 12562306a36Sopenharmony_ci struct sched_class *e, *end; 12662306a36Sopenharmony_ci void *found = NULL; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci /* Look for an entry with matching @val */ 12962306a36Sopenharmony_ci end = &s->tab[s->sched_size]; 13062306a36Sopenharmony_ci for (e = &s->tab[0]; e != end; ++e) { 13162306a36Sopenharmony_ci if (e->state == SCHED_STATE_UNUSED || 13262306a36Sopenharmony_ci e->bind_type != type) 13362306a36Sopenharmony_ci continue; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci switch (type) { 13662306a36Sopenharmony_ci case SCHED_QUEUE: { 13762306a36Sopenharmony_ci struct sched_queue_entry *qe; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci list_for_each_entry(qe, &e->entry_list, list) { 14062306a36Sopenharmony_ci if (qe->cntxt_id == val) { 14162306a36Sopenharmony_ci found = qe; 14262306a36Sopenharmony_ci break; 14362306a36Sopenharmony_ci } 14462306a36Sopenharmony_ci } 14562306a36Sopenharmony_ci break; 14662306a36Sopenharmony_ci } 14762306a36Sopenharmony_ci case SCHED_FLOWC: { 14862306a36Sopenharmony_ci struct sched_flowc_entry *fe; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci list_for_each_entry(fe, &e->entry_list, list) { 15162306a36Sopenharmony_ci if (fe->param.tid == val) { 15262306a36Sopenharmony_ci found = fe; 15362306a36Sopenharmony_ci break; 15462306a36Sopenharmony_ci } 15562306a36Sopenharmony_ci } 15662306a36Sopenharmony_ci break; 15762306a36Sopenharmony_ci } 15862306a36Sopenharmony_ci default: 15962306a36Sopenharmony_ci return NULL; 16062306a36Sopenharmony_ci } 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci if (found) 16362306a36Sopenharmony_ci break; 16462306a36Sopenharmony_ci } 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci return found; 16762306a36Sopenharmony_ci} 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_cistruct sched_class *cxgb4_sched_queue_lookup(struct net_device *dev, 17062306a36Sopenharmony_ci struct ch_sched_queue *p) 17162306a36Sopenharmony_ci{ 17262306a36Sopenharmony_ci struct port_info *pi = netdev2pinfo(dev); 17362306a36Sopenharmony_ci struct sched_queue_entry *qe = NULL; 17462306a36Sopenharmony_ci struct adapter *adap = pi->adapter; 17562306a36Sopenharmony_ci struct sge_eth_txq *txq; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci if (p->queue < 0 || p->queue >= pi->nqsets) 17862306a36Sopenharmony_ci return NULL; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci txq = &adap->sge.ethtxq[pi->first_qset + p->queue]; 18162306a36Sopenharmony_ci qe = t4_sched_entry_lookup(pi, SCHED_QUEUE, txq->q.cntxt_id); 18262306a36Sopenharmony_ci return qe ? &pi->sched_tbl->tab[qe->param.class] : NULL; 18362306a36Sopenharmony_ci} 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_cistatic int t4_sched_queue_unbind(struct port_info *pi, struct ch_sched_queue *p) 18662306a36Sopenharmony_ci{ 18762306a36Sopenharmony_ci struct sched_queue_entry *qe = NULL; 18862306a36Sopenharmony_ci struct adapter *adap = pi->adapter; 18962306a36Sopenharmony_ci struct sge_eth_txq *txq; 19062306a36Sopenharmony_ci struct sched_class *e; 19162306a36Sopenharmony_ci int err = 0; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci if (p->queue < 0 || p->queue >= pi->nqsets) 19462306a36Sopenharmony_ci return -ERANGE; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci txq = &adap->sge.ethtxq[pi->first_qset + p->queue]; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci /* Find the existing entry that the queue is bound to */ 19962306a36Sopenharmony_ci qe = t4_sched_entry_lookup(pi, SCHED_QUEUE, txq->q.cntxt_id); 20062306a36Sopenharmony_ci if (qe) { 20162306a36Sopenharmony_ci err = t4_sched_bind_unbind_op(pi, (void *)qe, SCHED_QUEUE, 20262306a36Sopenharmony_ci false); 20362306a36Sopenharmony_ci if (err) 20462306a36Sopenharmony_ci return err; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci e = &pi->sched_tbl->tab[qe->param.class]; 20762306a36Sopenharmony_ci list_del(&qe->list); 20862306a36Sopenharmony_ci kvfree(qe); 20962306a36Sopenharmony_ci if (atomic_dec_and_test(&e->refcnt)) 21062306a36Sopenharmony_ci cxgb4_sched_class_free(adap->port[pi->port_id], e->idx); 21162306a36Sopenharmony_ci } 21262306a36Sopenharmony_ci return err; 21362306a36Sopenharmony_ci} 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_cistatic int t4_sched_queue_bind(struct port_info *pi, struct ch_sched_queue *p) 21662306a36Sopenharmony_ci{ 21762306a36Sopenharmony_ci struct sched_table *s = pi->sched_tbl; 21862306a36Sopenharmony_ci struct sched_queue_entry *qe = NULL; 21962306a36Sopenharmony_ci struct adapter *adap = pi->adapter; 22062306a36Sopenharmony_ci struct sge_eth_txq *txq; 22162306a36Sopenharmony_ci struct sched_class *e; 22262306a36Sopenharmony_ci unsigned int qid; 22362306a36Sopenharmony_ci int err = 0; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci if (p->queue < 0 || p->queue >= pi->nqsets) 22662306a36Sopenharmony_ci return -ERANGE; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci qe = kvzalloc(sizeof(struct sched_queue_entry), GFP_KERNEL); 22962306a36Sopenharmony_ci if (!qe) 23062306a36Sopenharmony_ci return -ENOMEM; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci txq = &adap->sge.ethtxq[pi->first_qset + p->queue]; 23362306a36Sopenharmony_ci qid = txq->q.cntxt_id; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci /* Unbind queue from any existing class */ 23662306a36Sopenharmony_ci err = t4_sched_queue_unbind(pi, p); 23762306a36Sopenharmony_ci if (err) 23862306a36Sopenharmony_ci goto out_err; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci /* Bind queue to specified class */ 24162306a36Sopenharmony_ci qe->cntxt_id = qid; 24262306a36Sopenharmony_ci memcpy(&qe->param, p, sizeof(qe->param)); 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci e = &s->tab[qe->param.class]; 24562306a36Sopenharmony_ci err = t4_sched_bind_unbind_op(pi, (void *)qe, SCHED_QUEUE, true); 24662306a36Sopenharmony_ci if (err) 24762306a36Sopenharmony_ci goto out_err; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci list_add_tail(&qe->list, &e->entry_list); 25062306a36Sopenharmony_ci e->bind_type = SCHED_QUEUE; 25162306a36Sopenharmony_ci atomic_inc(&e->refcnt); 25262306a36Sopenharmony_ci return err; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ciout_err: 25562306a36Sopenharmony_ci kvfree(qe); 25662306a36Sopenharmony_ci return err; 25762306a36Sopenharmony_ci} 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_cistatic int t4_sched_flowc_unbind(struct port_info *pi, struct ch_sched_flowc *p) 26062306a36Sopenharmony_ci{ 26162306a36Sopenharmony_ci struct sched_flowc_entry *fe = NULL; 26262306a36Sopenharmony_ci struct adapter *adap = pi->adapter; 26362306a36Sopenharmony_ci struct sched_class *e; 26462306a36Sopenharmony_ci int err = 0; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci if (p->tid < 0 || p->tid >= adap->tids.neotids) 26762306a36Sopenharmony_ci return -ERANGE; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci /* Find the existing entry that the flowc is bound to */ 27062306a36Sopenharmony_ci fe = t4_sched_entry_lookup(pi, SCHED_FLOWC, p->tid); 27162306a36Sopenharmony_ci if (fe) { 27262306a36Sopenharmony_ci err = t4_sched_bind_unbind_op(pi, (void *)fe, SCHED_FLOWC, 27362306a36Sopenharmony_ci false); 27462306a36Sopenharmony_ci if (err) 27562306a36Sopenharmony_ci return err; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci e = &pi->sched_tbl->tab[fe->param.class]; 27862306a36Sopenharmony_ci list_del(&fe->list); 27962306a36Sopenharmony_ci kvfree(fe); 28062306a36Sopenharmony_ci if (atomic_dec_and_test(&e->refcnt)) 28162306a36Sopenharmony_ci cxgb4_sched_class_free(adap->port[pi->port_id], e->idx); 28262306a36Sopenharmony_ci } 28362306a36Sopenharmony_ci return err; 28462306a36Sopenharmony_ci} 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_cistatic int t4_sched_flowc_bind(struct port_info *pi, struct ch_sched_flowc *p) 28762306a36Sopenharmony_ci{ 28862306a36Sopenharmony_ci struct sched_table *s = pi->sched_tbl; 28962306a36Sopenharmony_ci struct sched_flowc_entry *fe = NULL; 29062306a36Sopenharmony_ci struct adapter *adap = pi->adapter; 29162306a36Sopenharmony_ci struct sched_class *e; 29262306a36Sopenharmony_ci int err = 0; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci if (p->tid < 0 || p->tid >= adap->tids.neotids) 29562306a36Sopenharmony_ci return -ERANGE; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci fe = kvzalloc(sizeof(*fe), GFP_KERNEL); 29862306a36Sopenharmony_ci if (!fe) 29962306a36Sopenharmony_ci return -ENOMEM; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci /* Unbind flowc from any existing class */ 30262306a36Sopenharmony_ci err = t4_sched_flowc_unbind(pi, p); 30362306a36Sopenharmony_ci if (err) 30462306a36Sopenharmony_ci goto out_err; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci /* Bind flowc to specified class */ 30762306a36Sopenharmony_ci memcpy(&fe->param, p, sizeof(fe->param)); 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci e = &s->tab[fe->param.class]; 31062306a36Sopenharmony_ci err = t4_sched_bind_unbind_op(pi, (void *)fe, SCHED_FLOWC, true); 31162306a36Sopenharmony_ci if (err) 31262306a36Sopenharmony_ci goto out_err; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci list_add_tail(&fe->list, &e->entry_list); 31562306a36Sopenharmony_ci e->bind_type = SCHED_FLOWC; 31662306a36Sopenharmony_ci atomic_inc(&e->refcnt); 31762306a36Sopenharmony_ci return err; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ciout_err: 32062306a36Sopenharmony_ci kvfree(fe); 32162306a36Sopenharmony_ci return err; 32262306a36Sopenharmony_ci} 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_cistatic void t4_sched_class_unbind_all(struct port_info *pi, 32562306a36Sopenharmony_ci struct sched_class *e, 32662306a36Sopenharmony_ci enum sched_bind_type type) 32762306a36Sopenharmony_ci{ 32862306a36Sopenharmony_ci if (!e) 32962306a36Sopenharmony_ci return; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci switch (type) { 33262306a36Sopenharmony_ci case SCHED_QUEUE: { 33362306a36Sopenharmony_ci struct sched_queue_entry *qe; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci list_for_each_entry(qe, &e->entry_list, list) 33662306a36Sopenharmony_ci t4_sched_queue_unbind(pi, &qe->param); 33762306a36Sopenharmony_ci break; 33862306a36Sopenharmony_ci } 33962306a36Sopenharmony_ci case SCHED_FLOWC: { 34062306a36Sopenharmony_ci struct sched_flowc_entry *fe; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci list_for_each_entry(fe, &e->entry_list, list) 34362306a36Sopenharmony_ci t4_sched_flowc_unbind(pi, &fe->param); 34462306a36Sopenharmony_ci break; 34562306a36Sopenharmony_ci } 34662306a36Sopenharmony_ci default: 34762306a36Sopenharmony_ci break; 34862306a36Sopenharmony_ci } 34962306a36Sopenharmony_ci} 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_cistatic int t4_sched_class_bind_unbind_op(struct port_info *pi, void *arg, 35262306a36Sopenharmony_ci enum sched_bind_type type, bool bind) 35362306a36Sopenharmony_ci{ 35462306a36Sopenharmony_ci int err = 0; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci if (!arg) 35762306a36Sopenharmony_ci return -EINVAL; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci switch (type) { 36062306a36Sopenharmony_ci case SCHED_QUEUE: { 36162306a36Sopenharmony_ci struct ch_sched_queue *qe = (struct ch_sched_queue *)arg; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci if (bind) 36462306a36Sopenharmony_ci err = t4_sched_queue_bind(pi, qe); 36562306a36Sopenharmony_ci else 36662306a36Sopenharmony_ci err = t4_sched_queue_unbind(pi, qe); 36762306a36Sopenharmony_ci break; 36862306a36Sopenharmony_ci } 36962306a36Sopenharmony_ci case SCHED_FLOWC: { 37062306a36Sopenharmony_ci struct ch_sched_flowc *fe = (struct ch_sched_flowc *)arg; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci if (bind) 37362306a36Sopenharmony_ci err = t4_sched_flowc_bind(pi, fe); 37462306a36Sopenharmony_ci else 37562306a36Sopenharmony_ci err = t4_sched_flowc_unbind(pi, fe); 37662306a36Sopenharmony_ci break; 37762306a36Sopenharmony_ci } 37862306a36Sopenharmony_ci default: 37962306a36Sopenharmony_ci err = -ENOTSUPP; 38062306a36Sopenharmony_ci break; 38162306a36Sopenharmony_ci } 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci return err; 38462306a36Sopenharmony_ci} 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci/** 38762306a36Sopenharmony_ci * cxgb4_sched_class_bind - Bind an entity to a scheduling class 38862306a36Sopenharmony_ci * @dev: net_device pointer 38962306a36Sopenharmony_ci * @arg: Entity opaque data 39062306a36Sopenharmony_ci * @type: Entity type (Queue) 39162306a36Sopenharmony_ci * 39262306a36Sopenharmony_ci * Binds an entity (queue) to a scheduling class. If the entity 39362306a36Sopenharmony_ci * is bound to another class, it will be unbound from the other class 39462306a36Sopenharmony_ci * and bound to the class specified in @arg. 39562306a36Sopenharmony_ci */ 39662306a36Sopenharmony_ciint cxgb4_sched_class_bind(struct net_device *dev, void *arg, 39762306a36Sopenharmony_ci enum sched_bind_type type) 39862306a36Sopenharmony_ci{ 39962306a36Sopenharmony_ci struct port_info *pi = netdev2pinfo(dev); 40062306a36Sopenharmony_ci u8 class_id; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci if (!can_sched(dev)) 40362306a36Sopenharmony_ci return -ENOTSUPP; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci if (!arg) 40662306a36Sopenharmony_ci return -EINVAL; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci switch (type) { 40962306a36Sopenharmony_ci case SCHED_QUEUE: { 41062306a36Sopenharmony_ci struct ch_sched_queue *qe = (struct ch_sched_queue *)arg; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci class_id = qe->class; 41362306a36Sopenharmony_ci break; 41462306a36Sopenharmony_ci } 41562306a36Sopenharmony_ci case SCHED_FLOWC: { 41662306a36Sopenharmony_ci struct ch_sched_flowc *fe = (struct ch_sched_flowc *)arg; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci class_id = fe->class; 41962306a36Sopenharmony_ci break; 42062306a36Sopenharmony_ci } 42162306a36Sopenharmony_ci default: 42262306a36Sopenharmony_ci return -ENOTSUPP; 42362306a36Sopenharmony_ci } 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci if (!valid_class_id(dev, class_id)) 42662306a36Sopenharmony_ci return -EINVAL; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci if (class_id == SCHED_CLS_NONE) 42962306a36Sopenharmony_ci return -ENOTSUPP; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci return t4_sched_class_bind_unbind_op(pi, arg, type, true); 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci} 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci/** 43662306a36Sopenharmony_ci * cxgb4_sched_class_unbind - Unbind an entity from a scheduling class 43762306a36Sopenharmony_ci * @dev: net_device pointer 43862306a36Sopenharmony_ci * @arg: Entity opaque data 43962306a36Sopenharmony_ci * @type: Entity type (Queue) 44062306a36Sopenharmony_ci * 44162306a36Sopenharmony_ci * Unbinds an entity (queue) from a scheduling class. 44262306a36Sopenharmony_ci */ 44362306a36Sopenharmony_ciint cxgb4_sched_class_unbind(struct net_device *dev, void *arg, 44462306a36Sopenharmony_ci enum sched_bind_type type) 44562306a36Sopenharmony_ci{ 44662306a36Sopenharmony_ci struct port_info *pi = netdev2pinfo(dev); 44762306a36Sopenharmony_ci u8 class_id; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci if (!can_sched(dev)) 45062306a36Sopenharmony_ci return -ENOTSUPP; 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci if (!arg) 45362306a36Sopenharmony_ci return -EINVAL; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci switch (type) { 45662306a36Sopenharmony_ci case SCHED_QUEUE: { 45762306a36Sopenharmony_ci struct ch_sched_queue *qe = (struct ch_sched_queue *)arg; 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci class_id = qe->class; 46062306a36Sopenharmony_ci break; 46162306a36Sopenharmony_ci } 46262306a36Sopenharmony_ci case SCHED_FLOWC: { 46362306a36Sopenharmony_ci struct ch_sched_flowc *fe = (struct ch_sched_flowc *)arg; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci class_id = fe->class; 46662306a36Sopenharmony_ci break; 46762306a36Sopenharmony_ci } 46862306a36Sopenharmony_ci default: 46962306a36Sopenharmony_ci return -ENOTSUPP; 47062306a36Sopenharmony_ci } 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci if (!valid_class_id(dev, class_id)) 47362306a36Sopenharmony_ci return -EINVAL; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci return t4_sched_class_bind_unbind_op(pi, arg, type, false); 47662306a36Sopenharmony_ci} 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci/* If @p is NULL, fetch any available unused class */ 47962306a36Sopenharmony_cistatic struct sched_class *t4_sched_class_lookup(struct port_info *pi, 48062306a36Sopenharmony_ci const struct ch_sched_params *p) 48162306a36Sopenharmony_ci{ 48262306a36Sopenharmony_ci struct sched_table *s = pi->sched_tbl; 48362306a36Sopenharmony_ci struct sched_class *found = NULL; 48462306a36Sopenharmony_ci struct sched_class *e, *end; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci if (!p) { 48762306a36Sopenharmony_ci /* Get any available unused class */ 48862306a36Sopenharmony_ci end = &s->tab[s->sched_size]; 48962306a36Sopenharmony_ci for (e = &s->tab[0]; e != end; ++e) { 49062306a36Sopenharmony_ci if (e->state == SCHED_STATE_UNUSED) { 49162306a36Sopenharmony_ci found = e; 49262306a36Sopenharmony_ci break; 49362306a36Sopenharmony_ci } 49462306a36Sopenharmony_ci } 49562306a36Sopenharmony_ci } else { 49662306a36Sopenharmony_ci /* Look for a class with matching scheduling parameters */ 49762306a36Sopenharmony_ci struct ch_sched_params info; 49862306a36Sopenharmony_ci struct ch_sched_params tp; 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci memcpy(&tp, p, sizeof(tp)); 50162306a36Sopenharmony_ci /* Don't try to match class parameter */ 50262306a36Sopenharmony_ci tp.u.params.class = SCHED_CLS_NONE; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci end = &s->tab[s->sched_size]; 50562306a36Sopenharmony_ci for (e = &s->tab[0]; e != end; ++e) { 50662306a36Sopenharmony_ci if (e->state == SCHED_STATE_UNUSED) 50762306a36Sopenharmony_ci continue; 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci memcpy(&info, &e->info, sizeof(info)); 51062306a36Sopenharmony_ci /* Don't try to match class parameter */ 51162306a36Sopenharmony_ci info.u.params.class = SCHED_CLS_NONE; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci if ((info.type == tp.type) && 51462306a36Sopenharmony_ci (!memcmp(&info.u.params, &tp.u.params, 51562306a36Sopenharmony_ci sizeof(info.u.params)))) { 51662306a36Sopenharmony_ci found = e; 51762306a36Sopenharmony_ci break; 51862306a36Sopenharmony_ci } 51962306a36Sopenharmony_ci } 52062306a36Sopenharmony_ci } 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci return found; 52362306a36Sopenharmony_ci} 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_cistatic struct sched_class *t4_sched_class_alloc(struct port_info *pi, 52662306a36Sopenharmony_ci struct ch_sched_params *p) 52762306a36Sopenharmony_ci{ 52862306a36Sopenharmony_ci struct sched_class *e = NULL; 52962306a36Sopenharmony_ci u8 class_id; 53062306a36Sopenharmony_ci int err; 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci if (!p) 53362306a36Sopenharmony_ci return NULL; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci class_id = p->u.params.class; 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci /* Only accept search for existing class with matching params 53862306a36Sopenharmony_ci * or allocation of new class with specified params 53962306a36Sopenharmony_ci */ 54062306a36Sopenharmony_ci if (class_id != SCHED_CLS_NONE) 54162306a36Sopenharmony_ci return NULL; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci /* See if there's an exisiting class with same requested sched 54462306a36Sopenharmony_ci * params. Classes can only be shared among FLOWC types. For 54562306a36Sopenharmony_ci * other types, always request a new class. 54662306a36Sopenharmony_ci */ 54762306a36Sopenharmony_ci if (p->u.params.mode == SCHED_CLASS_MODE_FLOW) 54862306a36Sopenharmony_ci e = t4_sched_class_lookup(pi, p); 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci if (!e) { 55162306a36Sopenharmony_ci struct ch_sched_params np; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci /* Fetch any available unused class */ 55462306a36Sopenharmony_ci e = t4_sched_class_lookup(pi, NULL); 55562306a36Sopenharmony_ci if (!e) 55662306a36Sopenharmony_ci return NULL; 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci memcpy(&np, p, sizeof(np)); 55962306a36Sopenharmony_ci np.u.params.class = e->idx; 56062306a36Sopenharmony_ci /* New class */ 56162306a36Sopenharmony_ci err = t4_sched_class_fw_cmd(pi, &np, SCHED_FW_OP_ADD); 56262306a36Sopenharmony_ci if (err) 56362306a36Sopenharmony_ci return NULL; 56462306a36Sopenharmony_ci memcpy(&e->info, &np, sizeof(e->info)); 56562306a36Sopenharmony_ci atomic_set(&e->refcnt, 0); 56662306a36Sopenharmony_ci e->state = SCHED_STATE_ACTIVE; 56762306a36Sopenharmony_ci } 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci return e; 57062306a36Sopenharmony_ci} 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci/** 57362306a36Sopenharmony_ci * cxgb4_sched_class_alloc - allocate a scheduling class 57462306a36Sopenharmony_ci * @dev: net_device pointer 57562306a36Sopenharmony_ci * @p: new scheduling class to create. 57662306a36Sopenharmony_ci * 57762306a36Sopenharmony_ci * Returns pointer to the scheduling class created. If @p is NULL, then 57862306a36Sopenharmony_ci * it allocates and returns any available unused scheduling class. If a 57962306a36Sopenharmony_ci * scheduling class with matching @p is found, then the matching class is 58062306a36Sopenharmony_ci * returned. 58162306a36Sopenharmony_ci */ 58262306a36Sopenharmony_cistruct sched_class *cxgb4_sched_class_alloc(struct net_device *dev, 58362306a36Sopenharmony_ci struct ch_sched_params *p) 58462306a36Sopenharmony_ci{ 58562306a36Sopenharmony_ci struct port_info *pi = netdev2pinfo(dev); 58662306a36Sopenharmony_ci u8 class_id; 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci if (!can_sched(dev)) 58962306a36Sopenharmony_ci return NULL; 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci class_id = p->u.params.class; 59262306a36Sopenharmony_ci if (!valid_class_id(dev, class_id)) 59362306a36Sopenharmony_ci return NULL; 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci return t4_sched_class_alloc(pi, p); 59662306a36Sopenharmony_ci} 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci/** 59962306a36Sopenharmony_ci * cxgb4_sched_class_free - free a scheduling class 60062306a36Sopenharmony_ci * @dev: net_device pointer 60162306a36Sopenharmony_ci * @classid: scheduling class id to free 60262306a36Sopenharmony_ci * 60362306a36Sopenharmony_ci * Frees a scheduling class if there are no users. 60462306a36Sopenharmony_ci */ 60562306a36Sopenharmony_civoid cxgb4_sched_class_free(struct net_device *dev, u8 classid) 60662306a36Sopenharmony_ci{ 60762306a36Sopenharmony_ci struct port_info *pi = netdev2pinfo(dev); 60862306a36Sopenharmony_ci struct sched_table *s = pi->sched_tbl; 60962306a36Sopenharmony_ci struct ch_sched_params p; 61062306a36Sopenharmony_ci struct sched_class *e; 61162306a36Sopenharmony_ci u32 speed; 61262306a36Sopenharmony_ci int ret; 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci e = &s->tab[classid]; 61562306a36Sopenharmony_ci if (!atomic_read(&e->refcnt) && e->state != SCHED_STATE_UNUSED) { 61662306a36Sopenharmony_ci /* Port based rate limiting needs explicit reset back 61762306a36Sopenharmony_ci * to max rate. But, we'll do explicit reset for all 61862306a36Sopenharmony_ci * types, instead of just port based type, to be on 61962306a36Sopenharmony_ci * the safer side. 62062306a36Sopenharmony_ci */ 62162306a36Sopenharmony_ci memcpy(&p, &e->info, sizeof(p)); 62262306a36Sopenharmony_ci /* Always reset mode to 0. Otherwise, FLOWC mode will 62362306a36Sopenharmony_ci * still be enabled even after resetting the traffic 62462306a36Sopenharmony_ci * class. 62562306a36Sopenharmony_ci */ 62662306a36Sopenharmony_ci p.u.params.mode = 0; 62762306a36Sopenharmony_ci p.u.params.minrate = 0; 62862306a36Sopenharmony_ci p.u.params.pktsize = 0; 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci ret = t4_get_link_params(pi, NULL, &speed, NULL); 63162306a36Sopenharmony_ci if (!ret) 63262306a36Sopenharmony_ci p.u.params.maxrate = speed * 1000; /* Mbps to Kbps */ 63362306a36Sopenharmony_ci else 63462306a36Sopenharmony_ci p.u.params.maxrate = SCHED_MAX_RATE_KBPS; 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci t4_sched_class_fw_cmd(pi, &p, SCHED_FW_OP_DEL); 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci e->state = SCHED_STATE_UNUSED; 63962306a36Sopenharmony_ci memset(&e->info, 0, sizeof(e->info)); 64062306a36Sopenharmony_ci } 64162306a36Sopenharmony_ci} 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_cistatic void t4_sched_class_free(struct net_device *dev, struct sched_class *e) 64462306a36Sopenharmony_ci{ 64562306a36Sopenharmony_ci struct port_info *pi = netdev2pinfo(dev); 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci t4_sched_class_unbind_all(pi, e, e->bind_type); 64862306a36Sopenharmony_ci cxgb4_sched_class_free(dev, e->idx); 64962306a36Sopenharmony_ci} 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_cistruct sched_table *t4_init_sched(unsigned int sched_size) 65262306a36Sopenharmony_ci{ 65362306a36Sopenharmony_ci struct sched_table *s; 65462306a36Sopenharmony_ci unsigned int i; 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci s = kvzalloc(struct_size(s, tab, sched_size), GFP_KERNEL); 65762306a36Sopenharmony_ci if (!s) 65862306a36Sopenharmony_ci return NULL; 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci s->sched_size = sched_size; 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci for (i = 0; i < s->sched_size; i++) { 66362306a36Sopenharmony_ci memset(&s->tab[i], 0, sizeof(struct sched_class)); 66462306a36Sopenharmony_ci s->tab[i].idx = i; 66562306a36Sopenharmony_ci s->tab[i].state = SCHED_STATE_UNUSED; 66662306a36Sopenharmony_ci INIT_LIST_HEAD(&s->tab[i].entry_list); 66762306a36Sopenharmony_ci atomic_set(&s->tab[i].refcnt, 0); 66862306a36Sopenharmony_ci } 66962306a36Sopenharmony_ci return s; 67062306a36Sopenharmony_ci} 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_civoid t4_cleanup_sched(struct adapter *adap) 67362306a36Sopenharmony_ci{ 67462306a36Sopenharmony_ci struct sched_table *s; 67562306a36Sopenharmony_ci unsigned int j, i; 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci for_each_port(adap, j) { 67862306a36Sopenharmony_ci struct port_info *pi = netdev2pinfo(adap->port[j]); 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci s = pi->sched_tbl; 68162306a36Sopenharmony_ci if (!s) 68262306a36Sopenharmony_ci continue; 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci for (i = 0; i < s->sched_size; i++) { 68562306a36Sopenharmony_ci struct sched_class *e; 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci e = &s->tab[i]; 68862306a36Sopenharmony_ci if (e->state == SCHED_STATE_ACTIVE) 68962306a36Sopenharmony_ci t4_sched_class_free(adap->port[j], e); 69062306a36Sopenharmony_ci } 69162306a36Sopenharmony_ci kvfree(s); 69262306a36Sopenharmony_ci } 69362306a36Sopenharmony_ci} 694