xref: /kernel/linux/linux-5.10/net/sched/act_gate.c (revision 8c2ecf20)
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