cxgb4: fix Tx multi channel port rate limit

T6 can support 2 egress traffic management channels per port to
double the total number of traffic classes that can be configured.
In this configuration, if the class belongs to the other channel,
then all the queues must be bound again explicitly to the new class,
for the rate limit parameters on the other channel to take effect.

So, always explicitly bind all queues to the port rate limit traffic
class, regardless of the traffic management channel that it belongs
to. Also, only bind queues to port rate limit traffic class, if all
the queues don't already belong to an existing different traffic
class.

Fixes: 4ec4762d8ec6 ("cxgb4: add TC-MATCHALL classifier egress offload")
Signed-off-by: Rahul Lakkireddy <rahul.lakkireddy@chelsio.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by Rahul Lakkireddy and committed by David S. Miller c856e2b6 09d4f10a

Changed files
+96 -3
drivers
net
+11 -3
drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
··· 3135 3135 { 3136 3136 struct port_info *pi = netdev_priv(dev); 3137 3137 struct adapter *adap = pi->adapter; 3138 + struct ch_sched_queue qe = { 0 }; 3139 + struct ch_sched_params p = { 0 }; 3138 3140 struct sched_class *e; 3139 - struct ch_sched_params p; 3140 - struct ch_sched_queue qe; 3141 3141 u32 req_rate; 3142 3142 int err = 0; 3143 3143 ··· 3152 3152 "Failed to rate limit on queue %d. Link Down?\n", 3153 3153 index); 3154 3154 return -EINVAL; 3155 + } 3156 + 3157 + qe.queue = index; 3158 + e = cxgb4_sched_queue_lookup(dev, &qe); 3159 + if (e && e->info.u.params.level != SCHED_CLASS_LEVEL_CL_RL) { 3160 + dev_err(adap->pdev_dev, 3161 + "Queue %u already bound to class %u of type: %u\n", 3162 + index, e->idx, e->info.u.params.level); 3163 + return -EBUSY; 3155 3164 } 3156 3165 3157 3166 /* Convert from Mbps to Kbps */ ··· 3192 3183 return 0; 3193 3184 3194 3185 /* Fetch any available unused or matching scheduling class */ 3195 - memset(&p, 0, sizeof(p)); 3196 3186 p.type = SCHED_CLASS_TYPE_PACKET; 3197 3187 p.u.params.level = SCHED_CLASS_LEVEL_CL_RL; 3198 3188 p.u.params.mode = SCHED_CLASS_MODE_CLASS;
+67
drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_matchall.c
··· 15 15 struct flow_action *actions = &cls->rule->action; 16 16 struct port_info *pi = netdev2pinfo(dev); 17 17 struct flow_action_entry *entry; 18 + struct ch_sched_queue qe; 19 + struct sched_class *e; 18 20 u64 max_link_rate; 19 21 u32 i, speed; 20 22 int ret; ··· 62 60 } 63 61 } 64 62 63 + for (i = 0; i < pi->nqsets; i++) { 64 + memset(&qe, 0, sizeof(qe)); 65 + qe.queue = i; 66 + 67 + e = cxgb4_sched_queue_lookup(dev, &qe); 68 + if (e && e->info.u.params.level != SCHED_CLASS_LEVEL_CH_RL) { 69 + NL_SET_ERR_MSG_MOD(extack, 70 + "Some queues are already bound to different class"); 71 + return -EBUSY; 72 + } 73 + } 74 + 65 75 return 0; 76 + } 77 + 78 + static int cxgb4_matchall_tc_bind_queues(struct net_device *dev, u32 tc) 79 + { 80 + struct port_info *pi = netdev2pinfo(dev); 81 + struct ch_sched_queue qe; 82 + int ret; 83 + u32 i; 84 + 85 + for (i = 0; i < pi->nqsets; i++) { 86 + qe.queue = i; 87 + qe.class = tc; 88 + ret = cxgb4_sched_class_bind(dev, &qe, SCHED_QUEUE); 89 + if (ret) 90 + goto out_free; 91 + } 92 + 93 + return 0; 94 + 95 + out_free: 96 + while (i--) { 97 + qe.queue = i; 98 + qe.class = SCHED_CLS_NONE; 99 + cxgb4_sched_class_unbind(dev, &qe, SCHED_QUEUE); 100 + } 101 + 102 + return ret; 103 + } 104 + 105 + static void cxgb4_matchall_tc_unbind_queues(struct net_device *dev) 106 + { 107 + struct port_info *pi = netdev2pinfo(dev); 108 + struct ch_sched_queue qe; 109 + u32 i; 110 + 111 + for (i = 0; i < pi->nqsets; i++) { 112 + qe.queue = i; 113 + qe.class = SCHED_CLS_NONE; 114 + cxgb4_sched_class_unbind(dev, &qe, SCHED_QUEUE); 115 + } 66 116 } 67 117 68 118 static int cxgb4_matchall_alloc_tc(struct net_device *dev, ··· 137 83 struct adapter *adap = netdev2adap(dev); 138 84 struct flow_action_entry *entry; 139 85 struct sched_class *e; 86 + int ret; 140 87 u32 i; 141 88 142 89 tc_port_matchall = &adap->tc_matchall->port_matchall[pi->port_id]; ··· 156 101 return -ENOMEM; 157 102 } 158 103 104 + ret = cxgb4_matchall_tc_bind_queues(dev, e->idx); 105 + if (ret) { 106 + NL_SET_ERR_MSG_MOD(extack, 107 + "Could not bind queues to traffic class"); 108 + goto out_free; 109 + } 110 + 159 111 tc_port_matchall->egress.hwtc = e->idx; 160 112 tc_port_matchall->egress.cookie = cls->cookie; 161 113 tc_port_matchall->egress.state = CXGB4_MATCHALL_STATE_ENABLED; 162 114 return 0; 115 + 116 + out_free: 117 + cxgb4_sched_class_free(dev, e->idx); 118 + return ret; 163 119 } 164 120 165 121 static void cxgb4_matchall_free_tc(struct net_device *dev) ··· 180 114 struct adapter *adap = netdev2adap(dev); 181 115 182 116 tc_port_matchall = &adap->tc_matchall->port_matchall[pi->port_id]; 117 + cxgb4_matchall_tc_unbind_queues(dev); 183 118 cxgb4_sched_class_free(dev, tc_port_matchall->egress.hwtc); 184 119 185 120 tc_port_matchall->egress.hwtc = SCHED_CLS_NONE;
+16
drivers/net/ethernet/chelsio/cxgb4/sched.c
··· 165 165 return found; 166 166 } 167 167 168 + struct sched_class *cxgb4_sched_queue_lookup(struct net_device *dev, 169 + struct ch_sched_queue *p) 170 + { 171 + struct port_info *pi = netdev2pinfo(dev); 172 + struct sched_queue_entry *qe = NULL; 173 + struct adapter *adap = pi->adapter; 174 + struct sge_eth_txq *txq; 175 + 176 + if (p->queue < 0 || p->queue >= pi->nqsets) 177 + return NULL; 178 + 179 + txq = &adap->sge.ethtxq[pi->first_qset + p->queue]; 180 + qe = t4_sched_entry_lookup(pi, SCHED_QUEUE, txq->q.cntxt_id); 181 + return qe ? &pi->sched_tbl->tab[qe->param.class] : NULL; 182 + } 183 + 168 184 static int t4_sched_queue_unbind(struct port_info *pi, struct ch_sched_queue *p) 169 185 { 170 186 struct sched_queue_entry *qe = NULL;
+2
drivers/net/ethernet/chelsio/cxgb4/sched.h
··· 103 103 return true; 104 104 } 105 105 106 + struct sched_class *cxgb4_sched_queue_lookup(struct net_device *dev, 107 + struct ch_sched_queue *p); 106 108 int cxgb4_sched_class_bind(struct net_device *dev, void *arg, 107 109 enum sched_bind_type type); 108 110 int cxgb4_sched_class_unbind(struct net_device *dev, void *arg,