Author | Tokens | Token Proportion | Commits | Commit Proportion |
---|---|---|---|---|
Rahul Lakkireddy | 2403 | 86.22% | 14 | 38.89% |
Dimitris Michailidis | 110 | 3.95% | 2 | 5.56% |
Jianbo Liu | 110 | 3.95% | 1 | 2.78% |
Kumar Sanghvi | 53 | 1.90% | 5 | 13.89% |
Casey Leedom | 21 | 0.75% | 1 | 2.78% |
Baowen Zheng | 20 | 0.72% | 1 | 2.78% |
Shahjada Abul Husain | 16 | 0.57% | 1 | 2.78% |
Arjun V | 16 | 0.57% | 1 | 2.78% |
Pablo Neira Ayuso | 11 | 0.39% | 2 | 5.56% |
Jiri Pirko | 8 | 0.29% | 3 | 8.33% |
Vipul Pandya | 8 | 0.29% | 2 | 5.56% |
Pieter Jansen van Vuuren | 7 | 0.25% | 1 | 2.78% |
Yue haibing | 2 | 0.07% | 1 | 2.78% |
Po Liu | 2 | 0.07% | 1 | 2.78% |
Total | 2787 | 36 |
// SPDX-License-Identifier: GPL-2.0-only /* Copyright (C) 2019 Chelsio Communications. All rights reserved. */ #include "cxgb4.h" #include "cxgb4_tc_matchall.h" #include "sched.h" #include "cxgb4_uld.h" #include "cxgb4_filter.h" #include "cxgb4_tc_flower.h" static int cxgb4_policer_validate(const struct flow_action *action, const struct flow_action_entry *act, struct netlink_ext_ack *extack) { if (act->police.exceed.act_id != FLOW_ACTION_DROP) { NL_SET_ERR_MSG_MOD(extack, "Offload not supported when exceed action is not drop"); return -EOPNOTSUPP; } if (act->police.notexceed.act_id != FLOW_ACTION_PIPE && act->police.notexceed.act_id != FLOW_ACTION_ACCEPT) { NL_SET_ERR_MSG_MOD(extack, "Offload not supported when conform action is not pipe or ok"); return -EOPNOTSUPP; } if (act->police.notexceed.act_id == FLOW_ACTION_ACCEPT && !flow_action_is_last_entry(action, act)) { NL_SET_ERR_MSG_MOD(extack, "Offload not supported when conform action is ok, but action is not last"); return -EOPNOTSUPP; } if (act->police.peakrate_bytes_ps || act->police.avrate || act->police.overhead) { NL_SET_ERR_MSG_MOD(extack, "Offload not supported when peakrate/avrate/overhead is configured"); return -EOPNOTSUPP; } if (act->police.rate_pkt_ps) { NL_SET_ERR_MSG_MOD(extack, "QoS offload not support packets per second"); return -EOPNOTSUPP; } return 0; } static int cxgb4_matchall_egress_validate(struct net_device *dev, struct tc_cls_matchall_offload *cls) { struct netlink_ext_ack *extack = cls->common.extack; struct flow_action *actions = &cls->rule->action; struct port_info *pi = netdev2pinfo(dev); struct flow_action_entry *entry; struct ch_sched_queue qe; struct sched_class *e; u64 max_link_rate; u32 i, speed; int ret; if (!flow_action_has_entries(actions)) { NL_SET_ERR_MSG_MOD(extack, "Egress MATCHALL offload needs at least 1 policing action"); return -EINVAL; } else if (!flow_offload_has_one_action(actions)) { NL_SET_ERR_MSG_MOD(extack, "Egress MATCHALL offload only supports 1 policing action"); return -EINVAL; } else if (pi->tc_block_shared) { NL_SET_ERR_MSG_MOD(extack, "Egress MATCHALL offload not supported with shared blocks"); return -EINVAL; } ret = t4_get_link_params(pi, NULL, &speed, NULL); if (ret) { NL_SET_ERR_MSG_MOD(extack, "Failed to get max speed supported by the link"); return -EINVAL; } /* Convert from Mbps to bps */ max_link_rate = (u64)speed * 1000 * 1000; flow_action_for_each(i, entry, actions) { switch (entry->id) { case FLOW_ACTION_POLICE: ret = cxgb4_policer_validate(actions, entry, extack); if (ret) return ret; /* Convert bytes per second to bits per second */ if (entry->police.rate_bytes_ps * 8 > max_link_rate) { NL_SET_ERR_MSG_MOD(extack, "Specified policing max rate is larger than underlying link speed"); return -ERANGE; } break; default: NL_SET_ERR_MSG_MOD(extack, "Only policing action supported with Egress MATCHALL offload"); return -EOPNOTSUPP; } } for (i = 0; i < pi->nqsets; i++) { memset(&qe, 0, sizeof(qe)); qe.queue = i; e = cxgb4_sched_queue_lookup(dev, &qe); if (e && e->info.u.params.level != SCHED_CLASS_LEVEL_CH_RL) { NL_SET_ERR_MSG_MOD(extack, "Some queues are already bound to different class"); return -EBUSY; } } return 0; } static int cxgb4_matchall_tc_bind_queues(struct net_device *dev, u32 tc) { struct port_info *pi = netdev2pinfo(dev); struct ch_sched_queue qe; int ret; u32 i; for (i = 0; i < pi->nqsets; i++) { qe.queue = i; qe.class = tc; ret = cxgb4_sched_class_bind(dev, &qe, SCHED_QUEUE); if (ret) goto out_free; } return 0; out_free: while (i--) { qe.queue = i; qe.class = SCHED_CLS_NONE; cxgb4_sched_class_unbind(dev, &qe, SCHED_QUEUE); } return ret; } static void cxgb4_matchall_tc_unbind_queues(struct net_device *dev) { struct port_info *pi = netdev2pinfo(dev); struct ch_sched_queue qe; u32 i; for (i = 0; i < pi->nqsets; i++) { qe.queue = i; qe.class = SCHED_CLS_NONE; cxgb4_sched_class_unbind(dev, &qe, SCHED_QUEUE); } } static int cxgb4_matchall_alloc_tc(struct net_device *dev, struct tc_cls_matchall_offload *cls) { struct ch_sched_params p = { .type = SCHED_CLASS_TYPE_PACKET, .u.params.level = SCHED_CLASS_LEVEL_CH_RL, .u.params.mode = SCHED_CLASS_MODE_CLASS, .u.params.rateunit = SCHED_CLASS_RATEUNIT_BITS, .u.params.ratemode = SCHED_CLASS_RATEMODE_ABS, .u.params.class = SCHED_CLS_NONE, .u.params.minrate = 0, .u.params.weight = 0, .u.params.pktsize = dev->mtu, }; struct netlink_ext_ack *extack = cls->common.extack; struct cxgb4_tc_port_matchall *tc_port_matchall; struct port_info *pi = netdev2pinfo(dev); struct adapter *adap = netdev2adap(dev); struct flow_action_entry *entry; struct sched_class *e; int ret; u32 i; tc_port_matchall = &adap->tc_matchall->port_matchall[pi->port_id]; flow_action_for_each(i, entry, &cls->rule->action) if (entry->id == FLOW_ACTION_POLICE) break; ret = cxgb4_policer_validate(&cls->rule->action, entry, extack); if (ret) return ret; /* Convert from bytes per second to Kbps */ p.u.params.maxrate = div_u64(entry->police.rate_bytes_ps * 8, 1000); p.u.params.channel = pi->tx_chan; e = cxgb4_sched_class_alloc(dev, &p); if (!e) { NL_SET_ERR_MSG_MOD(extack, "No free traffic class available for policing action"); return -ENOMEM; } ret = cxgb4_matchall_tc_bind_queues(dev, e->idx); if (ret) { NL_SET_ERR_MSG_MOD(extack, "Could not bind queues to traffic class"); goto out_free; } tc_port_matchall->egress.hwtc = e->idx; tc_port_matchall->egress.cookie = cls->cookie; tc_port_matchall->egress.state = CXGB4_MATCHALL_STATE_ENABLED; return 0; out_free: cxgb4_sched_class_free(dev, e->idx); return ret; } static void cxgb4_matchall_free_tc(struct net_device *dev) { struct cxgb4_tc_port_matchall *tc_port_matchall; struct port_info *pi = netdev2pinfo(dev); struct adapter *adap = netdev2adap(dev); tc_port_matchall = &adap->tc_matchall->port_matchall[pi->port_id]; cxgb4_matchall_tc_unbind_queues(dev); cxgb4_sched_class_free(dev, tc_port_matchall->egress.hwtc); tc_port_matchall->egress.hwtc = SCHED_CLS_NONE; tc_port_matchall->egress.cookie = 0; tc_port_matchall->egress.state = CXGB4_MATCHALL_STATE_DISABLED; } static int cxgb4_matchall_mirror_alloc(struct net_device *dev, struct tc_cls_matchall_offload *cls) { struct netlink_ext_ack *extack = cls->common.extack; struct cxgb4_tc_port_matchall *tc_port_matchall; struct port_info *pi = netdev2pinfo(dev); struct adapter *adap = netdev2adap(dev); struct flow_action_entry *act; int ret; u32 i; tc_port_matchall = &adap->tc_matchall->port_matchall[pi->port_id]; flow_action_for_each(i, act, &cls->rule->action) { if (act->id == FLOW_ACTION_MIRRED) { ret = cxgb4_port_mirror_alloc(dev); if (ret) { NL_SET_ERR_MSG_MOD(extack, "Couldn't allocate mirror"); return ret; } tc_port_matchall->ingress.viid_mirror = pi->viid_mirror; break; } } return 0; } static void cxgb4_matchall_mirror_free(struct net_device *dev) { struct cxgb4_tc_port_matchall *tc_port_matchall; struct port_info *pi = netdev2pinfo(dev); struct adapter *adap = netdev2adap(dev); tc_port_matchall = &adap->tc_matchall->port_matchall[pi->port_id]; if (!tc_port_matchall->ingress.viid_mirror) return; cxgb4_port_mirror_free(dev); tc_port_matchall->ingress.viid_mirror = 0; } static int cxgb4_matchall_del_filter(struct net_device *dev, u8 filter_type) { struct cxgb4_tc_port_matchall *tc_port_matchall; struct port_info *pi = netdev2pinfo(dev); struct adapter *adap = netdev2adap(dev); int ret; tc_port_matchall = &adap->tc_matchall->port_matchall[pi->port_id]; ret = cxgb4_del_filter(dev, tc_port_matchall->ingress.tid[filter_type], &tc_port_matchall->ingress.fs[filter_type]); if (ret) return ret; tc_port_matchall->ingress.tid[filter_type] = 0; return 0; } static int cxgb4_matchall_add_filter(struct net_device *dev, struct tc_cls_matchall_offload *cls, u8 filter_type) { struct netlink_ext_ack *extack = cls->common.extack; struct cxgb4_tc_port_matchall *tc_port_matchall; struct port_info *pi = netdev2pinfo(dev); struct adapter *adap = netdev2adap(dev); struct ch_filter_specification *fs; int ret, fidx; /* Get a free filter entry TID, where we can insert this new * rule. Only insert rule if its prio doesn't conflict with * existing rules. */ fidx = cxgb4_get_free_ftid(dev, filter_type ? PF_INET6 : PF_INET, false, cls->common.prio); if (fidx < 0) { NL_SET_ERR_MSG_MOD(extack, "No free LETCAM index available"); return -ENOMEM; } tc_port_matchall = &adap->tc_matchall->port_matchall[pi->port_id]; fs = &tc_port_matchall->ingress.fs[filter_type]; memset(fs, 0, sizeof(*fs)); if (fidx < adap->tids.nhpftids) fs->prio = 1; fs->tc_prio = cls->common.prio; fs->tc_cookie = cls->cookie; fs->type = filter_type; fs->hitcnts = 1; fs->val.pfvf_vld = 1; fs->val.pf = adap->pf; fs->val.vf = pi->vin; cxgb4_process_flow_actions(dev, &cls->rule->action, fs); ret = cxgb4_set_filter(dev, fidx, fs); if (ret) return ret; tc_port_matchall->ingress.tid[filter_type] = fidx; return 0; } static int cxgb4_matchall_alloc_filter(struct net_device *dev, struct tc_cls_matchall_offload *cls) { struct cxgb4_tc_port_matchall *tc_port_matchall; struct port_info *pi = netdev2pinfo(dev); struct adapter *adap = netdev2adap(dev); int ret, i; tc_port_matchall = &adap->tc_matchall->port_matchall[pi->port_id]; ret = cxgb4_matchall_mirror_alloc(dev, cls); if (ret) return ret; for (i = 0; i < CXGB4_FILTER_TYPE_MAX; i++) { ret = cxgb4_matchall_add_filter(dev, cls, i); if (ret) goto out_free; } tc_port_matchall->ingress.state = CXGB4_MATCHALL_STATE_ENABLED; return 0; out_free: while (i-- > 0) cxgb4_matchall_del_filter(dev, i); cxgb4_matchall_mirror_free(dev); return ret; } static int cxgb4_matchall_free_filter(struct net_device *dev) { struct cxgb4_tc_port_matchall *tc_port_matchall; struct port_info *pi = netdev2pinfo(dev); struct adapter *adap = netdev2adap(dev); int ret; u8 i; tc_port_matchall = &adap->tc_matchall->port_matchall[pi->port_id]; for (i = 0; i < CXGB4_FILTER_TYPE_MAX; i++) { ret = cxgb4_matchall_del_filter(dev, i); if (ret) return ret; } cxgb4_matchall_mirror_free(dev); tc_port_matchall->ingress.packets = 0; tc_port_matchall->ingress.bytes = 0; tc_port_matchall->ingress.last_used = 0; tc_port_matchall->ingress.state = CXGB4_MATCHALL_STATE_DISABLED; return 0; } int cxgb4_tc_matchall_replace(struct net_device *dev, struct tc_cls_matchall_offload *cls_matchall, bool ingress) { struct netlink_ext_ack *extack = cls_matchall->common.extack; struct cxgb4_tc_port_matchall *tc_port_matchall; struct port_info *pi = netdev2pinfo(dev); struct adapter *adap = netdev2adap(dev); int ret; tc_port_matchall = &adap->tc_matchall->port_matchall[pi->port_id]; if (ingress) { if (tc_port_matchall->ingress.state == CXGB4_MATCHALL_STATE_ENABLED) { NL_SET_ERR_MSG_MOD(extack, "Only 1 Ingress MATCHALL can be offloaded"); return -ENOMEM; } ret = cxgb4_validate_flow_actions(dev, &cls_matchall->rule->action, extack, 1); if (ret) return ret; return cxgb4_matchall_alloc_filter(dev, cls_matchall); } if (tc_port_matchall->egress.state == CXGB4_MATCHALL_STATE_ENABLED) { NL_SET_ERR_MSG_MOD(extack, "Only 1 Egress MATCHALL can be offloaded"); return -ENOMEM; } ret = cxgb4_matchall_egress_validate(dev, cls_matchall); if (ret) return ret; return cxgb4_matchall_alloc_tc(dev, cls_matchall); } int cxgb4_tc_matchall_destroy(struct net_device *dev, struct tc_cls_matchall_offload *cls_matchall, bool ingress) { struct cxgb4_tc_port_matchall *tc_port_matchall; struct port_info *pi = netdev2pinfo(dev); struct adapter *adap = netdev2adap(dev); tc_port_matchall = &adap->tc_matchall->port_matchall[pi->port_id]; if (ingress) { /* All the filter types of this matchall rule save the * same cookie. So, checking for the first one is * enough. */ if (cls_matchall->cookie != tc_port_matchall->ingress.fs[0].tc_cookie) return -ENOENT; return cxgb4_matchall_free_filter(dev); } if (cls_matchall->cookie != tc_port_matchall->egress.cookie) return -ENOENT; cxgb4_matchall_free_tc(dev); return 0; } int cxgb4_tc_matchall_stats(struct net_device *dev, struct tc_cls_matchall_offload *cls_matchall) { u64 tmp_packets, tmp_bytes, packets = 0, bytes = 0; struct cxgb4_tc_port_matchall *tc_port_matchall; struct cxgb4_matchall_ingress_entry *ingress; struct port_info *pi = netdev2pinfo(dev); struct adapter *adap = netdev2adap(dev); int ret; u8 i; tc_port_matchall = &adap->tc_matchall->port_matchall[pi->port_id]; if (tc_port_matchall->ingress.state == CXGB4_MATCHALL_STATE_DISABLED) return -ENOENT; ingress = &tc_port_matchall->ingress; for (i = 0; i < CXGB4_FILTER_TYPE_MAX; i++) { ret = cxgb4_get_filter_counters(dev, ingress->tid[i], &tmp_packets, &tmp_bytes, ingress->fs[i].hash); if (ret) return ret; packets += tmp_packets; bytes += tmp_bytes; } if (tc_port_matchall->ingress.packets != packets) { flow_stats_update(&cls_matchall->stats, bytes - tc_port_matchall->ingress.bytes, packets - tc_port_matchall->ingress.packets, 0, tc_port_matchall->ingress.last_used, FLOW_ACTION_HW_STATS_IMMEDIATE); tc_port_matchall->ingress.packets = packets; tc_port_matchall->ingress.bytes = bytes; tc_port_matchall->ingress.last_used = jiffies; } return 0; } static void cxgb4_matchall_disable_offload(struct net_device *dev) { struct cxgb4_tc_port_matchall *tc_port_matchall; struct port_info *pi = netdev2pinfo(dev); struct adapter *adap = netdev2adap(dev); tc_port_matchall = &adap->tc_matchall->port_matchall[pi->port_id]; if (tc_port_matchall->egress.state == CXGB4_MATCHALL_STATE_ENABLED) cxgb4_matchall_free_tc(dev); if (tc_port_matchall->ingress.state == CXGB4_MATCHALL_STATE_ENABLED) cxgb4_matchall_free_filter(dev); } int cxgb4_init_tc_matchall(struct adapter *adap) { struct cxgb4_tc_port_matchall *tc_port_matchall; struct cxgb4_tc_matchall *tc_matchall; int ret; tc_matchall = kzalloc(sizeof(*tc_matchall), GFP_KERNEL); if (!tc_matchall) return -ENOMEM; tc_port_matchall = kcalloc(adap->params.nports, sizeof(*tc_port_matchall), GFP_KERNEL); if (!tc_port_matchall) { ret = -ENOMEM; goto out_free_matchall; } tc_matchall->port_matchall = tc_port_matchall; adap->tc_matchall = tc_matchall; return 0; out_free_matchall: kfree(tc_matchall); return ret; } void cxgb4_cleanup_tc_matchall(struct adapter *adap) { u8 i; if (adap->tc_matchall) { if (adap->tc_matchall->port_matchall) { for (i = 0; i < adap->params.nports; i++) { struct net_device *dev = adap->port[i]; if (dev) cxgb4_matchall_disable_offload(dev); } kfree(adap->tc_matchall->port_matchall); } kfree(adap->tc_matchall); } }
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with Cregit http://github.com/cregit/cregit
Version 2.0-RC1