brintos

brintos / linux-shallow public Read only

0
0
Text · 16.0 KiB · a1b1446 Raw
694 lines · c
1/*2 * This file is part of the Chelsio T4 Ethernet driver for Linux.3 *4 * Copyright (c) 2016 Chelsio Communications, Inc. All rights reserved.5 *6 * This software is available to you under a choice of one of two7 * licenses.  You may choose to be licensed under the terms of the GNU8 * General Public License (GPL) Version 2, available from the file9 * COPYING in the main directory of this source tree, or the10 * OpenIB.org BSD license below:11 *12 *     Redistribution and use in source and binary forms, with or13 *     without modification, are permitted provided that the following14 *     conditions are met:15 *16 *      - Redistributions of source code must retain the above17 *        copyright notice, this list of conditions and the following18 *        disclaimer.19 *20 *      - Redistributions in binary form must reproduce the above21 *        copyright notice, this list of conditions and the following22 *        disclaimer in the documentation and/or other materials23 *        provided with the distribution.24 *25 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,26 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF27 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND28 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS29 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN30 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN31 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE32 * SOFTWARE.33 */34 35#include <linux/module.h>36#include <linux/netdevice.h>37 38#include "cxgb4.h"39#include "sched.h"40 41static int t4_sched_class_fw_cmd(struct port_info *pi,42				 struct ch_sched_params *p,43				 enum sched_fw_ops op)44{45	struct adapter *adap = pi->adapter;46	struct sched_table *s = pi->sched_tbl;47	struct sched_class *e;48	int err = 0;49 50	e = &s->tab[p->u.params.class];51	switch (op) {52	case SCHED_FW_OP_ADD:53	case SCHED_FW_OP_DEL:54		err = t4_sched_params(adap, p->type,55				      p->u.params.level, p->u.params.mode,56				      p->u.params.rateunit,57				      p->u.params.ratemode,58				      p->u.params.channel, e->idx,59				      p->u.params.minrate, p->u.params.maxrate,60				      p->u.params.weight, p->u.params.pktsize,61				      p->u.params.burstsize);62		break;63	default:64		err = -ENOTSUPP;65		break;66	}67 68	return err;69}70 71static int t4_sched_bind_unbind_op(struct port_info *pi, void *arg,72				   enum sched_bind_type type, bool bind)73{74	struct adapter *adap = pi->adapter;75	u32 fw_mnem, fw_class, fw_param;76	unsigned int pf = adap->pf;77	unsigned int vf = 0;78	int err = 0;79 80	switch (type) {81	case SCHED_QUEUE: {82		struct sched_queue_entry *qe;83 84		qe = (struct sched_queue_entry *)arg;85 86		/* Create a template for the FW_PARAMS_CMD mnemonic and87		 * value (TX Scheduling Class in this case).88		 */89		fw_mnem = (FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_DMAQ) |90			   FW_PARAMS_PARAM_X_V(91				   FW_PARAMS_PARAM_DMAQ_EQ_SCHEDCLASS_ETH));92		fw_class = bind ? qe->param.class : FW_SCHED_CLS_NONE;93		fw_param = (fw_mnem | FW_PARAMS_PARAM_YZ_V(qe->cntxt_id));94 95		pf = adap->pf;96		vf = 0;97 98		err = t4_set_params(adap, adap->mbox, pf, vf, 1,99				    &fw_param, &fw_class);100		break;101	}102	case SCHED_FLOWC: {103		struct sched_flowc_entry *fe;104 105		fe = (struct sched_flowc_entry *)arg;106 107		fw_class = bind ? fe->param.class : FW_SCHED_CLS_NONE;108		err = cxgb4_ethofld_send_flowc(adap->port[pi->port_id],109					       fe->param.tid, fw_class);110		break;111	}112	default:113		err = -ENOTSUPP;114		break;115	}116 117	return err;118}119 120static void *t4_sched_entry_lookup(struct port_info *pi,121				   enum sched_bind_type type,122				   const u32 val)123{124	struct sched_table *s = pi->sched_tbl;125	struct sched_class *e, *end;126	void *found = NULL;127 128	/* Look for an entry with matching @val */129	end = &s->tab[s->sched_size];130	for (e = &s->tab[0]; e != end; ++e) {131		if (e->state == SCHED_STATE_UNUSED ||132		    e->bind_type != type)133			continue;134 135		switch (type) {136		case SCHED_QUEUE: {137			struct sched_queue_entry *qe;138 139			list_for_each_entry(qe, &e->entry_list, list) {140				if (qe->cntxt_id == val) {141					found = qe;142					break;143				}144			}145			break;146		}147		case SCHED_FLOWC: {148			struct sched_flowc_entry *fe;149 150			list_for_each_entry(fe, &e->entry_list, list) {151				if (fe->param.tid == val) {152					found = fe;153					break;154				}155			}156			break;157		}158		default:159			return NULL;160		}161 162		if (found)163			break;164	}165 166	return found;167}168 169struct sched_class *cxgb4_sched_queue_lookup(struct net_device *dev,170					     struct ch_sched_queue *p)171{172	struct port_info *pi = netdev2pinfo(dev);173	struct sched_queue_entry *qe = NULL;174	struct adapter *adap = pi->adapter;175	struct sge_eth_txq *txq;176 177	if (p->queue < 0 || p->queue >= pi->nqsets)178		return NULL;179 180	txq = &adap->sge.ethtxq[pi->first_qset + p->queue];181	qe = t4_sched_entry_lookup(pi, SCHED_QUEUE, txq->q.cntxt_id);182	return qe ? &pi->sched_tbl->tab[qe->param.class] : NULL;183}184 185static int t4_sched_queue_unbind(struct port_info *pi, struct ch_sched_queue *p)186{187	struct sched_queue_entry *qe = NULL;188	struct adapter *adap = pi->adapter;189	struct sge_eth_txq *txq;190	struct sched_class *e;191	int err = 0;192 193	if (p->queue < 0 || p->queue >= pi->nqsets)194		return -ERANGE;195 196	txq = &adap->sge.ethtxq[pi->first_qset + p->queue];197 198	/* Find the existing entry that the queue is bound to */199	qe = t4_sched_entry_lookup(pi, SCHED_QUEUE, txq->q.cntxt_id);200	if (qe) {201		err = t4_sched_bind_unbind_op(pi, (void *)qe, SCHED_QUEUE,202					      false);203		if (err)204			return err;205 206		e = &pi->sched_tbl->tab[qe->param.class];207		list_del(&qe->list);208		kvfree(qe);209		if (atomic_dec_and_test(&e->refcnt))210			cxgb4_sched_class_free(adap->port[pi->port_id], e->idx);211	}212	return err;213}214 215static int t4_sched_queue_bind(struct port_info *pi, struct ch_sched_queue *p)216{217	struct sched_table *s = pi->sched_tbl;218	struct sched_queue_entry *qe = NULL;219	struct adapter *adap = pi->adapter;220	struct sge_eth_txq *txq;221	struct sched_class *e;222	unsigned int qid;223	int err = 0;224 225	if (p->queue < 0 || p->queue >= pi->nqsets)226		return -ERANGE;227 228	qe = kvzalloc(sizeof(struct sched_queue_entry), GFP_KERNEL);229	if (!qe)230		return -ENOMEM;231 232	txq = &adap->sge.ethtxq[pi->first_qset + p->queue];233	qid = txq->q.cntxt_id;234 235	/* Unbind queue from any existing class */236	err = t4_sched_queue_unbind(pi, p);237	if (err)238		goto out_err;239 240	/* Bind queue to specified class */241	qe->cntxt_id = qid;242	memcpy(&qe->param, p, sizeof(qe->param));243 244	e = &s->tab[qe->param.class];245	err = t4_sched_bind_unbind_op(pi, (void *)qe, SCHED_QUEUE, true);246	if (err)247		goto out_err;248 249	list_add_tail(&qe->list, &e->entry_list);250	e->bind_type = SCHED_QUEUE;251	atomic_inc(&e->refcnt);252	return err;253 254out_err:255	kvfree(qe);256	return err;257}258 259static int t4_sched_flowc_unbind(struct port_info *pi, struct ch_sched_flowc *p)260{261	struct sched_flowc_entry *fe = NULL;262	struct adapter *adap = pi->adapter;263	struct sched_class *e;264	int err = 0;265 266	if (p->tid < 0 || p->tid >= adap->tids.neotids)267		return -ERANGE;268 269	/* Find the existing entry that the flowc is bound to */270	fe = t4_sched_entry_lookup(pi, SCHED_FLOWC, p->tid);271	if (fe) {272		err = t4_sched_bind_unbind_op(pi, (void *)fe, SCHED_FLOWC,273					      false);274		if (err)275			return err;276 277		e = &pi->sched_tbl->tab[fe->param.class];278		list_del(&fe->list);279		kvfree(fe);280		if (atomic_dec_and_test(&e->refcnt))281			cxgb4_sched_class_free(adap->port[pi->port_id], e->idx);282	}283	return err;284}285 286static int t4_sched_flowc_bind(struct port_info *pi, struct ch_sched_flowc *p)287{288	struct sched_table *s = pi->sched_tbl;289	struct sched_flowc_entry *fe = NULL;290	struct adapter *adap = pi->adapter;291	struct sched_class *e;292	int err = 0;293 294	if (p->tid < 0 || p->tid >= adap->tids.neotids)295		return -ERANGE;296 297	fe = kvzalloc(sizeof(*fe), GFP_KERNEL);298	if (!fe)299		return -ENOMEM;300 301	/* Unbind flowc from any existing class */302	err = t4_sched_flowc_unbind(pi, p);303	if (err)304		goto out_err;305 306	/* Bind flowc to specified class */307	memcpy(&fe->param, p, sizeof(fe->param));308 309	e = &s->tab[fe->param.class];310	err = t4_sched_bind_unbind_op(pi, (void *)fe, SCHED_FLOWC, true);311	if (err)312		goto out_err;313 314	list_add_tail(&fe->list, &e->entry_list);315	e->bind_type = SCHED_FLOWC;316	atomic_inc(&e->refcnt);317	return err;318 319out_err:320	kvfree(fe);321	return err;322}323 324static void t4_sched_class_unbind_all(struct port_info *pi,325				      struct sched_class *e,326				      enum sched_bind_type type)327{328	if (!e)329		return;330 331	switch (type) {332	case SCHED_QUEUE: {333		struct sched_queue_entry *qe;334 335		list_for_each_entry(qe, &e->entry_list, list)336			t4_sched_queue_unbind(pi, &qe->param);337		break;338	}339	case SCHED_FLOWC: {340		struct sched_flowc_entry *fe;341 342		list_for_each_entry(fe, &e->entry_list, list)343			t4_sched_flowc_unbind(pi, &fe->param);344		break;345	}346	default:347		break;348	}349}350 351static int t4_sched_class_bind_unbind_op(struct port_info *pi, void *arg,352					 enum sched_bind_type type, bool bind)353{354	int err = 0;355 356	if (!arg)357		return -EINVAL;358 359	switch (type) {360	case SCHED_QUEUE: {361		struct ch_sched_queue *qe = (struct ch_sched_queue *)arg;362 363		if (bind)364			err = t4_sched_queue_bind(pi, qe);365		else366			err = t4_sched_queue_unbind(pi, qe);367		break;368	}369	case SCHED_FLOWC: {370		struct ch_sched_flowc *fe = (struct ch_sched_flowc *)arg;371 372		if (bind)373			err = t4_sched_flowc_bind(pi, fe);374		else375			err = t4_sched_flowc_unbind(pi, fe);376		break;377	}378	default:379		err = -ENOTSUPP;380		break;381	}382 383	return err;384}385 386/**387 * cxgb4_sched_class_bind - Bind an entity to a scheduling class388 * @dev: net_device pointer389 * @arg: Entity opaque data390 * @type: Entity type (Queue)391 *392 * Binds an entity (queue) to a scheduling class.  If the entity393 * is bound to another class, it will be unbound from the other class394 * and bound to the class specified in @arg.395 */396int cxgb4_sched_class_bind(struct net_device *dev, void *arg,397			   enum sched_bind_type type)398{399	struct port_info *pi = netdev2pinfo(dev);400	u8 class_id;401 402	if (!can_sched(dev))403		return -ENOTSUPP;404 405	if (!arg)406		return -EINVAL;407 408	switch (type) {409	case SCHED_QUEUE: {410		struct ch_sched_queue *qe = (struct ch_sched_queue *)arg;411 412		class_id = qe->class;413		break;414	}415	case SCHED_FLOWC: {416		struct ch_sched_flowc *fe = (struct ch_sched_flowc *)arg;417 418		class_id = fe->class;419		break;420	}421	default:422		return -ENOTSUPP;423	}424 425	if (!valid_class_id(dev, class_id))426		return -EINVAL;427 428	if (class_id == SCHED_CLS_NONE)429		return -ENOTSUPP;430 431	return t4_sched_class_bind_unbind_op(pi, arg, type, true);432 433}434 435/**436 * cxgb4_sched_class_unbind - Unbind an entity from a scheduling class437 * @dev: net_device pointer438 * @arg: Entity opaque data439 * @type: Entity type (Queue)440 *441 * Unbinds an entity (queue) from a scheduling class.442 */443int cxgb4_sched_class_unbind(struct net_device *dev, void *arg,444			     enum sched_bind_type type)445{446	struct port_info *pi = netdev2pinfo(dev);447	u8 class_id;448 449	if (!can_sched(dev))450		return -ENOTSUPP;451 452	if (!arg)453		return -EINVAL;454 455	switch (type) {456	case SCHED_QUEUE: {457		struct ch_sched_queue *qe = (struct ch_sched_queue *)arg;458 459		class_id = qe->class;460		break;461	}462	case SCHED_FLOWC: {463		struct ch_sched_flowc *fe = (struct ch_sched_flowc *)arg;464 465		class_id = fe->class;466		break;467	}468	default:469		return -ENOTSUPP;470	}471 472	if (!valid_class_id(dev, class_id))473		return -EINVAL;474 475	return t4_sched_class_bind_unbind_op(pi, arg, type, false);476}477 478/* If @p is NULL, fetch any available unused class */479static struct sched_class *t4_sched_class_lookup(struct port_info *pi,480						const struct ch_sched_params *p)481{482	struct sched_table *s = pi->sched_tbl;483	struct sched_class *found = NULL;484	struct sched_class *e, *end;485 486	if (!p) {487		/* Get any available unused class */488		end = &s->tab[s->sched_size];489		for (e = &s->tab[0]; e != end; ++e) {490			if (e->state == SCHED_STATE_UNUSED) {491				found = e;492				break;493			}494		}495	} else {496		/* Look for a class with matching scheduling parameters */497		struct ch_sched_params info;498		struct ch_sched_params tp;499 500		memcpy(&tp, p, sizeof(tp));501		/* Don't try to match class parameter */502		tp.u.params.class = SCHED_CLS_NONE;503 504		end = &s->tab[s->sched_size];505		for (e = &s->tab[0]; e != end; ++e) {506			if (e->state == SCHED_STATE_UNUSED)507				continue;508 509			memcpy(&info, &e->info, sizeof(info));510			/* Don't try to match class parameter */511			info.u.params.class = SCHED_CLS_NONE;512 513			if ((info.type == tp.type) &&514			    (!memcmp(&info.u.params, &tp.u.params,515				     sizeof(info.u.params)))) {516				found = e;517				break;518			}519		}520	}521 522	return found;523}524 525static struct sched_class *t4_sched_class_alloc(struct port_info *pi,526						struct ch_sched_params *p)527{528	struct sched_class *e = NULL;529	u8 class_id;530	int err;531 532	if (!p)533		return NULL;534 535	class_id = p->u.params.class;536 537	/* Only accept search for existing class with matching params538	 * or allocation of new class with specified params539	 */540	if (class_id != SCHED_CLS_NONE)541		return NULL;542 543	/* See if there's an exisiting class with same requested sched544	 * params. Classes can only be shared among FLOWC types. For545	 * other types, always request a new class.546	 */547	if (p->u.params.mode == SCHED_CLASS_MODE_FLOW)548		e = t4_sched_class_lookup(pi, p);549 550	if (!e) {551		struct ch_sched_params np;552 553		/* Fetch any available unused class */554		e = t4_sched_class_lookup(pi, NULL);555		if (!e)556			return NULL;557 558		memcpy(&np, p, sizeof(np));559		np.u.params.class = e->idx;560		/* New class */561		err = t4_sched_class_fw_cmd(pi, &np, SCHED_FW_OP_ADD);562		if (err)563			return NULL;564		memcpy(&e->info, &np, sizeof(e->info));565		atomic_set(&e->refcnt, 0);566		e->state = SCHED_STATE_ACTIVE;567	}568 569	return e;570}571 572/**573 * cxgb4_sched_class_alloc - allocate a scheduling class574 * @dev: net_device pointer575 * @p: new scheduling class to create.576 *577 * Returns pointer to the scheduling class created.  If @p is NULL, then578 * it allocates and returns any available unused scheduling class. If a579 * scheduling class with matching @p is found, then the matching class is580 * returned.581 */582struct sched_class *cxgb4_sched_class_alloc(struct net_device *dev,583					    struct ch_sched_params *p)584{585	struct port_info *pi = netdev2pinfo(dev);586	u8 class_id;587 588	if (!can_sched(dev))589		return NULL;590 591	class_id = p->u.params.class;592	if (!valid_class_id(dev, class_id))593		return NULL;594 595	return t4_sched_class_alloc(pi, p);596}597 598/**599 * cxgb4_sched_class_free - free a scheduling class600 * @dev: net_device pointer601 * @classid: scheduling class id to free602 *603 * Frees a scheduling class if there are no users.604 */605void cxgb4_sched_class_free(struct net_device *dev, u8 classid)606{607	struct port_info *pi = netdev2pinfo(dev);608	struct sched_table *s = pi->sched_tbl;609	struct ch_sched_params p;610	struct sched_class *e;611	u32 speed;612	int ret;613 614	e = &s->tab[classid];615	if (!atomic_read(&e->refcnt) && e->state != SCHED_STATE_UNUSED) {616		/* Port based rate limiting needs explicit reset back617		 * to max rate. But, we'll do explicit reset for all618		 * types, instead of just port based type, to be on619		 * the safer side.620		 */621		memcpy(&p, &e->info, sizeof(p));622		/* Always reset mode to 0. Otherwise, FLOWC mode will623		 * still be enabled even after resetting the traffic624		 * class.625		 */626		p.u.params.mode = 0;627		p.u.params.minrate = 0;628		p.u.params.pktsize = 0;629 630		ret = t4_get_link_params(pi, NULL, &speed, NULL);631		if (!ret)632			p.u.params.maxrate = speed * 1000; /* Mbps to Kbps */633		else634			p.u.params.maxrate = SCHED_MAX_RATE_KBPS;635 636		t4_sched_class_fw_cmd(pi, &p, SCHED_FW_OP_DEL);637 638		e->state = SCHED_STATE_UNUSED;639		memset(&e->info, 0, sizeof(e->info));640	}641}642 643static void t4_sched_class_free(struct net_device *dev, struct sched_class *e)644{645	struct port_info *pi = netdev2pinfo(dev);646 647	t4_sched_class_unbind_all(pi, e, e->bind_type);648	cxgb4_sched_class_free(dev, e->idx);649}650 651struct sched_table *t4_init_sched(unsigned int sched_size)652{653	struct sched_table *s;654	unsigned int i;655 656	s = kvzalloc(struct_size(s, tab, sched_size), GFP_KERNEL);657	if (!s)658		return NULL;659 660	s->sched_size = sched_size;661 662	for (i = 0; i < s->sched_size; i++) {663		memset(&s->tab[i], 0, sizeof(struct sched_class));664		s->tab[i].idx = i;665		s->tab[i].state = SCHED_STATE_UNUSED;666		INIT_LIST_HEAD(&s->tab[i].entry_list);667		atomic_set(&s->tab[i].refcnt, 0);668	}669	return s;670}671 672void t4_cleanup_sched(struct adapter *adap)673{674	struct sched_table *s;675	unsigned int j, i;676 677	for_each_port(adap, j) {678		struct port_info *pi = netdev2pinfo(adap->port[j]);679 680		s = pi->sched_tbl;681		if (!s)682			continue;683 684		for (i = 0; i < s->sched_size; i++) {685			struct sched_class *e;686 687			e = &s->tab[i];688			if (e->state == SCHED_STATE_ACTIVE)689				t4_sched_class_free(adap->port[j], e);690		}691		kvfree(s);692	}693}694