12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 24bba3925SPatrick McHardy /* 30c6965ddSJiri Pirko * net/sched/act_police.c Input police filter 44bba3925SPatrick McHardy * 54bba3925SPatrick McHardy * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> 64bba3925SPatrick McHardy * J Hadi Salim (action changes) 74bba3925SPatrick McHardy */ 84bba3925SPatrick McHardy 94bba3925SPatrick McHardy #include <linux/module.h> 104bba3925SPatrick McHardy #include <linux/types.h> 114bba3925SPatrick McHardy #include <linux/kernel.h> 124bba3925SPatrick McHardy #include <linux/string.h> 134bba3925SPatrick McHardy #include <linux/errno.h> 144bba3925SPatrick McHardy #include <linux/skbuff.h> 154bba3925SPatrick McHardy #include <linux/rtnetlink.h> 164bba3925SPatrick McHardy #include <linux/init.h> 175a0e3ad6STejun Heo #include <linux/slab.h> 184bba3925SPatrick McHardy #include <net/act_api.h> 19dc5fc579SArnaldo Carvalho de Melo #include <net/netlink.h> 20d6124d6bSDavide Caratti #include <net/pkt_cls.h> 21fa762da9SPieter Jansen van Vuuren #include <net/tc_act/tc_police.h> 22*871cf386SPedro Tammela #include <net/tc_wrapper.h> 231e9b3d53SPatrick McHardy 244bba3925SPatrick McHardy /* Each policer is serialized by its individual spinlock */ 254bba3925SPatrick McHardy 26a85a970aSWANG Cong static struct tc_action_ops act_police_ops; 27ddf97ccdSWANG Cong 2853b2bf3fSPatrick McHardy static const struct nla_policy police_policy[TCA_POLICE_MAX + 1] = { 2953b2bf3fSPatrick McHardy [TCA_POLICE_RATE] = { .len = TC_RTAB_SIZE }, 3053b2bf3fSPatrick McHardy [TCA_POLICE_PEAKRATE] = { .len = TC_RTAB_SIZE }, 3153b2bf3fSPatrick McHardy [TCA_POLICE_AVRATE] = { .type = NLA_U32 }, 3253b2bf3fSPatrick McHardy [TCA_POLICE_RESULT] = { .type = NLA_U32 }, 33d1967e49SDavid Dai [TCA_POLICE_RATE64] = { .type = NLA_U64 }, 34d1967e49SDavid Dai [TCA_POLICE_PEAKRATE64] = { .type = NLA_U64 }, 352ffe0395SBaowen Zheng [TCA_POLICE_PKTRATE64] = { .type = NLA_U64, .min = 1 }, 362ffe0395SBaowen Zheng [TCA_POLICE_PKTBURST64] = { .type = NLA_U64, .min = 1 }, 3753b2bf3fSPatrick McHardy }; 3853b2bf3fSPatrick McHardy 392ac06347SJamal Hadi Salim static int tcf_police_init(struct net *net, struct nlattr *nla, 40a85a970aSWANG Cong struct nlattr *est, struct tc_action **a, 41abbb0d33SVlad Buslov struct tcf_proto *tp, u32 flags, 42589dad6dSAlexander Aring struct netlink_ext_ack *extack) 434bba3925SPatrick McHardy { 44fd6d4338SDavide Caratti int ret = 0, tcfp_result = TC_ACT_OK, err, size; 45695176bfSCong Wang bool bind = flags & TCA_ACT_FLAGS_BIND; 467ba699c6SPatrick McHardy struct nlattr *tb[TCA_POLICE_MAX + 1]; 47d6124d6bSDavide Caratti struct tcf_chain *goto_ch = NULL; 484bba3925SPatrick McHardy struct tc_police *parm; 49e9ce1cd3SDavid S. Miller struct tcf_police *police; 504bba3925SPatrick McHardy struct qdisc_rate_table *R_tab = NULL, *P_tab = NULL; 51acd0a7abSZhengchao Shao struct tc_action_net *tn = net_generic(net, act_police_ops.net_id); 522d550dbaSDavide Caratti struct tcf_police_params *new; 530852e455SWANG Cong bool exists = false; 547be8ef2cSDmytro Linkin u32 index; 55d1967e49SDavid Dai u64 rate64, prate64; 562ffe0395SBaowen Zheng u64 pps, ppsburst; 574bba3925SPatrick McHardy 58cee63723SPatrick McHardy if (nla == NULL) 594bba3925SPatrick McHardy return -EINVAL; 604bba3925SPatrick McHardy 618cb08174SJohannes Berg err = nla_parse_nested_deprecated(tb, TCA_POLICE_MAX, nla, 628cb08174SJohannes Berg police_policy, NULL); 63cee63723SPatrick McHardy if (err < 0) 64cee63723SPatrick McHardy return err; 65cee63723SPatrick McHardy 667ba699c6SPatrick McHardy if (tb[TCA_POLICE_TBF] == NULL) 671e9b3d53SPatrick McHardy return -EINVAL; 687ba699c6SPatrick McHardy size = nla_len(tb[TCA_POLICE_TBF]); 691e9b3d53SPatrick McHardy if (size != sizeof(*parm) && size != sizeof(struct tc_police_compat)) 704bba3925SPatrick McHardy return -EINVAL; 714bba3925SPatrick McHardy 720852e455SWANG Cong parm = nla_data(tb[TCA_POLICE_TBF]); 737be8ef2cSDmytro Linkin index = parm->index; 747be8ef2cSDmytro Linkin err = tcf_idr_check_alloc(tn, &index, a, bind); 750190c1d4SVlad Buslov if (err < 0) 760190c1d4SVlad Buslov return err; 770190c1d4SVlad Buslov exists = err; 780852e455SWANG Cong if (exists && bind) 790852e455SWANG Cong return 0; 800852e455SWANG Cong 810852e455SWANG Cong if (!exists) { 827be8ef2cSDmytro Linkin ret = tcf_idr_create(tn, index, NULL, a, 8340bd094dSBaowen Zheng &act_police_ops, bind, true, flags); 840190c1d4SVlad Buslov if (ret) { 857be8ef2cSDmytro Linkin tcf_idr_cleanup(tn, index); 86a03e6fe5SWANG Cong return ret; 870190c1d4SVlad Buslov } 88a03e6fe5SWANG Cong ret = ACT_P_CREATED; 89484afd1bSDavide Caratti spin_lock_init(&(to_police(*a)->tcfp_lock)); 90695176bfSCong Wang } else if (!(flags & TCA_ACT_FLAGS_REPLACE)) { 9165a206c0SChris Mi tcf_idr_release(*a, bind); 920852e455SWANG Cong return -EEXIST; 93e9ce1cd3SDavid S. Miller } 94d6124d6bSDavide Caratti err = tcf_action_check_ctrlact(parm->action, tp, &goto_ch, extack); 95d6124d6bSDavide Caratti if (err < 0) 96d6124d6bSDavide Caratti goto release_idr; 974bba3925SPatrick McHardy 98a85a970aSWANG Cong police = to_police(*a); 994bba3925SPatrick McHardy if (parm->rate.rate) { 1004bba3925SPatrick McHardy err = -ENOMEM; 101e9bc3fa2SAlexander Aring R_tab = qdisc_get_rtab(&parm->rate, tb[TCA_POLICE_RATE], NULL); 1024bba3925SPatrick McHardy if (R_tab == NULL) 1034bba3925SPatrick McHardy goto failure; 104c1b56878SStephen Hemminger 1054bba3925SPatrick McHardy if (parm->peakrate.rate) { 1064bba3925SPatrick McHardy P_tab = qdisc_get_rtab(&parm->peakrate, 107e9bc3fa2SAlexander Aring tb[TCA_POLICE_PEAKRATE], NULL); 10871bcb09aSStephen Hemminger if (P_tab == NULL) 1094bba3925SPatrick McHardy goto failure; 1104bba3925SPatrick McHardy } 1114bba3925SPatrick McHardy } 11271bcb09aSStephen Hemminger 11371bcb09aSStephen Hemminger if (est) { 11493be42f9SDavide Caratti err = gen_replace_estimator(&police->tcf_bstats, 11593be42f9SDavide Caratti police->common.cpu_bstats, 11671bcb09aSStephen Hemminger &police->tcf_rate_est, 117edb09eb1SEric Dumazet &police->tcf_lock, 11829cbcd85SAhmed S. Darwish false, est); 11971bcb09aSStephen Hemminger if (err) 12074030603SWANG Cong goto failure; 121a883bf56SJarek Poplawski } else if (tb[TCA_POLICE_AVRATE] && 122a883bf56SJarek Poplawski (ret == ACT_P_CREATED || 1231c0d32fdSEric Dumazet !gen_estimator_active(&police->tcf_rate_est))) { 124a883bf56SJarek Poplawski err = -EINVAL; 12574030603SWANG Cong goto failure; 12671bcb09aSStephen Hemminger } 12771bcb09aSStephen Hemminger 128fd6d4338SDavide Caratti if (tb[TCA_POLICE_RESULT]) { 129fd6d4338SDavide Caratti tcfp_result = nla_get_u32(tb[TCA_POLICE_RESULT]); 130fd6d4338SDavide Caratti if (TC_ACT_EXT_CMP(tcfp_result, TC_ACT_GOTO_CHAIN)) { 131fd6d4338SDavide Caratti NL_SET_ERR_MSG(extack, 132fd6d4338SDavide Caratti "goto chain not allowed on fallback"); 133fd6d4338SDavide Caratti err = -EINVAL; 134fd6d4338SDavide Caratti goto failure; 135fd6d4338SDavide Caratti } 136fd6d4338SDavide Caratti } 137fd6d4338SDavide Caratti 1382ffe0395SBaowen Zheng if ((tb[TCA_POLICE_PKTRATE64] && !tb[TCA_POLICE_PKTBURST64]) || 1392ffe0395SBaowen Zheng (!tb[TCA_POLICE_PKTRATE64] && tb[TCA_POLICE_PKTBURST64])) { 1402ffe0395SBaowen Zheng NL_SET_ERR_MSG(extack, 1412ffe0395SBaowen Zheng "Both or neither packet-per-second burst and rate must be provided"); 1422ffe0395SBaowen Zheng err = -EINVAL; 1432ffe0395SBaowen Zheng goto failure; 1442ffe0395SBaowen Zheng } 1452ffe0395SBaowen Zheng 1462ffe0395SBaowen Zheng if (tb[TCA_POLICE_PKTRATE64] && R_tab) { 1472ffe0395SBaowen Zheng NL_SET_ERR_MSG(extack, 1482ffe0395SBaowen Zheng "packet-per-second and byte-per-second rate limits not allowed in same action"); 1492ffe0395SBaowen Zheng err = -EINVAL; 1502ffe0395SBaowen Zheng goto failure; 1512ffe0395SBaowen Zheng } 1522ffe0395SBaowen Zheng 1532d550dbaSDavide Caratti new = kzalloc(sizeof(*new), GFP_KERNEL); 1542d550dbaSDavide Caratti if (unlikely(!new)) { 1552d550dbaSDavide Caratti err = -ENOMEM; 1562d550dbaSDavide Caratti goto failure; 1572d550dbaSDavide Caratti } 1582d550dbaSDavide Caratti 15971bcb09aSStephen Hemminger /* No failure allowed after this point */ 160fd6d4338SDavide Caratti new->tcfp_result = tcfp_result; 1612d550dbaSDavide Caratti new->tcfp_mtu = parm->mtu; 1622d550dbaSDavide Caratti if (!new->tcfp_mtu) { 1632d550dbaSDavide Caratti new->tcfp_mtu = ~0; 164c6d14ff1SJiri Pirko if (R_tab) 1652d550dbaSDavide Caratti new->tcfp_mtu = 255 << R_tab->rate.cell_log; 1664bba3925SPatrick McHardy } 167c6d14ff1SJiri Pirko if (R_tab) { 1682d550dbaSDavide Caratti new->rate_present = true; 169d1967e49SDavid Dai rate64 = tb[TCA_POLICE_RATE64] ? 170d1967e49SDavid Dai nla_get_u64(tb[TCA_POLICE_RATE64]) : 0; 171d1967e49SDavid Dai psched_ratecfg_precompute(&new->rate, &R_tab->rate, rate64); 172c6d14ff1SJiri Pirko qdisc_put_rtab(R_tab); 173c6d14ff1SJiri Pirko } else { 1742d550dbaSDavide Caratti new->rate_present = false; 175c6d14ff1SJiri Pirko } 176c6d14ff1SJiri Pirko if (P_tab) { 1772d550dbaSDavide Caratti new->peak_present = true; 178d1967e49SDavid Dai prate64 = tb[TCA_POLICE_PEAKRATE64] ? 179d1967e49SDavid Dai nla_get_u64(tb[TCA_POLICE_PEAKRATE64]) : 0; 180d1967e49SDavid Dai psched_ratecfg_precompute(&new->peak, &P_tab->rate, prate64); 181c6d14ff1SJiri Pirko qdisc_put_rtab(P_tab); 182c6d14ff1SJiri Pirko } else { 1832d550dbaSDavide Caratti new->peak_present = false; 1844bba3925SPatrick McHardy } 1854bba3925SPatrick McHardy 1862d550dbaSDavide Caratti new->tcfp_burst = PSCHED_TICKS2NS(parm->burst); 187f2cbd485SDavide Caratti if (new->peak_present) 1882d550dbaSDavide Caratti new->tcfp_mtu_ptoks = (s64)psched_l2t_ns(&new->peak, 1892d550dbaSDavide Caratti new->tcfp_mtu); 1904bba3925SPatrick McHardy 1917ba699c6SPatrick McHardy if (tb[TCA_POLICE_AVRATE]) 1922d550dbaSDavide Caratti new->tcfp_ewma_rate = nla_get_u32(tb[TCA_POLICE_AVRATE]); 1934bba3925SPatrick McHardy 1942ffe0395SBaowen Zheng if (tb[TCA_POLICE_PKTRATE64]) { 1952ffe0395SBaowen Zheng pps = nla_get_u64(tb[TCA_POLICE_PKTRATE64]); 1962ffe0395SBaowen Zheng ppsburst = nla_get_u64(tb[TCA_POLICE_PKTBURST64]); 1972ffe0395SBaowen Zheng new->pps_present = true; 1982ffe0395SBaowen Zheng new->tcfp_pkt_burst = PSCHED_TICKS2NS(ppsburst); 1992ffe0395SBaowen Zheng psched_ppscfg_precompute(&new->ppsrate, pps); 2002ffe0395SBaowen Zheng } 2012ffe0395SBaowen Zheng 2022d550dbaSDavide Caratti spin_lock_bh(&police->tcf_lock); 203f2cbd485SDavide Caratti spin_lock_bh(&police->tcfp_lock); 204f2cbd485SDavide Caratti police->tcfp_t_c = ktime_get_ns(); 205f2cbd485SDavide Caratti police->tcfp_toks = new->tcfp_burst; 206f2cbd485SDavide Caratti if (new->peak_present) 207f2cbd485SDavide Caratti police->tcfp_ptoks = new->tcfp_mtu_ptoks; 208f2cbd485SDavide Caratti spin_unlock_bh(&police->tcfp_lock); 209d6124d6bSDavide Caratti goto_ch = tcf_action_set_ctrlact(*a, parm->action, goto_ch); 210445d3749SPaul E. McKenney new = rcu_replace_pointer(police->params, 2112d550dbaSDavide Caratti new, 2122d550dbaSDavide Caratti lockdep_is_held(&police->tcf_lock)); 213e9ce1cd3SDavid S. Miller spin_unlock_bh(&police->tcf_lock); 2144bba3925SPatrick McHardy 215d6124d6bSDavide Caratti if (goto_ch) 216d6124d6bSDavide Caratti tcf_chain_put_by_act(goto_ch); 2172d550dbaSDavide Caratti if (new) 2182d550dbaSDavide Caratti kfree_rcu(new, rcu); 2192d550dbaSDavide Caratti 2204bba3925SPatrick McHardy return ret; 2214bba3925SPatrick McHardy 2224bba3925SPatrick McHardy failure: 22371bcb09aSStephen Hemminger qdisc_put_rtab(P_tab); 22471bcb09aSStephen Hemminger qdisc_put_rtab(R_tab); 225d6124d6bSDavide Caratti if (goto_ch) 226d6124d6bSDavide Caratti tcf_chain_put_by_act(goto_ch); 227d6124d6bSDavide Caratti release_idr: 2285bf7f818SDavide Caratti tcf_idr_release(*a, bind); 2294bba3925SPatrick McHardy return err; 2304bba3925SPatrick McHardy } 2314bba3925SPatrick McHardy 2324ddc844eSDavide Caratti static bool tcf_police_mtu_check(struct sk_buff *skb, u32 limit) 2334ddc844eSDavide Caratti { 2344ddc844eSDavide Caratti u32 len; 2354ddc844eSDavide Caratti 2364ddc844eSDavide Caratti if (skb_is_gso(skb)) 2374ddc844eSDavide Caratti return skb_gso_validate_mac_len(skb, limit); 2384ddc844eSDavide Caratti 2394ddc844eSDavide Caratti len = qdisc_pkt_len(skb); 2404ddc844eSDavide Caratti if (skb_at_tc_ingress(skb)) 2414ddc844eSDavide Caratti len += skb->mac_len; 2424ddc844eSDavide Caratti 2434ddc844eSDavide Caratti return len <= limit; 2444ddc844eSDavide Caratti } 2454ddc844eSDavide Caratti 246*871cf386SPedro Tammela TC_INDIRECT_SCOPE int tcf_police_act(struct sk_buff *skb, 247*871cf386SPedro Tammela const struct tc_action *a, 2484bba3925SPatrick McHardy struct tcf_result *res) 2494bba3925SPatrick McHardy { 250a85a970aSWANG Cong struct tcf_police *police = to_police(a); 2512ffe0395SBaowen Zheng s64 now, toks, ppstoks = 0, ptoks = 0; 2522d550dbaSDavide Caratti struct tcf_police_params *p; 25393be42f9SDavide Caratti int ret; 25493be42f9SDavide Caratti 25593be42f9SDavide Caratti tcf_lastuse_update(&police->tcf_tm); 25650dc9a85SAhmed S. Darwish bstats_update(this_cpu_ptr(police->common.cpu_bstats), skb); 2574bba3925SPatrick McHardy 2582d550dbaSDavide Caratti ret = READ_ONCE(police->tcf_action); 2592d550dbaSDavide Caratti p = rcu_dereference_bh(police->params); 2602d550dbaSDavide Caratti 2612d550dbaSDavide Caratti if (p->tcfp_ewma_rate) { 2621c0d32fdSEric Dumazet struct gnet_stats_rate_est64 sample; 2631c0d32fdSEric Dumazet 2641c0d32fdSEric Dumazet if (!gen_estimator_read(&police->tcf_rate_est, &sample) || 2652d550dbaSDavide Caratti sample.bps >= p->tcfp_ewma_rate) 26693be42f9SDavide Caratti goto inc_overlimits; 2674bba3925SPatrick McHardy } 2684bba3925SPatrick McHardy 2694ddc844eSDavide Caratti if (tcf_police_mtu_check(skb, p->tcfp_mtu)) { 2702ffe0395SBaowen Zheng if (!p->rate_present && !p->pps_present) { 2712d550dbaSDavide Caratti ret = p->tcfp_result; 2722d550dbaSDavide Caratti goto end; 2734bba3925SPatrick McHardy } 2744bba3925SPatrick McHardy 275d2de875cSEric Dumazet now = ktime_get_ns(); 276f2cbd485SDavide Caratti spin_lock_bh(&police->tcfp_lock); 277f2cbd485SDavide Caratti toks = min_t(s64, now - police->tcfp_t_c, p->tcfp_burst); 2782d550dbaSDavide Caratti if (p->peak_present) { 279f2cbd485SDavide Caratti ptoks = toks + police->tcfp_ptoks; 2802d550dbaSDavide Caratti if (ptoks > p->tcfp_mtu_ptoks) 2812d550dbaSDavide Caratti ptoks = p->tcfp_mtu_ptoks; 2822d550dbaSDavide Caratti ptoks -= (s64)psched_l2t_ns(&p->peak, 283c6d14ff1SJiri Pirko qdisc_pkt_len(skb)); 2844bba3925SPatrick McHardy } 2852ffe0395SBaowen Zheng if (p->rate_present) { 286f2cbd485SDavide Caratti toks += police->tcfp_toks; 2872d550dbaSDavide Caratti if (toks > p->tcfp_burst) 2882d550dbaSDavide Caratti toks = p->tcfp_burst; 2892d550dbaSDavide Caratti toks -= (s64)psched_l2t_ns(&p->rate, qdisc_pkt_len(skb)); 2902ffe0395SBaowen Zheng } else if (p->pps_present) { 2912ffe0395SBaowen Zheng ppstoks = min_t(s64, now - police->tcfp_t_c, p->tcfp_pkt_burst); 2922ffe0395SBaowen Zheng ppstoks += police->tcfp_pkttoks; 2932ffe0395SBaowen Zheng if (ppstoks > p->tcfp_pkt_burst) 2942ffe0395SBaowen Zheng ppstoks = p->tcfp_pkt_burst; 2952ffe0395SBaowen Zheng ppstoks -= (s64)psched_pkt2t_ns(&p->ppsrate, 1); 2962ffe0395SBaowen Zheng } 2972ffe0395SBaowen Zheng if ((toks | ptoks | ppstoks) >= 0) { 298f2cbd485SDavide Caratti police->tcfp_t_c = now; 299f2cbd485SDavide Caratti police->tcfp_toks = toks; 300f2cbd485SDavide Caratti police->tcfp_ptoks = ptoks; 3012ffe0395SBaowen Zheng police->tcfp_pkttoks = ppstoks; 302f2cbd485SDavide Caratti spin_unlock_bh(&police->tcfp_lock); 3032d550dbaSDavide Caratti ret = p->tcfp_result; 30493be42f9SDavide Caratti goto inc_drops; 3054bba3925SPatrick McHardy } 306f2cbd485SDavide Caratti spin_unlock_bh(&police->tcfp_lock); 3074bba3925SPatrick McHardy } 3084bba3925SPatrick McHardy 30993be42f9SDavide Caratti inc_overlimits: 31093be42f9SDavide Caratti qstats_overlimit_inc(this_cpu_ptr(police->common.cpu_qstats)); 31193be42f9SDavide Caratti inc_drops: 31293be42f9SDavide Caratti if (ret == TC_ACT_SHOT) 31393be42f9SDavide Caratti qstats_drop_inc(this_cpu_ptr(police->common.cpu_qstats)); 3142d550dbaSDavide Caratti end: 31593be42f9SDavide Caratti return ret; 3164bba3925SPatrick McHardy } 3174bba3925SPatrick McHardy 3182d550dbaSDavide Caratti static void tcf_police_cleanup(struct tc_action *a) 3192d550dbaSDavide Caratti { 3202d550dbaSDavide Caratti struct tcf_police *police = to_police(a); 3212d550dbaSDavide Caratti struct tcf_police_params *p; 3222d550dbaSDavide Caratti 3232d550dbaSDavide Caratti p = rcu_dereference_protected(police->params, 1); 3242d550dbaSDavide Caratti if (p) 3252d550dbaSDavide Caratti kfree_rcu(p, rcu); 3262d550dbaSDavide Caratti } 3272d550dbaSDavide Caratti 32812f02b6bSPieter Jansen van Vuuren static void tcf_police_stats_update(struct tc_action *a, 3294b61d3e8SPo Liu u64 bytes, u64 packets, u64 drops, 33012f02b6bSPieter Jansen van Vuuren u64 lastuse, bool hw) 33112f02b6bSPieter Jansen van Vuuren { 33212f02b6bSPieter Jansen van Vuuren struct tcf_police *police = to_police(a); 33312f02b6bSPieter Jansen van Vuuren struct tcf_t *tm = &police->tcf_tm; 33412f02b6bSPieter Jansen van Vuuren 3354b61d3e8SPo Liu tcf_action_update_stats(a, bytes, packets, drops, hw); 33612f02b6bSPieter Jansen van Vuuren tm->lastuse = max_t(u64, tm->lastuse, lastuse); 33712f02b6bSPieter Jansen van Vuuren } 33812f02b6bSPieter Jansen van Vuuren 3392ac06347SJamal Hadi Salim static int tcf_police_dump(struct sk_buff *skb, struct tc_action *a, 3405a7a5555SJamal Hadi Salim int bind, int ref) 3414bba3925SPatrick McHardy { 34227a884dcSArnaldo Carvalho de Melo unsigned char *b = skb_tail_pointer(skb); 343a85a970aSWANG Cong struct tcf_police *police = to_police(a); 3442d550dbaSDavide Caratti struct tcf_police_params *p; 3450f04cfd0SJeff Mahoney struct tc_police opt = { 3460f04cfd0SJeff Mahoney .index = police->tcf_index, 347036bb443SVlad Buslov .refcnt = refcount_read(&police->tcf_refcnt) - ref, 348036bb443SVlad Buslov .bindcnt = atomic_read(&police->tcf_bindcnt) - bind, 3490f04cfd0SJeff Mahoney }; 3503d3ed181SJamal Hadi Salim struct tcf_t t; 3514bba3925SPatrick McHardy 352e329bc42SVlad Buslov spin_lock_bh(&police->tcf_lock); 353e329bc42SVlad Buslov opt.action = police->tcf_action; 3542d550dbaSDavide Caratti p = rcu_dereference_protected(police->params, 3552d550dbaSDavide Caratti lockdep_is_held(&police->tcf_lock)); 3562d550dbaSDavide Caratti opt.mtu = p->tcfp_mtu; 3572d550dbaSDavide Caratti opt.burst = PSCHED_NS2TICKS(p->tcfp_burst); 358d1967e49SDavid Dai if (p->rate_present) { 3592d550dbaSDavide Caratti psched_ratecfg_getrate(&opt.rate, &p->rate); 360d1967e49SDavid Dai if ((police->params->rate.rate_bytes_ps >= (1ULL << 32)) && 361d1967e49SDavid Dai nla_put_u64_64bit(skb, TCA_POLICE_RATE64, 362d1967e49SDavid Dai police->params->rate.rate_bytes_ps, 363d1967e49SDavid Dai TCA_POLICE_PAD)) 364d1967e49SDavid Dai goto nla_put_failure; 365d1967e49SDavid Dai } 366d1967e49SDavid Dai if (p->peak_present) { 3672d550dbaSDavide Caratti psched_ratecfg_getrate(&opt.peakrate, &p->peak); 368d1967e49SDavid Dai if ((police->params->peak.rate_bytes_ps >= (1ULL << 32)) && 369d1967e49SDavid Dai nla_put_u64_64bit(skb, TCA_POLICE_PEAKRATE64, 370d1967e49SDavid Dai police->params->peak.rate_bytes_ps, 371d1967e49SDavid Dai TCA_POLICE_PAD)) 372d1967e49SDavid Dai goto nla_put_failure; 373d1967e49SDavid Dai } 3742ffe0395SBaowen Zheng if (p->pps_present) { 3752ffe0395SBaowen Zheng if (nla_put_u64_64bit(skb, TCA_POLICE_PKTRATE64, 3762ffe0395SBaowen Zheng police->params->ppsrate.rate_pkts_ps, 3772ffe0395SBaowen Zheng TCA_POLICE_PAD)) 3782ffe0395SBaowen Zheng goto nla_put_failure; 3792ffe0395SBaowen Zheng if (nla_put_u64_64bit(skb, TCA_POLICE_PKTBURST64, 3802ffe0395SBaowen Zheng PSCHED_NS2TICKS(p->tcfp_pkt_burst), 3812ffe0395SBaowen Zheng TCA_POLICE_PAD)) 3822ffe0395SBaowen Zheng goto nla_put_failure; 3832ffe0395SBaowen Zheng } 3841b34ec43SDavid S. Miller if (nla_put(skb, TCA_POLICE_TBF, sizeof(opt), &opt)) 3851b34ec43SDavid S. Miller goto nla_put_failure; 3862d550dbaSDavide Caratti if (p->tcfp_result && 3872d550dbaSDavide Caratti nla_put_u32(skb, TCA_POLICE_RESULT, p->tcfp_result)) 3881b34ec43SDavid S. Miller goto nla_put_failure; 3892d550dbaSDavide Caratti if (p->tcfp_ewma_rate && 3902d550dbaSDavide Caratti nla_put_u32(skb, TCA_POLICE_AVRATE, p->tcfp_ewma_rate)) 3911b34ec43SDavid S. Miller goto nla_put_failure; 3923d3ed181SJamal Hadi Salim 393985fd98aSDavide Caratti tcf_tm_dump(&t, &police->tcf_tm); 3943d3ed181SJamal Hadi Salim if (nla_put_64bit(skb, TCA_POLICE_TM, sizeof(t), &t, TCA_POLICE_PAD)) 3953d3ed181SJamal Hadi Salim goto nla_put_failure; 396e329bc42SVlad Buslov spin_unlock_bh(&police->tcf_lock); 3973d3ed181SJamal Hadi Salim 3984bba3925SPatrick McHardy return skb->len; 3994bba3925SPatrick McHardy 4007ba699c6SPatrick McHardy nla_put_failure: 401e329bc42SVlad Buslov spin_unlock_bh(&police->tcf_lock); 402dc5fc579SArnaldo Carvalho de Melo nlmsg_trim(skb, b); 4034bba3925SPatrick McHardy return -1; 4044bba3925SPatrick McHardy } 4054bba3925SPatrick McHardy 406b50e462bSIdo Schimmel static int tcf_police_act_to_flow_act(int tc_act, u32 *extval, 407b50e462bSIdo Schimmel struct netlink_ext_ack *extack) 408b8cd5831SJianbo Liu { 409b8cd5831SJianbo Liu int act_id = -EOPNOTSUPP; 410b8cd5831SJianbo Liu 411b8cd5831SJianbo Liu if (!TC_ACT_EXT_OPCODE(tc_act)) { 412b8cd5831SJianbo Liu if (tc_act == TC_ACT_OK) 413b8cd5831SJianbo Liu act_id = FLOW_ACTION_ACCEPT; 414b8cd5831SJianbo Liu else if (tc_act == TC_ACT_SHOT) 415b8cd5831SJianbo Liu act_id = FLOW_ACTION_DROP; 416b8cd5831SJianbo Liu else if (tc_act == TC_ACT_PIPE) 417b8cd5831SJianbo Liu act_id = FLOW_ACTION_PIPE; 418b50e462bSIdo Schimmel else if (tc_act == TC_ACT_RECLASSIFY) 419b50e462bSIdo Schimmel NL_SET_ERR_MSG_MOD(extack, "Offload not supported when conform/exceed action is \"reclassify\""); 420b50e462bSIdo Schimmel else 421b50e462bSIdo Schimmel NL_SET_ERR_MSG_MOD(extack, "Unsupported conform/exceed action offload"); 422b8cd5831SJianbo Liu } else if (TC_ACT_EXT_CMP(tc_act, TC_ACT_GOTO_CHAIN)) { 423b8cd5831SJianbo Liu act_id = FLOW_ACTION_GOTO; 424b8cd5831SJianbo Liu *extval = tc_act & TC_ACT_EXT_VAL_MASK; 425b8cd5831SJianbo Liu } else if (TC_ACT_EXT_CMP(tc_act, TC_ACT_JUMP)) { 426b8cd5831SJianbo Liu act_id = FLOW_ACTION_JUMP; 427b8cd5831SJianbo Liu *extval = tc_act & TC_ACT_EXT_VAL_MASK; 428b50e462bSIdo Schimmel } else if (tc_act == TC_ACT_UNSPEC) { 429052f744fSVlad Buslov act_id = FLOW_ACTION_CONTINUE; 430b50e462bSIdo Schimmel } else { 431b50e462bSIdo Schimmel NL_SET_ERR_MSG_MOD(extack, "Unsupported conform/exceed action offload"); 432b8cd5831SJianbo Liu } 433b8cd5831SJianbo Liu 434b8cd5831SJianbo Liu return act_id; 435b8cd5831SJianbo Liu } 436b8cd5831SJianbo Liu 437c54e1d92SBaowen Zheng static int tcf_police_offload_act_setup(struct tc_action *act, void *entry_data, 438c2ccf84eSIdo Schimmel u32 *index_inc, bool bind, 439c2ccf84eSIdo Schimmel struct netlink_ext_ack *extack) 440c54e1d92SBaowen Zheng { 441c54e1d92SBaowen Zheng if (bind) { 442c54e1d92SBaowen Zheng struct flow_action_entry *entry = entry_data; 443b8cd5831SJianbo Liu struct tcf_police *police = to_police(act); 444b8cd5831SJianbo Liu struct tcf_police_params *p; 445b8cd5831SJianbo Liu int act_id; 446b8cd5831SJianbo Liu 447b8cd5831SJianbo Liu p = rcu_dereference_protected(police->params, 448b8cd5831SJianbo Liu lockdep_is_held(&police->tcf_lock)); 449c54e1d92SBaowen Zheng 450c54e1d92SBaowen Zheng entry->id = FLOW_ACTION_POLICE; 451c54e1d92SBaowen Zheng entry->police.burst = tcf_police_burst(act); 452c54e1d92SBaowen Zheng entry->police.rate_bytes_ps = 453c54e1d92SBaowen Zheng tcf_police_rate_bytes_ps(act); 454b8cd5831SJianbo Liu entry->police.peakrate_bytes_ps = tcf_police_peakrate_bytes_ps(act); 455b8cd5831SJianbo Liu entry->police.avrate = tcf_police_tcfp_ewma_rate(act); 456b8cd5831SJianbo Liu entry->police.overhead = tcf_police_rate_overhead(act); 457c54e1d92SBaowen Zheng entry->police.burst_pkt = tcf_police_burst_pkt(act); 458c54e1d92SBaowen Zheng entry->police.rate_pkt_ps = 459c54e1d92SBaowen Zheng tcf_police_rate_pkt_ps(act); 460c54e1d92SBaowen Zheng entry->police.mtu = tcf_police_tcfp_mtu(act); 461b8cd5831SJianbo Liu 462b8cd5831SJianbo Liu act_id = tcf_police_act_to_flow_act(police->tcf_action, 463b50e462bSIdo Schimmel &entry->police.exceed.extval, 464b50e462bSIdo Schimmel extack); 465b8cd5831SJianbo Liu if (act_id < 0) 466b8cd5831SJianbo Liu return act_id; 467b8cd5831SJianbo Liu 468b8cd5831SJianbo Liu entry->police.exceed.act_id = act_id; 469b8cd5831SJianbo Liu 470b8cd5831SJianbo Liu act_id = tcf_police_act_to_flow_act(p->tcfp_result, 471b50e462bSIdo Schimmel &entry->police.notexceed.extval, 472b50e462bSIdo Schimmel extack); 473b8cd5831SJianbo Liu if (act_id < 0) 474b8cd5831SJianbo Liu return act_id; 475b8cd5831SJianbo Liu 476b8cd5831SJianbo Liu entry->police.notexceed.act_id = act_id; 477b8cd5831SJianbo Liu 478c54e1d92SBaowen Zheng *index_inc = 1; 479c54e1d92SBaowen Zheng } else { 4808cbfe939SBaowen Zheng struct flow_offload_action *fl_action = entry_data; 4818cbfe939SBaowen Zheng 4828cbfe939SBaowen Zheng fl_action->id = FLOW_ACTION_POLICE; 483c54e1d92SBaowen Zheng } 484c54e1d92SBaowen Zheng 485c54e1d92SBaowen Zheng return 0; 486c54e1d92SBaowen Zheng } 487c54e1d92SBaowen Zheng 4884bba3925SPatrick McHardy MODULE_AUTHOR("Alexey Kuznetsov"); 4894bba3925SPatrick McHardy MODULE_DESCRIPTION("Policing actions"); 4904bba3925SPatrick McHardy MODULE_LICENSE("GPL"); 4914bba3925SPatrick McHardy 4924bba3925SPatrick McHardy static struct tc_action_ops act_police_ops = { 4934bba3925SPatrick McHardy .kind = "police", 494eddd2cf1SEli Cohen .id = TCA_ID_POLICE, 4954bba3925SPatrick McHardy .owner = THIS_MODULE, 49612f02b6bSPieter Jansen van Vuuren .stats_update = tcf_police_stats_update, 4972ac06347SJamal Hadi Salim .act = tcf_police_act, 4982ac06347SJamal Hadi Salim .dump = tcf_police_dump, 4992ac06347SJamal Hadi Salim .init = tcf_police_init, 5002d550dbaSDavide Caratti .cleanup = tcf_police_cleanup, 501c54e1d92SBaowen Zheng .offload_act_setup = tcf_police_offload_act_setup, 502a85a970aSWANG Cong .size = sizeof(struct tcf_police), 503ddf97ccdSWANG Cong }; 504ddf97ccdSWANG Cong 505ddf97ccdSWANG Cong static __net_init int police_init_net(struct net *net) 506ddf97ccdSWANG Cong { 507acd0a7abSZhengchao Shao struct tc_action_net *tn = net_generic(net, act_police_ops.net_id); 508ddf97ccdSWANG Cong 509981471bdSCong Wang return tc_action_net_init(net, tn, &act_police_ops); 510ddf97ccdSWANG Cong } 511ddf97ccdSWANG Cong 512039af9c6SCong Wang static void __net_exit police_exit_net(struct list_head *net_list) 513ddf97ccdSWANG Cong { 514acd0a7abSZhengchao Shao tc_action_net_exit(net_list, act_police_ops.net_id); 515ddf97ccdSWANG Cong } 516ddf97ccdSWANG Cong 517ddf97ccdSWANG Cong static struct pernet_operations police_net_ops = { 518ddf97ccdSWANG Cong .init = police_init_net, 519039af9c6SCong Wang .exit_batch = police_exit_net, 520acd0a7abSZhengchao Shao .id = &act_police_ops.net_id, 521ddf97ccdSWANG Cong .size = sizeof(struct tc_action_net), 5224bba3925SPatrick McHardy }; 5234bba3925SPatrick McHardy 5245a7a5555SJamal Hadi Salim static int __init police_init_module(void) 5254bba3925SPatrick McHardy { 526ddf97ccdSWANG Cong return tcf_register_action(&act_police_ops, &police_net_ops); 5274bba3925SPatrick McHardy } 5284bba3925SPatrick McHardy 5295a7a5555SJamal Hadi Salim static void __exit police_cleanup_module(void) 5304bba3925SPatrick McHardy { 531ddf97ccdSWANG Cong tcf_unregister_action(&act_police_ops, &police_net_ops); 5324bba3925SPatrick McHardy } 5334bba3925SPatrick McHardy 5344bba3925SPatrick McHardy module_init(police_init_module); 5354bba3925SPatrick McHardy module_exit(police_cleanup_module); 536