Date: Tue, 2 May 2017 20:38:11 +0000 (UTC) From: Navdeep Parhar <np@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r317702 - in head/sys: conf dev/cxgbe dev/cxgbe/common modules/cxgbe/if_cxgbe Message-ID: <201705022038.v42KcBaQ053975@repo.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: np Date: Tue May 2 20:38:10 2017 New Revision: 317702 URL: https://svnweb.freebsd.org/changeset/base/317702 Log: cxgbe(4): Support routines for Tx traffic scheduling. - Create a new file, t4_sched.c, and move all of the code related to traffic management from t4_main.c and t4_sge.c to this file. - Track both Channel Rate Limiter (ch_rl) and Class Rate Limiter (cl_rl) parameters in the PF driver. - Initialize all the cl_rl limiters with somewhat arbitrary default rates and provide routines to update them on the fly. - Provide routines to reserve and release traffic classes. MFC after: 1 month Sponsored by: Chelsio Communications Added: head/sys/dev/cxgbe/t4_sched.c (contents, props changed) Modified: head/sys/conf/files head/sys/dev/cxgbe/adapter.h head/sys/dev/cxgbe/common/common.h head/sys/dev/cxgbe/common/t4_hw.c head/sys/dev/cxgbe/t4_main.c head/sys/dev/cxgbe/t4_sge.c head/sys/dev/cxgbe/t4_vf.c head/sys/modules/cxgbe/if_cxgbe/Makefile Modified: head/sys/conf/files ============================================================================== --- head/sys/conf/files Tue May 2 20:08:04 2017 (r317701) +++ head/sys/conf/files Tue May 2 20:38:10 2017 (r317702) @@ -1331,6 +1331,8 @@ dev/cxgbe/t4_main.c optional cxgbe pci compile-with "${NORMAL_C} -I$S/dev/cxgbe" dev/cxgbe/t4_netmap.c optional cxgbe pci \ compile-with "${NORMAL_C} -I$S/dev/cxgbe" +dev/cxgbe/t4_sched.c optional cxgbe pci \ + compile-with "${NORMAL_C} -I$S/dev/cxgbe" dev/cxgbe/t4_sge.c optional cxgbe pci \ compile-with "${NORMAL_C} -I$S/dev/cxgbe" dev/cxgbe/t4_l2t.c optional cxgbe pci \ Modified: head/sys/dev/cxgbe/adapter.h ============================================================================== --- head/sys/dev/cxgbe/adapter.h Tue May 2 20:08:04 2017 (r317701) +++ head/sys/dev/cxgbe/adapter.h Tue May 2 20:38:10 2017 (r317702) @@ -231,15 +231,36 @@ struct vi_info { uint8_t hw_addr[ETHER_ADDR_LEN]; /* factory MAC address, won't change */ }; +struct tx_ch_rl_params { + enum fw_sched_params_rate ratemode; /* %port (REL) or kbps (ABS) */ + uint32_t maxrate; +}; + enum { - /* tx_sched_class flags */ - TX_SC_OK = (1 << 0), /* Set up in hardware, active. */ + TX_CLRL_REFRESH = (1 << 0), /* Need to update hardware state. */ + TX_CLRL_ERROR = (1 << 1), /* Error, hardware state unknown. */ }; -struct tx_sched_class { +struct tx_cl_rl_params { int refcount; - int flags; - struct t4_sched_class_params params; + u_int flags; + enum fw_sched_params_rate ratemode; /* %port REL or ABS value */ + enum fw_sched_params_unit rateunit; /* kbps or pps (when ABS) */ + enum fw_sched_params_mode mode; /* aggr or per-flow */ + uint32_t maxrate; + uint16_t pktsize; +}; + +/* Tx scheduler parameters for a channel/port */ +struct tx_sched_params { + /* Channel Rate Limiter */ + struct tx_ch_rl_params ch_rl; + + /* Class WRR */ + /* XXX */ + + /* Class Rate Limiter */ + struct tx_cl_rl_params cl_rl[]; }; struct port_info { @@ -251,7 +272,7 @@ struct port_info { int up_vis; int uld_vis; - struct tx_sched_class *tc; /* traffic classes for this channel */ + struct tx_sched_params *sched_params; struct mtx pi_lock; char lockname[16]; @@ -825,6 +846,9 @@ struct adapter { struct memwin memwin[NUM_MEMWIN]; /* memory windows */ + struct mtx tc_lock; + struct task tc_task; + const char *last_op; const void *last_op_thr; int last_op_flags; @@ -1106,8 +1130,6 @@ int t4_detach_common(device_t); int t4_filter_rpl(struct sge_iq *, const struct rss_header *, struct mbuf *); int t4_map_bars_0_and_4(struct adapter *); int t4_map_bar_2(struct adapter *); -int t4_set_sched_class(struct adapter *, struct t4_sched_params *); -int t4_set_sched_queue(struct adapter *, struct t4_sched_queue *); int t4_setup_intr_handlers(struct adapter *); void t4_sysctls(struct adapter *); int begin_synchronized_op(struct adapter *, struct vi_info *, int, char *); @@ -1168,6 +1190,15 @@ int t4_set_tracer(struct adapter *, stru int t4_trace_pkt(struct sge_iq *, const struct rss_header *, struct mbuf *); int t5_trace_pkt(struct sge_iq *, const struct rss_header *, struct mbuf *); +/* t4_sched.c */ +int t4_set_sched_class(struct adapter *, struct t4_sched_params *); +int t4_set_sched_queue(struct adapter *, struct t4_sched_queue *); +int t4_init_tx_sched(struct adapter *); +int t4_free_tx_sched(struct adapter *); +void t4_update_tx_sched(struct adapter *); +int t4_reserve_cl_rl_kbps(struct adapter *, int, u_int, int *); +void t4_release_cl_rl_kbps(struct adapter *, int, int); + static inline struct wrqe * alloc_wrqe(int wr_len, struct sge_wrq *wrq) { Modified: head/sys/dev/cxgbe/common/common.h ============================================================================== --- head/sys/dev/cxgbe/common/common.h Tue May 2 20:08:04 2017 (r317701) +++ head/sys/dev/cxgbe/common/common.h Tue May 2 20:38:10 2017 (r317702) @@ -774,6 +774,13 @@ int t4_sched_params(struct adapter *adap int rateunit, int ratemode, int channel, int cl, int minrate, int maxrate, int weight, int pktsize, int sleep_ok); +int t4_sched_params_ch_rl(struct adapter *adapter, int channel, int ratemode, + unsigned int maxrate, int sleep_ok); +int t4_sched_params_cl_wrr(struct adapter *adapter, int channel, int cl, + int weight, int sleep_ok); +int t4_sched_params_cl_rl_kbps(struct adapter *adapter, int channel, int cl, + int mode, unsigned int maxrate, int pktsize, + int sleep_ok); int t4_config_watchdog(struct adapter *adapter, unsigned int mbox, unsigned int pf, unsigned int vf, unsigned int timeout, unsigned int action); Modified: head/sys/dev/cxgbe/common/t4_hw.c ============================================================================== --- head/sys/dev/cxgbe/common/t4_hw.c Tue May 2 20:08:04 2017 (r317701) +++ head/sys/dev/cxgbe/common/t4_hw.c Tue May 2 20:38:10 2017 (r317702) @@ -9396,6 +9396,79 @@ int t4_sched_params(struct adapter *adap NULL, sleep_ok); } +int t4_sched_params_ch_rl(struct adapter *adapter, int channel, int ratemode, + unsigned int maxrate, int sleep_ok) +{ + 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 = FW_SCHED_TYPE_PKTSCHED; + cmd.u.params.level = FW_SCHED_PARAMS_LEVEL_CH_RL; + cmd.u.params.ch = channel; + cmd.u.params.rate = ratemode; /* REL or ABS */ + cmd.u.params.max = cpu_to_be32(maxrate);/* % or kbps */ + + return t4_wr_mbox_meat(adapter,adapter->mbox, &cmd, sizeof(cmd), + NULL, sleep_ok); +} + +int t4_sched_params_cl_wrr(struct adapter *adapter, int channel, int cl, + int weight, int sleep_ok) +{ + struct fw_sched_cmd cmd; + + if (weight < 0 || weight > 100) + return -EINVAL; + + 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 = FW_SCHED_TYPE_PKTSCHED; + cmd.u.params.level = FW_SCHED_PARAMS_LEVEL_CL_WRR; + cmd.u.params.ch = channel; + cmd.u.params.cl = cl; + cmd.u.params.weight = cpu_to_be16(weight); + + return t4_wr_mbox_meat(adapter,adapter->mbox, &cmd, sizeof(cmd), + NULL, sleep_ok); +} + +int t4_sched_params_cl_rl_kbps(struct adapter *adapter, int channel, int cl, + int mode, unsigned int maxrate, int pktsize, int sleep_ok) +{ + 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 = FW_SCHED_TYPE_PKTSCHED; + cmd.u.params.level = FW_SCHED_PARAMS_LEVEL_CL_RL; + cmd.u.params.mode = mode; + cmd.u.params.ch = channel; + cmd.u.params.cl = cl; + cmd.u.params.unit = FW_SCHED_PARAMS_UNIT_BITRATE; + cmd.u.params.rate = FW_SCHED_PARAMS_RATE_ABS; + cmd.u.params.max = cpu_to_be32(maxrate); + cmd.u.params.pktsize = cpu_to_be16(pktsize); + + return t4_wr_mbox_meat(adapter,adapter->mbox, &cmd, sizeof(cmd), + NULL, sleep_ok); +} + /* * t4_config_watchdog - configure (enable/disable) a watchdog timer * @adapter: the adapter Modified: head/sys/dev/cxgbe/t4_main.c ============================================================================== --- head/sys/dev/cxgbe/t4_main.c Tue May 2 20:08:04 2017 (r317701) +++ head/sys/dev/cxgbe/t4_main.c Tue May 2 20:38:10 2017 (r317702) @@ -998,9 +998,6 @@ t4_attach(device_t dev) mtx_init(&pi->pi_lock, pi->lockname, 0, MTX_DEF); sc->chan_map[pi->tx_chan] = i; - pi->tc = malloc(sizeof(struct tx_sched_class) * - sc->chip_params->nsched_cls, M_CXGBE, M_ZERO | M_WAITOK); - if (port_top_speed(pi) >= 10) { n10g++; } else { @@ -1088,6 +1085,7 @@ t4_attach(device_t dev) M_ZERO | M_WAITOK); t4_init_l2t(sc, M_WAITOK); + t4_init_tx_sched(sc); /* * Second pass over the ports. This time we know the number of rx and @@ -1312,6 +1310,9 @@ t4_detach_common(device_t dev) for (i = 0; i < sc->intr_count; i++) t4_free_irq(sc, &sc->irq[i]); + if ((sc->flags & (IS_VF | FW_OK)) == FW_OK) + t4_free_tx_sched(sc); + for (i = 0; i < MAX_NPORTS; i++) { pi = sc->port[i]; if (pi) { @@ -1321,7 +1322,6 @@ t4_detach_common(device_t dev) mtx_destroy(&pi->pi_lock); free(pi->vi, M_CXGBE); - free(pi->tc, M_CXGBE); free(pi, M_CXGBE); } } @@ -5338,9 +5338,9 @@ cxgbe_sysctls(struct port_info *pi) * dev.(cxgbe|cxl).X.tc. */ oid = SYSCTL_ADD_NODE(ctx, children, OID_AUTO, "tc", CTLFLAG_RD, NULL, - "Tx scheduler traffic classes"); + "Tx scheduler traffic classes (cl_rl)"); for (i = 0; i < sc->chip_params->nsched_cls; i++) { - struct tx_sched_class *tc = &pi->tc[i]; + struct tx_cl_rl_params *tc = &pi->sched_params->cl_rl[i]; snprintf(name, sizeof(name), "%d", i); children2 = SYSCTL_CHILDREN(SYSCTL_ADD_NODE(ctx, @@ -7855,10 +7855,9 @@ static int sysctl_tc_params(SYSCTL_HANDLER_ARGS) { struct adapter *sc = arg1; - struct tx_sched_class *tc; - struct t4_sched_class_params p; + struct tx_cl_rl_params tc; struct sbuf *sb; - int i, rc, port_id, flags, mbps, gbps; + int i, rc, port_id, mbps, gbps; rc = sysctl_wire_old_buffer(req, 0); if (rc != 0) @@ -7873,52 +7872,34 @@ sysctl_tc_params(SYSCTL_HANDLER_ARGS) MPASS(sc->port[port_id] != NULL); i = arg2 & 0xffff; MPASS(i < sc->chip_params->nsched_cls); - tc = &sc->port[port_id]->tc[i]; - - rc = begin_synchronized_op(sc, NULL, HOLD_LOCK | SLEEP_OK | INTR_OK, - "t4tc_p"); - if (rc) - goto done; - flags = tc->flags; - p = tc->params; - end_synchronized_op(sc, LOCK_HELD); - if ((flags & TX_SC_OK) == 0) { - sbuf_printf(sb, "none"); - goto done; - } + mtx_lock(&sc->tc_lock); + tc = sc->port[port_id]->sched_params->cl_rl[i]; + mtx_unlock(&sc->tc_lock); - if (p.level == SCHED_CLASS_LEVEL_CL_WRR) { - sbuf_printf(sb, "cl-wrr weight %u", p.weight); - goto done; - } else if (p.level == SCHED_CLASS_LEVEL_CL_RL) - sbuf_printf(sb, "cl-rl"); - else if (p.level == SCHED_CLASS_LEVEL_CH_RL) - sbuf_printf(sb, "ch-rl"); - else { - rc = ENXIO; + if (tc.flags & TX_CLRL_ERROR) { + sbuf_printf(sb, "error"); goto done; } - if (p.ratemode == SCHED_CLASS_RATEMODE_REL) { + if (tc.ratemode == SCHED_CLASS_RATEMODE_REL) { /* XXX: top speed or actual link speed? */ gbps = port_top_speed(sc->port[port_id]); - sbuf_printf(sb, " %u%% of %uGbps", p.maxrate, gbps); - } - else if (p.ratemode == SCHED_CLASS_RATEMODE_ABS) { - switch (p.rateunit) { + sbuf_printf(sb, " %u%% of %uGbps", tc.maxrate, gbps); + } else if (tc.ratemode == SCHED_CLASS_RATEMODE_ABS) { + switch (tc.rateunit) { case SCHED_CLASS_RATEUNIT_BITS: - mbps = p.maxrate / 1000; - gbps = p.maxrate / 1000000; - if (p.maxrate == gbps * 1000000) + mbps = tc.maxrate / 1000; + gbps = tc.maxrate / 1000000; + if (tc.maxrate == gbps * 1000000) sbuf_printf(sb, " %uGbps", gbps); - else if (p.maxrate == mbps * 1000) + else if (tc.maxrate == mbps * 1000) sbuf_printf(sb, " %uMbps", mbps); else - sbuf_printf(sb, " %uKbps", p.maxrate); + sbuf_printf(sb, " %uKbps", tc.maxrate); break; case SCHED_CLASS_RATEUNIT_PKTS: - sbuf_printf(sb, " %upps", p.maxrate); + sbuf_printf(sb, " %upps", tc.maxrate); break; default: rc = ENXIO; @@ -7926,7 +7907,7 @@ sysctl_tc_params(SYSCTL_HANDLER_ARGS) } } - switch (p.mode) { + switch (tc.mode) { case SCHED_CLASS_MODE_CLASS: sbuf_printf(sb, " aggregate"); break; @@ -8828,225 +8809,6 @@ read_i2c(struct adapter *sc, struct t4_i return (rc); } -static int -in_range(int val, int lo, int hi) -{ - - return (val < 0 || (val <= hi && val >= lo)); -} - -static int -set_sched_class_config(struct adapter *sc, int minmax) -{ - int rc; - - if (minmax < 0) - return (EINVAL); - - rc = begin_synchronized_op(sc, NULL, SLEEP_OK | INTR_OK, "t4sscc"); - if (rc) - return (rc); - rc = -t4_sched_config(sc, FW_SCHED_TYPE_PKTSCHED, minmax, 1); - end_synchronized_op(sc, 0); - - return (rc); -} - -static int -set_sched_class_params(struct adapter *sc, struct t4_sched_class_params *p, - int sleep_ok) -{ - int rc, top_speed, fw_level, fw_mode, fw_rateunit, fw_ratemode; - struct port_info *pi; - struct tx_sched_class *tc; - - if (p->level == SCHED_CLASS_LEVEL_CL_RL) - fw_level = FW_SCHED_PARAMS_LEVEL_CL_RL; - else if (p->level == SCHED_CLASS_LEVEL_CL_WRR) - fw_level = FW_SCHED_PARAMS_LEVEL_CL_WRR; - else if (p->level == SCHED_CLASS_LEVEL_CH_RL) - fw_level = FW_SCHED_PARAMS_LEVEL_CH_RL; - else - return (EINVAL); - - if (p->mode == SCHED_CLASS_MODE_CLASS) - fw_mode = FW_SCHED_PARAMS_MODE_CLASS; - else if (p->mode == SCHED_CLASS_MODE_FLOW) - fw_mode = FW_SCHED_PARAMS_MODE_FLOW; - else - return (EINVAL); - - if (p->rateunit == SCHED_CLASS_RATEUNIT_BITS) - fw_rateunit = FW_SCHED_PARAMS_UNIT_BITRATE; - else if (p->rateunit == SCHED_CLASS_RATEUNIT_PKTS) - fw_rateunit = FW_SCHED_PARAMS_UNIT_PKTRATE; - else - return (EINVAL); - - if (p->ratemode == SCHED_CLASS_RATEMODE_REL) - fw_ratemode = FW_SCHED_PARAMS_RATE_REL; - else if (p->ratemode == SCHED_CLASS_RATEMODE_ABS) - fw_ratemode = FW_SCHED_PARAMS_RATE_ABS; - else - return (EINVAL); - - /* Vet our parameters ... */ - if (!in_range(p->channel, 0, sc->chip_params->nchan - 1)) - return (ERANGE); - - pi = sc->port[sc->chan_map[p->channel]]; - if (pi == NULL) - return (ENXIO); - MPASS(pi->tx_chan == p->channel); - top_speed = port_top_speed(pi) * 1000000; /* Gbps -> Kbps */ - - if (!in_range(p->cl, 0, sc->chip_params->nsched_cls) || - !in_range(p->minrate, 0, top_speed) || - !in_range(p->maxrate, 0, top_speed) || - !in_range(p->weight, 0, 100)) - return (ERANGE); - - /* - * Translate any unset parameters into the firmware's - * nomenclature and/or fail the call if the parameters - * are required ... - */ - if (p->rateunit < 0 || p->ratemode < 0 || p->channel < 0 || p->cl < 0) - return (EINVAL); - - if (p->minrate < 0) - p->minrate = 0; - if (p->maxrate < 0) { - if (p->level == SCHED_CLASS_LEVEL_CL_RL || - p->level == SCHED_CLASS_LEVEL_CH_RL) - return (EINVAL); - else - p->maxrate = 0; - } - if (p->weight < 0) { - if (p->level == SCHED_CLASS_LEVEL_CL_WRR) - return (EINVAL); - else - p->weight = 0; - } - if (p->pktsize < 0) { - if (p->level == SCHED_CLASS_LEVEL_CL_RL || - p->level == SCHED_CLASS_LEVEL_CH_RL) - return (EINVAL); - else - p->pktsize = 0; - } - - rc = begin_synchronized_op(sc, NULL, - sleep_ok ? (SLEEP_OK | INTR_OK) : HOLD_LOCK, "t4sscp"); - if (rc) - return (rc); - tc = &pi->tc[p->cl]; - tc->params = *p; - rc = -t4_sched_params(sc, FW_SCHED_TYPE_PKTSCHED, fw_level, fw_mode, - fw_rateunit, fw_ratemode, p->channel, p->cl, p->minrate, p->maxrate, - p->weight, p->pktsize, sleep_ok); - if (rc == 0) - tc->flags |= TX_SC_OK; - else { - /* - * Unknown state at this point, see tc->params for what was - * attempted. - */ - tc->flags &= ~TX_SC_OK; - } - end_synchronized_op(sc, sleep_ok ? 0 : LOCK_HELD); - - return (rc); -} - -int -t4_set_sched_class(struct adapter *sc, struct t4_sched_params *p) -{ - - if (p->type != SCHED_CLASS_TYPE_PACKET) - return (EINVAL); - - if (p->subcmd == SCHED_CLASS_SUBCMD_CONFIG) - return (set_sched_class_config(sc, p->u.config.minmax)); - - if (p->subcmd == SCHED_CLASS_SUBCMD_PARAMS) - return (set_sched_class_params(sc, &p->u.params, 1)); - - return (EINVAL); -} - -int -t4_set_sched_queue(struct adapter *sc, struct t4_sched_queue *p) -{ - struct port_info *pi = NULL; - struct vi_info *vi; - 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 (p->port >= sc->params.nports) { - rc = EINVAL; - goto done; - } - - /* XXX: Only supported for the main VI. */ - pi = sc->port[p->port]; - vi = &pi->vi[0]; - if (!(vi->flags & VI_INIT_DONE)) { - /* tx queues not set up yet */ - rc = EAGAIN; - goto done; - } - - if (!in_range(p->queue, 0, vi->ntxq - 1) || - !in_range(p->cl, 0, sc->chip_params->nsched_cls - 1)) { - 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[vi->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(vi, 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) { Added: head/sys/dev/cxgbe/t4_sched.c ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/sys/dev/cxgbe/t4_sched.c Tue May 2 20:38:10 2017 (r317702) @@ -0,0 +1,461 @@ +/*- + * Copyright (c) 2017 Chelsio Communications, Inc. + * All rights reserved. + * Written by: Navdeep Parhar <np@FreeBSD.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include "opt_inet.h" +#include "opt_inet6.h" + +#include <sys/types.h> +#include <sys/malloc.h> +#include <sys/queue.h> +#include <sys/sbuf.h> +#include <sys/taskqueue.h> +#include <sys/sysctl.h> + +#include "common/common.h" +#include "common/t4_regs.h" +#include "common/t4_regs_values.h" +#include "common/t4_msg.h" + + +static int +in_range(int val, int lo, int hi) +{ + + return (val < 0 || (val <= hi && val >= lo)); +} + +static int +set_sched_class_config(struct adapter *sc, int minmax) +{ + int rc; + + if (minmax < 0) + return (EINVAL); + + rc = begin_synchronized_op(sc, NULL, SLEEP_OK | INTR_OK, "t4sscc"); + if (rc) + return (rc); + rc = -t4_sched_config(sc, FW_SCHED_TYPE_PKTSCHED, minmax, 1); + end_synchronized_op(sc, 0); + + return (rc); +} + +static int +set_sched_class_params(struct adapter *sc, struct t4_sched_class_params *p, + int sleep_ok) +{ + int rc, top_speed, fw_level, fw_mode, fw_rateunit, fw_ratemode; + struct port_info *pi; + struct tx_cl_rl_params *tc; + + if (p->level == SCHED_CLASS_LEVEL_CL_RL) + fw_level = FW_SCHED_PARAMS_LEVEL_CL_RL; + else if (p->level == SCHED_CLASS_LEVEL_CL_WRR) + fw_level = FW_SCHED_PARAMS_LEVEL_CL_WRR; + else if (p->level == SCHED_CLASS_LEVEL_CH_RL) + fw_level = FW_SCHED_PARAMS_LEVEL_CH_RL; + else + return (EINVAL); + + if (p->mode == SCHED_CLASS_MODE_CLASS) + fw_mode = FW_SCHED_PARAMS_MODE_CLASS; + else if (p->mode == SCHED_CLASS_MODE_FLOW) + fw_mode = FW_SCHED_PARAMS_MODE_FLOW; + else + return (EINVAL); + + if (p->rateunit == SCHED_CLASS_RATEUNIT_BITS) + fw_rateunit = FW_SCHED_PARAMS_UNIT_BITRATE; + else if (p->rateunit == SCHED_CLASS_RATEUNIT_PKTS) + fw_rateunit = FW_SCHED_PARAMS_UNIT_PKTRATE; + else + return (EINVAL); + + if (p->ratemode == SCHED_CLASS_RATEMODE_REL) + fw_ratemode = FW_SCHED_PARAMS_RATE_REL; + else if (p->ratemode == SCHED_CLASS_RATEMODE_ABS) + fw_ratemode = FW_SCHED_PARAMS_RATE_ABS; + else + return (EINVAL); + + /* Vet our parameters ... */ + if (!in_range(p->channel, 0, sc->chip_params->nchan - 1)) + return (ERANGE); + + pi = sc->port[sc->chan_map[p->channel]]; + if (pi == NULL) + return (ENXIO); + MPASS(pi->tx_chan == p->channel); + top_speed = port_top_speed(pi) * 1000000; /* Gbps -> Kbps */ + + if (!in_range(p->cl, 0, sc->chip_params->nsched_cls) || + !in_range(p->minrate, 0, top_speed) || + !in_range(p->maxrate, 0, top_speed) || + !in_range(p->weight, 0, 100)) + return (ERANGE); + + /* + * Translate any unset parameters into the firmware's + * nomenclature and/or fail the call if the parameters + * are required ... + */ + if (p->rateunit < 0 || p->ratemode < 0 || p->channel < 0 || p->cl < 0) + return (EINVAL); + + if (p->minrate < 0) + p->minrate = 0; + if (p->maxrate < 0) { + if (p->level == SCHED_CLASS_LEVEL_CL_RL || + p->level == SCHED_CLASS_LEVEL_CH_RL) + return (EINVAL); + else + p->maxrate = 0; + } + if (p->weight < 0) { + if (p->level == SCHED_CLASS_LEVEL_CL_WRR) + return (EINVAL); + else + p->weight = 0; + } + if (p->pktsize < 0) { + if (p->level == SCHED_CLASS_LEVEL_CL_RL || + p->level == SCHED_CLASS_LEVEL_CH_RL) + return (EINVAL); + else + p->pktsize = 0; + } + + rc = begin_synchronized_op(sc, NULL, + sleep_ok ? (SLEEP_OK | INTR_OK) : HOLD_LOCK, "t4sscp"); + if (rc) + return (rc); + if (p->level == SCHED_CLASS_LEVEL_CL_RL) { + tc = &pi->sched_params->cl_rl[p->cl]; + if (tc->refcount > 0) { + rc = EBUSY; + goto done; + } else { + tc->ratemode = fw_ratemode; + tc->rateunit = fw_rateunit; + tc->mode = fw_mode; + tc->maxrate = p->maxrate; + tc->pktsize = p->pktsize; + } + } + rc = -t4_sched_params(sc, FW_SCHED_TYPE_PKTSCHED, fw_level, fw_mode, + fw_rateunit, fw_ratemode, p->channel, p->cl, p->minrate, p->maxrate, + p->weight, p->pktsize, sleep_ok); + if (p->level == SCHED_CLASS_LEVEL_CL_RL && rc != 0) { + /* + * Unknown state at this point, see parameters in tc for what + * was attempted. + */ + tc->flags |= TX_CLRL_ERROR; + } +done: + end_synchronized_op(sc, sleep_ok ? 0 : LOCK_HELD); + + return (rc); +} + +static void +update_tx_sched(void *context, int pending) +{ + int i, j, mode, rateunit, ratemode, maxrate, pktsize, rc; + struct port_info *pi; + struct tx_cl_rl_params *tc; + struct adapter *sc = context; + const int n = sc->chip_params->nsched_cls; + + mtx_lock(&sc->tc_lock); + for_each_port(sc, i) { + pi = sc->port[i]; + tc = &pi->sched_params->cl_rl[0]; + for (j = 0; j < n; j++, tc++) { + MPASS(mtx_owned(&sc->tc_lock)); + if ((tc->flags & TX_CLRL_REFRESH) == 0) + continue; + + mode = tc->mode; + rateunit = tc->rateunit; + ratemode = tc->ratemode; + maxrate = tc->maxrate; + pktsize = tc->pktsize; + mtx_unlock(&sc->tc_lock); + + if (begin_synchronized_op(sc, NULL, SLEEP_OK | INTR_OK, + "t4utxs") != 0) { + mtx_lock(&sc->tc_lock); + continue; + } + rc = t4_sched_params(sc, FW_SCHED_TYPE_PKTSCHED, + FW_SCHED_PARAMS_LEVEL_CL_RL, mode, rateunit, + ratemode, pi->tx_chan, j, 0, maxrate, 0, pktsize, + 1); + end_synchronized_op(sc, 0); + + mtx_lock(&sc->tc_lock); + if (rc != 0) { + tc->flags |= TX_CLRL_ERROR; + } else if (tc->mode == mode && + tc->rateunit == rateunit && + tc->maxrate == maxrate && + tc->pktsize == tc->pktsize) { + tc->flags &= ~(TX_CLRL_REFRESH | TX_CLRL_ERROR); + } + } + } + mtx_unlock(&sc->tc_lock); +} + +int +t4_set_sched_class(struct adapter *sc, struct t4_sched_params *p) +{ + + if (p->type != SCHED_CLASS_TYPE_PACKET) + return (EINVAL); + + if (p->subcmd == SCHED_CLASS_SUBCMD_CONFIG) + return (set_sched_class_config(sc, p->u.config.minmax)); + + if (p->subcmd == SCHED_CLASS_SUBCMD_PARAMS) + return (set_sched_class_params(sc, &p->u.params, 1)); + + return (EINVAL); +} + +int +t4_set_sched_queue(struct adapter *sc, struct t4_sched_queue *p) +{ + struct port_info *pi = NULL; + struct vi_info *vi; + 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 (p->port >= sc->params.nports) { + rc = EINVAL; + goto done; + } + + /* XXX: Only supported for the main VI. */ + pi = sc->port[p->port]; + vi = &pi->vi[0]; + if (!(vi->flags & VI_INIT_DONE)) { + /* tx queues not set up yet */ + rc = EAGAIN; + goto done; + } + + if (!in_range(p->queue, 0, vi->ntxq - 1) || + !in_range(p->cl, 0, sc->chip_params->nsched_cls - 1)) { + 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[vi->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(vi, 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_init_tx_sched(struct adapter *sc) +{ + int i, j; + const int n = sc->chip_params->nsched_cls; + struct port_info *pi; + struct tx_cl_rl_params *tc; + static const uint32_t init_kbps[] = { + 100 * 1000, + 200 * 1000, + 400 * 1000, + 500 * 1000, + 800 * 1000, + 1000 * 1000, + 1200 * 1000, + 1500 * 1000, + 1800 * 1000, + 2000 * 1000, + 2500 * 1000, + 3000 * 1000, + 3500 * 1000, + 4000 * 1000, + 5000 * 1000, + 10000 * 1000 + }; + + mtx_init(&sc->tc_lock, "tx_sched lock", NULL, MTX_DEF); + TASK_INIT(&sc->tc_task, 0, update_tx_sched, sc); + for_each_port(sc, i) { + pi = sc->port[i]; + pi->sched_params = malloc(sizeof(*pi->sched_params) + + n * sizeof(*tc), M_CXGBE, M_ZERO | M_WAITOK); + tc = &pi->sched_params->cl_rl[0]; + for (j = 0; j < n; j++, tc++) { + tc->flags = TX_CLRL_REFRESH; + tc->refcount = 0; + tc->ratemode = FW_SCHED_PARAMS_RATE_ABS; + tc->rateunit = FW_SCHED_PARAMS_UNIT_BITRATE; + tc->mode = FW_SCHED_PARAMS_MODE_FLOW; + tc->maxrate = init_kbps[min(j, nitems(init_kbps) - 1)]; + tc->pktsize = ETHERMTU; /* XXX */ + + t4_sched_params_cl_rl_kbps(sc, pi->tx_chan, j, tc->mode, + tc->maxrate, tc->pktsize, 1); + } + } + + return (0); +} + +int +t4_free_tx_sched(struct adapter *sc) +{ + int i; + + taskqueue_drain(taskqueue_thread, &sc->tc_task); + + for_each_port(sc, i) + free(sc->port[i]->sched_params, M_CXGBE); + + if (mtx_initialized(&sc->tc_lock)) + mtx_destroy(&sc->tc_lock); + + return (0); +} + +void +t4_update_tx_sched(struct adapter *sc) +{ + + taskqueue_enqueue(taskqueue_thread, &sc->tc_task); +} + +int +t4_reserve_cl_rl_kbps(struct adapter *sc, int port_id, u_int maxrate, + int *tc_idx) +{ + int rc = 0, fa = -1, i; + struct tx_cl_rl_params *tc; + + MPASS(port_id >= 0 && port_id < sc->params.nports); + + tc = &sc->port[port_id]->sched_params->cl_rl[0]; + mtx_lock(&sc->tc_lock); + for (i = 0; i < sc->chip_params->nsched_cls; i++, tc++) { + if (fa < 0 && tc->refcount == 0) + fa = i; + + if (tc->ratemode == FW_SCHED_PARAMS_RATE_ABS && + tc->rateunit == FW_SCHED_PARAMS_UNIT_BITRATE && + tc->mode == FW_SCHED_PARAMS_MODE_FLOW && + tc->maxrate == maxrate) { + tc->refcount++; + *tc_idx = i; + goto done; + } + } + /* Not found */ + MPASS(i == sc->chip_params->nsched_cls); + if (fa != -1) { + tc = &sc->port[port_id]->sched_params->cl_rl[fa]; + tc->flags = TX_CLRL_REFRESH; + tc->refcount = 1; + tc->ratemode = FW_SCHED_PARAMS_RATE_ABS; + tc->rateunit = FW_SCHED_PARAMS_UNIT_BITRATE; + tc->mode = FW_SCHED_PARAMS_MODE_FLOW; + tc->maxrate = maxrate; + tc->pktsize = ETHERMTU; /* XXX */ + *tc_idx = fa; *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201705022038.v42KcBaQ053975>