MFC r257654, r257772, r258441, r258689, r258698, r258879, r259048, and

r259103.

r257654:
cxgbe(4): Exclude MPS_RPLC_MAP_CTL (0x11114) from the register dump.  Turns
out it's a write-only register with strange side effects on read.

r257772:
cxgbe(4): Tidy up the display for payload memory statistics (pm_stats).

r258441:
cxgbe(4): update the internal list of device features.

r258689:
Disable an assertion that relies on some code[1] that isn't in HEAD yet.

r258698:
cxgbetool: "modinfo" command to display SFP+ module information.

r258879:
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.).

r259048:
Two new cxgbetool subcommands to set up scheduler classes and to bind
them to NIC queues.

r259103:
cxgbe(4): save a copy of the RSS map for each port for the driver's use.
This commit is contained in:
np 2013-12-09 22:40:22 +00:00
parent 44822de64a
commit 7777b8aff6
8 changed files with 823 additions and 30 deletions

View File

@ -194,6 +194,7 @@ struct port_info {
unsigned long flags;
int if_flags;
uint16_t *rss;
uint16_t viid;
int16_t xact_addr_filt;/* index of exact MAC address filter */
uint16_t rss_size; /* size of VI's RSS table slice */

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

@ -5661,3 +5661,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
@ -3155,7 +3157,7 @@ port_full_init(struct port_info *pi)
struct ifnet *ifp = pi->ifp;
uint16_t *rss;
struct sge_rxq *rxq;
int rc, i;
int rc, i, j;
ASSERT_SYNCHRONIZED_OP(sc);
KASSERT((pi->flags & PORT_INIT_DONE) == 0,
@ -3172,21 +3174,25 @@ port_full_init(struct port_info *pi)
goto done; /* error message displayed already */
/*
* Setup RSS for this port.
* Setup RSS for this port. Save a copy of the RSS table for later use.
*/
rss = malloc(pi->nrxq * sizeof (*rss), M_CXGBE,
M_ZERO | M_WAITOK);
for_each_rxq(pi, i, rxq) {
rss[i] = rxq->iq.abs_id;
rss = malloc(pi->rss_size * sizeof (*rss), M_CXGBE, M_ZERO | M_WAITOK);
for (i = 0; i < pi->rss_size;) {
for_each_rxq(pi, j, rxq) {
rss[i++] = rxq->iq.abs_id;
if (i == pi->rss_size)
break;
}
}
rc = -t4_config_rss_range(sc, sc->mbox, pi->viid, 0,
pi->rss_size, rss, pi->nrxq);
free(rss, M_CXGBE);
rc = -t4_config_rss_range(sc, sc->mbox, pi->viid, 0, pi->rss_size, rss,
pi->rss_size);
if (rc != 0) {
if_printf(ifp, "rss_config failed: %d\n", rc);
goto done;
}
pi->rss = rss;
pi->flags |= PORT_INIT_DONE;
done:
if (rc != 0)
@ -3235,6 +3241,7 @@ port_full_uninit(struct port_info *pi)
quiesce_fl(sc, &ofld_rxq->fl);
}
#endif
free(pi->rss, M_CXGBE);
}
t4_teardown_port_queues(pi);
@ -3401,7 +3408,8 @@ t4_get_regs(struct adapter *sc, struct t4_regdump *regs, uint8_t *buf)
0xd004, 0xd03c,
0xdfc0, 0xdfe0,
0xe000, 0xea7c,
0xf000, 0x11190,
0xf000, 0x11110,
0x11118, 0x11190,
0x19040, 0x1906c,
0x19078, 0x19080,
0x1908c, 0x19124,
@ -3607,7 +3615,8 @@ t4_get_regs(struct adapter *sc, struct t4_regdump *regs, uint8_t *buf)
0xd004, 0xd03c,
0xdfc0, 0xdfe0,
0xe000, 0x11088,
0x1109c, 0x1117c,
0x1109c, 0x11110,
0x11118, 0x1117c,
0x11190, 0x11204,
0x19040, 0x1906c,
0x19078, 0x19080,
@ -4165,13 +4174,15 @@ t4_sysctls(struct adapter *sc)
struct sysctl_oid_list *children, *c0;
static char *caps[] = {
"\20\1PPP\2QFC\3DCBX", /* caps[0] linkcaps */
"\20\1NIC\2VM\3IDS\4UM\5UM_ISGL", /* caps[1] niccaps */
"\20\1NIC\2VM\3IDS\4UM\5UM_ISGL" /* caps[1] niccaps */
"\6HASHFILTER\7ETHOFLD",
"\20\1TOE", /* caps[2] toecaps */
"\20\1RDDP\2RDMAC", /* caps[3] rdmacaps */
"\20\1INITIATOR_PDU\2TARGET_PDU" /* caps[4] iscsicaps */
"\3INITIATOR_CNXOFLD\4TARGET_CNXOFLD"
"\5INITIATOR_SSNOFLD\6TARGET_SSNOFLD",
"\20\1INITIATOR\2TARGET\3CTRL_OFLD" /* caps[5] fcoecaps */
"\4PO_INITIAOR\5PO_TARGET"
};
static char *doorbells = {"\20\1UDB\2WCWR\3UDBWC\4KDB"};
@ -5953,10 +5964,13 @@ sysctl_pm_stats(SYSCTL_HANDLER_ARGS)
struct adapter *sc = arg1;
struct sbuf *sb;
int rc, i;
uint32_t tx_cnt[PM_NSTATS], rx_cnt[PM_NSTATS];
uint64_t tx_cyc[PM_NSTATS], rx_cyc[PM_NSTATS];
static const char *pm_stats[] = {
"Read:", "Write bypass:", "Write mem:", "Flush:", "FIFO wait:"
uint32_t cnt[PM_NSTATS];
uint64_t cyc[PM_NSTATS];
static const char *rx_stats[] = {
"Read:", "Write bypass:", "Write mem:", "Flush:"
};
static const char *tx_stats[] = {
"Read:", "Write bypass:", "Write mem:", "Bypass + mem:"
};
rc = sysctl_wire_old_buffer(req, 0);
@ -5967,14 +5981,17 @@ sysctl_pm_stats(SYSCTL_HANDLER_ARGS)
if (sb == NULL)
return (ENOMEM);
t4_pmtx_get_stats(sc, tx_cnt, tx_cyc);
t4_pmrx_get_stats(sc, rx_cnt, rx_cyc);
t4_pmtx_get_stats(sc, cnt, cyc);
sbuf_printf(sb, " Tx pcmds Tx bytes");
for (i = 0; i < ARRAY_SIZE(tx_stats); i++)
sbuf_printf(sb, "\n%-13s %10u %20ju", tx_stats[i], cnt[i],
cyc[i]);
sbuf_printf(sb, " Tx count Tx cycles "
"Rx count Rx cycles");
for (i = 0; i < PM_NSTATS; i++)
sbuf_printf(sb, "\n%-13s %10u %20ju %10u %20ju",
pm_stats[i], tx_cnt[i], tx_cyc[i], rx_cnt[i], rx_cyc[i]);
t4_pmrx_get_stats(sc, cnt, cyc);
sbuf_printf(sb, "\n Rx pcmds Rx bytes");
for (i = 0; i < ARRAY_SIZE(rx_stats); i++)
sbuf_printf(sb, "\n%-13s %10u %20ju", rx_stats[i], cnt[i],
cyc[i]);
rc = sbuf_finish(sb);
sbuf_delete(sb);
@ -7285,6 +7302,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)
{
@ -7528,6 +7767,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;

View File

@ -1391,7 +1391,7 @@ rxb_free(struct mbuf *m, void *arg1, void *arg2)
{
uma_zone_t zone = arg1;
caddr_t cl = arg2;
#ifdef INVARIANTS
#ifdef notyet
u_int refcount;
refcount = *find_buf_refcnt(cl);
@ -1677,7 +1677,7 @@ t4_eth_rx(struct sge_iq *iq, const struct rss_header *rss, struct mbuf *m0)
m0->m_pkthdr.rcvif = ifp;
m0->m_flags |= M_FLOWID;
m0->m_pkthdr.flowid = rss->hash_val;
m0->m_pkthdr.flowid = be32toh(rss->hash_val);
if (cpl->csum_calc && !cpl->err_vec) {
if (ifp->if_capenable & IFCAP_RXCSUM &&

View File

@ -3,7 +3,7 @@
PROG= cxgbetool
SRCS= cxgbetool.c
NO_MAN=
CFLAGS+= -I${.CURDIR}/../../../sys/dev/cxgbe -I.
CFLAGS+= -I${.CURDIR}/../../../sys/dev/cxgbe -I${.CURDIR}/../../../sys -I.
BINDIR?= /usr/sbin
.include <bsd.prog.mk>

View File

@ -46,11 +46,12 @@ __FBSDID("$FreeBSD$");
#include <net/ethernet.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <net/sff8472.h>
#include "t4_ioctl.h"
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
#define in_range(val, lo, hi) ( val < 0 || (val <= hi && val >= lo))
#define max(x, y) ((x) > (y) ? (x) : (y))
static const char *progname, *nexus;
@ -94,12 +95,15 @@ usage(FILE *fp)
"\ti2c <port> <devaddr> <addr> [<len>] read from i2c device\n"
"\tloadfw <fw-image.bin> install firmware\n"
"\tmemdump <addr> <len> dump a memory range\n"
"\tmodinfo <port> optics/cable information\n"
"\treg <address>[=<val>] read/write register\n"
"\treg64 <address>[=<val>] read/write 64 bit register\n"
"\tregdump [<module>] ... dump registers\n"
"\tsched-class params <param> <val> .. configure TX scheduler class\n"
"\tsched-queue <port> <queue> <class> bind NIC queues to TX Scheduling class\n"
"\tstdio interactive mode\n"
"\ttcb <tid> read TCB\n"
"\ttracer <idx> tx<n>|rx<n> set and enable a tracer)\n"
"\ttracer <idx> tx<n>|rx<n> set and enable a tracer\n"
"\ttracer <idx> disable|enable disable or enable a tracer\n"
"\ttracer list list all tracers\n"
);
@ -321,7 +325,7 @@ dump_regs_t4(int argc, const char *argv[], const uint32_t *regs)
T4_MODREGS(ma),
{ "edc0", t4_edc_0_regs },
{ "edc1", t4_edc_1_regs },
T4_MODREGS(cim),
T4_MODREGS(cim),
T4_MODREGS(tp),
T4_MODREGS(ulp_rx),
T4_MODREGS(ulp_tx),
@ -333,7 +337,7 @@ dump_regs_t4(int argc, const char *argv[], const uint32_t *regs)
{ "i2c", t4_i2cm_regs },
T4_MODREGS(mi),
T4_MODREGS(uart),
T4_MODREGS(pmu),
T4_MODREGS(pmu),
T4_MODREGS(sf),
T4_MODREGS(pl),
T4_MODREGS(le),
@ -1868,6 +1872,420 @@ tracer_cmd(int argc, const char *argv[])
return set_tracer(idx, argc - 1, argv + 1);
}
static int
modinfo(int argc, const char *argv[])
{
long port;
char string[16], *p;
struct t4_i2c_data i2cd;
int rc, i;
uint16_t temp, vcc, tx_bias, tx_power, rx_power;
if (argc != 1) {
warnx("must supply a port");
return (EINVAL);
}
p = str_to_number(argv[0], &port, NULL);
if (*p || port > UCHAR_MAX) {
warnx("invalid port id \"%s\"", argv[0]);
return (EINVAL);
}
bzero(&i2cd, sizeof(i2cd));
i2cd.len = 1;
i2cd.port_id = port;
i2cd.dev_addr = SFF_8472_BASE;
i2cd.offset = SFF_8472_ID;
if ((rc = doit(CHELSIO_T4_GET_I2C, &i2cd)) != 0)
goto fail;
if (i2cd.data[0] > SFF_8472_ID_LAST)
printf("Unknown ID\n");
else
printf("ID: %s\n", sff_8472_id[i2cd.data[0]]);
bzero(&string, sizeof(string));
for (i = SFF_8472_VENDOR_START; i < SFF_8472_VENDOR_END; i++) {
i2cd.offset = i;
if ((rc = doit(CHELSIO_T4_GET_I2C, &i2cd)) != 0)
goto fail;
string[i - SFF_8472_VENDOR_START] = i2cd.data[0];
}
printf("Vendor %s\n", string);
bzero(&string, sizeof(string));
for (i = SFF_8472_SN_START; i < SFF_8472_SN_END; i++) {
i2cd.offset = i;
if ((rc = doit(CHELSIO_T4_GET_I2C, &i2cd)) != 0)
goto fail;
string[i - SFF_8472_SN_START] = i2cd.data[0];
}
printf("SN %s\n", string);
bzero(&string, sizeof(string));
for (i = SFF_8472_PN_START; i < SFF_8472_PN_END; i++) {
i2cd.offset = i;
if ((rc = doit(CHELSIO_T4_GET_I2C, &i2cd)) != 0)
goto fail;
string[i - SFF_8472_PN_START] = i2cd.data[0];
}
printf("PN %s\n", string);
bzero(&string, sizeof(string));
for (i = SFF_8472_REV_START; i < SFF_8472_REV_END; i++) {
i2cd.offset = i;
if ((rc = doit(CHELSIO_T4_GET_I2C, &i2cd)) != 0)
goto fail;
string[i - SFF_8472_REV_START] = i2cd.data[0];
}
printf("Rev %s\n", string);
i2cd.offset = SFF_8472_DIAG_TYPE;
if ((rc = doit(CHELSIO_T4_GET_I2C, &i2cd)) != 0)
goto fail;
if ((char )i2cd.data[0] & (SFF_8472_DIAG_IMPL |
SFF_8472_DIAG_INTERNAL)) {
/* Switch to reading from the Diagnostic address. */
i2cd.dev_addr = SFF_8472_DIAG;
i2cd.len = 1;
i2cd.offset = SFF_8472_TEMP;
if ((rc = doit(CHELSIO_T4_GET_I2C, &i2cd)) != 0)
goto fail;
temp = i2cd.data[0] << 8;
printf("Temp: ");
if ((temp & SFF_8472_TEMP_SIGN) == SFF_8472_TEMP_SIGN)
printf("-");
else
printf("+");
printf("%dC\n", (temp & SFF_8472_TEMP_MSK) >>
SFF_8472_TEMP_SHIFT);
i2cd.offset = SFF_8472_VCC;
if ((rc = doit(CHELSIO_T4_GET_I2C, &i2cd)) != 0)
goto fail;
vcc = i2cd.data[0] << 8;
printf("Vcc %fV\n", vcc / SFF_8472_VCC_FACTOR);
i2cd.offset = SFF_8472_TX_BIAS;
if ((rc = doit(CHELSIO_T4_GET_I2C, &i2cd)) != 0)
goto fail;
tx_bias = i2cd.data[0] << 8;
printf("TX Bias %fuA\n", tx_bias / SFF_8472_BIAS_FACTOR);
i2cd.offset = SFF_8472_TX_POWER;
if ((rc = doit(CHELSIO_T4_GET_I2C, &i2cd)) != 0)
goto fail;
tx_power = i2cd.data[0] << 8;
printf("TX Power %fmW\n", tx_power / SFF_8472_POWER_FACTOR);
i2cd.offset = SFF_8472_RX_POWER;
if ((rc = doit(CHELSIO_T4_GET_I2C, &i2cd)) != 0)
goto fail;
rx_power = i2cd.data[0] << 8;
printf("RX Power %fmW\n", rx_power / SFF_8472_POWER_FACTOR);
} else
printf("Diagnostics not supported.\n");
return(0);
fail:
if (rc == EPERM)
warnx("No module/cable in port %ld", port);
return (rc);
}
/* XXX: pass in a low/high and do range checks as well */
static int
get_sched_param(const char *param, const char *args[], long *val)
{
char *p;
if (strcmp(param, args[0]) != 0)
return (EINVAL);
p = str_to_number(args[1], val, NULL);
if (*p) {
warnx("parameter \"%s\" has bad value \"%s\"", args[0],
args[1]);
return (EINVAL);
}
return (0);
}
static int
sched_class(int argc, const char *argv[])
{
struct t4_sched_params op;
int errs, i;
memset(&op, 0xff, sizeof(op));
op.subcmd = -1;
op.type = -1;
if (argc == 0) {
warnx("missing scheduling sub-command");
return (EINVAL);
}
if (!strcmp(argv[0], "config")) {
op.subcmd = SCHED_CLASS_SUBCMD_CONFIG;
op.u.config.minmax = -1;
} else if (!strcmp(argv[0], "params")) {
op.subcmd = SCHED_CLASS_SUBCMD_PARAMS;
op.u.params.level = op.u.params.mode = op.u.params.rateunit =
op.u.params.ratemode = op.u.params.channel =
op.u.params.cl = op.u.params.minrate = op.u.params.maxrate =
op.u.params.weight = op.u.params.pktsize = -1;
} else {
warnx("invalid scheduling sub-command \"%s\"", argv[0]);
return (EINVAL);
}
/* Decode remaining arguments ... */
errs = 0;
for (i = 1; i < argc; i += 2) {
const char **args = &argv[i];
long l;
if (i + 1 == argc) {
warnx("missing argument for \"%s\"", args[0]);
errs++;
break;
}
if (!strcmp(args[0], "type")) {
if (!strcmp(args[1], "packet"))
op.type = SCHED_CLASS_TYPE_PACKET;
else {
warnx("invalid type parameter \"%s\"", args[1]);
errs++;
}
continue;
}
if (op.subcmd == SCHED_CLASS_SUBCMD_CONFIG) {
if(!get_sched_param("minmax", args, &l))
op.u.config.minmax = (int8_t)l;
else {
warnx("unknown scheduler config parameter "
"\"%s\"", args[0]);
errs++;
}
continue;
}
/* Rest applies only to SUBCMD_PARAMS */
if (op.subcmd != SCHED_CLASS_SUBCMD_PARAMS)
continue;
if (!strcmp(args[0], "level")) {
if (!strcmp(args[1], "cl-rl"))
op.u.params.level = SCHED_CLASS_LEVEL_CL_RL;
else if (!strcmp(args[1], "cl-wrr"))
op.u.params.level = SCHED_CLASS_LEVEL_CL_WRR;
else if (!strcmp(args[1], "ch-rl"))
op.u.params.level = SCHED_CLASS_LEVEL_CH_RL;
else {
warnx("invalid level parameter \"%s\"",
args[1]);
errs++;
}
} else if (!strcmp(args[0], "mode")) {
if (!strcmp(args[1], "class"))
op.u.params.mode = SCHED_CLASS_MODE_CLASS;
else if (!strcmp(args[1], "flow"))
op.u.params.mode = SCHED_CLASS_MODE_FLOW;
else {
warnx("invalid mode parameter \"%s\"", args[1]);
errs++;
}
} else if (!strcmp(args[0], "rate-unit")) {
if (!strcmp(args[1], "bits"))
op.u.params.rateunit = SCHED_CLASS_RATEUNIT_BITS;
else if (!strcmp(args[1], "pkts"))
op.u.params.rateunit = SCHED_CLASS_RATEUNIT_PKTS;
else {
warnx("invalid rate-unit parameter \"%s\"",
args[1]);
errs++;
}
} else if (!strcmp(args[0], "rate-mode")) {
if (!strcmp(args[1], "relative"))
op.u.params.ratemode = SCHED_CLASS_RATEMODE_REL;
else if (!strcmp(args[1], "absolute"))
op.u.params.ratemode = SCHED_CLASS_RATEMODE_ABS;
else {
warnx("invalid rate-mode parameter \"%s\"",
args[1]);
errs++;
}
} else if (!get_sched_param("channel", args, &l))
op.u.params.channel = (int8_t)l;
else if (!get_sched_param("class", args, &l))
op.u.params.cl = (int8_t)l;
else if (!get_sched_param("min-rate", args, &l))
op.u.params.minrate = (int32_t)l;
else if (!get_sched_param("max-rate", args, &l))
op.u.params.maxrate = (int32_t)l;
else if (!get_sched_param("weight", args, &l))
op.u.params.weight = (int16_t)l;
else if (!get_sched_param("pkt-size", args, &l))
op.u.params.pktsize = (int16_t)l;
else {
warnx("unknown scheduler parameter \"%s\"", args[0]);
errs++;
}
}
/*
* Catch some logical fallacies in terms of argument combinations here
* so we can offer more than just the EINVAL return from the driver.
* The driver will be able to catch a lot more issues since it knows
* the specifics of the device hardware capabilities like how many
* channels, classes, etc. the device supports.
*/
if (op.type < 0) {
warnx("sched \"type\" parameter missing");
errs++;
}
if (op.subcmd == SCHED_CLASS_SUBCMD_CONFIG) {
if (op.u.config.minmax < 0) {
warnx("sched config \"minmax\" parameter missing");
errs++;
}
}
if (op.subcmd == SCHED_CLASS_SUBCMD_PARAMS) {
if (op.u.params.level < 0) {
warnx("sched params \"level\" parameter missing");
errs++;
}
if (op.u.params.mode < 0) {
warnx("sched params \"mode\" parameter missing");
errs++;
}
if (op.u.params.rateunit < 0) {
warnx("sched params \"rate-unit\" parameter missing");
errs++;
}
if (op.u.params.ratemode < 0) {
warnx("sched params \"rate-mode\" parameter missing");
errs++;
}
if (op.u.params.channel < 0) {
warnx("sched params \"channel\" missing");
errs++;
}
if (op.u.params.cl < 0) {
warnx("sched params \"class\" missing");
errs++;
}
if (op.u.params.maxrate < 0 &&
(op.u.params.level == SCHED_CLASS_LEVEL_CL_RL ||
op.u.params.level == SCHED_CLASS_LEVEL_CH_RL)) {
warnx("sched params \"max-rate\" missing for "
"rate-limit level");
errs++;
}
if (op.u.params.weight < 0 &&
op.u.params.level == SCHED_CLASS_LEVEL_CL_WRR) {
warnx("sched params \"weight\" missing for "
"weighted-round-robin level");
errs++;
}
if (op.u.params.pktsize < 0 &&
(op.u.params.level == SCHED_CLASS_LEVEL_CL_RL ||
op.u.params.level == SCHED_CLASS_LEVEL_CH_RL)) {
warnx("sched params \"pkt-size\" missing for "
"rate-limit level");
errs++;
}
if (op.u.params.mode == SCHED_CLASS_MODE_FLOW &&
op.u.params.ratemode != SCHED_CLASS_RATEMODE_ABS) {
warnx("sched params mode flow needs rate-mode absolute");
errs++;
}
if (op.u.params.ratemode == SCHED_CLASS_RATEMODE_REL &&
!in_range(op.u.params.maxrate, 1, 100)) {
warnx("sched params \"max-rate\" takes "
"percentage value(1-100) for rate-mode relative");
errs++;
}
if (op.u.params.ratemode == SCHED_CLASS_RATEMODE_ABS &&
!in_range(op.u.params.maxrate, 1, 10000000)) {
warnx("sched params \"max-rate\" takes "
"value(1-10000000) for rate-mode absolute");
errs++;
}
if (op.u.params.maxrate > 0 &&
op.u.params.maxrate < op.u.params.minrate) {
warnx("sched params \"max-rate\" is less than "
"\"min-rate\"");
errs++;
}
}
if (errs > 0) {
warnx("%d error%s in sched-class command", errs,
errs == 1 ? "" : "s");
return (EINVAL);
}
return doit(CHELSIO_T4_SCHED_CLASS, &op);
}
static int
sched_queue(int argc, const char *argv[])
{
struct t4_sched_queue op = {0};
char *p;
long val;
if (argc != 3) {
/* need "<port> <queue> <class> */
warnx("incorrect number of arguments.");
return (EINVAL);
}
p = str_to_number(argv[0], &val, NULL);
if (*p || val > UCHAR_MAX) {
warnx("invalid port id \"%s\"", argv[0]);
return (EINVAL);
}
op.port = (uint8_t)val;
if (!strcmp(argv[1], "all") || !strcmp(argv[1], "*"))
op.queue = -1;
else {
p = str_to_number(argv[1], &val, NULL);
if (*p || val < -1) {
warnx("invalid queue \"%s\"", argv[1]);
return (EINVAL);
}
op.queue = (int8_t)val;
}
if (!strcmp(argv[2], "unbind") || !strcmp(argv[2], "clear"))
op.cl = -1;
else {
p = str_to_number(argv[2], &val, NULL);
if (*p || val < -1) {
warnx("invalid class \"%s\"", argv[2]);
return (EINVAL);
}
op.cl = (int8_t)val;
}
return doit(CHELSIO_T4_SCHED_QUEUE, &op);
}
static int
run_cmd(int argc, const char *argv[])
{
@ -1900,6 +2318,12 @@ run_cmd(int argc, const char *argv[])
rc = clearstats(argc, argv);
else if (!strcmp(cmd, "tracer"))
rc = tracer_cmd(argc, argv);
else if (!strcmp(cmd, "modinfo"))
rc = modinfo(argc, argv);
else if (!strcmp(cmd, "sched-class"))
rc = sched_class(argc, argv);
else if (!strcmp(cmd, "sched-queue"))
rc = sched_queue(argc, argv);
else {
rc = EINVAL;
warnx("invalid command \"%s\"", cmd);