1*4ec4762dSRahul Lakkireddy // SPDX-License-Identifier: GPL-2.0-only 2*4ec4762dSRahul Lakkireddy /* Copyright (C) 2019 Chelsio Communications. All rights reserved. */ 3*4ec4762dSRahul Lakkireddy 4*4ec4762dSRahul Lakkireddy #include "cxgb4.h" 5*4ec4762dSRahul Lakkireddy #include "cxgb4_tc_matchall.h" 6*4ec4762dSRahul Lakkireddy #include "sched.h" 7*4ec4762dSRahul Lakkireddy 8*4ec4762dSRahul Lakkireddy static int cxgb4_matchall_egress_validate(struct net_device *dev, 9*4ec4762dSRahul Lakkireddy struct tc_cls_matchall_offload *cls) 10*4ec4762dSRahul Lakkireddy { 11*4ec4762dSRahul Lakkireddy struct netlink_ext_ack *extack = cls->common.extack; 12*4ec4762dSRahul Lakkireddy struct flow_action *actions = &cls->rule->action; 13*4ec4762dSRahul Lakkireddy struct port_info *pi = netdev2pinfo(dev); 14*4ec4762dSRahul Lakkireddy struct flow_action_entry *entry; 15*4ec4762dSRahul Lakkireddy u64 max_link_rate; 16*4ec4762dSRahul Lakkireddy u32 i, speed; 17*4ec4762dSRahul Lakkireddy int ret; 18*4ec4762dSRahul Lakkireddy 19*4ec4762dSRahul Lakkireddy if (!flow_action_has_entries(actions)) { 20*4ec4762dSRahul Lakkireddy NL_SET_ERR_MSG_MOD(extack, 21*4ec4762dSRahul Lakkireddy "Egress MATCHALL offload needs at least 1 policing action"); 22*4ec4762dSRahul Lakkireddy return -EINVAL; 23*4ec4762dSRahul Lakkireddy } else if (!flow_offload_has_one_action(actions)) { 24*4ec4762dSRahul Lakkireddy NL_SET_ERR_MSG_MOD(extack, 25*4ec4762dSRahul Lakkireddy "Egress MATCHALL offload only supports 1 policing action"); 26*4ec4762dSRahul Lakkireddy return -EINVAL; 27*4ec4762dSRahul Lakkireddy } else if (pi->tc_block_shared) { 28*4ec4762dSRahul Lakkireddy NL_SET_ERR_MSG_MOD(extack, 29*4ec4762dSRahul Lakkireddy "Egress MATCHALL offload not supported with shared blocks"); 30*4ec4762dSRahul Lakkireddy return -EINVAL; 31*4ec4762dSRahul Lakkireddy } 32*4ec4762dSRahul Lakkireddy 33*4ec4762dSRahul Lakkireddy ret = t4_get_link_params(pi, NULL, &speed, NULL); 34*4ec4762dSRahul Lakkireddy if (ret) { 35*4ec4762dSRahul Lakkireddy NL_SET_ERR_MSG_MOD(extack, 36*4ec4762dSRahul Lakkireddy "Failed to get max speed supported by the link"); 37*4ec4762dSRahul Lakkireddy return -EINVAL; 38*4ec4762dSRahul Lakkireddy } 39*4ec4762dSRahul Lakkireddy 40*4ec4762dSRahul Lakkireddy /* Convert from Mbps to bps */ 41*4ec4762dSRahul Lakkireddy max_link_rate = (u64)speed * 1000 * 1000; 42*4ec4762dSRahul Lakkireddy 43*4ec4762dSRahul Lakkireddy flow_action_for_each(i, entry, actions) { 44*4ec4762dSRahul Lakkireddy switch (entry->id) { 45*4ec4762dSRahul Lakkireddy case FLOW_ACTION_POLICE: 46*4ec4762dSRahul Lakkireddy /* Convert bytes per second to bits per second */ 47*4ec4762dSRahul Lakkireddy if (entry->police.rate_bytes_ps * 8 > max_link_rate) { 48*4ec4762dSRahul Lakkireddy NL_SET_ERR_MSG_MOD(extack, 49*4ec4762dSRahul Lakkireddy "Specified policing max rate is larger than underlying link speed"); 50*4ec4762dSRahul Lakkireddy return -ERANGE; 51*4ec4762dSRahul Lakkireddy } 52*4ec4762dSRahul Lakkireddy break; 53*4ec4762dSRahul Lakkireddy default: 54*4ec4762dSRahul Lakkireddy NL_SET_ERR_MSG_MOD(extack, 55*4ec4762dSRahul Lakkireddy "Only policing action supported with Egress MATCHALL offload"); 56*4ec4762dSRahul Lakkireddy return -EOPNOTSUPP; 57*4ec4762dSRahul Lakkireddy } 58*4ec4762dSRahul Lakkireddy } 59*4ec4762dSRahul Lakkireddy 60*4ec4762dSRahul Lakkireddy return 0; 61*4ec4762dSRahul Lakkireddy } 62*4ec4762dSRahul Lakkireddy 63*4ec4762dSRahul Lakkireddy static int cxgb4_matchall_alloc_tc(struct net_device *dev, 64*4ec4762dSRahul Lakkireddy struct tc_cls_matchall_offload *cls) 65*4ec4762dSRahul Lakkireddy { 66*4ec4762dSRahul Lakkireddy struct ch_sched_params p = { 67*4ec4762dSRahul Lakkireddy .type = SCHED_CLASS_TYPE_PACKET, 68*4ec4762dSRahul Lakkireddy .u.params.level = SCHED_CLASS_LEVEL_CH_RL, 69*4ec4762dSRahul Lakkireddy .u.params.mode = SCHED_CLASS_MODE_CLASS, 70*4ec4762dSRahul Lakkireddy .u.params.rateunit = SCHED_CLASS_RATEUNIT_BITS, 71*4ec4762dSRahul Lakkireddy .u.params.ratemode = SCHED_CLASS_RATEMODE_ABS, 72*4ec4762dSRahul Lakkireddy .u.params.class = SCHED_CLS_NONE, 73*4ec4762dSRahul Lakkireddy .u.params.minrate = 0, 74*4ec4762dSRahul Lakkireddy .u.params.weight = 0, 75*4ec4762dSRahul Lakkireddy .u.params.pktsize = dev->mtu, 76*4ec4762dSRahul Lakkireddy }; 77*4ec4762dSRahul Lakkireddy struct netlink_ext_ack *extack = cls->common.extack; 78*4ec4762dSRahul Lakkireddy struct cxgb4_tc_port_matchall *tc_port_matchall; 79*4ec4762dSRahul Lakkireddy struct port_info *pi = netdev2pinfo(dev); 80*4ec4762dSRahul Lakkireddy struct adapter *adap = netdev2adap(dev); 81*4ec4762dSRahul Lakkireddy struct flow_action_entry *entry; 82*4ec4762dSRahul Lakkireddy struct sched_class *e; 83*4ec4762dSRahul Lakkireddy u32 i; 84*4ec4762dSRahul Lakkireddy 85*4ec4762dSRahul Lakkireddy tc_port_matchall = &adap->tc_matchall->port_matchall[pi->port_id]; 86*4ec4762dSRahul Lakkireddy 87*4ec4762dSRahul Lakkireddy flow_action_for_each(i, entry, &cls->rule->action) 88*4ec4762dSRahul Lakkireddy if (entry->id == FLOW_ACTION_POLICE) 89*4ec4762dSRahul Lakkireddy break; 90*4ec4762dSRahul Lakkireddy 91*4ec4762dSRahul Lakkireddy /* Convert from bytes per second to Kbps */ 92*4ec4762dSRahul Lakkireddy p.u.params.maxrate = div_u64(entry->police.rate_bytes_ps * 8, 1000); 93*4ec4762dSRahul Lakkireddy p.u.params.channel = pi->tx_chan; 94*4ec4762dSRahul Lakkireddy e = cxgb4_sched_class_alloc(dev, &p); 95*4ec4762dSRahul Lakkireddy if (!e) { 96*4ec4762dSRahul Lakkireddy NL_SET_ERR_MSG_MOD(extack, 97*4ec4762dSRahul Lakkireddy "No free traffic class available for policing action"); 98*4ec4762dSRahul Lakkireddy return -ENOMEM; 99*4ec4762dSRahul Lakkireddy } 100*4ec4762dSRahul Lakkireddy 101*4ec4762dSRahul Lakkireddy tc_port_matchall->egress.hwtc = e->idx; 102*4ec4762dSRahul Lakkireddy tc_port_matchall->egress.cookie = cls->cookie; 103*4ec4762dSRahul Lakkireddy tc_port_matchall->egress.state = CXGB4_MATCHALL_STATE_ENABLED; 104*4ec4762dSRahul Lakkireddy return 0; 105*4ec4762dSRahul Lakkireddy } 106*4ec4762dSRahul Lakkireddy 107*4ec4762dSRahul Lakkireddy static void cxgb4_matchall_free_tc(struct net_device *dev) 108*4ec4762dSRahul Lakkireddy { 109*4ec4762dSRahul Lakkireddy struct cxgb4_tc_port_matchall *tc_port_matchall; 110*4ec4762dSRahul Lakkireddy struct port_info *pi = netdev2pinfo(dev); 111*4ec4762dSRahul Lakkireddy struct adapter *adap = netdev2adap(dev); 112*4ec4762dSRahul Lakkireddy 113*4ec4762dSRahul Lakkireddy tc_port_matchall = &adap->tc_matchall->port_matchall[pi->port_id]; 114*4ec4762dSRahul Lakkireddy cxgb4_sched_class_free(dev, tc_port_matchall->egress.hwtc); 115*4ec4762dSRahul Lakkireddy 116*4ec4762dSRahul Lakkireddy tc_port_matchall->egress.hwtc = SCHED_CLS_NONE; 117*4ec4762dSRahul Lakkireddy tc_port_matchall->egress.cookie = 0; 118*4ec4762dSRahul Lakkireddy tc_port_matchall->egress.state = CXGB4_MATCHALL_STATE_DISABLED; 119*4ec4762dSRahul Lakkireddy } 120*4ec4762dSRahul Lakkireddy 121*4ec4762dSRahul Lakkireddy int cxgb4_tc_matchall_replace(struct net_device *dev, 122*4ec4762dSRahul Lakkireddy struct tc_cls_matchall_offload *cls_matchall) 123*4ec4762dSRahul Lakkireddy { 124*4ec4762dSRahul Lakkireddy struct netlink_ext_ack *extack = cls_matchall->common.extack; 125*4ec4762dSRahul Lakkireddy struct cxgb4_tc_port_matchall *tc_port_matchall; 126*4ec4762dSRahul Lakkireddy struct port_info *pi = netdev2pinfo(dev); 127*4ec4762dSRahul Lakkireddy struct adapter *adap = netdev2adap(dev); 128*4ec4762dSRahul Lakkireddy int ret; 129*4ec4762dSRahul Lakkireddy 130*4ec4762dSRahul Lakkireddy tc_port_matchall = &adap->tc_matchall->port_matchall[pi->port_id]; 131*4ec4762dSRahul Lakkireddy if (tc_port_matchall->egress.state == CXGB4_MATCHALL_STATE_ENABLED) { 132*4ec4762dSRahul Lakkireddy NL_SET_ERR_MSG_MOD(extack, 133*4ec4762dSRahul Lakkireddy "Only 1 Egress MATCHALL can be offloaded"); 134*4ec4762dSRahul Lakkireddy return -ENOMEM; 135*4ec4762dSRahul Lakkireddy } 136*4ec4762dSRahul Lakkireddy 137*4ec4762dSRahul Lakkireddy ret = cxgb4_matchall_egress_validate(dev, cls_matchall); 138*4ec4762dSRahul Lakkireddy if (ret) 139*4ec4762dSRahul Lakkireddy return ret; 140*4ec4762dSRahul Lakkireddy 141*4ec4762dSRahul Lakkireddy return cxgb4_matchall_alloc_tc(dev, cls_matchall); 142*4ec4762dSRahul Lakkireddy } 143*4ec4762dSRahul Lakkireddy 144*4ec4762dSRahul Lakkireddy int cxgb4_tc_matchall_destroy(struct net_device *dev, 145*4ec4762dSRahul Lakkireddy struct tc_cls_matchall_offload *cls_matchall) 146*4ec4762dSRahul Lakkireddy { 147*4ec4762dSRahul Lakkireddy struct cxgb4_tc_port_matchall *tc_port_matchall; 148*4ec4762dSRahul Lakkireddy struct port_info *pi = netdev2pinfo(dev); 149*4ec4762dSRahul Lakkireddy struct adapter *adap = netdev2adap(dev); 150*4ec4762dSRahul Lakkireddy 151*4ec4762dSRahul Lakkireddy tc_port_matchall = &adap->tc_matchall->port_matchall[pi->port_id]; 152*4ec4762dSRahul Lakkireddy if (cls_matchall->cookie != tc_port_matchall->egress.cookie) 153*4ec4762dSRahul Lakkireddy return -ENOENT; 154*4ec4762dSRahul Lakkireddy 155*4ec4762dSRahul Lakkireddy cxgb4_matchall_free_tc(dev); 156*4ec4762dSRahul Lakkireddy return 0; 157*4ec4762dSRahul Lakkireddy } 158*4ec4762dSRahul Lakkireddy 159*4ec4762dSRahul Lakkireddy static void cxgb4_matchall_disable_offload(struct net_device *dev) 160*4ec4762dSRahul Lakkireddy { 161*4ec4762dSRahul Lakkireddy struct cxgb4_tc_port_matchall *tc_port_matchall; 162*4ec4762dSRahul Lakkireddy struct port_info *pi = netdev2pinfo(dev); 163*4ec4762dSRahul Lakkireddy struct adapter *adap = netdev2adap(dev); 164*4ec4762dSRahul Lakkireddy 165*4ec4762dSRahul Lakkireddy tc_port_matchall = &adap->tc_matchall->port_matchall[pi->port_id]; 166*4ec4762dSRahul Lakkireddy if (tc_port_matchall->egress.state == CXGB4_MATCHALL_STATE_ENABLED) 167*4ec4762dSRahul Lakkireddy cxgb4_matchall_free_tc(dev); 168*4ec4762dSRahul Lakkireddy } 169*4ec4762dSRahul Lakkireddy 170*4ec4762dSRahul Lakkireddy int cxgb4_init_tc_matchall(struct adapter *adap) 171*4ec4762dSRahul Lakkireddy { 172*4ec4762dSRahul Lakkireddy struct cxgb4_tc_port_matchall *tc_port_matchall; 173*4ec4762dSRahul Lakkireddy struct cxgb4_tc_matchall *tc_matchall; 174*4ec4762dSRahul Lakkireddy int ret; 175*4ec4762dSRahul Lakkireddy 176*4ec4762dSRahul Lakkireddy tc_matchall = kzalloc(sizeof(*tc_matchall), GFP_KERNEL); 177*4ec4762dSRahul Lakkireddy if (!tc_matchall) 178*4ec4762dSRahul Lakkireddy return -ENOMEM; 179*4ec4762dSRahul Lakkireddy 180*4ec4762dSRahul Lakkireddy tc_port_matchall = kcalloc(adap->params.nports, 181*4ec4762dSRahul Lakkireddy sizeof(*tc_port_matchall), 182*4ec4762dSRahul Lakkireddy GFP_KERNEL); 183*4ec4762dSRahul Lakkireddy if (!tc_port_matchall) { 184*4ec4762dSRahul Lakkireddy ret = -ENOMEM; 185*4ec4762dSRahul Lakkireddy goto out_free_matchall; 186*4ec4762dSRahul Lakkireddy } 187*4ec4762dSRahul Lakkireddy 188*4ec4762dSRahul Lakkireddy tc_matchall->port_matchall = tc_port_matchall; 189*4ec4762dSRahul Lakkireddy adap->tc_matchall = tc_matchall; 190*4ec4762dSRahul Lakkireddy return 0; 191*4ec4762dSRahul Lakkireddy 192*4ec4762dSRahul Lakkireddy out_free_matchall: 193*4ec4762dSRahul Lakkireddy kfree(tc_matchall); 194*4ec4762dSRahul Lakkireddy return ret; 195*4ec4762dSRahul Lakkireddy } 196*4ec4762dSRahul Lakkireddy 197*4ec4762dSRahul Lakkireddy void cxgb4_cleanup_tc_matchall(struct adapter *adap) 198*4ec4762dSRahul Lakkireddy { 199*4ec4762dSRahul Lakkireddy u8 i; 200*4ec4762dSRahul Lakkireddy 201*4ec4762dSRahul Lakkireddy if (adap->tc_matchall) { 202*4ec4762dSRahul Lakkireddy if (adap->tc_matchall->port_matchall) { 203*4ec4762dSRahul Lakkireddy for (i = 0; i < adap->params.nports; i++) { 204*4ec4762dSRahul Lakkireddy struct net_device *dev = adap->port[i]; 205*4ec4762dSRahul Lakkireddy 206*4ec4762dSRahul Lakkireddy if (dev) 207*4ec4762dSRahul Lakkireddy cxgb4_matchall_disable_offload(dev); 208*4ec4762dSRahul Lakkireddy } 209*4ec4762dSRahul Lakkireddy kfree(adap->tc_matchall->port_matchall); 210*4ec4762dSRahul Lakkireddy } 211*4ec4762dSRahul Lakkireddy kfree(adap->tc_matchall); 212*4ec4762dSRahul Lakkireddy } 213*4ec4762dSRahul Lakkireddy } 214