cxgbe(4): T4_SET_SCHED_CLASS and T4_SET_SCHED_QUEUE ioctls to program

scheduling classes in the chip and to bind tx queue(s) to a scheduling
class respectively.  These can be used for various kinds of tx traffic
throttling (to force selected tx queues to drain at a fixed Kbps rate,
or a % of the port's total bandwidth, or at a fixed pps rate, etc.).

Obtained from:	Chelsio
This commit is contained in:
Navdeep Parhar 2013-12-03 18:34:52 +00:00
parent 9151c81ccb
commit 05337b80ee
4 changed files with 353 additions and 0 deletions

View File

@ -587,4 +587,8 @@ int t4_sge_ctxt_rd_bd(struct adapter *adap, unsigned int cid, enum ctxt_type cty
int t4_sge_ctxt_flush(struct adapter *adap, unsigned int mbox);
int t4_handle_fw_rpl(struct adapter *adap, const __be64 *rpl);
int t4_fwaddrspace_write(struct adapter *adap, unsigned int mbox, u32 addr, u32 val);
int t4_sched_config(struct adapter *adapter, int type, int minmaxen);
int t4_sched_params(struct adapter *adapter, int type, int level, int mode,
int rateunit, int ratemode, int channel, int cl,
int minrate, int maxrate, int weight, int pktsize);
#endif /* __CHELSIO_COMMON_H */

View File

@ -5664,3 +5664,50 @@ int __devinit t4_port_init(struct port_info *p, int mbox, int pf, int vf)
return 0;
}
int t4_sched_config(struct adapter *adapter, int type, int minmaxen)
{
struct fw_sched_cmd cmd;
memset(&cmd, 0, sizeof(cmd));
cmd.op_to_write = cpu_to_be32(V_FW_CMD_OP(FW_SCHED_CMD) |
F_FW_CMD_REQUEST |
F_FW_CMD_WRITE);
cmd.retval_len16 = cpu_to_be32(FW_LEN16(cmd));
cmd.u.config.sc = FW_SCHED_SC_CONFIG;
cmd.u.config.type = type;
cmd.u.config.minmaxen = minmaxen;
return t4_wr_mbox_meat(adapter,adapter->mbox, &cmd, sizeof(cmd),
NULL, 1);
}
int t4_sched_params(struct adapter *adapter, int type, int level, int mode,
int rateunit, int ratemode, int channel, int cl,
int minrate, int maxrate, int weight, int pktsize)
{
struct fw_sched_cmd cmd;
memset(&cmd, 0, sizeof(cmd));
cmd.op_to_write = cpu_to_be32(V_FW_CMD_OP(FW_SCHED_CMD) |
F_FW_CMD_REQUEST |
F_FW_CMD_WRITE);
cmd.retval_len16 = cpu_to_be32(FW_LEN16(cmd));
cmd.u.params.sc = FW_SCHED_SC_PARAMS;
cmd.u.params.type = type;
cmd.u.params.level = level;
cmd.u.params.mode = mode;
cmd.u.params.ch = channel;
cmd.u.params.cl = cl;
cmd.u.params.unit = rateunit;
cmd.u.params.rate = ratemode;
cmd.u.params.min = cpu_to_be32(minrate);
cmd.u.params.max = cpu_to_be32(maxrate);
cmd.u.params.weight = cpu_to_be16(weight);
cmd.u.params.pktsize = cpu_to_be16(pktsize);
return t4_wr_mbox_meat(adapter,adapter->mbox, &cmd, sizeof(cmd),
NULL, 1);
}

View File

@ -208,6 +208,74 @@ struct t4_filter {
struct t4_filter_specification fs;
};
/*
* Support for "sched-class" command to allow a TX Scheduling Class to be
* programmed with various parameters.
*/
struct t4_sched_params {
int8_t subcmd; /* sub-command */
int8_t type; /* packet or flow */
union {
struct { /* sub-command SCHED_CLASS_CONFIG */
int8_t minmax; /* minmax enable */
} config;
struct { /* sub-command SCHED_CLASS_PARAMS */
int8_t level; /* scheduler hierarchy level */
int8_t mode; /* per-class or per-flow */
int8_t rateunit; /* bit or packet rate */
int8_t ratemode; /* %port relative or kbps
absolute */
int8_t channel; /* scheduler channel [0..N] */
int8_t cl; /* scheduler class [0..N] */
int32_t minrate; /* minimum rate */
int32_t maxrate; /* maximum rate */
int16_t weight; /* percent weight */
int16_t pktsize; /* average packet size */
} params;
uint8_t reserved[6 + 8 * 8];
} u;
};
enum {
SCHED_CLASS_SUBCMD_CONFIG, /* config sub-command */
SCHED_CLASS_SUBCMD_PARAMS, /* params sub-command */
};
enum {
SCHED_CLASS_TYPE_PACKET,
};
enum {
SCHED_CLASS_LEVEL_CL_RL, /* class rate limiter */
SCHED_CLASS_LEVEL_CL_WRR, /* class weighted round robin */
SCHED_CLASS_LEVEL_CH_RL, /* channel rate limiter */
};
enum {
SCHED_CLASS_MODE_CLASS, /* per-class scheduling */
SCHED_CLASS_MODE_FLOW, /* per-flow scheduling */
};
enum {
SCHED_CLASS_RATEUNIT_BITS, /* bit rate scheduling */
SCHED_CLASS_RATEUNIT_PKTS, /* packet rate scheduling */
};
enum {
SCHED_CLASS_RATEMODE_REL, /* percent of port bandwidth */
SCHED_CLASS_RATEMODE_ABS, /* Kb/s */
};
/*
* Support for "sched_queue" command to allow one or more NIC TX Queues to be
* bound to a TX Scheduling Class.
*/
struct t4_sched_queue {
uint8_t port;
int8_t queue; /* queue index; -1 => all queues */
int8_t cl; /* class index; -1 => unbind */
};
#define T4_SGE_CONTEXT_SIZE 24
enum {
SGE_CONTEXT_EGRESS,
@ -261,6 +329,10 @@ struct t4_tracer {
#define CHELSIO_T4_GET_MEM _IOW('f', T4_GET_MEM, struct t4_mem_range)
#define CHELSIO_T4_GET_I2C _IOWR('f', T4_GET_I2C, struct t4_i2c_data)
#define CHELSIO_T4_CLEAR_STATS _IOW('f', T4_CLEAR_STATS, uint32_t)
#define CHELSIO_T4_SCHED_CLASS _IOW('f', T4_SET_SCHED_CLASS, \
struct t4_sched_params)
#define CHELSIO_T4_SCHED_QUEUE _IOW('f', T4_SET_SCHED_QUEUE, \
struct t4_sched_queue)
#define CHELSIO_T4_GET_TRACER _IOWR('f', T4_GET_TRACER, struct t4_tracer)
#define CHELSIO_T4_SET_TRACER _IOW('f', T4_SET_TRACER, struct t4_tracer)
#endif

View File

@ -425,6 +425,8 @@ static int get_sge_context(struct adapter *, struct t4_sge_context *);
static int load_fw(struct adapter *, struct t4_data *);
static int read_card_mem(struct adapter *, int, struct t4_mem_range *);
static int read_i2c(struct adapter *, struct t4_i2c_data *);
static int set_sched_class(struct adapter *, struct t4_sched_params *);
static int set_sched_queue(struct adapter *, struct t4_sched_queue *);
#ifdef TCP_OFFLOAD
static int toe_capability(struct port_info *, int);
#endif
@ -7295,6 +7297,228 @@ read_i2c(struct adapter *sc, struct t4_i2c_data *i2cd)
return (rc);
}
static int
in_range(int val, int lo, int hi)
{
return (val < 0 || (val <= hi && val >= lo));
}
static int
set_sched_class(struct adapter *sc, struct t4_sched_params *p)
{
int fw_subcmd, fw_type, rc;
rc = begin_synchronized_op(sc, NULL, SLEEP_OK | INTR_OK, "t4setsc");
if (rc)
return (rc);
if (!(sc->flags & FULL_INIT_DONE)) {
rc = EAGAIN;
goto done;
}
/*
* Translate the cxgbetool parameters into T4 firmware parameters. (The
* sub-command and type are in common locations.)
*/
if (p->subcmd == SCHED_CLASS_SUBCMD_CONFIG)
fw_subcmd = FW_SCHED_SC_CONFIG;
else if (p->subcmd == SCHED_CLASS_SUBCMD_PARAMS)
fw_subcmd = FW_SCHED_SC_PARAMS;
else {
rc = EINVAL;
goto done;
}
if (p->type == SCHED_CLASS_TYPE_PACKET)
fw_type = FW_SCHED_TYPE_PKTSCHED;
else {
rc = EINVAL;
goto done;
}
if (fw_subcmd == FW_SCHED_SC_CONFIG) {
/* Vet our parameters ..*/
if (p->u.config.minmax < 0) {
rc = EINVAL;
goto done;
}
/* And pass the request to the firmware ...*/
rc = -t4_sched_config(sc, fw_type, p->u.config.minmax);
goto done;
}
if (fw_subcmd == FW_SCHED_SC_PARAMS) {
int fw_level;
int fw_mode;
int fw_rateunit;
int fw_ratemode;
if (p->u.params.level == SCHED_CLASS_LEVEL_CL_RL)
fw_level = FW_SCHED_PARAMS_LEVEL_CL_RL;
else if (p->u.params.level == SCHED_CLASS_LEVEL_CL_WRR)
fw_level = FW_SCHED_PARAMS_LEVEL_CL_WRR;
else if (p->u.params.level == SCHED_CLASS_LEVEL_CH_RL)
fw_level = FW_SCHED_PARAMS_LEVEL_CH_RL;
else {
rc = EINVAL;
goto done;
}
if (p->u.params.mode == SCHED_CLASS_MODE_CLASS)
fw_mode = FW_SCHED_PARAMS_MODE_CLASS;
else if (p->u.params.mode == SCHED_CLASS_MODE_FLOW)
fw_mode = FW_SCHED_PARAMS_MODE_FLOW;
else {
rc = EINVAL;
goto done;
}
if (p->u.params.rateunit == SCHED_CLASS_RATEUNIT_BITS)
fw_rateunit = FW_SCHED_PARAMS_UNIT_BITRATE;
else if (p->u.params.rateunit == SCHED_CLASS_RATEUNIT_PKTS)
fw_rateunit = FW_SCHED_PARAMS_UNIT_PKTRATE;
else {
rc = EINVAL;
goto done;
}
if (p->u.params.ratemode == SCHED_CLASS_RATEMODE_REL)
fw_ratemode = FW_SCHED_PARAMS_RATE_REL;
else if (p->u.params.ratemode == SCHED_CLASS_RATEMODE_ABS)
fw_ratemode = FW_SCHED_PARAMS_RATE_ABS;
else {
rc = EINVAL;
goto done;
}
/* Vet our parameters ... */
if (!in_range(p->u.params.channel, 0, 3) ||
!in_range(p->u.params.cl, 0, is_t4(sc) ? 15 : 16) ||
!in_range(p->u.params.minrate, 0, 10000000) ||
!in_range(p->u.params.maxrate, 0, 10000000) ||
!in_range(p->u.params.weight, 0, 100)) {
rc = ERANGE;
goto done;
}
/*
* Translate any unset parameters into the firmware's
* nomenclature and/or fail the call if the parameters
* are required ...
*/
if (p->u.params.rateunit < 0 || p->u.params.ratemode < 0 ||
p->u.params.channel < 0 || p->u.params.cl < 0) {
rc = EINVAL;
goto done;
}
if (p->u.params.minrate < 0)
p->u.params.minrate = 0;
if (p->u.params.maxrate < 0) {
if (p->u.params.level == SCHED_CLASS_LEVEL_CL_RL ||
p->u.params.level == SCHED_CLASS_LEVEL_CH_RL) {
rc = EINVAL;
goto done;
} else
p->u.params.maxrate = 0;
}
if (p->u.params.weight < 0) {
if (p->u.params.level == SCHED_CLASS_LEVEL_CL_WRR) {
rc = EINVAL;
goto done;
} else
p->u.params.weight = 0;
}
if (p->u.params.pktsize < 0) {
if (p->u.params.level == SCHED_CLASS_LEVEL_CL_RL ||
p->u.params.level == SCHED_CLASS_LEVEL_CH_RL) {
rc = EINVAL;
goto done;
} else
p->u.params.pktsize = 0;
}
/* See what the firmware thinks of the request ... */
rc = -t4_sched_params(sc, fw_type, fw_level, fw_mode,
fw_rateunit, fw_ratemode, p->u.params.channel,
p->u.params.cl, p->u.params.minrate, p->u.params.maxrate,
p->u.params.weight, p->u.params.pktsize);
goto done;
}
rc = EINVAL;
done:
end_synchronized_op(sc, 0);
return (rc);
}
static int
set_sched_queue(struct adapter *sc, struct t4_sched_queue *p)
{
struct port_info *pi = NULL;
struct sge_txq *txq;
uint32_t fw_mnem, fw_queue, fw_class;
int i, rc;
rc = begin_synchronized_op(sc, NULL, SLEEP_OK | INTR_OK, "t4setsq");
if (rc)
return (rc);
if (!(sc->flags & FULL_INIT_DONE)) {
rc = EAGAIN;
goto done;
}
if (p->port >= sc->params.nports) {
rc = EINVAL;
goto done;
}
pi = sc->port[p->port];
if (!in_range(p->queue, 0, pi->ntxq - 1) || !in_range(p->cl, 0, 7)) {
rc = EINVAL;
goto done;
}
/*
* Create a template for the FW_PARAMS_CMD mnemonic and value (TX
* Scheduling Class in this case).
*/
fw_mnem = (V_FW_PARAMS_MNEM(FW_PARAMS_MNEM_DMAQ) |
V_FW_PARAMS_PARAM_X(FW_PARAMS_PARAM_DMAQ_EQ_SCHEDCLASS_ETH));
fw_class = p->cl < 0 ? 0xffffffff : p->cl;
/*
* If op.queue is non-negative, then we're only changing the scheduling
* on a single specified TX queue.
*/
if (p->queue >= 0) {
txq = &sc->sge.txq[pi->first_txq + p->queue];
fw_queue = (fw_mnem | V_FW_PARAMS_PARAM_YZ(txq->eq.cntxt_id));
rc = -t4_set_params(sc, sc->mbox, sc->pf, 0, 1, &fw_queue,
&fw_class);
goto done;
}
/*
* Change the scheduling on all the TX queues for the
* interface.
*/
for_each_txq(pi, i, txq) {
fw_queue = (fw_mnem | V_FW_PARAMS_PARAM_YZ(txq->eq.cntxt_id));
rc = -t4_set_params(sc, sc->mbox, sc->pf, 0, 1, &fw_queue,
&fw_class);
if (rc)
goto done;
}
rc = 0;
done:
end_synchronized_op(sc, 0);
return (rc);
}
int
t4_os_find_pci_capability(struct adapter *sc, int cap)
{
@ -7538,6 +7762,12 @@ t4_ioctl(struct cdev *dev, unsigned long cmd, caddr_t data, int fflag,
}
break;
}
case CHELSIO_T4_SCHED_CLASS:
rc = set_sched_class(sc, (struct t4_sched_params *)data);
break;
case CHELSIO_T4_SCHED_QUEUE:
rc = set_sched_queue(sc, (struct t4_sched_queue *)data);
break;
case CHELSIO_T4_GET_TRACER:
rc = t4_get_tracer(sc, (struct t4_tracer *)data);
break;