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:
parent
9151c81ccb
commit
05337b80ee
@ -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 */
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user