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