18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* Copyright 2020 NXP */ 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci#include <linux/module.h> 58c2ecf20Sopenharmony_ci#include <linux/types.h> 68c2ecf20Sopenharmony_ci#include <linux/kernel.h> 78c2ecf20Sopenharmony_ci#include <linux/string.h> 88c2ecf20Sopenharmony_ci#include <linux/errno.h> 98c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 108c2ecf20Sopenharmony_ci#include <linux/rtnetlink.h> 118c2ecf20Sopenharmony_ci#include <linux/init.h> 128c2ecf20Sopenharmony_ci#include <linux/slab.h> 138c2ecf20Sopenharmony_ci#include <net/act_api.h> 148c2ecf20Sopenharmony_ci#include <net/netlink.h> 158c2ecf20Sopenharmony_ci#include <net/pkt_cls.h> 168c2ecf20Sopenharmony_ci#include <net/tc_act/tc_gate.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_cistatic unsigned int gate_net_id; 198c2ecf20Sopenharmony_cistatic struct tc_action_ops act_gate_ops; 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_cistatic ktime_t gate_get_time(struct tcf_gate *gact) 228c2ecf20Sopenharmony_ci{ 238c2ecf20Sopenharmony_ci ktime_t mono = ktime_get(); 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci switch (gact->tk_offset) { 268c2ecf20Sopenharmony_ci case TK_OFFS_MAX: 278c2ecf20Sopenharmony_ci return mono; 288c2ecf20Sopenharmony_ci default: 298c2ecf20Sopenharmony_ci return ktime_mono_to_any(mono, gact->tk_offset); 308c2ecf20Sopenharmony_ci } 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci return KTIME_MAX; 338c2ecf20Sopenharmony_ci} 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistatic void gate_get_start_time(struct tcf_gate *gact, ktime_t *start) 368c2ecf20Sopenharmony_ci{ 378c2ecf20Sopenharmony_ci struct tcf_gate_params *param = &gact->param; 388c2ecf20Sopenharmony_ci ktime_t now, base, cycle; 398c2ecf20Sopenharmony_ci u64 n; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci base = ns_to_ktime(param->tcfg_basetime); 428c2ecf20Sopenharmony_ci now = gate_get_time(gact); 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci if (ktime_after(base, now)) { 458c2ecf20Sopenharmony_ci *start = base; 468c2ecf20Sopenharmony_ci return; 478c2ecf20Sopenharmony_ci } 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci cycle = param->tcfg_cycletime; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci n = div64_u64(ktime_sub_ns(now, base), cycle); 528c2ecf20Sopenharmony_ci *start = ktime_add_ns(base, (n + 1) * cycle); 538c2ecf20Sopenharmony_ci} 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistatic void gate_start_timer(struct tcf_gate *gact, ktime_t start) 568c2ecf20Sopenharmony_ci{ 578c2ecf20Sopenharmony_ci ktime_t expires; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci expires = hrtimer_get_expires(&gact->hitimer); 608c2ecf20Sopenharmony_ci if (expires == 0) 618c2ecf20Sopenharmony_ci expires = KTIME_MAX; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci start = min_t(ktime_t, start, expires); 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci hrtimer_start(&gact->hitimer, start, HRTIMER_MODE_ABS_SOFT); 668c2ecf20Sopenharmony_ci} 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistatic enum hrtimer_restart gate_timer_func(struct hrtimer *timer) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci struct tcf_gate *gact = container_of(timer, struct tcf_gate, 718c2ecf20Sopenharmony_ci hitimer); 728c2ecf20Sopenharmony_ci struct tcf_gate_params *p = &gact->param; 738c2ecf20Sopenharmony_ci struct tcfg_gate_entry *next; 748c2ecf20Sopenharmony_ci ktime_t close_time, now; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci spin_lock(&gact->tcf_lock); 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci next = gact->next_entry; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci /* cycle start, clear pending bit, clear total octets */ 818c2ecf20Sopenharmony_ci gact->current_gate_status = next->gate_state ? GATE_ACT_GATE_OPEN : 0; 828c2ecf20Sopenharmony_ci gact->current_entry_octets = 0; 838c2ecf20Sopenharmony_ci gact->current_max_octets = next->maxoctets; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci gact->current_close_time = ktime_add_ns(gact->current_close_time, 868c2ecf20Sopenharmony_ci next->interval); 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci close_time = gact->current_close_time; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci if (list_is_last(&next->list, &p->entries)) 918c2ecf20Sopenharmony_ci next = list_first_entry(&p->entries, 928c2ecf20Sopenharmony_ci struct tcfg_gate_entry, list); 938c2ecf20Sopenharmony_ci else 948c2ecf20Sopenharmony_ci next = list_next_entry(next, list); 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci now = gate_get_time(gact); 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci if (ktime_after(now, close_time)) { 998c2ecf20Sopenharmony_ci ktime_t cycle, base; 1008c2ecf20Sopenharmony_ci u64 n; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci cycle = p->tcfg_cycletime; 1038c2ecf20Sopenharmony_ci base = ns_to_ktime(p->tcfg_basetime); 1048c2ecf20Sopenharmony_ci n = div64_u64(ktime_sub_ns(now, base), cycle); 1058c2ecf20Sopenharmony_ci close_time = ktime_add_ns(base, (n + 1) * cycle); 1068c2ecf20Sopenharmony_ci } 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci gact->next_entry = next; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci hrtimer_set_expires(&gact->hitimer, close_time); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci spin_unlock(&gact->tcf_lock); 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci return HRTIMER_RESTART; 1158c2ecf20Sopenharmony_ci} 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_cistatic int tcf_gate_act(struct sk_buff *skb, const struct tc_action *a, 1188c2ecf20Sopenharmony_ci struct tcf_result *res) 1198c2ecf20Sopenharmony_ci{ 1208c2ecf20Sopenharmony_ci struct tcf_gate *gact = to_gate(a); 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci spin_lock(&gact->tcf_lock); 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci tcf_lastuse_update(&gact->tcf_tm); 1258c2ecf20Sopenharmony_ci bstats_update(&gact->tcf_bstats, skb); 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci if (unlikely(gact->current_gate_status & GATE_ACT_PENDING)) { 1288c2ecf20Sopenharmony_ci spin_unlock(&gact->tcf_lock); 1298c2ecf20Sopenharmony_ci return gact->tcf_action; 1308c2ecf20Sopenharmony_ci } 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci if (!(gact->current_gate_status & GATE_ACT_GATE_OPEN)) 1338c2ecf20Sopenharmony_ci goto drop; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci if (gact->current_max_octets >= 0) { 1368c2ecf20Sopenharmony_ci gact->current_entry_octets += qdisc_pkt_len(skb); 1378c2ecf20Sopenharmony_ci if (gact->current_entry_octets > gact->current_max_octets) { 1388c2ecf20Sopenharmony_ci gact->tcf_qstats.overlimits++; 1398c2ecf20Sopenharmony_ci goto drop; 1408c2ecf20Sopenharmony_ci } 1418c2ecf20Sopenharmony_ci } 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci spin_unlock(&gact->tcf_lock); 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci return gact->tcf_action; 1468c2ecf20Sopenharmony_cidrop: 1478c2ecf20Sopenharmony_ci gact->tcf_qstats.drops++; 1488c2ecf20Sopenharmony_ci spin_unlock(&gact->tcf_lock); 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci return TC_ACT_SHOT; 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_cistatic const struct nla_policy entry_policy[TCA_GATE_ENTRY_MAX + 1] = { 1548c2ecf20Sopenharmony_ci [TCA_GATE_ENTRY_INDEX] = { .type = NLA_U32 }, 1558c2ecf20Sopenharmony_ci [TCA_GATE_ENTRY_GATE] = { .type = NLA_FLAG }, 1568c2ecf20Sopenharmony_ci [TCA_GATE_ENTRY_INTERVAL] = { .type = NLA_U32 }, 1578c2ecf20Sopenharmony_ci [TCA_GATE_ENTRY_IPV] = { .type = NLA_S32 }, 1588c2ecf20Sopenharmony_ci [TCA_GATE_ENTRY_MAX_OCTETS] = { .type = NLA_S32 }, 1598c2ecf20Sopenharmony_ci}; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_cistatic const struct nla_policy gate_policy[TCA_GATE_MAX + 1] = { 1628c2ecf20Sopenharmony_ci [TCA_GATE_PARMS] = 1638c2ecf20Sopenharmony_ci NLA_POLICY_EXACT_LEN(sizeof(struct tc_gate)), 1648c2ecf20Sopenharmony_ci [TCA_GATE_PRIORITY] = { .type = NLA_S32 }, 1658c2ecf20Sopenharmony_ci [TCA_GATE_ENTRY_LIST] = { .type = NLA_NESTED }, 1668c2ecf20Sopenharmony_ci [TCA_GATE_BASE_TIME] = { .type = NLA_U64 }, 1678c2ecf20Sopenharmony_ci [TCA_GATE_CYCLE_TIME] = { .type = NLA_U64 }, 1688c2ecf20Sopenharmony_ci [TCA_GATE_CYCLE_TIME_EXT] = { .type = NLA_U64 }, 1698c2ecf20Sopenharmony_ci [TCA_GATE_FLAGS] = { .type = NLA_U32 }, 1708c2ecf20Sopenharmony_ci [TCA_GATE_CLOCKID] = { .type = NLA_S32 }, 1718c2ecf20Sopenharmony_ci}; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_cistatic int fill_gate_entry(struct nlattr **tb, struct tcfg_gate_entry *entry, 1748c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 1758c2ecf20Sopenharmony_ci{ 1768c2ecf20Sopenharmony_ci u32 interval = 0; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci entry->gate_state = nla_get_flag(tb[TCA_GATE_ENTRY_GATE]); 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci if (tb[TCA_GATE_ENTRY_INTERVAL]) 1818c2ecf20Sopenharmony_ci interval = nla_get_u32(tb[TCA_GATE_ENTRY_INTERVAL]); 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci if (interval == 0) { 1848c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Invalid interval for schedule entry"); 1858c2ecf20Sopenharmony_ci return -EINVAL; 1868c2ecf20Sopenharmony_ci } 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci entry->interval = interval; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci if (tb[TCA_GATE_ENTRY_IPV]) 1918c2ecf20Sopenharmony_ci entry->ipv = nla_get_s32(tb[TCA_GATE_ENTRY_IPV]); 1928c2ecf20Sopenharmony_ci else 1938c2ecf20Sopenharmony_ci entry->ipv = -1; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci if (tb[TCA_GATE_ENTRY_MAX_OCTETS]) 1968c2ecf20Sopenharmony_ci entry->maxoctets = nla_get_s32(tb[TCA_GATE_ENTRY_MAX_OCTETS]); 1978c2ecf20Sopenharmony_ci else 1988c2ecf20Sopenharmony_ci entry->maxoctets = -1; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci return 0; 2018c2ecf20Sopenharmony_ci} 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_cistatic int parse_gate_entry(struct nlattr *n, struct tcfg_gate_entry *entry, 2048c2ecf20Sopenharmony_ci int index, struct netlink_ext_ack *extack) 2058c2ecf20Sopenharmony_ci{ 2068c2ecf20Sopenharmony_ci struct nlattr *tb[TCA_GATE_ENTRY_MAX + 1] = { }; 2078c2ecf20Sopenharmony_ci int err; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci err = nla_parse_nested(tb, TCA_GATE_ENTRY_MAX, n, entry_policy, extack); 2108c2ecf20Sopenharmony_ci if (err < 0) { 2118c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Could not parse nested entry"); 2128c2ecf20Sopenharmony_ci return -EINVAL; 2138c2ecf20Sopenharmony_ci } 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci entry->index = index; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci return fill_gate_entry(tb, entry, extack); 2188c2ecf20Sopenharmony_ci} 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_cistatic void release_entry_list(struct list_head *entries) 2218c2ecf20Sopenharmony_ci{ 2228c2ecf20Sopenharmony_ci struct tcfg_gate_entry *entry, *e; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci list_for_each_entry_safe(entry, e, entries, list) { 2258c2ecf20Sopenharmony_ci list_del(&entry->list); 2268c2ecf20Sopenharmony_ci kfree(entry); 2278c2ecf20Sopenharmony_ci } 2288c2ecf20Sopenharmony_ci} 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_cistatic int parse_gate_list(struct nlattr *list_attr, 2318c2ecf20Sopenharmony_ci struct tcf_gate_params *sched, 2328c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 2338c2ecf20Sopenharmony_ci{ 2348c2ecf20Sopenharmony_ci struct tcfg_gate_entry *entry; 2358c2ecf20Sopenharmony_ci struct nlattr *n; 2368c2ecf20Sopenharmony_ci int err, rem; 2378c2ecf20Sopenharmony_ci int i = 0; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci if (!list_attr) 2408c2ecf20Sopenharmony_ci return -EINVAL; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci nla_for_each_nested(n, list_attr, rem) { 2438c2ecf20Sopenharmony_ci if (nla_type(n) != TCA_GATE_ONE_ENTRY) { 2448c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Attribute isn't type 'entry'"); 2458c2ecf20Sopenharmony_ci continue; 2468c2ecf20Sopenharmony_ci } 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci entry = kzalloc(sizeof(*entry), GFP_ATOMIC); 2498c2ecf20Sopenharmony_ci if (!entry) { 2508c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Not enough memory for entry"); 2518c2ecf20Sopenharmony_ci err = -ENOMEM; 2528c2ecf20Sopenharmony_ci goto release_list; 2538c2ecf20Sopenharmony_ci } 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci err = parse_gate_entry(n, entry, i, extack); 2568c2ecf20Sopenharmony_ci if (err < 0) { 2578c2ecf20Sopenharmony_ci kfree(entry); 2588c2ecf20Sopenharmony_ci goto release_list; 2598c2ecf20Sopenharmony_ci } 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci list_add_tail(&entry->list, &sched->entries); 2628c2ecf20Sopenharmony_ci i++; 2638c2ecf20Sopenharmony_ci } 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci sched->num_entries = i; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci return i; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_cirelease_list: 2708c2ecf20Sopenharmony_ci release_entry_list(&sched->entries); 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci return err; 2738c2ecf20Sopenharmony_ci} 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_cistatic void gate_setup_timer(struct tcf_gate *gact, u64 basetime, 2768c2ecf20Sopenharmony_ci enum tk_offsets tko, s32 clockid, 2778c2ecf20Sopenharmony_ci bool do_init) 2788c2ecf20Sopenharmony_ci{ 2798c2ecf20Sopenharmony_ci if (!do_init) { 2808c2ecf20Sopenharmony_ci if (basetime == gact->param.tcfg_basetime && 2818c2ecf20Sopenharmony_ci tko == gact->tk_offset && 2828c2ecf20Sopenharmony_ci clockid == gact->param.tcfg_clockid) 2838c2ecf20Sopenharmony_ci return; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci spin_unlock_bh(&gact->tcf_lock); 2868c2ecf20Sopenharmony_ci hrtimer_cancel(&gact->hitimer); 2878c2ecf20Sopenharmony_ci spin_lock_bh(&gact->tcf_lock); 2888c2ecf20Sopenharmony_ci } 2898c2ecf20Sopenharmony_ci gact->param.tcfg_basetime = basetime; 2908c2ecf20Sopenharmony_ci gact->param.tcfg_clockid = clockid; 2918c2ecf20Sopenharmony_ci gact->tk_offset = tko; 2928c2ecf20Sopenharmony_ci hrtimer_init(&gact->hitimer, clockid, HRTIMER_MODE_ABS_SOFT); 2938c2ecf20Sopenharmony_ci gact->hitimer.function = gate_timer_func; 2948c2ecf20Sopenharmony_ci} 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_cistatic int tcf_gate_init(struct net *net, struct nlattr *nla, 2978c2ecf20Sopenharmony_ci struct nlattr *est, struct tc_action **a, 2988c2ecf20Sopenharmony_ci int ovr, int bind, bool rtnl_held, 2998c2ecf20Sopenharmony_ci struct tcf_proto *tp, u32 flags, 3008c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 3018c2ecf20Sopenharmony_ci{ 3028c2ecf20Sopenharmony_ci struct tc_action_net *tn = net_generic(net, gate_net_id); 3038c2ecf20Sopenharmony_ci enum tk_offsets tk_offset = TK_OFFS_TAI; 3048c2ecf20Sopenharmony_ci struct nlattr *tb[TCA_GATE_MAX + 1]; 3058c2ecf20Sopenharmony_ci struct tcf_chain *goto_ch = NULL; 3068c2ecf20Sopenharmony_ci u64 cycletime = 0, basetime = 0; 3078c2ecf20Sopenharmony_ci struct tcf_gate_params *p; 3088c2ecf20Sopenharmony_ci s32 clockid = CLOCK_TAI; 3098c2ecf20Sopenharmony_ci struct tcf_gate *gact; 3108c2ecf20Sopenharmony_ci struct tc_gate *parm; 3118c2ecf20Sopenharmony_ci int ret = 0, err; 3128c2ecf20Sopenharmony_ci u32 gflags = 0; 3138c2ecf20Sopenharmony_ci s32 prio = -1; 3148c2ecf20Sopenharmony_ci ktime_t start; 3158c2ecf20Sopenharmony_ci u32 index; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci if (!nla) 3188c2ecf20Sopenharmony_ci return -EINVAL; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci err = nla_parse_nested(tb, TCA_GATE_MAX, nla, gate_policy, extack); 3218c2ecf20Sopenharmony_ci if (err < 0) 3228c2ecf20Sopenharmony_ci return err; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci if (!tb[TCA_GATE_PARMS]) 3258c2ecf20Sopenharmony_ci return -EINVAL; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci if (tb[TCA_GATE_CLOCKID]) { 3288c2ecf20Sopenharmony_ci clockid = nla_get_s32(tb[TCA_GATE_CLOCKID]); 3298c2ecf20Sopenharmony_ci switch (clockid) { 3308c2ecf20Sopenharmony_ci case CLOCK_REALTIME: 3318c2ecf20Sopenharmony_ci tk_offset = TK_OFFS_REAL; 3328c2ecf20Sopenharmony_ci break; 3338c2ecf20Sopenharmony_ci case CLOCK_MONOTONIC: 3348c2ecf20Sopenharmony_ci tk_offset = TK_OFFS_MAX; 3358c2ecf20Sopenharmony_ci break; 3368c2ecf20Sopenharmony_ci case CLOCK_BOOTTIME: 3378c2ecf20Sopenharmony_ci tk_offset = TK_OFFS_BOOT; 3388c2ecf20Sopenharmony_ci break; 3398c2ecf20Sopenharmony_ci case CLOCK_TAI: 3408c2ecf20Sopenharmony_ci tk_offset = TK_OFFS_TAI; 3418c2ecf20Sopenharmony_ci break; 3428c2ecf20Sopenharmony_ci default: 3438c2ecf20Sopenharmony_ci NL_SET_ERR_MSG(extack, "Invalid 'clockid'"); 3448c2ecf20Sopenharmony_ci return -EINVAL; 3458c2ecf20Sopenharmony_ci } 3468c2ecf20Sopenharmony_ci } 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci parm = nla_data(tb[TCA_GATE_PARMS]); 3498c2ecf20Sopenharmony_ci index = parm->index; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci err = tcf_idr_check_alloc(tn, &index, a, bind); 3528c2ecf20Sopenharmony_ci if (err < 0) 3538c2ecf20Sopenharmony_ci return err; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci if (err && bind) 3568c2ecf20Sopenharmony_ci return 0; 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci if (!err) { 3598c2ecf20Sopenharmony_ci ret = tcf_idr_create(tn, index, est, a, 3608c2ecf20Sopenharmony_ci &act_gate_ops, bind, false, flags); 3618c2ecf20Sopenharmony_ci if (ret) { 3628c2ecf20Sopenharmony_ci tcf_idr_cleanup(tn, index); 3638c2ecf20Sopenharmony_ci return ret; 3648c2ecf20Sopenharmony_ci } 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci ret = ACT_P_CREATED; 3678c2ecf20Sopenharmony_ci } else if (!ovr) { 3688c2ecf20Sopenharmony_ci tcf_idr_release(*a, bind); 3698c2ecf20Sopenharmony_ci return -EEXIST; 3708c2ecf20Sopenharmony_ci } 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci if (tb[TCA_GATE_PRIORITY]) 3738c2ecf20Sopenharmony_ci prio = nla_get_s32(tb[TCA_GATE_PRIORITY]); 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci if (tb[TCA_GATE_BASE_TIME]) 3768c2ecf20Sopenharmony_ci basetime = nla_get_u64(tb[TCA_GATE_BASE_TIME]); 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci if (tb[TCA_GATE_FLAGS]) 3798c2ecf20Sopenharmony_ci gflags = nla_get_u32(tb[TCA_GATE_FLAGS]); 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci gact = to_gate(*a); 3828c2ecf20Sopenharmony_ci if (ret == ACT_P_CREATED) 3838c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&gact->param.entries); 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci err = tcf_action_check_ctrlact(parm->action, tp, &goto_ch, extack); 3868c2ecf20Sopenharmony_ci if (err < 0) 3878c2ecf20Sopenharmony_ci goto release_idr; 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci spin_lock_bh(&gact->tcf_lock); 3908c2ecf20Sopenharmony_ci p = &gact->param; 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci if (tb[TCA_GATE_CYCLE_TIME]) 3938c2ecf20Sopenharmony_ci cycletime = nla_get_u64(tb[TCA_GATE_CYCLE_TIME]); 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci if (tb[TCA_GATE_ENTRY_LIST]) { 3968c2ecf20Sopenharmony_ci err = parse_gate_list(tb[TCA_GATE_ENTRY_LIST], p, extack); 3978c2ecf20Sopenharmony_ci if (err < 0) 3988c2ecf20Sopenharmony_ci goto chain_put; 3998c2ecf20Sopenharmony_ci } 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci if (!cycletime) { 4028c2ecf20Sopenharmony_ci struct tcfg_gate_entry *entry; 4038c2ecf20Sopenharmony_ci ktime_t cycle = 0; 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci list_for_each_entry(entry, &p->entries, list) 4068c2ecf20Sopenharmony_ci cycle = ktime_add_ns(cycle, entry->interval); 4078c2ecf20Sopenharmony_ci cycletime = cycle; 4088c2ecf20Sopenharmony_ci if (!cycletime) { 4098c2ecf20Sopenharmony_ci err = -EINVAL; 4108c2ecf20Sopenharmony_ci goto chain_put; 4118c2ecf20Sopenharmony_ci } 4128c2ecf20Sopenharmony_ci } 4138c2ecf20Sopenharmony_ci p->tcfg_cycletime = cycletime; 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci if (tb[TCA_GATE_CYCLE_TIME_EXT]) 4168c2ecf20Sopenharmony_ci p->tcfg_cycletime_ext = 4178c2ecf20Sopenharmony_ci nla_get_u64(tb[TCA_GATE_CYCLE_TIME_EXT]); 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci gate_setup_timer(gact, basetime, tk_offset, clockid, 4208c2ecf20Sopenharmony_ci ret == ACT_P_CREATED); 4218c2ecf20Sopenharmony_ci p->tcfg_priority = prio; 4228c2ecf20Sopenharmony_ci p->tcfg_flags = gflags; 4238c2ecf20Sopenharmony_ci gate_get_start_time(gact, &start); 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci gact->current_close_time = start; 4268c2ecf20Sopenharmony_ci gact->current_gate_status = GATE_ACT_GATE_OPEN | GATE_ACT_PENDING; 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci gact->next_entry = list_first_entry(&p->entries, 4298c2ecf20Sopenharmony_ci struct tcfg_gate_entry, list); 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci goto_ch = tcf_action_set_ctrlact(*a, parm->action, goto_ch); 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci gate_start_timer(gact, start); 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci spin_unlock_bh(&gact->tcf_lock); 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci if (goto_ch) 4388c2ecf20Sopenharmony_ci tcf_chain_put_by_act(goto_ch); 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci return ret; 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_cichain_put: 4438c2ecf20Sopenharmony_ci spin_unlock_bh(&gact->tcf_lock); 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci if (goto_ch) 4468c2ecf20Sopenharmony_ci tcf_chain_put_by_act(goto_ch); 4478c2ecf20Sopenharmony_cirelease_idr: 4488c2ecf20Sopenharmony_ci /* action is not inserted in any list: it's safe to init hitimer 4498c2ecf20Sopenharmony_ci * without taking tcf_lock. 4508c2ecf20Sopenharmony_ci */ 4518c2ecf20Sopenharmony_ci if (ret == ACT_P_CREATED) 4528c2ecf20Sopenharmony_ci gate_setup_timer(gact, gact->param.tcfg_basetime, 4538c2ecf20Sopenharmony_ci gact->tk_offset, gact->param.tcfg_clockid, 4548c2ecf20Sopenharmony_ci true); 4558c2ecf20Sopenharmony_ci tcf_idr_release(*a, bind); 4568c2ecf20Sopenharmony_ci return err; 4578c2ecf20Sopenharmony_ci} 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_cistatic void tcf_gate_cleanup(struct tc_action *a) 4608c2ecf20Sopenharmony_ci{ 4618c2ecf20Sopenharmony_ci struct tcf_gate *gact = to_gate(a); 4628c2ecf20Sopenharmony_ci struct tcf_gate_params *p; 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci p = &gact->param; 4658c2ecf20Sopenharmony_ci hrtimer_cancel(&gact->hitimer); 4668c2ecf20Sopenharmony_ci release_entry_list(&p->entries); 4678c2ecf20Sopenharmony_ci} 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_cistatic int dumping_entry(struct sk_buff *skb, 4708c2ecf20Sopenharmony_ci struct tcfg_gate_entry *entry) 4718c2ecf20Sopenharmony_ci{ 4728c2ecf20Sopenharmony_ci struct nlattr *item; 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci item = nla_nest_start_noflag(skb, TCA_GATE_ONE_ENTRY); 4758c2ecf20Sopenharmony_ci if (!item) 4768c2ecf20Sopenharmony_ci return -ENOSPC; 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci if (nla_put_u32(skb, TCA_GATE_ENTRY_INDEX, entry->index)) 4798c2ecf20Sopenharmony_ci goto nla_put_failure; 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci if (entry->gate_state && nla_put_flag(skb, TCA_GATE_ENTRY_GATE)) 4828c2ecf20Sopenharmony_ci goto nla_put_failure; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci if (nla_put_u32(skb, TCA_GATE_ENTRY_INTERVAL, entry->interval)) 4858c2ecf20Sopenharmony_ci goto nla_put_failure; 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci if (nla_put_s32(skb, TCA_GATE_ENTRY_MAX_OCTETS, entry->maxoctets)) 4888c2ecf20Sopenharmony_ci goto nla_put_failure; 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci if (nla_put_s32(skb, TCA_GATE_ENTRY_IPV, entry->ipv)) 4918c2ecf20Sopenharmony_ci goto nla_put_failure; 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci return nla_nest_end(skb, item); 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_cinla_put_failure: 4968c2ecf20Sopenharmony_ci nla_nest_cancel(skb, item); 4978c2ecf20Sopenharmony_ci return -1; 4988c2ecf20Sopenharmony_ci} 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_cistatic int tcf_gate_dump(struct sk_buff *skb, struct tc_action *a, 5018c2ecf20Sopenharmony_ci int bind, int ref) 5028c2ecf20Sopenharmony_ci{ 5038c2ecf20Sopenharmony_ci unsigned char *b = skb_tail_pointer(skb); 5048c2ecf20Sopenharmony_ci struct tcf_gate *gact = to_gate(a); 5058c2ecf20Sopenharmony_ci struct tc_gate opt = { 5068c2ecf20Sopenharmony_ci .index = gact->tcf_index, 5078c2ecf20Sopenharmony_ci .refcnt = refcount_read(&gact->tcf_refcnt) - ref, 5088c2ecf20Sopenharmony_ci .bindcnt = atomic_read(&gact->tcf_bindcnt) - bind, 5098c2ecf20Sopenharmony_ci }; 5108c2ecf20Sopenharmony_ci struct tcfg_gate_entry *entry; 5118c2ecf20Sopenharmony_ci struct tcf_gate_params *p; 5128c2ecf20Sopenharmony_ci struct nlattr *entry_list; 5138c2ecf20Sopenharmony_ci struct tcf_t t; 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci spin_lock_bh(&gact->tcf_lock); 5168c2ecf20Sopenharmony_ci opt.action = gact->tcf_action; 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci p = &gact->param; 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci if (nla_put(skb, TCA_GATE_PARMS, sizeof(opt), &opt)) 5218c2ecf20Sopenharmony_ci goto nla_put_failure; 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci if (nla_put_u64_64bit(skb, TCA_GATE_BASE_TIME, 5248c2ecf20Sopenharmony_ci p->tcfg_basetime, TCA_GATE_PAD)) 5258c2ecf20Sopenharmony_ci goto nla_put_failure; 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci if (nla_put_u64_64bit(skb, TCA_GATE_CYCLE_TIME, 5288c2ecf20Sopenharmony_ci p->tcfg_cycletime, TCA_GATE_PAD)) 5298c2ecf20Sopenharmony_ci goto nla_put_failure; 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci if (nla_put_u64_64bit(skb, TCA_GATE_CYCLE_TIME_EXT, 5328c2ecf20Sopenharmony_ci p->tcfg_cycletime_ext, TCA_GATE_PAD)) 5338c2ecf20Sopenharmony_ci goto nla_put_failure; 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci if (nla_put_s32(skb, TCA_GATE_CLOCKID, p->tcfg_clockid)) 5368c2ecf20Sopenharmony_ci goto nla_put_failure; 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci if (nla_put_u32(skb, TCA_GATE_FLAGS, p->tcfg_flags)) 5398c2ecf20Sopenharmony_ci goto nla_put_failure; 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci if (nla_put_s32(skb, TCA_GATE_PRIORITY, p->tcfg_priority)) 5428c2ecf20Sopenharmony_ci goto nla_put_failure; 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci entry_list = nla_nest_start_noflag(skb, TCA_GATE_ENTRY_LIST); 5458c2ecf20Sopenharmony_ci if (!entry_list) 5468c2ecf20Sopenharmony_ci goto nla_put_failure; 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci list_for_each_entry(entry, &p->entries, list) { 5498c2ecf20Sopenharmony_ci if (dumping_entry(skb, entry) < 0) 5508c2ecf20Sopenharmony_ci goto nla_put_failure; 5518c2ecf20Sopenharmony_ci } 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci nla_nest_end(skb, entry_list); 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci tcf_tm_dump(&t, &gact->tcf_tm); 5568c2ecf20Sopenharmony_ci if (nla_put_64bit(skb, TCA_GATE_TM, sizeof(t), &t, TCA_GATE_PAD)) 5578c2ecf20Sopenharmony_ci goto nla_put_failure; 5588c2ecf20Sopenharmony_ci spin_unlock_bh(&gact->tcf_lock); 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci return skb->len; 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_cinla_put_failure: 5638c2ecf20Sopenharmony_ci spin_unlock_bh(&gact->tcf_lock); 5648c2ecf20Sopenharmony_ci nlmsg_trim(skb, b); 5658c2ecf20Sopenharmony_ci return -1; 5668c2ecf20Sopenharmony_ci} 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_cistatic int tcf_gate_walker(struct net *net, struct sk_buff *skb, 5698c2ecf20Sopenharmony_ci struct netlink_callback *cb, int type, 5708c2ecf20Sopenharmony_ci const struct tc_action_ops *ops, 5718c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 5728c2ecf20Sopenharmony_ci{ 5738c2ecf20Sopenharmony_ci struct tc_action_net *tn = net_generic(net, gate_net_id); 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci return tcf_generic_walker(tn, skb, cb, type, ops, extack); 5768c2ecf20Sopenharmony_ci} 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_cistatic void tcf_gate_stats_update(struct tc_action *a, u64 bytes, u64 packets, 5798c2ecf20Sopenharmony_ci u64 drops, u64 lastuse, bool hw) 5808c2ecf20Sopenharmony_ci{ 5818c2ecf20Sopenharmony_ci struct tcf_gate *gact = to_gate(a); 5828c2ecf20Sopenharmony_ci struct tcf_t *tm = &gact->tcf_tm; 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci tcf_action_update_stats(a, bytes, packets, drops, hw); 5858c2ecf20Sopenharmony_ci tm->lastuse = max_t(u64, tm->lastuse, lastuse); 5868c2ecf20Sopenharmony_ci} 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_cistatic int tcf_gate_search(struct net *net, struct tc_action **a, u32 index) 5898c2ecf20Sopenharmony_ci{ 5908c2ecf20Sopenharmony_ci struct tc_action_net *tn = net_generic(net, gate_net_id); 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci return tcf_idr_search(tn, a, index); 5938c2ecf20Sopenharmony_ci} 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_cistatic size_t tcf_gate_get_fill_size(const struct tc_action *act) 5968c2ecf20Sopenharmony_ci{ 5978c2ecf20Sopenharmony_ci return nla_total_size(sizeof(struct tc_gate)); 5988c2ecf20Sopenharmony_ci} 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_cistatic struct tc_action_ops act_gate_ops = { 6018c2ecf20Sopenharmony_ci .kind = "gate", 6028c2ecf20Sopenharmony_ci .id = TCA_ID_GATE, 6038c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 6048c2ecf20Sopenharmony_ci .act = tcf_gate_act, 6058c2ecf20Sopenharmony_ci .dump = tcf_gate_dump, 6068c2ecf20Sopenharmony_ci .init = tcf_gate_init, 6078c2ecf20Sopenharmony_ci .cleanup = tcf_gate_cleanup, 6088c2ecf20Sopenharmony_ci .walk = tcf_gate_walker, 6098c2ecf20Sopenharmony_ci .stats_update = tcf_gate_stats_update, 6108c2ecf20Sopenharmony_ci .get_fill_size = tcf_gate_get_fill_size, 6118c2ecf20Sopenharmony_ci .lookup = tcf_gate_search, 6128c2ecf20Sopenharmony_ci .size = sizeof(struct tcf_gate), 6138c2ecf20Sopenharmony_ci}; 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_cistatic __net_init int gate_init_net(struct net *net) 6168c2ecf20Sopenharmony_ci{ 6178c2ecf20Sopenharmony_ci struct tc_action_net *tn = net_generic(net, gate_net_id); 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci return tc_action_net_init(net, tn, &act_gate_ops); 6208c2ecf20Sopenharmony_ci} 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_cistatic void __net_exit gate_exit_net(struct list_head *net_list) 6238c2ecf20Sopenharmony_ci{ 6248c2ecf20Sopenharmony_ci tc_action_net_exit(net_list, gate_net_id); 6258c2ecf20Sopenharmony_ci} 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_cistatic struct pernet_operations gate_net_ops = { 6288c2ecf20Sopenharmony_ci .init = gate_init_net, 6298c2ecf20Sopenharmony_ci .exit_batch = gate_exit_net, 6308c2ecf20Sopenharmony_ci .id = &gate_net_id, 6318c2ecf20Sopenharmony_ci .size = sizeof(struct tc_action_net), 6328c2ecf20Sopenharmony_ci}; 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_cistatic int __init gate_init_module(void) 6358c2ecf20Sopenharmony_ci{ 6368c2ecf20Sopenharmony_ci return tcf_register_action(&act_gate_ops, &gate_net_ops); 6378c2ecf20Sopenharmony_ci} 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_cistatic void __exit gate_cleanup_module(void) 6408c2ecf20Sopenharmony_ci{ 6418c2ecf20Sopenharmony_ci tcf_unregister_action(&act_gate_ops, &gate_net_ops); 6428c2ecf20Sopenharmony_ci} 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_cimodule_init(gate_init_module); 6458c2ecf20Sopenharmony_cimodule_exit(gate_cleanup_module); 6468c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 647