diff --git a/sys/dev/cxgbe/common/common.h b/sys/dev/cxgbe/common/common.h index 1b946a7917ca..efda04d5b897 100644 --- a/sys/dev/cxgbe/common/common.h +++ b/sys/dev/cxgbe/common/common.h @@ -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 */ diff --git a/sys/dev/cxgbe/common/t4_hw.c b/sys/dev/cxgbe/common/t4_hw.c index ffb015a97464..105593c7c1fe 100644 --- a/sys/dev/cxgbe/common/t4_hw.c +++ b/sys/dev/cxgbe/common/t4_hw.c @@ -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); +} diff --git a/sys/dev/cxgbe/t4_ioctl.h b/sys/dev/cxgbe/t4_ioctl.h index a080f64bc364..0d6dec5d70c7 100644 --- a/sys/dev/cxgbe/t4_ioctl.h +++ b/sys/dev/cxgbe/t4_ioctl.h @@ -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 diff --git a/sys/dev/cxgbe/t4_main.c b/sys/dev/cxgbe/t4_main.c index ac0cd2a2b099..4975349dc711 100644 --- a/sys/dev/cxgbe/t4_main.c +++ b/sys/dev/cxgbe/t4_main.c @@ -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;