162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci/* net/sched/sch_taprio.c	 Time Aware Priority Scheduler
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Authors:	Vinicius Costa Gomes <vinicius.gomes@intel.com>
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/ethtool.h>
1062306a36Sopenharmony_ci#include <linux/ethtool_netlink.h>
1162306a36Sopenharmony_ci#include <linux/types.h>
1262306a36Sopenharmony_ci#include <linux/slab.h>
1362306a36Sopenharmony_ci#include <linux/kernel.h>
1462306a36Sopenharmony_ci#include <linux/string.h>
1562306a36Sopenharmony_ci#include <linux/list.h>
1662306a36Sopenharmony_ci#include <linux/errno.h>
1762306a36Sopenharmony_ci#include <linux/skbuff.h>
1862306a36Sopenharmony_ci#include <linux/math64.h>
1962306a36Sopenharmony_ci#include <linux/module.h>
2062306a36Sopenharmony_ci#include <linux/spinlock.h>
2162306a36Sopenharmony_ci#include <linux/rcupdate.h>
2262306a36Sopenharmony_ci#include <linux/time.h>
2362306a36Sopenharmony_ci#include <net/gso.h>
2462306a36Sopenharmony_ci#include <net/netlink.h>
2562306a36Sopenharmony_ci#include <net/pkt_sched.h>
2662306a36Sopenharmony_ci#include <net/pkt_cls.h>
2762306a36Sopenharmony_ci#include <net/sch_generic.h>
2862306a36Sopenharmony_ci#include <net/sock.h>
2962306a36Sopenharmony_ci#include <net/tcp.h>
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#define TAPRIO_STAT_NOT_SET	(~0ULL)
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci#include "sch_mqprio_lib.h"
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_cistatic LIST_HEAD(taprio_list);
3662306a36Sopenharmony_cistatic struct static_key_false taprio_have_broken_mqprio;
3762306a36Sopenharmony_cistatic struct static_key_false taprio_have_working_mqprio;
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci#define TAPRIO_ALL_GATES_OPEN -1
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci#define TXTIME_ASSIST_IS_ENABLED(flags) ((flags) & TCA_TAPRIO_ATTR_FLAG_TXTIME_ASSIST)
4262306a36Sopenharmony_ci#define FULL_OFFLOAD_IS_ENABLED(flags) ((flags) & TCA_TAPRIO_ATTR_FLAG_FULL_OFFLOAD)
4362306a36Sopenharmony_ci#define TAPRIO_FLAGS_INVALID U32_MAX
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_cistruct sched_entry {
4662306a36Sopenharmony_ci	/* Durations between this GCL entry and the GCL entry where the
4762306a36Sopenharmony_ci	 * respective traffic class gate closes
4862306a36Sopenharmony_ci	 */
4962306a36Sopenharmony_ci	u64 gate_duration[TC_MAX_QUEUE];
5062306a36Sopenharmony_ci	atomic_t budget[TC_MAX_QUEUE];
5162306a36Sopenharmony_ci	/* The qdisc makes some effort so that no packet leaves
5262306a36Sopenharmony_ci	 * after this time
5362306a36Sopenharmony_ci	 */
5462306a36Sopenharmony_ci	ktime_t gate_close_time[TC_MAX_QUEUE];
5562306a36Sopenharmony_ci	struct list_head list;
5662306a36Sopenharmony_ci	/* Used to calculate when to advance the schedule */
5762306a36Sopenharmony_ci	ktime_t end_time;
5862306a36Sopenharmony_ci	ktime_t next_txtime;
5962306a36Sopenharmony_ci	int index;
6062306a36Sopenharmony_ci	u32 gate_mask;
6162306a36Sopenharmony_ci	u32 interval;
6262306a36Sopenharmony_ci	u8 command;
6362306a36Sopenharmony_ci};
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_cistruct sched_gate_list {
6662306a36Sopenharmony_ci	/* Longest non-zero contiguous gate durations per traffic class,
6762306a36Sopenharmony_ci	 * or 0 if a traffic class gate never opens during the schedule.
6862306a36Sopenharmony_ci	 */
6962306a36Sopenharmony_ci	u64 max_open_gate_duration[TC_MAX_QUEUE];
7062306a36Sopenharmony_ci	u32 max_frm_len[TC_MAX_QUEUE]; /* for the fast path */
7162306a36Sopenharmony_ci	u32 max_sdu[TC_MAX_QUEUE]; /* for dump */
7262306a36Sopenharmony_ci	struct rcu_head rcu;
7362306a36Sopenharmony_ci	struct list_head entries;
7462306a36Sopenharmony_ci	size_t num_entries;
7562306a36Sopenharmony_ci	ktime_t cycle_end_time;
7662306a36Sopenharmony_ci	s64 cycle_time;
7762306a36Sopenharmony_ci	s64 cycle_time_extension;
7862306a36Sopenharmony_ci	s64 base_time;
7962306a36Sopenharmony_ci};
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_cistruct taprio_sched {
8262306a36Sopenharmony_ci	struct Qdisc **qdiscs;
8362306a36Sopenharmony_ci	struct Qdisc *root;
8462306a36Sopenharmony_ci	u32 flags;
8562306a36Sopenharmony_ci	enum tk_offsets tk_offset;
8662306a36Sopenharmony_ci	int clockid;
8762306a36Sopenharmony_ci	bool offloaded;
8862306a36Sopenharmony_ci	bool detected_mqprio;
8962306a36Sopenharmony_ci	bool broken_mqprio;
9062306a36Sopenharmony_ci	atomic64_t picos_per_byte; /* Using picoseconds because for 10Gbps+
9162306a36Sopenharmony_ci				    * speeds it's sub-nanoseconds per byte
9262306a36Sopenharmony_ci				    */
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	/* Protects the update side of the RCU protected current_entry */
9562306a36Sopenharmony_ci	spinlock_t current_entry_lock;
9662306a36Sopenharmony_ci	struct sched_entry __rcu *current_entry;
9762306a36Sopenharmony_ci	struct sched_gate_list __rcu *oper_sched;
9862306a36Sopenharmony_ci	struct sched_gate_list __rcu *admin_sched;
9962306a36Sopenharmony_ci	struct hrtimer advance_timer;
10062306a36Sopenharmony_ci	struct list_head taprio_list;
10162306a36Sopenharmony_ci	int cur_txq[TC_MAX_QUEUE];
10262306a36Sopenharmony_ci	u32 max_sdu[TC_MAX_QUEUE]; /* save info from the user */
10362306a36Sopenharmony_ci	u32 fp[TC_QOPT_MAX_QUEUE]; /* only for dump and offloading */
10462306a36Sopenharmony_ci	u32 txtime_delay;
10562306a36Sopenharmony_ci};
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_cistruct __tc_taprio_qopt_offload {
10862306a36Sopenharmony_ci	refcount_t users;
10962306a36Sopenharmony_ci	struct tc_taprio_qopt_offload offload;
11062306a36Sopenharmony_ci};
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_cistatic void taprio_calculate_gate_durations(struct taprio_sched *q,
11362306a36Sopenharmony_ci					    struct sched_gate_list *sched)
11462306a36Sopenharmony_ci{
11562306a36Sopenharmony_ci	struct net_device *dev = qdisc_dev(q->root);
11662306a36Sopenharmony_ci	int num_tc = netdev_get_num_tc(dev);
11762306a36Sopenharmony_ci	struct sched_entry *entry, *cur;
11862306a36Sopenharmony_ci	int tc;
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	list_for_each_entry(entry, &sched->entries, list) {
12162306a36Sopenharmony_ci		u32 gates_still_open = entry->gate_mask;
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci		/* For each traffic class, calculate each open gate duration,
12462306a36Sopenharmony_ci		 * starting at this schedule entry and ending at the schedule
12562306a36Sopenharmony_ci		 * entry containing a gate close event for that TC.
12662306a36Sopenharmony_ci		 */
12762306a36Sopenharmony_ci		cur = entry;
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci		do {
13062306a36Sopenharmony_ci			if (!gates_still_open)
13162306a36Sopenharmony_ci				break;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci			for (tc = 0; tc < num_tc; tc++) {
13462306a36Sopenharmony_ci				if (!(gates_still_open & BIT(tc)))
13562306a36Sopenharmony_ci					continue;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci				if (cur->gate_mask & BIT(tc))
13862306a36Sopenharmony_ci					entry->gate_duration[tc] += cur->interval;
13962306a36Sopenharmony_ci				else
14062306a36Sopenharmony_ci					gates_still_open &= ~BIT(tc);
14162306a36Sopenharmony_ci			}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci			cur = list_next_entry_circular(cur, &sched->entries, list);
14462306a36Sopenharmony_ci		} while (cur != entry);
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci		/* Keep track of the maximum gate duration for each traffic
14762306a36Sopenharmony_ci		 * class, taking care to not confuse a traffic class which is
14862306a36Sopenharmony_ci		 * temporarily closed with one that is always closed.
14962306a36Sopenharmony_ci		 */
15062306a36Sopenharmony_ci		for (tc = 0; tc < num_tc; tc++)
15162306a36Sopenharmony_ci			if (entry->gate_duration[tc] &&
15262306a36Sopenharmony_ci			    sched->max_open_gate_duration[tc] < entry->gate_duration[tc])
15362306a36Sopenharmony_ci				sched->max_open_gate_duration[tc] = entry->gate_duration[tc];
15462306a36Sopenharmony_ci	}
15562306a36Sopenharmony_ci}
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_cistatic bool taprio_entry_allows_tx(ktime_t skb_end_time,
15862306a36Sopenharmony_ci				   struct sched_entry *entry, int tc)
15962306a36Sopenharmony_ci{
16062306a36Sopenharmony_ci	return ktime_before(skb_end_time, entry->gate_close_time[tc]);
16162306a36Sopenharmony_ci}
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_cistatic ktime_t sched_base_time(const struct sched_gate_list *sched)
16462306a36Sopenharmony_ci{
16562306a36Sopenharmony_ci	if (!sched)
16662306a36Sopenharmony_ci		return KTIME_MAX;
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	return ns_to_ktime(sched->base_time);
16962306a36Sopenharmony_ci}
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_cistatic ktime_t taprio_mono_to_any(const struct taprio_sched *q, ktime_t mono)
17262306a36Sopenharmony_ci{
17362306a36Sopenharmony_ci	/* This pairs with WRITE_ONCE() in taprio_parse_clockid() */
17462306a36Sopenharmony_ci	enum tk_offsets tk_offset = READ_ONCE(q->tk_offset);
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	switch (tk_offset) {
17762306a36Sopenharmony_ci	case TK_OFFS_MAX:
17862306a36Sopenharmony_ci		return mono;
17962306a36Sopenharmony_ci	default:
18062306a36Sopenharmony_ci		return ktime_mono_to_any(mono, tk_offset);
18162306a36Sopenharmony_ci	}
18262306a36Sopenharmony_ci}
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_cistatic ktime_t taprio_get_time(const struct taprio_sched *q)
18562306a36Sopenharmony_ci{
18662306a36Sopenharmony_ci	return taprio_mono_to_any(q, ktime_get());
18762306a36Sopenharmony_ci}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_cistatic void taprio_free_sched_cb(struct rcu_head *head)
19062306a36Sopenharmony_ci{
19162306a36Sopenharmony_ci	struct sched_gate_list *sched = container_of(head, struct sched_gate_list, rcu);
19262306a36Sopenharmony_ci	struct sched_entry *entry, *n;
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	list_for_each_entry_safe(entry, n, &sched->entries, list) {
19562306a36Sopenharmony_ci		list_del(&entry->list);
19662306a36Sopenharmony_ci		kfree(entry);
19762306a36Sopenharmony_ci	}
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	kfree(sched);
20062306a36Sopenharmony_ci}
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_cistatic void switch_schedules(struct taprio_sched *q,
20362306a36Sopenharmony_ci			     struct sched_gate_list **admin,
20462306a36Sopenharmony_ci			     struct sched_gate_list **oper)
20562306a36Sopenharmony_ci{
20662306a36Sopenharmony_ci	rcu_assign_pointer(q->oper_sched, *admin);
20762306a36Sopenharmony_ci	rcu_assign_pointer(q->admin_sched, NULL);
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	if (*oper)
21062306a36Sopenharmony_ci		call_rcu(&(*oper)->rcu, taprio_free_sched_cb);
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	*oper = *admin;
21362306a36Sopenharmony_ci	*admin = NULL;
21462306a36Sopenharmony_ci}
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci/* Get how much time has been already elapsed in the current cycle. */
21762306a36Sopenharmony_cistatic s32 get_cycle_time_elapsed(struct sched_gate_list *sched, ktime_t time)
21862306a36Sopenharmony_ci{
21962306a36Sopenharmony_ci	ktime_t time_since_sched_start;
22062306a36Sopenharmony_ci	s32 time_elapsed;
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	time_since_sched_start = ktime_sub(time, sched->base_time);
22362306a36Sopenharmony_ci	div_s64_rem(time_since_sched_start, sched->cycle_time, &time_elapsed);
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	return time_elapsed;
22662306a36Sopenharmony_ci}
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_cistatic ktime_t get_interval_end_time(struct sched_gate_list *sched,
22962306a36Sopenharmony_ci				     struct sched_gate_list *admin,
23062306a36Sopenharmony_ci				     struct sched_entry *entry,
23162306a36Sopenharmony_ci				     ktime_t intv_start)
23262306a36Sopenharmony_ci{
23362306a36Sopenharmony_ci	s32 cycle_elapsed = get_cycle_time_elapsed(sched, intv_start);
23462306a36Sopenharmony_ci	ktime_t intv_end, cycle_ext_end, cycle_end;
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	cycle_end = ktime_add_ns(intv_start, sched->cycle_time - cycle_elapsed);
23762306a36Sopenharmony_ci	intv_end = ktime_add_ns(intv_start, entry->interval);
23862306a36Sopenharmony_ci	cycle_ext_end = ktime_add(cycle_end, sched->cycle_time_extension);
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	if (ktime_before(intv_end, cycle_end))
24162306a36Sopenharmony_ci		return intv_end;
24262306a36Sopenharmony_ci	else if (admin && admin != sched &&
24362306a36Sopenharmony_ci		 ktime_after(admin->base_time, cycle_end) &&
24462306a36Sopenharmony_ci		 ktime_before(admin->base_time, cycle_ext_end))
24562306a36Sopenharmony_ci		return admin->base_time;
24662306a36Sopenharmony_ci	else
24762306a36Sopenharmony_ci		return cycle_end;
24862306a36Sopenharmony_ci}
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_cistatic int length_to_duration(struct taprio_sched *q, int len)
25162306a36Sopenharmony_ci{
25262306a36Sopenharmony_ci	return div_u64(len * atomic64_read(&q->picos_per_byte), PSEC_PER_NSEC);
25362306a36Sopenharmony_ci}
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_cistatic int duration_to_length(struct taprio_sched *q, u64 duration)
25662306a36Sopenharmony_ci{
25762306a36Sopenharmony_ci	return div_u64(duration * PSEC_PER_NSEC, atomic64_read(&q->picos_per_byte));
25862306a36Sopenharmony_ci}
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci/* Sets sched->max_sdu[] and sched->max_frm_len[] to the minimum between the
26162306a36Sopenharmony_ci * q->max_sdu[] requested by the user and the max_sdu dynamically determined by
26262306a36Sopenharmony_ci * the maximum open gate durations at the given link speed.
26362306a36Sopenharmony_ci */
26462306a36Sopenharmony_cistatic void taprio_update_queue_max_sdu(struct taprio_sched *q,
26562306a36Sopenharmony_ci					struct sched_gate_list *sched,
26662306a36Sopenharmony_ci					struct qdisc_size_table *stab)
26762306a36Sopenharmony_ci{
26862306a36Sopenharmony_ci	struct net_device *dev = qdisc_dev(q->root);
26962306a36Sopenharmony_ci	int num_tc = netdev_get_num_tc(dev);
27062306a36Sopenharmony_ci	u32 max_sdu_from_user;
27162306a36Sopenharmony_ci	u32 max_sdu_dynamic;
27262306a36Sopenharmony_ci	u32 max_sdu;
27362306a36Sopenharmony_ci	int tc;
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	for (tc = 0; tc < num_tc; tc++) {
27662306a36Sopenharmony_ci		max_sdu_from_user = q->max_sdu[tc] ?: U32_MAX;
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci		/* TC gate never closes => keep the queueMaxSDU
27962306a36Sopenharmony_ci		 * selected by the user
28062306a36Sopenharmony_ci		 */
28162306a36Sopenharmony_ci		if (sched->max_open_gate_duration[tc] == sched->cycle_time) {
28262306a36Sopenharmony_ci			max_sdu_dynamic = U32_MAX;
28362306a36Sopenharmony_ci		} else {
28462306a36Sopenharmony_ci			u32 max_frm_len;
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci			max_frm_len = duration_to_length(q, sched->max_open_gate_duration[tc]);
28762306a36Sopenharmony_ci			/* Compensate for L1 overhead from size table,
28862306a36Sopenharmony_ci			 * but don't let the frame size go negative
28962306a36Sopenharmony_ci			 */
29062306a36Sopenharmony_ci			if (stab) {
29162306a36Sopenharmony_ci				max_frm_len -= stab->szopts.overhead;
29262306a36Sopenharmony_ci				max_frm_len = max_t(int, max_frm_len,
29362306a36Sopenharmony_ci						    dev->hard_header_len + 1);
29462306a36Sopenharmony_ci			}
29562306a36Sopenharmony_ci			max_sdu_dynamic = max_frm_len - dev->hard_header_len;
29662306a36Sopenharmony_ci			if (max_sdu_dynamic > dev->max_mtu)
29762306a36Sopenharmony_ci				max_sdu_dynamic = U32_MAX;
29862306a36Sopenharmony_ci		}
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci		max_sdu = min(max_sdu_dynamic, max_sdu_from_user);
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci		if (max_sdu != U32_MAX) {
30362306a36Sopenharmony_ci			sched->max_frm_len[tc] = max_sdu + dev->hard_header_len;
30462306a36Sopenharmony_ci			sched->max_sdu[tc] = max_sdu;
30562306a36Sopenharmony_ci		} else {
30662306a36Sopenharmony_ci			sched->max_frm_len[tc] = U32_MAX; /* never oversized */
30762306a36Sopenharmony_ci			sched->max_sdu[tc] = 0;
30862306a36Sopenharmony_ci		}
30962306a36Sopenharmony_ci	}
31062306a36Sopenharmony_ci}
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci/* Returns the entry corresponding to next available interval. If
31362306a36Sopenharmony_ci * validate_interval is set, it only validates whether the timestamp occurs
31462306a36Sopenharmony_ci * when the gate corresponding to the skb's traffic class is open.
31562306a36Sopenharmony_ci */
31662306a36Sopenharmony_cistatic struct sched_entry *find_entry_to_transmit(struct sk_buff *skb,
31762306a36Sopenharmony_ci						  struct Qdisc *sch,
31862306a36Sopenharmony_ci						  struct sched_gate_list *sched,
31962306a36Sopenharmony_ci						  struct sched_gate_list *admin,
32062306a36Sopenharmony_ci						  ktime_t time,
32162306a36Sopenharmony_ci						  ktime_t *interval_start,
32262306a36Sopenharmony_ci						  ktime_t *interval_end,
32362306a36Sopenharmony_ci						  bool validate_interval)
32462306a36Sopenharmony_ci{
32562306a36Sopenharmony_ci	ktime_t curr_intv_start, curr_intv_end, cycle_end, packet_transmit_time;
32662306a36Sopenharmony_ci	ktime_t earliest_txtime = KTIME_MAX, txtime, cycle, transmit_end_time;
32762306a36Sopenharmony_ci	struct sched_entry *entry = NULL, *entry_found = NULL;
32862306a36Sopenharmony_ci	struct taprio_sched *q = qdisc_priv(sch);
32962306a36Sopenharmony_ci	struct net_device *dev = qdisc_dev(sch);
33062306a36Sopenharmony_ci	bool entry_available = false;
33162306a36Sopenharmony_ci	s32 cycle_elapsed;
33262306a36Sopenharmony_ci	int tc, n;
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	tc = netdev_get_prio_tc_map(dev, skb->priority);
33562306a36Sopenharmony_ci	packet_transmit_time = length_to_duration(q, qdisc_pkt_len(skb));
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	*interval_start = 0;
33862306a36Sopenharmony_ci	*interval_end = 0;
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	if (!sched)
34162306a36Sopenharmony_ci		return NULL;
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	cycle = sched->cycle_time;
34462306a36Sopenharmony_ci	cycle_elapsed = get_cycle_time_elapsed(sched, time);
34562306a36Sopenharmony_ci	curr_intv_end = ktime_sub_ns(time, cycle_elapsed);
34662306a36Sopenharmony_ci	cycle_end = ktime_add_ns(curr_intv_end, cycle);
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	list_for_each_entry(entry, &sched->entries, list) {
34962306a36Sopenharmony_ci		curr_intv_start = curr_intv_end;
35062306a36Sopenharmony_ci		curr_intv_end = get_interval_end_time(sched, admin, entry,
35162306a36Sopenharmony_ci						      curr_intv_start);
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci		if (ktime_after(curr_intv_start, cycle_end))
35462306a36Sopenharmony_ci			break;
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci		if (!(entry->gate_mask & BIT(tc)) ||
35762306a36Sopenharmony_ci		    packet_transmit_time > entry->interval)
35862306a36Sopenharmony_ci			continue;
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci		txtime = entry->next_txtime;
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci		if (ktime_before(txtime, time) || validate_interval) {
36362306a36Sopenharmony_ci			transmit_end_time = ktime_add_ns(time, packet_transmit_time);
36462306a36Sopenharmony_ci			if ((ktime_before(curr_intv_start, time) &&
36562306a36Sopenharmony_ci			     ktime_before(transmit_end_time, curr_intv_end)) ||
36662306a36Sopenharmony_ci			    (ktime_after(curr_intv_start, time) && !validate_interval)) {
36762306a36Sopenharmony_ci				entry_found = entry;
36862306a36Sopenharmony_ci				*interval_start = curr_intv_start;
36962306a36Sopenharmony_ci				*interval_end = curr_intv_end;
37062306a36Sopenharmony_ci				break;
37162306a36Sopenharmony_ci			} else if (!entry_available && !validate_interval) {
37262306a36Sopenharmony_ci				/* Here, we are just trying to find out the
37362306a36Sopenharmony_ci				 * first available interval in the next cycle.
37462306a36Sopenharmony_ci				 */
37562306a36Sopenharmony_ci				entry_available = true;
37662306a36Sopenharmony_ci				entry_found = entry;
37762306a36Sopenharmony_ci				*interval_start = ktime_add_ns(curr_intv_start, cycle);
37862306a36Sopenharmony_ci				*interval_end = ktime_add_ns(curr_intv_end, cycle);
37962306a36Sopenharmony_ci			}
38062306a36Sopenharmony_ci		} else if (ktime_before(txtime, earliest_txtime) &&
38162306a36Sopenharmony_ci			   !entry_available) {
38262306a36Sopenharmony_ci			earliest_txtime = txtime;
38362306a36Sopenharmony_ci			entry_found = entry;
38462306a36Sopenharmony_ci			n = div_s64(ktime_sub(txtime, curr_intv_start), cycle);
38562306a36Sopenharmony_ci			*interval_start = ktime_add(curr_intv_start, n * cycle);
38662306a36Sopenharmony_ci			*interval_end = ktime_add(curr_intv_end, n * cycle);
38762306a36Sopenharmony_ci		}
38862306a36Sopenharmony_ci	}
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	return entry_found;
39162306a36Sopenharmony_ci}
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_cistatic bool is_valid_interval(struct sk_buff *skb, struct Qdisc *sch)
39462306a36Sopenharmony_ci{
39562306a36Sopenharmony_ci	struct taprio_sched *q = qdisc_priv(sch);
39662306a36Sopenharmony_ci	struct sched_gate_list *sched, *admin;
39762306a36Sopenharmony_ci	ktime_t interval_start, interval_end;
39862306a36Sopenharmony_ci	struct sched_entry *entry;
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	rcu_read_lock();
40162306a36Sopenharmony_ci	sched = rcu_dereference(q->oper_sched);
40262306a36Sopenharmony_ci	admin = rcu_dereference(q->admin_sched);
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	entry = find_entry_to_transmit(skb, sch, sched, admin, skb->tstamp,
40562306a36Sopenharmony_ci				       &interval_start, &interval_end, true);
40662306a36Sopenharmony_ci	rcu_read_unlock();
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	return entry;
40962306a36Sopenharmony_ci}
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_cistatic bool taprio_flags_valid(u32 flags)
41262306a36Sopenharmony_ci{
41362306a36Sopenharmony_ci	/* Make sure no other flag bits are set. */
41462306a36Sopenharmony_ci	if (flags & ~(TCA_TAPRIO_ATTR_FLAG_TXTIME_ASSIST |
41562306a36Sopenharmony_ci		      TCA_TAPRIO_ATTR_FLAG_FULL_OFFLOAD))
41662306a36Sopenharmony_ci		return false;
41762306a36Sopenharmony_ci	/* txtime-assist and full offload are mutually exclusive */
41862306a36Sopenharmony_ci	if ((flags & TCA_TAPRIO_ATTR_FLAG_TXTIME_ASSIST) &&
41962306a36Sopenharmony_ci	    (flags & TCA_TAPRIO_ATTR_FLAG_FULL_OFFLOAD))
42062306a36Sopenharmony_ci		return false;
42162306a36Sopenharmony_ci	return true;
42262306a36Sopenharmony_ci}
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci/* This returns the tstamp value set by TCP in terms of the set clock. */
42562306a36Sopenharmony_cistatic ktime_t get_tcp_tstamp(struct taprio_sched *q, struct sk_buff *skb)
42662306a36Sopenharmony_ci{
42762306a36Sopenharmony_ci	unsigned int offset = skb_network_offset(skb);
42862306a36Sopenharmony_ci	const struct ipv6hdr *ipv6h;
42962306a36Sopenharmony_ci	const struct iphdr *iph;
43062306a36Sopenharmony_ci	struct ipv6hdr _ipv6h;
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	ipv6h = skb_header_pointer(skb, offset, sizeof(_ipv6h), &_ipv6h);
43362306a36Sopenharmony_ci	if (!ipv6h)
43462306a36Sopenharmony_ci		return 0;
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	if (ipv6h->version == 4) {
43762306a36Sopenharmony_ci		iph = (struct iphdr *)ipv6h;
43862306a36Sopenharmony_ci		offset += iph->ihl * 4;
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci		/* special-case 6in4 tunnelling, as that is a common way to get
44162306a36Sopenharmony_ci		 * v6 connectivity in the home
44262306a36Sopenharmony_ci		 */
44362306a36Sopenharmony_ci		if (iph->protocol == IPPROTO_IPV6) {
44462306a36Sopenharmony_ci			ipv6h = skb_header_pointer(skb, offset,
44562306a36Sopenharmony_ci						   sizeof(_ipv6h), &_ipv6h);
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci			if (!ipv6h || ipv6h->nexthdr != IPPROTO_TCP)
44862306a36Sopenharmony_ci				return 0;
44962306a36Sopenharmony_ci		} else if (iph->protocol != IPPROTO_TCP) {
45062306a36Sopenharmony_ci			return 0;
45162306a36Sopenharmony_ci		}
45262306a36Sopenharmony_ci	} else if (ipv6h->version == 6 && ipv6h->nexthdr != IPPROTO_TCP) {
45362306a36Sopenharmony_ci		return 0;
45462306a36Sopenharmony_ci	}
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	return taprio_mono_to_any(q, skb->skb_mstamp_ns);
45762306a36Sopenharmony_ci}
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci/* There are a few scenarios where we will have to modify the txtime from
46062306a36Sopenharmony_ci * what is read from next_txtime in sched_entry. They are:
46162306a36Sopenharmony_ci * 1. If txtime is in the past,
46262306a36Sopenharmony_ci *    a. The gate for the traffic class is currently open and packet can be
46362306a36Sopenharmony_ci *       transmitted before it closes, schedule the packet right away.
46462306a36Sopenharmony_ci *    b. If the gate corresponding to the traffic class is going to open later
46562306a36Sopenharmony_ci *       in the cycle, set the txtime of packet to the interval start.
46662306a36Sopenharmony_ci * 2. If txtime is in the future, there are packets corresponding to the
46762306a36Sopenharmony_ci *    current traffic class waiting to be transmitted. So, the following
46862306a36Sopenharmony_ci *    possibilities exist:
46962306a36Sopenharmony_ci *    a. We can transmit the packet before the window containing the txtime
47062306a36Sopenharmony_ci *       closes.
47162306a36Sopenharmony_ci *    b. The window might close before the transmission can be completed
47262306a36Sopenharmony_ci *       successfully. So, schedule the packet in the next open window.
47362306a36Sopenharmony_ci */
47462306a36Sopenharmony_cistatic long get_packet_txtime(struct sk_buff *skb, struct Qdisc *sch)
47562306a36Sopenharmony_ci{
47662306a36Sopenharmony_ci	ktime_t transmit_end_time, interval_end, interval_start, tcp_tstamp;
47762306a36Sopenharmony_ci	struct taprio_sched *q = qdisc_priv(sch);
47862306a36Sopenharmony_ci	struct sched_gate_list *sched, *admin;
47962306a36Sopenharmony_ci	ktime_t minimum_time, now, txtime;
48062306a36Sopenharmony_ci	int len, packet_transmit_time;
48162306a36Sopenharmony_ci	struct sched_entry *entry;
48262306a36Sopenharmony_ci	bool sched_changed;
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	now = taprio_get_time(q);
48562306a36Sopenharmony_ci	minimum_time = ktime_add_ns(now, q->txtime_delay);
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	tcp_tstamp = get_tcp_tstamp(q, skb);
48862306a36Sopenharmony_ci	minimum_time = max_t(ktime_t, minimum_time, tcp_tstamp);
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	rcu_read_lock();
49162306a36Sopenharmony_ci	admin = rcu_dereference(q->admin_sched);
49262306a36Sopenharmony_ci	sched = rcu_dereference(q->oper_sched);
49362306a36Sopenharmony_ci	if (admin && ktime_after(minimum_time, admin->base_time))
49462306a36Sopenharmony_ci		switch_schedules(q, &admin, &sched);
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	/* Until the schedule starts, all the queues are open */
49762306a36Sopenharmony_ci	if (!sched || ktime_before(minimum_time, sched->base_time)) {
49862306a36Sopenharmony_ci		txtime = minimum_time;
49962306a36Sopenharmony_ci		goto done;
50062306a36Sopenharmony_ci	}
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	len = qdisc_pkt_len(skb);
50362306a36Sopenharmony_ci	packet_transmit_time = length_to_duration(q, len);
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci	do {
50662306a36Sopenharmony_ci		sched_changed = false;
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci		entry = find_entry_to_transmit(skb, sch, sched, admin,
50962306a36Sopenharmony_ci					       minimum_time,
51062306a36Sopenharmony_ci					       &interval_start, &interval_end,
51162306a36Sopenharmony_ci					       false);
51262306a36Sopenharmony_ci		if (!entry) {
51362306a36Sopenharmony_ci			txtime = 0;
51462306a36Sopenharmony_ci			goto done;
51562306a36Sopenharmony_ci		}
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci		txtime = entry->next_txtime;
51862306a36Sopenharmony_ci		txtime = max_t(ktime_t, txtime, minimum_time);
51962306a36Sopenharmony_ci		txtime = max_t(ktime_t, txtime, interval_start);
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci		if (admin && admin != sched &&
52262306a36Sopenharmony_ci		    ktime_after(txtime, admin->base_time)) {
52362306a36Sopenharmony_ci			sched = admin;
52462306a36Sopenharmony_ci			sched_changed = true;
52562306a36Sopenharmony_ci			continue;
52662306a36Sopenharmony_ci		}
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci		transmit_end_time = ktime_add(txtime, packet_transmit_time);
52962306a36Sopenharmony_ci		minimum_time = transmit_end_time;
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci		/* Update the txtime of current entry to the next time it's
53262306a36Sopenharmony_ci		 * interval starts.
53362306a36Sopenharmony_ci		 */
53462306a36Sopenharmony_ci		if (ktime_after(transmit_end_time, interval_end))
53562306a36Sopenharmony_ci			entry->next_txtime = ktime_add(interval_start, sched->cycle_time);
53662306a36Sopenharmony_ci	} while (sched_changed || ktime_after(transmit_end_time, interval_end));
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	entry->next_txtime = transmit_end_time;
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_cidone:
54162306a36Sopenharmony_ci	rcu_read_unlock();
54262306a36Sopenharmony_ci	return txtime;
54362306a36Sopenharmony_ci}
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci/* Devices with full offload are expected to honor this in hardware */
54662306a36Sopenharmony_cistatic bool taprio_skb_exceeds_queue_max_sdu(struct Qdisc *sch,
54762306a36Sopenharmony_ci					     struct sk_buff *skb)
54862306a36Sopenharmony_ci{
54962306a36Sopenharmony_ci	struct taprio_sched *q = qdisc_priv(sch);
55062306a36Sopenharmony_ci	struct net_device *dev = qdisc_dev(sch);
55162306a36Sopenharmony_ci	struct sched_gate_list *sched;
55262306a36Sopenharmony_ci	int prio = skb->priority;
55362306a36Sopenharmony_ci	bool exceeds = false;
55462306a36Sopenharmony_ci	u8 tc;
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	tc = netdev_get_prio_tc_map(dev, prio);
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci	rcu_read_lock();
55962306a36Sopenharmony_ci	sched = rcu_dereference(q->oper_sched);
56062306a36Sopenharmony_ci	if (sched && skb->len > sched->max_frm_len[tc])
56162306a36Sopenharmony_ci		exceeds = true;
56262306a36Sopenharmony_ci	rcu_read_unlock();
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci	return exceeds;
56562306a36Sopenharmony_ci}
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_cistatic int taprio_enqueue_one(struct sk_buff *skb, struct Qdisc *sch,
56862306a36Sopenharmony_ci			      struct Qdisc *child, struct sk_buff **to_free)
56962306a36Sopenharmony_ci{
57062306a36Sopenharmony_ci	struct taprio_sched *q = qdisc_priv(sch);
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	/* sk_flags are only safe to use on full sockets. */
57362306a36Sopenharmony_ci	if (skb->sk && sk_fullsock(skb->sk) && sock_flag(skb->sk, SOCK_TXTIME)) {
57462306a36Sopenharmony_ci		if (!is_valid_interval(skb, sch))
57562306a36Sopenharmony_ci			return qdisc_drop(skb, sch, to_free);
57662306a36Sopenharmony_ci	} else if (TXTIME_ASSIST_IS_ENABLED(q->flags)) {
57762306a36Sopenharmony_ci		skb->tstamp = get_packet_txtime(skb, sch);
57862306a36Sopenharmony_ci		if (!skb->tstamp)
57962306a36Sopenharmony_ci			return qdisc_drop(skb, sch, to_free);
58062306a36Sopenharmony_ci	}
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci	qdisc_qstats_backlog_inc(sch, skb);
58362306a36Sopenharmony_ci	sch->q.qlen++;
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci	return qdisc_enqueue(skb, child, to_free);
58662306a36Sopenharmony_ci}
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_cistatic int taprio_enqueue_segmented(struct sk_buff *skb, struct Qdisc *sch,
58962306a36Sopenharmony_ci				    struct Qdisc *child,
59062306a36Sopenharmony_ci				    struct sk_buff **to_free)
59162306a36Sopenharmony_ci{
59262306a36Sopenharmony_ci	unsigned int slen = 0, numsegs = 0, len = qdisc_pkt_len(skb);
59362306a36Sopenharmony_ci	netdev_features_t features = netif_skb_features(skb);
59462306a36Sopenharmony_ci	struct sk_buff *segs, *nskb;
59562306a36Sopenharmony_ci	int ret;
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci	segs = skb_gso_segment(skb, features & ~NETIF_F_GSO_MASK);
59862306a36Sopenharmony_ci	if (IS_ERR_OR_NULL(segs))
59962306a36Sopenharmony_ci		return qdisc_drop(skb, sch, to_free);
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci	skb_list_walk_safe(segs, segs, nskb) {
60262306a36Sopenharmony_ci		skb_mark_not_on_list(segs);
60362306a36Sopenharmony_ci		qdisc_skb_cb(segs)->pkt_len = segs->len;
60462306a36Sopenharmony_ci		slen += segs->len;
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci		/* FIXME: we should be segmenting to a smaller size
60762306a36Sopenharmony_ci		 * rather than dropping these
60862306a36Sopenharmony_ci		 */
60962306a36Sopenharmony_ci		if (taprio_skb_exceeds_queue_max_sdu(sch, segs))
61062306a36Sopenharmony_ci			ret = qdisc_drop(segs, sch, to_free);
61162306a36Sopenharmony_ci		else
61262306a36Sopenharmony_ci			ret = taprio_enqueue_one(segs, sch, child, to_free);
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci		if (ret != NET_XMIT_SUCCESS) {
61562306a36Sopenharmony_ci			if (net_xmit_drop_count(ret))
61662306a36Sopenharmony_ci				qdisc_qstats_drop(sch);
61762306a36Sopenharmony_ci		} else {
61862306a36Sopenharmony_ci			numsegs++;
61962306a36Sopenharmony_ci		}
62062306a36Sopenharmony_ci	}
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci	if (numsegs > 1)
62362306a36Sopenharmony_ci		qdisc_tree_reduce_backlog(sch, 1 - numsegs, len - slen);
62462306a36Sopenharmony_ci	consume_skb(skb);
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci	return numsegs > 0 ? NET_XMIT_SUCCESS : NET_XMIT_DROP;
62762306a36Sopenharmony_ci}
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci/* Will not be called in the full offload case, since the TX queues are
63062306a36Sopenharmony_ci * attached to the Qdisc created using qdisc_create_dflt()
63162306a36Sopenharmony_ci */
63262306a36Sopenharmony_cistatic int taprio_enqueue(struct sk_buff *skb, struct Qdisc *sch,
63362306a36Sopenharmony_ci			  struct sk_buff **to_free)
63462306a36Sopenharmony_ci{
63562306a36Sopenharmony_ci	struct taprio_sched *q = qdisc_priv(sch);
63662306a36Sopenharmony_ci	struct Qdisc *child;
63762306a36Sopenharmony_ci	int queue;
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci	queue = skb_get_queue_mapping(skb);
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci	child = q->qdiscs[queue];
64262306a36Sopenharmony_ci	if (unlikely(!child))
64362306a36Sopenharmony_ci		return qdisc_drop(skb, sch, to_free);
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci	if (taprio_skb_exceeds_queue_max_sdu(sch, skb)) {
64662306a36Sopenharmony_ci		/* Large packets might not be transmitted when the transmission
64762306a36Sopenharmony_ci		 * duration exceeds any configured interval. Therefore, segment
64862306a36Sopenharmony_ci		 * the skb into smaller chunks. Drivers with full offload are
64962306a36Sopenharmony_ci		 * expected to handle this in hardware.
65062306a36Sopenharmony_ci		 */
65162306a36Sopenharmony_ci		if (skb_is_gso(skb))
65262306a36Sopenharmony_ci			return taprio_enqueue_segmented(skb, sch, child,
65362306a36Sopenharmony_ci							to_free);
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci		return qdisc_drop(skb, sch, to_free);
65662306a36Sopenharmony_ci	}
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci	return taprio_enqueue_one(skb, sch, child, to_free);
65962306a36Sopenharmony_ci}
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_cistatic struct sk_buff *taprio_peek(struct Qdisc *sch)
66262306a36Sopenharmony_ci{
66362306a36Sopenharmony_ci	WARN_ONCE(1, "taprio only supports operating as root qdisc, peek() not implemented");
66462306a36Sopenharmony_ci	return NULL;
66562306a36Sopenharmony_ci}
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_cistatic void taprio_set_budgets(struct taprio_sched *q,
66862306a36Sopenharmony_ci			       struct sched_gate_list *sched,
66962306a36Sopenharmony_ci			       struct sched_entry *entry)
67062306a36Sopenharmony_ci{
67162306a36Sopenharmony_ci	struct net_device *dev = qdisc_dev(q->root);
67262306a36Sopenharmony_ci	int num_tc = netdev_get_num_tc(dev);
67362306a36Sopenharmony_ci	int tc, budget;
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci	for (tc = 0; tc < num_tc; tc++) {
67662306a36Sopenharmony_ci		/* Traffic classes which never close have infinite budget */
67762306a36Sopenharmony_ci		if (entry->gate_duration[tc] == sched->cycle_time)
67862306a36Sopenharmony_ci			budget = INT_MAX;
67962306a36Sopenharmony_ci		else
68062306a36Sopenharmony_ci			budget = div64_u64((u64)entry->gate_duration[tc] * PSEC_PER_NSEC,
68162306a36Sopenharmony_ci					   atomic64_read(&q->picos_per_byte));
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_ci		atomic_set(&entry->budget[tc], budget);
68462306a36Sopenharmony_ci	}
68562306a36Sopenharmony_ci}
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_ci/* When an skb is sent, it consumes from the budget of all traffic classes */
68862306a36Sopenharmony_cistatic int taprio_update_budgets(struct sched_entry *entry, size_t len,
68962306a36Sopenharmony_ci				 int tc_consumed, int num_tc)
69062306a36Sopenharmony_ci{
69162306a36Sopenharmony_ci	int tc, budget, new_budget = 0;
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci	for (tc = 0; tc < num_tc; tc++) {
69462306a36Sopenharmony_ci		budget = atomic_read(&entry->budget[tc]);
69562306a36Sopenharmony_ci		/* Don't consume from infinite budget */
69662306a36Sopenharmony_ci		if (budget == INT_MAX) {
69762306a36Sopenharmony_ci			if (tc == tc_consumed)
69862306a36Sopenharmony_ci				new_budget = budget;
69962306a36Sopenharmony_ci			continue;
70062306a36Sopenharmony_ci		}
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci		if (tc == tc_consumed)
70362306a36Sopenharmony_ci			new_budget = atomic_sub_return(len, &entry->budget[tc]);
70462306a36Sopenharmony_ci		else
70562306a36Sopenharmony_ci			atomic_sub(len, &entry->budget[tc]);
70662306a36Sopenharmony_ci	}
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ci	return new_budget;
70962306a36Sopenharmony_ci}
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_cistatic struct sk_buff *taprio_dequeue_from_txq(struct Qdisc *sch, int txq,
71262306a36Sopenharmony_ci					       struct sched_entry *entry,
71362306a36Sopenharmony_ci					       u32 gate_mask)
71462306a36Sopenharmony_ci{
71562306a36Sopenharmony_ci	struct taprio_sched *q = qdisc_priv(sch);
71662306a36Sopenharmony_ci	struct net_device *dev = qdisc_dev(sch);
71762306a36Sopenharmony_ci	struct Qdisc *child = q->qdiscs[txq];
71862306a36Sopenharmony_ci	int num_tc = netdev_get_num_tc(dev);
71962306a36Sopenharmony_ci	struct sk_buff *skb;
72062306a36Sopenharmony_ci	ktime_t guard;
72162306a36Sopenharmony_ci	int prio;
72262306a36Sopenharmony_ci	int len;
72362306a36Sopenharmony_ci	u8 tc;
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ci	if (unlikely(!child))
72662306a36Sopenharmony_ci		return NULL;
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci	if (TXTIME_ASSIST_IS_ENABLED(q->flags))
72962306a36Sopenharmony_ci		goto skip_peek_checks;
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci	skb = child->ops->peek(child);
73262306a36Sopenharmony_ci	if (!skb)
73362306a36Sopenharmony_ci		return NULL;
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ci	prio = skb->priority;
73662306a36Sopenharmony_ci	tc = netdev_get_prio_tc_map(dev, prio);
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_ci	if (!(gate_mask & BIT(tc)))
73962306a36Sopenharmony_ci		return NULL;
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_ci	len = qdisc_pkt_len(skb);
74262306a36Sopenharmony_ci	guard = ktime_add_ns(taprio_get_time(q), length_to_duration(q, len));
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci	/* In the case that there's no gate entry, there's no
74562306a36Sopenharmony_ci	 * guard band ...
74662306a36Sopenharmony_ci	 */
74762306a36Sopenharmony_ci	if (gate_mask != TAPRIO_ALL_GATES_OPEN &&
74862306a36Sopenharmony_ci	    !taprio_entry_allows_tx(guard, entry, tc))
74962306a36Sopenharmony_ci		return NULL;
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_ci	/* ... and no budget. */
75262306a36Sopenharmony_ci	if (gate_mask != TAPRIO_ALL_GATES_OPEN &&
75362306a36Sopenharmony_ci	    taprio_update_budgets(entry, len, tc, num_tc) < 0)
75462306a36Sopenharmony_ci		return NULL;
75562306a36Sopenharmony_ci
75662306a36Sopenharmony_ciskip_peek_checks:
75762306a36Sopenharmony_ci	skb = child->ops->dequeue(child);
75862306a36Sopenharmony_ci	if (unlikely(!skb))
75962306a36Sopenharmony_ci		return NULL;
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci	qdisc_bstats_update(sch, skb);
76262306a36Sopenharmony_ci	qdisc_qstats_backlog_dec(sch, skb);
76362306a36Sopenharmony_ci	sch->q.qlen--;
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci	return skb;
76662306a36Sopenharmony_ci}
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_cistatic void taprio_next_tc_txq(struct net_device *dev, int tc, int *txq)
76962306a36Sopenharmony_ci{
77062306a36Sopenharmony_ci	int offset = dev->tc_to_txq[tc].offset;
77162306a36Sopenharmony_ci	int count = dev->tc_to_txq[tc].count;
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_ci	(*txq)++;
77462306a36Sopenharmony_ci	if (*txq == offset + count)
77562306a36Sopenharmony_ci		*txq = offset;
77662306a36Sopenharmony_ci}
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ci/* Prioritize higher traffic classes, and select among TXQs belonging to the
77962306a36Sopenharmony_ci * same TC using round robin
78062306a36Sopenharmony_ci */
78162306a36Sopenharmony_cistatic struct sk_buff *taprio_dequeue_tc_priority(struct Qdisc *sch,
78262306a36Sopenharmony_ci						  struct sched_entry *entry,
78362306a36Sopenharmony_ci						  u32 gate_mask)
78462306a36Sopenharmony_ci{
78562306a36Sopenharmony_ci	struct taprio_sched *q = qdisc_priv(sch);
78662306a36Sopenharmony_ci	struct net_device *dev = qdisc_dev(sch);
78762306a36Sopenharmony_ci	int num_tc = netdev_get_num_tc(dev);
78862306a36Sopenharmony_ci	struct sk_buff *skb;
78962306a36Sopenharmony_ci	int tc;
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci	for (tc = num_tc - 1; tc >= 0; tc--) {
79262306a36Sopenharmony_ci		int first_txq = q->cur_txq[tc];
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci		if (!(gate_mask & BIT(tc)))
79562306a36Sopenharmony_ci			continue;
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_ci		do {
79862306a36Sopenharmony_ci			skb = taprio_dequeue_from_txq(sch, q->cur_txq[tc],
79962306a36Sopenharmony_ci						      entry, gate_mask);
80062306a36Sopenharmony_ci
80162306a36Sopenharmony_ci			taprio_next_tc_txq(dev, tc, &q->cur_txq[tc]);
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_ci			if (q->cur_txq[tc] >= dev->num_tx_queues)
80462306a36Sopenharmony_ci				q->cur_txq[tc] = first_txq;
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_ci			if (skb)
80762306a36Sopenharmony_ci				return skb;
80862306a36Sopenharmony_ci		} while (q->cur_txq[tc] != first_txq);
80962306a36Sopenharmony_ci	}
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_ci	return NULL;
81262306a36Sopenharmony_ci}
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_ci/* Broken way of prioritizing smaller TXQ indices and ignoring the traffic
81562306a36Sopenharmony_ci * class other than to determine whether the gate is open or not
81662306a36Sopenharmony_ci */
81762306a36Sopenharmony_cistatic struct sk_buff *taprio_dequeue_txq_priority(struct Qdisc *sch,
81862306a36Sopenharmony_ci						   struct sched_entry *entry,
81962306a36Sopenharmony_ci						   u32 gate_mask)
82062306a36Sopenharmony_ci{
82162306a36Sopenharmony_ci	struct net_device *dev = qdisc_dev(sch);
82262306a36Sopenharmony_ci	struct sk_buff *skb;
82362306a36Sopenharmony_ci	int i;
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ci	for (i = 0; i < dev->num_tx_queues; i++) {
82662306a36Sopenharmony_ci		skb = taprio_dequeue_from_txq(sch, i, entry, gate_mask);
82762306a36Sopenharmony_ci		if (skb)
82862306a36Sopenharmony_ci			return skb;
82962306a36Sopenharmony_ci	}
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_ci	return NULL;
83262306a36Sopenharmony_ci}
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_ci/* Will not be called in the full offload case, since the TX queues are
83562306a36Sopenharmony_ci * attached to the Qdisc created using qdisc_create_dflt()
83662306a36Sopenharmony_ci */
83762306a36Sopenharmony_cistatic struct sk_buff *taprio_dequeue(struct Qdisc *sch)
83862306a36Sopenharmony_ci{
83962306a36Sopenharmony_ci	struct taprio_sched *q = qdisc_priv(sch);
84062306a36Sopenharmony_ci	struct sk_buff *skb = NULL;
84162306a36Sopenharmony_ci	struct sched_entry *entry;
84262306a36Sopenharmony_ci	u32 gate_mask;
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci	rcu_read_lock();
84562306a36Sopenharmony_ci	entry = rcu_dereference(q->current_entry);
84662306a36Sopenharmony_ci	/* if there's no entry, it means that the schedule didn't
84762306a36Sopenharmony_ci	 * start yet, so force all gates to be open, this is in
84862306a36Sopenharmony_ci	 * accordance to IEEE 802.1Qbv-2015 Section 8.6.9.4.5
84962306a36Sopenharmony_ci	 * "AdminGateStates"
85062306a36Sopenharmony_ci	 */
85162306a36Sopenharmony_ci	gate_mask = entry ? entry->gate_mask : TAPRIO_ALL_GATES_OPEN;
85262306a36Sopenharmony_ci	if (!gate_mask)
85362306a36Sopenharmony_ci		goto done;
85462306a36Sopenharmony_ci
85562306a36Sopenharmony_ci	if (static_branch_unlikely(&taprio_have_broken_mqprio) &&
85662306a36Sopenharmony_ci	    !static_branch_likely(&taprio_have_working_mqprio)) {
85762306a36Sopenharmony_ci		/* Single NIC kind which is broken */
85862306a36Sopenharmony_ci		skb = taprio_dequeue_txq_priority(sch, entry, gate_mask);
85962306a36Sopenharmony_ci	} else if (static_branch_likely(&taprio_have_working_mqprio) &&
86062306a36Sopenharmony_ci		   !static_branch_unlikely(&taprio_have_broken_mqprio)) {
86162306a36Sopenharmony_ci		/* Single NIC kind which prioritizes properly */
86262306a36Sopenharmony_ci		skb = taprio_dequeue_tc_priority(sch, entry, gate_mask);
86362306a36Sopenharmony_ci	} else {
86462306a36Sopenharmony_ci		/* Mixed NIC kinds present in system, need dynamic testing */
86562306a36Sopenharmony_ci		if (q->broken_mqprio)
86662306a36Sopenharmony_ci			skb = taprio_dequeue_txq_priority(sch, entry, gate_mask);
86762306a36Sopenharmony_ci		else
86862306a36Sopenharmony_ci			skb = taprio_dequeue_tc_priority(sch, entry, gate_mask);
86962306a36Sopenharmony_ci	}
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_cidone:
87262306a36Sopenharmony_ci	rcu_read_unlock();
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_ci	return skb;
87562306a36Sopenharmony_ci}
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_cistatic bool should_restart_cycle(const struct sched_gate_list *oper,
87862306a36Sopenharmony_ci				 const struct sched_entry *entry)
87962306a36Sopenharmony_ci{
88062306a36Sopenharmony_ci	if (list_is_last(&entry->list, &oper->entries))
88162306a36Sopenharmony_ci		return true;
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_ci	if (ktime_compare(entry->end_time, oper->cycle_end_time) == 0)
88462306a36Sopenharmony_ci		return true;
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_ci	return false;
88762306a36Sopenharmony_ci}
88862306a36Sopenharmony_ci
88962306a36Sopenharmony_cistatic bool should_change_schedules(const struct sched_gate_list *admin,
89062306a36Sopenharmony_ci				    const struct sched_gate_list *oper,
89162306a36Sopenharmony_ci				    ktime_t end_time)
89262306a36Sopenharmony_ci{
89362306a36Sopenharmony_ci	ktime_t next_base_time, extension_time;
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_ci	if (!admin)
89662306a36Sopenharmony_ci		return false;
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ci	next_base_time = sched_base_time(admin);
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_ci	/* This is the simple case, the end_time would fall after
90162306a36Sopenharmony_ci	 * the next schedule base_time.
90262306a36Sopenharmony_ci	 */
90362306a36Sopenharmony_ci	if (ktime_compare(next_base_time, end_time) <= 0)
90462306a36Sopenharmony_ci		return true;
90562306a36Sopenharmony_ci
90662306a36Sopenharmony_ci	/* This is the cycle_time_extension case, if the end_time
90762306a36Sopenharmony_ci	 * plus the amount that can be extended would fall after the
90862306a36Sopenharmony_ci	 * next schedule base_time, we can extend the current schedule
90962306a36Sopenharmony_ci	 * for that amount.
91062306a36Sopenharmony_ci	 */
91162306a36Sopenharmony_ci	extension_time = ktime_add_ns(end_time, oper->cycle_time_extension);
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_ci	/* FIXME: the IEEE 802.1Q-2018 Specification isn't clear about
91462306a36Sopenharmony_ci	 * how precisely the extension should be made. So after
91562306a36Sopenharmony_ci	 * conformance testing, this logic may change.
91662306a36Sopenharmony_ci	 */
91762306a36Sopenharmony_ci	if (ktime_compare(next_base_time, extension_time) <= 0)
91862306a36Sopenharmony_ci		return true;
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_ci	return false;
92162306a36Sopenharmony_ci}
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_cistatic enum hrtimer_restart advance_sched(struct hrtimer *timer)
92462306a36Sopenharmony_ci{
92562306a36Sopenharmony_ci	struct taprio_sched *q = container_of(timer, struct taprio_sched,
92662306a36Sopenharmony_ci					      advance_timer);
92762306a36Sopenharmony_ci	struct net_device *dev = qdisc_dev(q->root);
92862306a36Sopenharmony_ci	struct sched_gate_list *oper, *admin;
92962306a36Sopenharmony_ci	int num_tc = netdev_get_num_tc(dev);
93062306a36Sopenharmony_ci	struct sched_entry *entry, *next;
93162306a36Sopenharmony_ci	struct Qdisc *sch = q->root;
93262306a36Sopenharmony_ci	ktime_t end_time;
93362306a36Sopenharmony_ci	int tc;
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_ci	spin_lock(&q->current_entry_lock);
93662306a36Sopenharmony_ci	entry = rcu_dereference_protected(q->current_entry,
93762306a36Sopenharmony_ci					  lockdep_is_held(&q->current_entry_lock));
93862306a36Sopenharmony_ci	oper = rcu_dereference_protected(q->oper_sched,
93962306a36Sopenharmony_ci					 lockdep_is_held(&q->current_entry_lock));
94062306a36Sopenharmony_ci	admin = rcu_dereference_protected(q->admin_sched,
94162306a36Sopenharmony_ci					  lockdep_is_held(&q->current_entry_lock));
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_ci	if (!oper)
94462306a36Sopenharmony_ci		switch_schedules(q, &admin, &oper);
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_ci	/* This can happen in two cases: 1. this is the very first run
94762306a36Sopenharmony_ci	 * of this function (i.e. we weren't running any schedule
94862306a36Sopenharmony_ci	 * previously); 2. The previous schedule just ended. The first
94962306a36Sopenharmony_ci	 * entry of all schedules are pre-calculated during the
95062306a36Sopenharmony_ci	 * schedule initialization.
95162306a36Sopenharmony_ci	 */
95262306a36Sopenharmony_ci	if (unlikely(!entry || entry->end_time == oper->base_time)) {
95362306a36Sopenharmony_ci		next = list_first_entry(&oper->entries, struct sched_entry,
95462306a36Sopenharmony_ci					list);
95562306a36Sopenharmony_ci		end_time = next->end_time;
95662306a36Sopenharmony_ci		goto first_run;
95762306a36Sopenharmony_ci	}
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_ci	if (should_restart_cycle(oper, entry)) {
96062306a36Sopenharmony_ci		next = list_first_entry(&oper->entries, struct sched_entry,
96162306a36Sopenharmony_ci					list);
96262306a36Sopenharmony_ci		oper->cycle_end_time = ktime_add_ns(oper->cycle_end_time,
96362306a36Sopenharmony_ci						    oper->cycle_time);
96462306a36Sopenharmony_ci	} else {
96562306a36Sopenharmony_ci		next = list_next_entry(entry, list);
96662306a36Sopenharmony_ci	}
96762306a36Sopenharmony_ci
96862306a36Sopenharmony_ci	end_time = ktime_add_ns(entry->end_time, next->interval);
96962306a36Sopenharmony_ci	end_time = min_t(ktime_t, end_time, oper->cycle_end_time);
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_ci	for (tc = 0; tc < num_tc; tc++) {
97262306a36Sopenharmony_ci		if (next->gate_duration[tc] == oper->cycle_time)
97362306a36Sopenharmony_ci			next->gate_close_time[tc] = KTIME_MAX;
97462306a36Sopenharmony_ci		else
97562306a36Sopenharmony_ci			next->gate_close_time[tc] = ktime_add_ns(entry->end_time,
97662306a36Sopenharmony_ci								 next->gate_duration[tc]);
97762306a36Sopenharmony_ci	}
97862306a36Sopenharmony_ci
97962306a36Sopenharmony_ci	if (should_change_schedules(admin, oper, end_time)) {
98062306a36Sopenharmony_ci		/* Set things so the next time this runs, the new
98162306a36Sopenharmony_ci		 * schedule runs.
98262306a36Sopenharmony_ci		 */
98362306a36Sopenharmony_ci		end_time = sched_base_time(admin);
98462306a36Sopenharmony_ci		switch_schedules(q, &admin, &oper);
98562306a36Sopenharmony_ci	}
98662306a36Sopenharmony_ci
98762306a36Sopenharmony_ci	next->end_time = end_time;
98862306a36Sopenharmony_ci	taprio_set_budgets(q, oper, next);
98962306a36Sopenharmony_ci
99062306a36Sopenharmony_cifirst_run:
99162306a36Sopenharmony_ci	rcu_assign_pointer(q->current_entry, next);
99262306a36Sopenharmony_ci	spin_unlock(&q->current_entry_lock);
99362306a36Sopenharmony_ci
99462306a36Sopenharmony_ci	hrtimer_set_expires(&q->advance_timer, end_time);
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_ci	rcu_read_lock();
99762306a36Sopenharmony_ci	__netif_schedule(sch);
99862306a36Sopenharmony_ci	rcu_read_unlock();
99962306a36Sopenharmony_ci
100062306a36Sopenharmony_ci	return HRTIMER_RESTART;
100162306a36Sopenharmony_ci}
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_cistatic const struct nla_policy entry_policy[TCA_TAPRIO_SCHED_ENTRY_MAX + 1] = {
100462306a36Sopenharmony_ci	[TCA_TAPRIO_SCHED_ENTRY_INDEX]	   = { .type = NLA_U32 },
100562306a36Sopenharmony_ci	[TCA_TAPRIO_SCHED_ENTRY_CMD]	   = { .type = NLA_U8 },
100662306a36Sopenharmony_ci	[TCA_TAPRIO_SCHED_ENTRY_GATE_MASK] = { .type = NLA_U32 },
100762306a36Sopenharmony_ci	[TCA_TAPRIO_SCHED_ENTRY_INTERVAL]  = { .type = NLA_U32 },
100862306a36Sopenharmony_ci};
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_cistatic const struct nla_policy taprio_tc_policy[TCA_TAPRIO_TC_ENTRY_MAX + 1] = {
101162306a36Sopenharmony_ci	[TCA_TAPRIO_TC_ENTRY_INDEX]	   = NLA_POLICY_MAX(NLA_U32,
101262306a36Sopenharmony_ci							    TC_QOPT_MAX_QUEUE),
101362306a36Sopenharmony_ci	[TCA_TAPRIO_TC_ENTRY_MAX_SDU]	   = { .type = NLA_U32 },
101462306a36Sopenharmony_ci	[TCA_TAPRIO_TC_ENTRY_FP]	   = NLA_POLICY_RANGE(NLA_U32,
101562306a36Sopenharmony_ci							      TC_FP_EXPRESS,
101662306a36Sopenharmony_ci							      TC_FP_PREEMPTIBLE),
101762306a36Sopenharmony_ci};
101862306a36Sopenharmony_ci
101962306a36Sopenharmony_cistatic struct netlink_range_validation_signed taprio_cycle_time_range = {
102062306a36Sopenharmony_ci	.min = 0,
102162306a36Sopenharmony_ci	.max = INT_MAX,
102262306a36Sopenharmony_ci};
102362306a36Sopenharmony_ci
102462306a36Sopenharmony_cistatic const struct nla_policy taprio_policy[TCA_TAPRIO_ATTR_MAX + 1] = {
102562306a36Sopenharmony_ci	[TCA_TAPRIO_ATTR_PRIOMAP]	       = {
102662306a36Sopenharmony_ci		.len = sizeof(struct tc_mqprio_qopt)
102762306a36Sopenharmony_ci	},
102862306a36Sopenharmony_ci	[TCA_TAPRIO_ATTR_SCHED_ENTRY_LIST]           = { .type = NLA_NESTED },
102962306a36Sopenharmony_ci	[TCA_TAPRIO_ATTR_SCHED_BASE_TIME]            = { .type = NLA_S64 },
103062306a36Sopenharmony_ci	[TCA_TAPRIO_ATTR_SCHED_SINGLE_ENTRY]         = { .type = NLA_NESTED },
103162306a36Sopenharmony_ci	[TCA_TAPRIO_ATTR_SCHED_CLOCKID]              = { .type = NLA_S32 },
103262306a36Sopenharmony_ci	[TCA_TAPRIO_ATTR_SCHED_CYCLE_TIME]           =
103362306a36Sopenharmony_ci		NLA_POLICY_FULL_RANGE_SIGNED(NLA_S64, &taprio_cycle_time_range),
103462306a36Sopenharmony_ci	[TCA_TAPRIO_ATTR_SCHED_CYCLE_TIME_EXTENSION] = { .type = NLA_S64 },
103562306a36Sopenharmony_ci	[TCA_TAPRIO_ATTR_FLAGS]                      = { .type = NLA_U32 },
103662306a36Sopenharmony_ci	[TCA_TAPRIO_ATTR_TXTIME_DELAY]		     = { .type = NLA_U32 },
103762306a36Sopenharmony_ci	[TCA_TAPRIO_ATTR_TC_ENTRY]		     = { .type = NLA_NESTED },
103862306a36Sopenharmony_ci};
103962306a36Sopenharmony_ci
104062306a36Sopenharmony_cistatic int fill_sched_entry(struct taprio_sched *q, struct nlattr **tb,
104162306a36Sopenharmony_ci			    struct sched_entry *entry,
104262306a36Sopenharmony_ci			    struct netlink_ext_ack *extack)
104362306a36Sopenharmony_ci{
104462306a36Sopenharmony_ci	int min_duration = length_to_duration(q, ETH_ZLEN);
104562306a36Sopenharmony_ci	u32 interval = 0;
104662306a36Sopenharmony_ci
104762306a36Sopenharmony_ci	if (tb[TCA_TAPRIO_SCHED_ENTRY_CMD])
104862306a36Sopenharmony_ci		entry->command = nla_get_u8(
104962306a36Sopenharmony_ci			tb[TCA_TAPRIO_SCHED_ENTRY_CMD]);
105062306a36Sopenharmony_ci
105162306a36Sopenharmony_ci	if (tb[TCA_TAPRIO_SCHED_ENTRY_GATE_MASK])
105262306a36Sopenharmony_ci		entry->gate_mask = nla_get_u32(
105362306a36Sopenharmony_ci			tb[TCA_TAPRIO_SCHED_ENTRY_GATE_MASK]);
105462306a36Sopenharmony_ci
105562306a36Sopenharmony_ci	if (tb[TCA_TAPRIO_SCHED_ENTRY_INTERVAL])
105662306a36Sopenharmony_ci		interval = nla_get_u32(
105762306a36Sopenharmony_ci			tb[TCA_TAPRIO_SCHED_ENTRY_INTERVAL]);
105862306a36Sopenharmony_ci
105962306a36Sopenharmony_ci	/* The interval should allow at least the minimum ethernet
106062306a36Sopenharmony_ci	 * frame to go out.
106162306a36Sopenharmony_ci	 */
106262306a36Sopenharmony_ci	if (interval < min_duration) {
106362306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Invalid interval for schedule entry");
106462306a36Sopenharmony_ci		return -EINVAL;
106562306a36Sopenharmony_ci	}
106662306a36Sopenharmony_ci
106762306a36Sopenharmony_ci	entry->interval = interval;
106862306a36Sopenharmony_ci
106962306a36Sopenharmony_ci	return 0;
107062306a36Sopenharmony_ci}
107162306a36Sopenharmony_ci
107262306a36Sopenharmony_cistatic int parse_sched_entry(struct taprio_sched *q, struct nlattr *n,
107362306a36Sopenharmony_ci			     struct sched_entry *entry, int index,
107462306a36Sopenharmony_ci			     struct netlink_ext_ack *extack)
107562306a36Sopenharmony_ci{
107662306a36Sopenharmony_ci	struct nlattr *tb[TCA_TAPRIO_SCHED_ENTRY_MAX + 1] = { };
107762306a36Sopenharmony_ci	int err;
107862306a36Sopenharmony_ci
107962306a36Sopenharmony_ci	err = nla_parse_nested_deprecated(tb, TCA_TAPRIO_SCHED_ENTRY_MAX, n,
108062306a36Sopenharmony_ci					  entry_policy, NULL);
108162306a36Sopenharmony_ci	if (err < 0) {
108262306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Could not parse nested entry");
108362306a36Sopenharmony_ci		return -EINVAL;
108462306a36Sopenharmony_ci	}
108562306a36Sopenharmony_ci
108662306a36Sopenharmony_ci	entry->index = index;
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_ci	return fill_sched_entry(q, tb, entry, extack);
108962306a36Sopenharmony_ci}
109062306a36Sopenharmony_ci
109162306a36Sopenharmony_cistatic int parse_sched_list(struct taprio_sched *q, struct nlattr *list,
109262306a36Sopenharmony_ci			    struct sched_gate_list *sched,
109362306a36Sopenharmony_ci			    struct netlink_ext_ack *extack)
109462306a36Sopenharmony_ci{
109562306a36Sopenharmony_ci	struct nlattr *n;
109662306a36Sopenharmony_ci	int err, rem;
109762306a36Sopenharmony_ci	int i = 0;
109862306a36Sopenharmony_ci
109962306a36Sopenharmony_ci	if (!list)
110062306a36Sopenharmony_ci		return -EINVAL;
110162306a36Sopenharmony_ci
110262306a36Sopenharmony_ci	nla_for_each_nested(n, list, rem) {
110362306a36Sopenharmony_ci		struct sched_entry *entry;
110462306a36Sopenharmony_ci
110562306a36Sopenharmony_ci		if (nla_type(n) != TCA_TAPRIO_SCHED_ENTRY) {
110662306a36Sopenharmony_ci			NL_SET_ERR_MSG(extack, "Attribute is not of type 'entry'");
110762306a36Sopenharmony_ci			continue;
110862306a36Sopenharmony_ci		}
110962306a36Sopenharmony_ci
111062306a36Sopenharmony_ci		entry = kzalloc(sizeof(*entry), GFP_KERNEL);
111162306a36Sopenharmony_ci		if (!entry) {
111262306a36Sopenharmony_ci			NL_SET_ERR_MSG(extack, "Not enough memory for entry");
111362306a36Sopenharmony_ci			return -ENOMEM;
111462306a36Sopenharmony_ci		}
111562306a36Sopenharmony_ci
111662306a36Sopenharmony_ci		err = parse_sched_entry(q, n, entry, i, extack);
111762306a36Sopenharmony_ci		if (err < 0) {
111862306a36Sopenharmony_ci			kfree(entry);
111962306a36Sopenharmony_ci			return err;
112062306a36Sopenharmony_ci		}
112162306a36Sopenharmony_ci
112262306a36Sopenharmony_ci		list_add_tail(&entry->list, &sched->entries);
112362306a36Sopenharmony_ci		i++;
112462306a36Sopenharmony_ci	}
112562306a36Sopenharmony_ci
112662306a36Sopenharmony_ci	sched->num_entries = i;
112762306a36Sopenharmony_ci
112862306a36Sopenharmony_ci	return i;
112962306a36Sopenharmony_ci}
113062306a36Sopenharmony_ci
113162306a36Sopenharmony_cistatic int parse_taprio_schedule(struct taprio_sched *q, struct nlattr **tb,
113262306a36Sopenharmony_ci				 struct sched_gate_list *new,
113362306a36Sopenharmony_ci				 struct netlink_ext_ack *extack)
113462306a36Sopenharmony_ci{
113562306a36Sopenharmony_ci	int err = 0;
113662306a36Sopenharmony_ci
113762306a36Sopenharmony_ci	if (tb[TCA_TAPRIO_ATTR_SCHED_SINGLE_ENTRY]) {
113862306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Adding a single entry is not supported");
113962306a36Sopenharmony_ci		return -ENOTSUPP;
114062306a36Sopenharmony_ci	}
114162306a36Sopenharmony_ci
114262306a36Sopenharmony_ci	if (tb[TCA_TAPRIO_ATTR_SCHED_BASE_TIME])
114362306a36Sopenharmony_ci		new->base_time = nla_get_s64(tb[TCA_TAPRIO_ATTR_SCHED_BASE_TIME]);
114462306a36Sopenharmony_ci
114562306a36Sopenharmony_ci	if (tb[TCA_TAPRIO_ATTR_SCHED_CYCLE_TIME_EXTENSION])
114662306a36Sopenharmony_ci		new->cycle_time_extension = nla_get_s64(tb[TCA_TAPRIO_ATTR_SCHED_CYCLE_TIME_EXTENSION]);
114762306a36Sopenharmony_ci
114862306a36Sopenharmony_ci	if (tb[TCA_TAPRIO_ATTR_SCHED_CYCLE_TIME])
114962306a36Sopenharmony_ci		new->cycle_time = nla_get_s64(tb[TCA_TAPRIO_ATTR_SCHED_CYCLE_TIME]);
115062306a36Sopenharmony_ci
115162306a36Sopenharmony_ci	if (tb[TCA_TAPRIO_ATTR_SCHED_ENTRY_LIST])
115262306a36Sopenharmony_ci		err = parse_sched_list(q, tb[TCA_TAPRIO_ATTR_SCHED_ENTRY_LIST],
115362306a36Sopenharmony_ci				       new, extack);
115462306a36Sopenharmony_ci	if (err < 0)
115562306a36Sopenharmony_ci		return err;
115662306a36Sopenharmony_ci
115762306a36Sopenharmony_ci	if (!new->cycle_time) {
115862306a36Sopenharmony_ci		struct sched_entry *entry;
115962306a36Sopenharmony_ci		ktime_t cycle = 0;
116062306a36Sopenharmony_ci
116162306a36Sopenharmony_ci		list_for_each_entry(entry, &new->entries, list)
116262306a36Sopenharmony_ci			cycle = ktime_add_ns(cycle, entry->interval);
116362306a36Sopenharmony_ci
116462306a36Sopenharmony_ci		if (!cycle) {
116562306a36Sopenharmony_ci			NL_SET_ERR_MSG(extack, "'cycle_time' can never be 0");
116662306a36Sopenharmony_ci			return -EINVAL;
116762306a36Sopenharmony_ci		}
116862306a36Sopenharmony_ci
116962306a36Sopenharmony_ci		if (cycle < 0 || cycle > INT_MAX) {
117062306a36Sopenharmony_ci			NL_SET_ERR_MSG(extack, "'cycle_time' is too big");
117162306a36Sopenharmony_ci			return -EINVAL;
117262306a36Sopenharmony_ci		}
117362306a36Sopenharmony_ci
117462306a36Sopenharmony_ci		new->cycle_time = cycle;
117562306a36Sopenharmony_ci	}
117662306a36Sopenharmony_ci
117762306a36Sopenharmony_ci	taprio_calculate_gate_durations(q, new);
117862306a36Sopenharmony_ci
117962306a36Sopenharmony_ci	return 0;
118062306a36Sopenharmony_ci}
118162306a36Sopenharmony_ci
118262306a36Sopenharmony_cistatic int taprio_parse_mqprio_opt(struct net_device *dev,
118362306a36Sopenharmony_ci				   struct tc_mqprio_qopt *qopt,
118462306a36Sopenharmony_ci				   struct netlink_ext_ack *extack,
118562306a36Sopenharmony_ci				   u32 taprio_flags)
118662306a36Sopenharmony_ci{
118762306a36Sopenharmony_ci	bool allow_overlapping_txqs = TXTIME_ASSIST_IS_ENABLED(taprio_flags);
118862306a36Sopenharmony_ci
118962306a36Sopenharmony_ci	if (!qopt && !dev->num_tc) {
119062306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "'mqprio' configuration is necessary");
119162306a36Sopenharmony_ci		return -EINVAL;
119262306a36Sopenharmony_ci	}
119362306a36Sopenharmony_ci
119462306a36Sopenharmony_ci	/* If num_tc is already set, it means that the user already
119562306a36Sopenharmony_ci	 * configured the mqprio part
119662306a36Sopenharmony_ci	 */
119762306a36Sopenharmony_ci	if (dev->num_tc)
119862306a36Sopenharmony_ci		return 0;
119962306a36Sopenharmony_ci
120062306a36Sopenharmony_ci	/* taprio imposes that traffic classes map 1:n to tx queues */
120162306a36Sopenharmony_ci	if (qopt->num_tc > dev->num_tx_queues) {
120262306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Number of traffic classes is greater than number of HW queues");
120362306a36Sopenharmony_ci		return -EINVAL;
120462306a36Sopenharmony_ci	}
120562306a36Sopenharmony_ci
120662306a36Sopenharmony_ci	/* For some reason, in txtime-assist mode, we allow TXQ ranges for
120762306a36Sopenharmony_ci	 * different TCs to overlap, and just validate the TXQ ranges.
120862306a36Sopenharmony_ci	 */
120962306a36Sopenharmony_ci	return mqprio_validate_qopt(dev, qopt, true, allow_overlapping_txqs,
121062306a36Sopenharmony_ci				    extack);
121162306a36Sopenharmony_ci}
121262306a36Sopenharmony_ci
121362306a36Sopenharmony_cistatic int taprio_get_start_time(struct Qdisc *sch,
121462306a36Sopenharmony_ci				 struct sched_gate_list *sched,
121562306a36Sopenharmony_ci				 ktime_t *start)
121662306a36Sopenharmony_ci{
121762306a36Sopenharmony_ci	struct taprio_sched *q = qdisc_priv(sch);
121862306a36Sopenharmony_ci	ktime_t now, base, cycle;
121962306a36Sopenharmony_ci	s64 n;
122062306a36Sopenharmony_ci
122162306a36Sopenharmony_ci	base = sched_base_time(sched);
122262306a36Sopenharmony_ci	now = taprio_get_time(q);
122362306a36Sopenharmony_ci
122462306a36Sopenharmony_ci	if (ktime_after(base, now)) {
122562306a36Sopenharmony_ci		*start = base;
122662306a36Sopenharmony_ci		return 0;
122762306a36Sopenharmony_ci	}
122862306a36Sopenharmony_ci
122962306a36Sopenharmony_ci	cycle = sched->cycle_time;
123062306a36Sopenharmony_ci
123162306a36Sopenharmony_ci	/* The qdisc is expected to have at least one sched_entry.  Moreover,
123262306a36Sopenharmony_ci	 * any entry must have 'interval' > 0. Thus if the cycle time is zero,
123362306a36Sopenharmony_ci	 * something went really wrong. In that case, we should warn about this
123462306a36Sopenharmony_ci	 * inconsistent state and return error.
123562306a36Sopenharmony_ci	 */
123662306a36Sopenharmony_ci	if (WARN_ON(!cycle))
123762306a36Sopenharmony_ci		return -EFAULT;
123862306a36Sopenharmony_ci
123962306a36Sopenharmony_ci	/* Schedule the start time for the beginning of the next
124062306a36Sopenharmony_ci	 * cycle.
124162306a36Sopenharmony_ci	 */
124262306a36Sopenharmony_ci	n = div64_s64(ktime_sub_ns(now, base), cycle);
124362306a36Sopenharmony_ci	*start = ktime_add_ns(base, (n + 1) * cycle);
124462306a36Sopenharmony_ci	return 0;
124562306a36Sopenharmony_ci}
124662306a36Sopenharmony_ci
124762306a36Sopenharmony_cistatic void setup_first_end_time(struct taprio_sched *q,
124862306a36Sopenharmony_ci				 struct sched_gate_list *sched, ktime_t base)
124962306a36Sopenharmony_ci{
125062306a36Sopenharmony_ci	struct net_device *dev = qdisc_dev(q->root);
125162306a36Sopenharmony_ci	int num_tc = netdev_get_num_tc(dev);
125262306a36Sopenharmony_ci	struct sched_entry *first;
125362306a36Sopenharmony_ci	ktime_t cycle;
125462306a36Sopenharmony_ci	int tc;
125562306a36Sopenharmony_ci
125662306a36Sopenharmony_ci	first = list_first_entry(&sched->entries,
125762306a36Sopenharmony_ci				 struct sched_entry, list);
125862306a36Sopenharmony_ci
125962306a36Sopenharmony_ci	cycle = sched->cycle_time;
126062306a36Sopenharmony_ci
126162306a36Sopenharmony_ci	/* FIXME: find a better place to do this */
126262306a36Sopenharmony_ci	sched->cycle_end_time = ktime_add_ns(base, cycle);
126362306a36Sopenharmony_ci
126462306a36Sopenharmony_ci	first->end_time = ktime_add_ns(base, first->interval);
126562306a36Sopenharmony_ci	taprio_set_budgets(q, sched, first);
126662306a36Sopenharmony_ci
126762306a36Sopenharmony_ci	for (tc = 0; tc < num_tc; tc++) {
126862306a36Sopenharmony_ci		if (first->gate_duration[tc] == sched->cycle_time)
126962306a36Sopenharmony_ci			first->gate_close_time[tc] = KTIME_MAX;
127062306a36Sopenharmony_ci		else
127162306a36Sopenharmony_ci			first->gate_close_time[tc] = ktime_add_ns(base, first->gate_duration[tc]);
127262306a36Sopenharmony_ci	}
127362306a36Sopenharmony_ci
127462306a36Sopenharmony_ci	rcu_assign_pointer(q->current_entry, NULL);
127562306a36Sopenharmony_ci}
127662306a36Sopenharmony_ci
127762306a36Sopenharmony_cistatic void taprio_start_sched(struct Qdisc *sch,
127862306a36Sopenharmony_ci			       ktime_t start, struct sched_gate_list *new)
127962306a36Sopenharmony_ci{
128062306a36Sopenharmony_ci	struct taprio_sched *q = qdisc_priv(sch);
128162306a36Sopenharmony_ci	ktime_t expires;
128262306a36Sopenharmony_ci
128362306a36Sopenharmony_ci	if (FULL_OFFLOAD_IS_ENABLED(q->flags))
128462306a36Sopenharmony_ci		return;
128562306a36Sopenharmony_ci
128662306a36Sopenharmony_ci	expires = hrtimer_get_expires(&q->advance_timer);
128762306a36Sopenharmony_ci	if (expires == 0)
128862306a36Sopenharmony_ci		expires = KTIME_MAX;
128962306a36Sopenharmony_ci
129062306a36Sopenharmony_ci	/* If the new schedule starts before the next expiration, we
129162306a36Sopenharmony_ci	 * reprogram it to the earliest one, so we change the admin
129262306a36Sopenharmony_ci	 * schedule to the operational one at the right time.
129362306a36Sopenharmony_ci	 */
129462306a36Sopenharmony_ci	start = min_t(ktime_t, start, expires);
129562306a36Sopenharmony_ci
129662306a36Sopenharmony_ci	hrtimer_start(&q->advance_timer, start, HRTIMER_MODE_ABS);
129762306a36Sopenharmony_ci}
129862306a36Sopenharmony_ci
129962306a36Sopenharmony_cistatic void taprio_set_picos_per_byte(struct net_device *dev,
130062306a36Sopenharmony_ci				      struct taprio_sched *q)
130162306a36Sopenharmony_ci{
130262306a36Sopenharmony_ci	struct ethtool_link_ksettings ecmd;
130362306a36Sopenharmony_ci	int speed = SPEED_10;
130462306a36Sopenharmony_ci	int picos_per_byte;
130562306a36Sopenharmony_ci	int err;
130662306a36Sopenharmony_ci
130762306a36Sopenharmony_ci	err = __ethtool_get_link_ksettings(dev, &ecmd);
130862306a36Sopenharmony_ci	if (err < 0)
130962306a36Sopenharmony_ci		goto skip;
131062306a36Sopenharmony_ci
131162306a36Sopenharmony_ci	if (ecmd.base.speed && ecmd.base.speed != SPEED_UNKNOWN)
131262306a36Sopenharmony_ci		speed = ecmd.base.speed;
131362306a36Sopenharmony_ci
131462306a36Sopenharmony_ciskip:
131562306a36Sopenharmony_ci	picos_per_byte = (USEC_PER_SEC * 8) / speed;
131662306a36Sopenharmony_ci
131762306a36Sopenharmony_ci	atomic64_set(&q->picos_per_byte, picos_per_byte);
131862306a36Sopenharmony_ci	netdev_dbg(dev, "taprio: set %s's picos_per_byte to: %lld, linkspeed: %d\n",
131962306a36Sopenharmony_ci		   dev->name, (long long)atomic64_read(&q->picos_per_byte),
132062306a36Sopenharmony_ci		   ecmd.base.speed);
132162306a36Sopenharmony_ci}
132262306a36Sopenharmony_ci
132362306a36Sopenharmony_cistatic int taprio_dev_notifier(struct notifier_block *nb, unsigned long event,
132462306a36Sopenharmony_ci			       void *ptr)
132562306a36Sopenharmony_ci{
132662306a36Sopenharmony_ci	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
132762306a36Sopenharmony_ci	struct sched_gate_list *oper, *admin;
132862306a36Sopenharmony_ci	struct qdisc_size_table *stab;
132962306a36Sopenharmony_ci	struct taprio_sched *q;
133062306a36Sopenharmony_ci
133162306a36Sopenharmony_ci	ASSERT_RTNL();
133262306a36Sopenharmony_ci
133362306a36Sopenharmony_ci	if (event != NETDEV_UP && event != NETDEV_CHANGE)
133462306a36Sopenharmony_ci		return NOTIFY_DONE;
133562306a36Sopenharmony_ci
133662306a36Sopenharmony_ci	list_for_each_entry(q, &taprio_list, taprio_list) {
133762306a36Sopenharmony_ci		if (dev != qdisc_dev(q->root))
133862306a36Sopenharmony_ci			continue;
133962306a36Sopenharmony_ci
134062306a36Sopenharmony_ci		taprio_set_picos_per_byte(dev, q);
134162306a36Sopenharmony_ci
134262306a36Sopenharmony_ci		stab = rtnl_dereference(q->root->stab);
134362306a36Sopenharmony_ci
134462306a36Sopenharmony_ci		oper = rtnl_dereference(q->oper_sched);
134562306a36Sopenharmony_ci		if (oper)
134662306a36Sopenharmony_ci			taprio_update_queue_max_sdu(q, oper, stab);
134762306a36Sopenharmony_ci
134862306a36Sopenharmony_ci		admin = rtnl_dereference(q->admin_sched);
134962306a36Sopenharmony_ci		if (admin)
135062306a36Sopenharmony_ci			taprio_update_queue_max_sdu(q, admin, stab);
135162306a36Sopenharmony_ci
135262306a36Sopenharmony_ci		break;
135362306a36Sopenharmony_ci	}
135462306a36Sopenharmony_ci
135562306a36Sopenharmony_ci	return NOTIFY_DONE;
135662306a36Sopenharmony_ci}
135762306a36Sopenharmony_ci
135862306a36Sopenharmony_cistatic void setup_txtime(struct taprio_sched *q,
135962306a36Sopenharmony_ci			 struct sched_gate_list *sched, ktime_t base)
136062306a36Sopenharmony_ci{
136162306a36Sopenharmony_ci	struct sched_entry *entry;
136262306a36Sopenharmony_ci	u64 interval = 0;
136362306a36Sopenharmony_ci
136462306a36Sopenharmony_ci	list_for_each_entry(entry, &sched->entries, list) {
136562306a36Sopenharmony_ci		entry->next_txtime = ktime_add_ns(base, interval);
136662306a36Sopenharmony_ci		interval += entry->interval;
136762306a36Sopenharmony_ci	}
136862306a36Sopenharmony_ci}
136962306a36Sopenharmony_ci
137062306a36Sopenharmony_cistatic struct tc_taprio_qopt_offload *taprio_offload_alloc(int num_entries)
137162306a36Sopenharmony_ci{
137262306a36Sopenharmony_ci	struct __tc_taprio_qopt_offload *__offload;
137362306a36Sopenharmony_ci
137462306a36Sopenharmony_ci	__offload = kzalloc(struct_size(__offload, offload.entries, num_entries),
137562306a36Sopenharmony_ci			    GFP_KERNEL);
137662306a36Sopenharmony_ci	if (!__offload)
137762306a36Sopenharmony_ci		return NULL;
137862306a36Sopenharmony_ci
137962306a36Sopenharmony_ci	refcount_set(&__offload->users, 1);
138062306a36Sopenharmony_ci
138162306a36Sopenharmony_ci	return &__offload->offload;
138262306a36Sopenharmony_ci}
138362306a36Sopenharmony_ci
138462306a36Sopenharmony_cistruct tc_taprio_qopt_offload *taprio_offload_get(struct tc_taprio_qopt_offload
138562306a36Sopenharmony_ci						  *offload)
138662306a36Sopenharmony_ci{
138762306a36Sopenharmony_ci	struct __tc_taprio_qopt_offload *__offload;
138862306a36Sopenharmony_ci
138962306a36Sopenharmony_ci	__offload = container_of(offload, struct __tc_taprio_qopt_offload,
139062306a36Sopenharmony_ci				 offload);
139162306a36Sopenharmony_ci
139262306a36Sopenharmony_ci	refcount_inc(&__offload->users);
139362306a36Sopenharmony_ci
139462306a36Sopenharmony_ci	return offload;
139562306a36Sopenharmony_ci}
139662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(taprio_offload_get);
139762306a36Sopenharmony_ci
139862306a36Sopenharmony_civoid taprio_offload_free(struct tc_taprio_qopt_offload *offload)
139962306a36Sopenharmony_ci{
140062306a36Sopenharmony_ci	struct __tc_taprio_qopt_offload *__offload;
140162306a36Sopenharmony_ci
140262306a36Sopenharmony_ci	__offload = container_of(offload, struct __tc_taprio_qopt_offload,
140362306a36Sopenharmony_ci				 offload);
140462306a36Sopenharmony_ci
140562306a36Sopenharmony_ci	if (!refcount_dec_and_test(&__offload->users))
140662306a36Sopenharmony_ci		return;
140762306a36Sopenharmony_ci
140862306a36Sopenharmony_ci	kfree(__offload);
140962306a36Sopenharmony_ci}
141062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(taprio_offload_free);
141162306a36Sopenharmony_ci
141262306a36Sopenharmony_ci/* The function will only serve to keep the pointers to the "oper" and "admin"
141362306a36Sopenharmony_ci * schedules valid in relation to their base times, so when calling dump() the
141462306a36Sopenharmony_ci * users looks at the right schedules.
141562306a36Sopenharmony_ci * When using full offload, the admin configuration is promoted to oper at the
141662306a36Sopenharmony_ci * base_time in the PHC time domain.  But because the system time is not
141762306a36Sopenharmony_ci * necessarily in sync with that, we can't just trigger a hrtimer to call
141862306a36Sopenharmony_ci * switch_schedules at the right hardware time.
141962306a36Sopenharmony_ci * At the moment we call this by hand right away from taprio, but in the future
142062306a36Sopenharmony_ci * it will be useful to create a mechanism for drivers to notify taprio of the
142162306a36Sopenharmony_ci * offload state (PENDING, ACTIVE, INACTIVE) so it can be visible in dump().
142262306a36Sopenharmony_ci * This is left as TODO.
142362306a36Sopenharmony_ci */
142462306a36Sopenharmony_cistatic void taprio_offload_config_changed(struct taprio_sched *q)
142562306a36Sopenharmony_ci{
142662306a36Sopenharmony_ci	struct sched_gate_list *oper, *admin;
142762306a36Sopenharmony_ci
142862306a36Sopenharmony_ci	oper = rtnl_dereference(q->oper_sched);
142962306a36Sopenharmony_ci	admin = rtnl_dereference(q->admin_sched);
143062306a36Sopenharmony_ci
143162306a36Sopenharmony_ci	switch_schedules(q, &admin, &oper);
143262306a36Sopenharmony_ci}
143362306a36Sopenharmony_ci
143462306a36Sopenharmony_cistatic u32 tc_map_to_queue_mask(struct net_device *dev, u32 tc_mask)
143562306a36Sopenharmony_ci{
143662306a36Sopenharmony_ci	u32 i, queue_mask = 0;
143762306a36Sopenharmony_ci
143862306a36Sopenharmony_ci	for (i = 0; i < dev->num_tc; i++) {
143962306a36Sopenharmony_ci		u32 offset, count;
144062306a36Sopenharmony_ci
144162306a36Sopenharmony_ci		if (!(tc_mask & BIT(i)))
144262306a36Sopenharmony_ci			continue;
144362306a36Sopenharmony_ci
144462306a36Sopenharmony_ci		offset = dev->tc_to_txq[i].offset;
144562306a36Sopenharmony_ci		count = dev->tc_to_txq[i].count;
144662306a36Sopenharmony_ci
144762306a36Sopenharmony_ci		queue_mask |= GENMASK(offset + count - 1, offset);
144862306a36Sopenharmony_ci	}
144962306a36Sopenharmony_ci
145062306a36Sopenharmony_ci	return queue_mask;
145162306a36Sopenharmony_ci}
145262306a36Sopenharmony_ci
145362306a36Sopenharmony_cistatic void taprio_sched_to_offload(struct net_device *dev,
145462306a36Sopenharmony_ci				    struct sched_gate_list *sched,
145562306a36Sopenharmony_ci				    struct tc_taprio_qopt_offload *offload,
145662306a36Sopenharmony_ci				    const struct tc_taprio_caps *caps)
145762306a36Sopenharmony_ci{
145862306a36Sopenharmony_ci	struct sched_entry *entry;
145962306a36Sopenharmony_ci	int i = 0;
146062306a36Sopenharmony_ci
146162306a36Sopenharmony_ci	offload->base_time = sched->base_time;
146262306a36Sopenharmony_ci	offload->cycle_time = sched->cycle_time;
146362306a36Sopenharmony_ci	offload->cycle_time_extension = sched->cycle_time_extension;
146462306a36Sopenharmony_ci
146562306a36Sopenharmony_ci	list_for_each_entry(entry, &sched->entries, list) {
146662306a36Sopenharmony_ci		struct tc_taprio_sched_entry *e = &offload->entries[i];
146762306a36Sopenharmony_ci
146862306a36Sopenharmony_ci		e->command = entry->command;
146962306a36Sopenharmony_ci		e->interval = entry->interval;
147062306a36Sopenharmony_ci		if (caps->gate_mask_per_txq)
147162306a36Sopenharmony_ci			e->gate_mask = tc_map_to_queue_mask(dev,
147262306a36Sopenharmony_ci							    entry->gate_mask);
147362306a36Sopenharmony_ci		else
147462306a36Sopenharmony_ci			e->gate_mask = entry->gate_mask;
147562306a36Sopenharmony_ci
147662306a36Sopenharmony_ci		i++;
147762306a36Sopenharmony_ci	}
147862306a36Sopenharmony_ci
147962306a36Sopenharmony_ci	offload->num_entries = i;
148062306a36Sopenharmony_ci}
148162306a36Sopenharmony_ci
148262306a36Sopenharmony_cistatic void taprio_detect_broken_mqprio(struct taprio_sched *q)
148362306a36Sopenharmony_ci{
148462306a36Sopenharmony_ci	struct net_device *dev = qdisc_dev(q->root);
148562306a36Sopenharmony_ci	struct tc_taprio_caps caps;
148662306a36Sopenharmony_ci
148762306a36Sopenharmony_ci	qdisc_offload_query_caps(dev, TC_SETUP_QDISC_TAPRIO,
148862306a36Sopenharmony_ci				 &caps, sizeof(caps));
148962306a36Sopenharmony_ci
149062306a36Sopenharmony_ci	q->broken_mqprio = caps.broken_mqprio;
149162306a36Sopenharmony_ci	if (q->broken_mqprio)
149262306a36Sopenharmony_ci		static_branch_inc(&taprio_have_broken_mqprio);
149362306a36Sopenharmony_ci	else
149462306a36Sopenharmony_ci		static_branch_inc(&taprio_have_working_mqprio);
149562306a36Sopenharmony_ci
149662306a36Sopenharmony_ci	q->detected_mqprio = true;
149762306a36Sopenharmony_ci}
149862306a36Sopenharmony_ci
149962306a36Sopenharmony_cistatic void taprio_cleanup_broken_mqprio(struct taprio_sched *q)
150062306a36Sopenharmony_ci{
150162306a36Sopenharmony_ci	if (!q->detected_mqprio)
150262306a36Sopenharmony_ci		return;
150362306a36Sopenharmony_ci
150462306a36Sopenharmony_ci	if (q->broken_mqprio)
150562306a36Sopenharmony_ci		static_branch_dec(&taprio_have_broken_mqprio);
150662306a36Sopenharmony_ci	else
150762306a36Sopenharmony_ci		static_branch_dec(&taprio_have_working_mqprio);
150862306a36Sopenharmony_ci}
150962306a36Sopenharmony_ci
151062306a36Sopenharmony_cistatic int taprio_enable_offload(struct net_device *dev,
151162306a36Sopenharmony_ci				 struct taprio_sched *q,
151262306a36Sopenharmony_ci				 struct sched_gate_list *sched,
151362306a36Sopenharmony_ci				 struct netlink_ext_ack *extack)
151462306a36Sopenharmony_ci{
151562306a36Sopenharmony_ci	const struct net_device_ops *ops = dev->netdev_ops;
151662306a36Sopenharmony_ci	struct tc_taprio_qopt_offload *offload;
151762306a36Sopenharmony_ci	struct tc_taprio_caps caps;
151862306a36Sopenharmony_ci	int tc, err = 0;
151962306a36Sopenharmony_ci
152062306a36Sopenharmony_ci	if (!ops->ndo_setup_tc) {
152162306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack,
152262306a36Sopenharmony_ci			       "Device does not support taprio offload");
152362306a36Sopenharmony_ci		return -EOPNOTSUPP;
152462306a36Sopenharmony_ci	}
152562306a36Sopenharmony_ci
152662306a36Sopenharmony_ci	qdisc_offload_query_caps(dev, TC_SETUP_QDISC_TAPRIO,
152762306a36Sopenharmony_ci				 &caps, sizeof(caps));
152862306a36Sopenharmony_ci
152962306a36Sopenharmony_ci	if (!caps.supports_queue_max_sdu) {
153062306a36Sopenharmony_ci		for (tc = 0; tc < TC_MAX_QUEUE; tc++) {
153162306a36Sopenharmony_ci			if (q->max_sdu[tc]) {
153262306a36Sopenharmony_ci				NL_SET_ERR_MSG_MOD(extack,
153362306a36Sopenharmony_ci						   "Device does not handle queueMaxSDU");
153462306a36Sopenharmony_ci				return -EOPNOTSUPP;
153562306a36Sopenharmony_ci			}
153662306a36Sopenharmony_ci		}
153762306a36Sopenharmony_ci	}
153862306a36Sopenharmony_ci
153962306a36Sopenharmony_ci	offload = taprio_offload_alloc(sched->num_entries);
154062306a36Sopenharmony_ci	if (!offload) {
154162306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack,
154262306a36Sopenharmony_ci			       "Not enough memory for enabling offload mode");
154362306a36Sopenharmony_ci		return -ENOMEM;
154462306a36Sopenharmony_ci	}
154562306a36Sopenharmony_ci	offload->cmd = TAPRIO_CMD_REPLACE;
154662306a36Sopenharmony_ci	offload->extack = extack;
154762306a36Sopenharmony_ci	mqprio_qopt_reconstruct(dev, &offload->mqprio.qopt);
154862306a36Sopenharmony_ci	offload->mqprio.extack = extack;
154962306a36Sopenharmony_ci	taprio_sched_to_offload(dev, sched, offload, &caps);
155062306a36Sopenharmony_ci	mqprio_fp_to_offload(q->fp, &offload->mqprio);
155162306a36Sopenharmony_ci
155262306a36Sopenharmony_ci	for (tc = 0; tc < TC_MAX_QUEUE; tc++)
155362306a36Sopenharmony_ci		offload->max_sdu[tc] = q->max_sdu[tc];
155462306a36Sopenharmony_ci
155562306a36Sopenharmony_ci	err = ops->ndo_setup_tc(dev, TC_SETUP_QDISC_TAPRIO, offload);
155662306a36Sopenharmony_ci	if (err < 0) {
155762306a36Sopenharmony_ci		NL_SET_ERR_MSG_WEAK(extack,
155862306a36Sopenharmony_ci				    "Device failed to setup taprio offload");
155962306a36Sopenharmony_ci		goto done;
156062306a36Sopenharmony_ci	}
156162306a36Sopenharmony_ci
156262306a36Sopenharmony_ci	q->offloaded = true;
156362306a36Sopenharmony_ci
156462306a36Sopenharmony_cidone:
156562306a36Sopenharmony_ci	/* The offload structure may linger around via a reference taken by the
156662306a36Sopenharmony_ci	 * device driver, so clear up the netlink extack pointer so that the
156762306a36Sopenharmony_ci	 * driver isn't tempted to dereference data which stopped being valid
156862306a36Sopenharmony_ci	 */
156962306a36Sopenharmony_ci	offload->extack = NULL;
157062306a36Sopenharmony_ci	offload->mqprio.extack = NULL;
157162306a36Sopenharmony_ci	taprio_offload_free(offload);
157262306a36Sopenharmony_ci
157362306a36Sopenharmony_ci	return err;
157462306a36Sopenharmony_ci}
157562306a36Sopenharmony_ci
157662306a36Sopenharmony_cistatic int taprio_disable_offload(struct net_device *dev,
157762306a36Sopenharmony_ci				  struct taprio_sched *q,
157862306a36Sopenharmony_ci				  struct netlink_ext_ack *extack)
157962306a36Sopenharmony_ci{
158062306a36Sopenharmony_ci	const struct net_device_ops *ops = dev->netdev_ops;
158162306a36Sopenharmony_ci	struct tc_taprio_qopt_offload *offload;
158262306a36Sopenharmony_ci	int err;
158362306a36Sopenharmony_ci
158462306a36Sopenharmony_ci	if (!q->offloaded)
158562306a36Sopenharmony_ci		return 0;
158662306a36Sopenharmony_ci
158762306a36Sopenharmony_ci	offload = taprio_offload_alloc(0);
158862306a36Sopenharmony_ci	if (!offload) {
158962306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack,
159062306a36Sopenharmony_ci			       "Not enough memory to disable offload mode");
159162306a36Sopenharmony_ci		return -ENOMEM;
159262306a36Sopenharmony_ci	}
159362306a36Sopenharmony_ci	offload->cmd = TAPRIO_CMD_DESTROY;
159462306a36Sopenharmony_ci
159562306a36Sopenharmony_ci	err = ops->ndo_setup_tc(dev, TC_SETUP_QDISC_TAPRIO, offload);
159662306a36Sopenharmony_ci	if (err < 0) {
159762306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack,
159862306a36Sopenharmony_ci			       "Device failed to disable offload");
159962306a36Sopenharmony_ci		goto out;
160062306a36Sopenharmony_ci	}
160162306a36Sopenharmony_ci
160262306a36Sopenharmony_ci	q->offloaded = false;
160362306a36Sopenharmony_ci
160462306a36Sopenharmony_ciout:
160562306a36Sopenharmony_ci	taprio_offload_free(offload);
160662306a36Sopenharmony_ci
160762306a36Sopenharmony_ci	return err;
160862306a36Sopenharmony_ci}
160962306a36Sopenharmony_ci
161062306a36Sopenharmony_ci/* If full offload is enabled, the only possible clockid is the net device's
161162306a36Sopenharmony_ci * PHC. For that reason, specifying a clockid through netlink is incorrect.
161262306a36Sopenharmony_ci * For txtime-assist, it is implicitly assumed that the device's PHC is kept
161362306a36Sopenharmony_ci * in sync with the specified clockid via a user space daemon such as phc2sys.
161462306a36Sopenharmony_ci * For both software taprio and txtime-assist, the clockid is used for the
161562306a36Sopenharmony_ci * hrtimer that advances the schedule and hence mandatory.
161662306a36Sopenharmony_ci */
161762306a36Sopenharmony_cistatic int taprio_parse_clockid(struct Qdisc *sch, struct nlattr **tb,
161862306a36Sopenharmony_ci				struct netlink_ext_ack *extack)
161962306a36Sopenharmony_ci{
162062306a36Sopenharmony_ci	struct taprio_sched *q = qdisc_priv(sch);
162162306a36Sopenharmony_ci	struct net_device *dev = qdisc_dev(sch);
162262306a36Sopenharmony_ci	int err = -EINVAL;
162362306a36Sopenharmony_ci
162462306a36Sopenharmony_ci	if (FULL_OFFLOAD_IS_ENABLED(q->flags)) {
162562306a36Sopenharmony_ci		const struct ethtool_ops *ops = dev->ethtool_ops;
162662306a36Sopenharmony_ci		struct ethtool_ts_info info = {
162762306a36Sopenharmony_ci			.cmd = ETHTOOL_GET_TS_INFO,
162862306a36Sopenharmony_ci			.phc_index = -1,
162962306a36Sopenharmony_ci		};
163062306a36Sopenharmony_ci
163162306a36Sopenharmony_ci		if (tb[TCA_TAPRIO_ATTR_SCHED_CLOCKID]) {
163262306a36Sopenharmony_ci			NL_SET_ERR_MSG(extack,
163362306a36Sopenharmony_ci				       "The 'clockid' cannot be specified for full offload");
163462306a36Sopenharmony_ci			goto out;
163562306a36Sopenharmony_ci		}
163662306a36Sopenharmony_ci
163762306a36Sopenharmony_ci		if (ops && ops->get_ts_info)
163862306a36Sopenharmony_ci			err = ops->get_ts_info(dev, &info);
163962306a36Sopenharmony_ci
164062306a36Sopenharmony_ci		if (err || info.phc_index < 0) {
164162306a36Sopenharmony_ci			NL_SET_ERR_MSG(extack,
164262306a36Sopenharmony_ci				       "Device does not have a PTP clock");
164362306a36Sopenharmony_ci			err = -ENOTSUPP;
164462306a36Sopenharmony_ci			goto out;
164562306a36Sopenharmony_ci		}
164662306a36Sopenharmony_ci	} else if (tb[TCA_TAPRIO_ATTR_SCHED_CLOCKID]) {
164762306a36Sopenharmony_ci		int clockid = nla_get_s32(tb[TCA_TAPRIO_ATTR_SCHED_CLOCKID]);
164862306a36Sopenharmony_ci		enum tk_offsets tk_offset;
164962306a36Sopenharmony_ci
165062306a36Sopenharmony_ci		/* We only support static clockids and we don't allow
165162306a36Sopenharmony_ci		 * for it to be modified after the first init.
165262306a36Sopenharmony_ci		 */
165362306a36Sopenharmony_ci		if (clockid < 0 ||
165462306a36Sopenharmony_ci		    (q->clockid != -1 && q->clockid != clockid)) {
165562306a36Sopenharmony_ci			NL_SET_ERR_MSG(extack,
165662306a36Sopenharmony_ci				       "Changing the 'clockid' of a running schedule is not supported");
165762306a36Sopenharmony_ci			err = -ENOTSUPP;
165862306a36Sopenharmony_ci			goto out;
165962306a36Sopenharmony_ci		}
166062306a36Sopenharmony_ci
166162306a36Sopenharmony_ci		switch (clockid) {
166262306a36Sopenharmony_ci		case CLOCK_REALTIME:
166362306a36Sopenharmony_ci			tk_offset = TK_OFFS_REAL;
166462306a36Sopenharmony_ci			break;
166562306a36Sopenharmony_ci		case CLOCK_MONOTONIC:
166662306a36Sopenharmony_ci			tk_offset = TK_OFFS_MAX;
166762306a36Sopenharmony_ci			break;
166862306a36Sopenharmony_ci		case CLOCK_BOOTTIME:
166962306a36Sopenharmony_ci			tk_offset = TK_OFFS_BOOT;
167062306a36Sopenharmony_ci			break;
167162306a36Sopenharmony_ci		case CLOCK_TAI:
167262306a36Sopenharmony_ci			tk_offset = TK_OFFS_TAI;
167362306a36Sopenharmony_ci			break;
167462306a36Sopenharmony_ci		default:
167562306a36Sopenharmony_ci			NL_SET_ERR_MSG(extack, "Invalid 'clockid'");
167662306a36Sopenharmony_ci			err = -EINVAL;
167762306a36Sopenharmony_ci			goto out;
167862306a36Sopenharmony_ci		}
167962306a36Sopenharmony_ci		/* This pairs with READ_ONCE() in taprio_mono_to_any */
168062306a36Sopenharmony_ci		WRITE_ONCE(q->tk_offset, tk_offset);
168162306a36Sopenharmony_ci
168262306a36Sopenharmony_ci		q->clockid = clockid;
168362306a36Sopenharmony_ci	} else {
168462306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Specifying a 'clockid' is mandatory");
168562306a36Sopenharmony_ci		goto out;
168662306a36Sopenharmony_ci	}
168762306a36Sopenharmony_ci
168862306a36Sopenharmony_ci	/* Everything went ok, return success. */
168962306a36Sopenharmony_ci	err = 0;
169062306a36Sopenharmony_ci
169162306a36Sopenharmony_ciout:
169262306a36Sopenharmony_ci	return err;
169362306a36Sopenharmony_ci}
169462306a36Sopenharmony_ci
169562306a36Sopenharmony_cistatic int taprio_parse_tc_entry(struct Qdisc *sch,
169662306a36Sopenharmony_ci				 struct nlattr *opt,
169762306a36Sopenharmony_ci				 u32 max_sdu[TC_QOPT_MAX_QUEUE],
169862306a36Sopenharmony_ci				 u32 fp[TC_QOPT_MAX_QUEUE],
169962306a36Sopenharmony_ci				 unsigned long *seen_tcs,
170062306a36Sopenharmony_ci				 struct netlink_ext_ack *extack)
170162306a36Sopenharmony_ci{
170262306a36Sopenharmony_ci	struct nlattr *tb[TCA_TAPRIO_TC_ENTRY_MAX + 1] = { };
170362306a36Sopenharmony_ci	struct net_device *dev = qdisc_dev(sch);
170462306a36Sopenharmony_ci	int err, tc;
170562306a36Sopenharmony_ci	u32 val;
170662306a36Sopenharmony_ci
170762306a36Sopenharmony_ci	err = nla_parse_nested(tb, TCA_TAPRIO_TC_ENTRY_MAX, opt,
170862306a36Sopenharmony_ci			       taprio_tc_policy, extack);
170962306a36Sopenharmony_ci	if (err < 0)
171062306a36Sopenharmony_ci		return err;
171162306a36Sopenharmony_ci
171262306a36Sopenharmony_ci	if (!tb[TCA_TAPRIO_TC_ENTRY_INDEX]) {
171362306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "TC entry index missing");
171462306a36Sopenharmony_ci		return -EINVAL;
171562306a36Sopenharmony_ci	}
171662306a36Sopenharmony_ci
171762306a36Sopenharmony_ci	tc = nla_get_u32(tb[TCA_TAPRIO_TC_ENTRY_INDEX]);
171862306a36Sopenharmony_ci	if (tc >= TC_QOPT_MAX_QUEUE) {
171962306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "TC entry index out of range");
172062306a36Sopenharmony_ci		return -ERANGE;
172162306a36Sopenharmony_ci	}
172262306a36Sopenharmony_ci
172362306a36Sopenharmony_ci	if (*seen_tcs & BIT(tc)) {
172462306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Duplicate TC entry");
172562306a36Sopenharmony_ci		return -EINVAL;
172662306a36Sopenharmony_ci	}
172762306a36Sopenharmony_ci
172862306a36Sopenharmony_ci	*seen_tcs |= BIT(tc);
172962306a36Sopenharmony_ci
173062306a36Sopenharmony_ci	if (tb[TCA_TAPRIO_TC_ENTRY_MAX_SDU]) {
173162306a36Sopenharmony_ci		val = nla_get_u32(tb[TCA_TAPRIO_TC_ENTRY_MAX_SDU]);
173262306a36Sopenharmony_ci		if (val > dev->max_mtu) {
173362306a36Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "TC max SDU exceeds device max MTU");
173462306a36Sopenharmony_ci			return -ERANGE;
173562306a36Sopenharmony_ci		}
173662306a36Sopenharmony_ci
173762306a36Sopenharmony_ci		max_sdu[tc] = val;
173862306a36Sopenharmony_ci	}
173962306a36Sopenharmony_ci
174062306a36Sopenharmony_ci	if (tb[TCA_TAPRIO_TC_ENTRY_FP])
174162306a36Sopenharmony_ci		fp[tc] = nla_get_u32(tb[TCA_TAPRIO_TC_ENTRY_FP]);
174262306a36Sopenharmony_ci
174362306a36Sopenharmony_ci	return 0;
174462306a36Sopenharmony_ci}
174562306a36Sopenharmony_ci
174662306a36Sopenharmony_cistatic int taprio_parse_tc_entries(struct Qdisc *sch,
174762306a36Sopenharmony_ci				   struct nlattr *opt,
174862306a36Sopenharmony_ci				   struct netlink_ext_ack *extack)
174962306a36Sopenharmony_ci{
175062306a36Sopenharmony_ci	struct taprio_sched *q = qdisc_priv(sch);
175162306a36Sopenharmony_ci	struct net_device *dev = qdisc_dev(sch);
175262306a36Sopenharmony_ci	u32 max_sdu[TC_QOPT_MAX_QUEUE];
175362306a36Sopenharmony_ci	bool have_preemption = false;
175462306a36Sopenharmony_ci	unsigned long seen_tcs = 0;
175562306a36Sopenharmony_ci	u32 fp[TC_QOPT_MAX_QUEUE];
175662306a36Sopenharmony_ci	struct nlattr *n;
175762306a36Sopenharmony_ci	int tc, rem;
175862306a36Sopenharmony_ci	int err = 0;
175962306a36Sopenharmony_ci
176062306a36Sopenharmony_ci	for (tc = 0; tc < TC_QOPT_MAX_QUEUE; tc++) {
176162306a36Sopenharmony_ci		max_sdu[tc] = q->max_sdu[tc];
176262306a36Sopenharmony_ci		fp[tc] = q->fp[tc];
176362306a36Sopenharmony_ci	}
176462306a36Sopenharmony_ci
176562306a36Sopenharmony_ci	nla_for_each_nested(n, opt, rem) {
176662306a36Sopenharmony_ci		if (nla_type(n) != TCA_TAPRIO_ATTR_TC_ENTRY)
176762306a36Sopenharmony_ci			continue;
176862306a36Sopenharmony_ci
176962306a36Sopenharmony_ci		err = taprio_parse_tc_entry(sch, n, max_sdu, fp, &seen_tcs,
177062306a36Sopenharmony_ci					    extack);
177162306a36Sopenharmony_ci		if (err)
177262306a36Sopenharmony_ci			return err;
177362306a36Sopenharmony_ci	}
177462306a36Sopenharmony_ci
177562306a36Sopenharmony_ci	for (tc = 0; tc < TC_QOPT_MAX_QUEUE; tc++) {
177662306a36Sopenharmony_ci		q->max_sdu[tc] = max_sdu[tc];
177762306a36Sopenharmony_ci		q->fp[tc] = fp[tc];
177862306a36Sopenharmony_ci		if (fp[tc] != TC_FP_EXPRESS)
177962306a36Sopenharmony_ci			have_preemption = true;
178062306a36Sopenharmony_ci	}
178162306a36Sopenharmony_ci
178262306a36Sopenharmony_ci	if (have_preemption) {
178362306a36Sopenharmony_ci		if (!FULL_OFFLOAD_IS_ENABLED(q->flags)) {
178462306a36Sopenharmony_ci			NL_SET_ERR_MSG(extack,
178562306a36Sopenharmony_ci				       "Preemption only supported with full offload");
178662306a36Sopenharmony_ci			return -EOPNOTSUPP;
178762306a36Sopenharmony_ci		}
178862306a36Sopenharmony_ci
178962306a36Sopenharmony_ci		if (!ethtool_dev_mm_supported(dev)) {
179062306a36Sopenharmony_ci			NL_SET_ERR_MSG(extack,
179162306a36Sopenharmony_ci				       "Device does not support preemption");
179262306a36Sopenharmony_ci			return -EOPNOTSUPP;
179362306a36Sopenharmony_ci		}
179462306a36Sopenharmony_ci	}
179562306a36Sopenharmony_ci
179662306a36Sopenharmony_ci	return err;
179762306a36Sopenharmony_ci}
179862306a36Sopenharmony_ci
179962306a36Sopenharmony_cistatic int taprio_mqprio_cmp(const struct net_device *dev,
180062306a36Sopenharmony_ci			     const struct tc_mqprio_qopt *mqprio)
180162306a36Sopenharmony_ci{
180262306a36Sopenharmony_ci	int i;
180362306a36Sopenharmony_ci
180462306a36Sopenharmony_ci	if (!mqprio || mqprio->num_tc != dev->num_tc)
180562306a36Sopenharmony_ci		return -1;
180662306a36Sopenharmony_ci
180762306a36Sopenharmony_ci	for (i = 0; i < mqprio->num_tc; i++)
180862306a36Sopenharmony_ci		if (dev->tc_to_txq[i].count != mqprio->count[i] ||
180962306a36Sopenharmony_ci		    dev->tc_to_txq[i].offset != mqprio->offset[i])
181062306a36Sopenharmony_ci			return -1;
181162306a36Sopenharmony_ci
181262306a36Sopenharmony_ci	for (i = 0; i <= TC_BITMASK; i++)
181362306a36Sopenharmony_ci		if (dev->prio_tc_map[i] != mqprio->prio_tc_map[i])
181462306a36Sopenharmony_ci			return -1;
181562306a36Sopenharmony_ci
181662306a36Sopenharmony_ci	return 0;
181762306a36Sopenharmony_ci}
181862306a36Sopenharmony_ci
181962306a36Sopenharmony_ci/* The semantics of the 'flags' argument in relation to 'change()'
182062306a36Sopenharmony_ci * requests, are interpreted following two rules (which are applied in
182162306a36Sopenharmony_ci * this order): (1) an omitted 'flags' argument is interpreted as
182262306a36Sopenharmony_ci * zero; (2) the 'flags' of a "running" taprio instance cannot be
182362306a36Sopenharmony_ci * changed.
182462306a36Sopenharmony_ci */
182562306a36Sopenharmony_cistatic int taprio_new_flags(const struct nlattr *attr, u32 old,
182662306a36Sopenharmony_ci			    struct netlink_ext_ack *extack)
182762306a36Sopenharmony_ci{
182862306a36Sopenharmony_ci	u32 new = 0;
182962306a36Sopenharmony_ci
183062306a36Sopenharmony_ci	if (attr)
183162306a36Sopenharmony_ci		new = nla_get_u32(attr);
183262306a36Sopenharmony_ci
183362306a36Sopenharmony_ci	if (old != TAPRIO_FLAGS_INVALID && old != new) {
183462306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Changing 'flags' of a running schedule is not supported");
183562306a36Sopenharmony_ci		return -EOPNOTSUPP;
183662306a36Sopenharmony_ci	}
183762306a36Sopenharmony_ci
183862306a36Sopenharmony_ci	if (!taprio_flags_valid(new)) {
183962306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Specified 'flags' are not valid");
184062306a36Sopenharmony_ci		return -EINVAL;
184162306a36Sopenharmony_ci	}
184262306a36Sopenharmony_ci
184362306a36Sopenharmony_ci	return new;
184462306a36Sopenharmony_ci}
184562306a36Sopenharmony_ci
184662306a36Sopenharmony_cistatic int taprio_change(struct Qdisc *sch, struct nlattr *opt,
184762306a36Sopenharmony_ci			 struct netlink_ext_ack *extack)
184862306a36Sopenharmony_ci{
184962306a36Sopenharmony_ci	struct qdisc_size_table *stab = rtnl_dereference(sch->stab);
185062306a36Sopenharmony_ci	struct nlattr *tb[TCA_TAPRIO_ATTR_MAX + 1] = { };
185162306a36Sopenharmony_ci	struct sched_gate_list *oper, *admin, *new_admin;
185262306a36Sopenharmony_ci	struct taprio_sched *q = qdisc_priv(sch);
185362306a36Sopenharmony_ci	struct net_device *dev = qdisc_dev(sch);
185462306a36Sopenharmony_ci	struct tc_mqprio_qopt *mqprio = NULL;
185562306a36Sopenharmony_ci	unsigned long flags;
185662306a36Sopenharmony_ci	ktime_t start;
185762306a36Sopenharmony_ci	int i, err;
185862306a36Sopenharmony_ci
185962306a36Sopenharmony_ci	err = nla_parse_nested_deprecated(tb, TCA_TAPRIO_ATTR_MAX, opt,
186062306a36Sopenharmony_ci					  taprio_policy, extack);
186162306a36Sopenharmony_ci	if (err < 0)
186262306a36Sopenharmony_ci		return err;
186362306a36Sopenharmony_ci
186462306a36Sopenharmony_ci	if (tb[TCA_TAPRIO_ATTR_PRIOMAP])
186562306a36Sopenharmony_ci		mqprio = nla_data(tb[TCA_TAPRIO_ATTR_PRIOMAP]);
186662306a36Sopenharmony_ci
186762306a36Sopenharmony_ci	err = taprio_new_flags(tb[TCA_TAPRIO_ATTR_FLAGS],
186862306a36Sopenharmony_ci			       q->flags, extack);
186962306a36Sopenharmony_ci	if (err < 0)
187062306a36Sopenharmony_ci		return err;
187162306a36Sopenharmony_ci
187262306a36Sopenharmony_ci	q->flags = err;
187362306a36Sopenharmony_ci
187462306a36Sopenharmony_ci	err = taprio_parse_mqprio_opt(dev, mqprio, extack, q->flags);
187562306a36Sopenharmony_ci	if (err < 0)
187662306a36Sopenharmony_ci		return err;
187762306a36Sopenharmony_ci
187862306a36Sopenharmony_ci	err = taprio_parse_tc_entries(sch, opt, extack);
187962306a36Sopenharmony_ci	if (err)
188062306a36Sopenharmony_ci		return err;
188162306a36Sopenharmony_ci
188262306a36Sopenharmony_ci	new_admin = kzalloc(sizeof(*new_admin), GFP_KERNEL);
188362306a36Sopenharmony_ci	if (!new_admin) {
188462306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Not enough memory for a new schedule");
188562306a36Sopenharmony_ci		return -ENOMEM;
188662306a36Sopenharmony_ci	}
188762306a36Sopenharmony_ci	INIT_LIST_HEAD(&new_admin->entries);
188862306a36Sopenharmony_ci
188962306a36Sopenharmony_ci	oper = rtnl_dereference(q->oper_sched);
189062306a36Sopenharmony_ci	admin = rtnl_dereference(q->admin_sched);
189162306a36Sopenharmony_ci
189262306a36Sopenharmony_ci	/* no changes - no new mqprio settings */
189362306a36Sopenharmony_ci	if (!taprio_mqprio_cmp(dev, mqprio))
189462306a36Sopenharmony_ci		mqprio = NULL;
189562306a36Sopenharmony_ci
189662306a36Sopenharmony_ci	if (mqprio && (oper || admin)) {
189762306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Changing the traffic mapping of a running schedule is not supported");
189862306a36Sopenharmony_ci		err = -ENOTSUPP;
189962306a36Sopenharmony_ci		goto free_sched;
190062306a36Sopenharmony_ci	}
190162306a36Sopenharmony_ci
190262306a36Sopenharmony_ci	if (mqprio) {
190362306a36Sopenharmony_ci		err = netdev_set_num_tc(dev, mqprio->num_tc);
190462306a36Sopenharmony_ci		if (err)
190562306a36Sopenharmony_ci			goto free_sched;
190662306a36Sopenharmony_ci		for (i = 0; i < mqprio->num_tc; i++) {
190762306a36Sopenharmony_ci			netdev_set_tc_queue(dev, i,
190862306a36Sopenharmony_ci					    mqprio->count[i],
190962306a36Sopenharmony_ci					    mqprio->offset[i]);
191062306a36Sopenharmony_ci			q->cur_txq[i] = mqprio->offset[i];
191162306a36Sopenharmony_ci		}
191262306a36Sopenharmony_ci
191362306a36Sopenharmony_ci		/* Always use supplied priority mappings */
191462306a36Sopenharmony_ci		for (i = 0; i <= TC_BITMASK; i++)
191562306a36Sopenharmony_ci			netdev_set_prio_tc_map(dev, i,
191662306a36Sopenharmony_ci					       mqprio->prio_tc_map[i]);
191762306a36Sopenharmony_ci	}
191862306a36Sopenharmony_ci
191962306a36Sopenharmony_ci	err = parse_taprio_schedule(q, tb, new_admin, extack);
192062306a36Sopenharmony_ci	if (err < 0)
192162306a36Sopenharmony_ci		goto free_sched;
192262306a36Sopenharmony_ci
192362306a36Sopenharmony_ci	if (new_admin->num_entries == 0) {
192462306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "There should be at least one entry in the schedule");
192562306a36Sopenharmony_ci		err = -EINVAL;
192662306a36Sopenharmony_ci		goto free_sched;
192762306a36Sopenharmony_ci	}
192862306a36Sopenharmony_ci
192962306a36Sopenharmony_ci	err = taprio_parse_clockid(sch, tb, extack);
193062306a36Sopenharmony_ci	if (err < 0)
193162306a36Sopenharmony_ci		goto free_sched;
193262306a36Sopenharmony_ci
193362306a36Sopenharmony_ci	taprio_set_picos_per_byte(dev, q);
193462306a36Sopenharmony_ci	taprio_update_queue_max_sdu(q, new_admin, stab);
193562306a36Sopenharmony_ci
193662306a36Sopenharmony_ci	if (FULL_OFFLOAD_IS_ENABLED(q->flags))
193762306a36Sopenharmony_ci		err = taprio_enable_offload(dev, q, new_admin, extack);
193862306a36Sopenharmony_ci	else
193962306a36Sopenharmony_ci		err = taprio_disable_offload(dev, q, extack);
194062306a36Sopenharmony_ci	if (err)
194162306a36Sopenharmony_ci		goto free_sched;
194262306a36Sopenharmony_ci
194362306a36Sopenharmony_ci	/* Protects against enqueue()/dequeue() */
194462306a36Sopenharmony_ci	spin_lock_bh(qdisc_lock(sch));
194562306a36Sopenharmony_ci
194662306a36Sopenharmony_ci	if (tb[TCA_TAPRIO_ATTR_TXTIME_DELAY]) {
194762306a36Sopenharmony_ci		if (!TXTIME_ASSIST_IS_ENABLED(q->flags)) {
194862306a36Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "txtime-delay can only be set when txtime-assist mode is enabled");
194962306a36Sopenharmony_ci			err = -EINVAL;
195062306a36Sopenharmony_ci			goto unlock;
195162306a36Sopenharmony_ci		}
195262306a36Sopenharmony_ci
195362306a36Sopenharmony_ci		q->txtime_delay = nla_get_u32(tb[TCA_TAPRIO_ATTR_TXTIME_DELAY]);
195462306a36Sopenharmony_ci	}
195562306a36Sopenharmony_ci
195662306a36Sopenharmony_ci	if (!TXTIME_ASSIST_IS_ENABLED(q->flags) &&
195762306a36Sopenharmony_ci	    !FULL_OFFLOAD_IS_ENABLED(q->flags) &&
195862306a36Sopenharmony_ci	    !hrtimer_active(&q->advance_timer)) {
195962306a36Sopenharmony_ci		hrtimer_init(&q->advance_timer, q->clockid, HRTIMER_MODE_ABS);
196062306a36Sopenharmony_ci		q->advance_timer.function = advance_sched;
196162306a36Sopenharmony_ci	}
196262306a36Sopenharmony_ci
196362306a36Sopenharmony_ci	err = taprio_get_start_time(sch, new_admin, &start);
196462306a36Sopenharmony_ci	if (err < 0) {
196562306a36Sopenharmony_ci		NL_SET_ERR_MSG(extack, "Internal error: failed get start time");
196662306a36Sopenharmony_ci		goto unlock;
196762306a36Sopenharmony_ci	}
196862306a36Sopenharmony_ci
196962306a36Sopenharmony_ci	setup_txtime(q, new_admin, start);
197062306a36Sopenharmony_ci
197162306a36Sopenharmony_ci	if (TXTIME_ASSIST_IS_ENABLED(q->flags)) {
197262306a36Sopenharmony_ci		if (!oper) {
197362306a36Sopenharmony_ci			rcu_assign_pointer(q->oper_sched, new_admin);
197462306a36Sopenharmony_ci			err = 0;
197562306a36Sopenharmony_ci			new_admin = NULL;
197662306a36Sopenharmony_ci			goto unlock;
197762306a36Sopenharmony_ci		}
197862306a36Sopenharmony_ci
197962306a36Sopenharmony_ci		rcu_assign_pointer(q->admin_sched, new_admin);
198062306a36Sopenharmony_ci		if (admin)
198162306a36Sopenharmony_ci			call_rcu(&admin->rcu, taprio_free_sched_cb);
198262306a36Sopenharmony_ci	} else {
198362306a36Sopenharmony_ci		setup_first_end_time(q, new_admin, start);
198462306a36Sopenharmony_ci
198562306a36Sopenharmony_ci		/* Protects against advance_sched() */
198662306a36Sopenharmony_ci		spin_lock_irqsave(&q->current_entry_lock, flags);
198762306a36Sopenharmony_ci
198862306a36Sopenharmony_ci		taprio_start_sched(sch, start, new_admin);
198962306a36Sopenharmony_ci
199062306a36Sopenharmony_ci		rcu_assign_pointer(q->admin_sched, new_admin);
199162306a36Sopenharmony_ci		if (admin)
199262306a36Sopenharmony_ci			call_rcu(&admin->rcu, taprio_free_sched_cb);
199362306a36Sopenharmony_ci
199462306a36Sopenharmony_ci		spin_unlock_irqrestore(&q->current_entry_lock, flags);
199562306a36Sopenharmony_ci
199662306a36Sopenharmony_ci		if (FULL_OFFLOAD_IS_ENABLED(q->flags))
199762306a36Sopenharmony_ci			taprio_offload_config_changed(q);
199862306a36Sopenharmony_ci	}
199962306a36Sopenharmony_ci
200062306a36Sopenharmony_ci	new_admin = NULL;
200162306a36Sopenharmony_ci	err = 0;
200262306a36Sopenharmony_ci
200362306a36Sopenharmony_ci	if (!stab)
200462306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack,
200562306a36Sopenharmony_ci				   "Size table not specified, frame length estimations may be inaccurate");
200662306a36Sopenharmony_ci
200762306a36Sopenharmony_ciunlock:
200862306a36Sopenharmony_ci	spin_unlock_bh(qdisc_lock(sch));
200962306a36Sopenharmony_ci
201062306a36Sopenharmony_cifree_sched:
201162306a36Sopenharmony_ci	if (new_admin)
201262306a36Sopenharmony_ci		call_rcu(&new_admin->rcu, taprio_free_sched_cb);
201362306a36Sopenharmony_ci
201462306a36Sopenharmony_ci	return err;
201562306a36Sopenharmony_ci}
201662306a36Sopenharmony_ci
201762306a36Sopenharmony_cistatic void taprio_reset(struct Qdisc *sch)
201862306a36Sopenharmony_ci{
201962306a36Sopenharmony_ci	struct taprio_sched *q = qdisc_priv(sch);
202062306a36Sopenharmony_ci	struct net_device *dev = qdisc_dev(sch);
202162306a36Sopenharmony_ci	int i;
202262306a36Sopenharmony_ci
202362306a36Sopenharmony_ci	hrtimer_cancel(&q->advance_timer);
202462306a36Sopenharmony_ci
202562306a36Sopenharmony_ci	if (q->qdiscs) {
202662306a36Sopenharmony_ci		for (i = 0; i < dev->num_tx_queues; i++)
202762306a36Sopenharmony_ci			if (q->qdiscs[i])
202862306a36Sopenharmony_ci				qdisc_reset(q->qdiscs[i]);
202962306a36Sopenharmony_ci	}
203062306a36Sopenharmony_ci}
203162306a36Sopenharmony_ci
203262306a36Sopenharmony_cistatic void taprio_destroy(struct Qdisc *sch)
203362306a36Sopenharmony_ci{
203462306a36Sopenharmony_ci	struct taprio_sched *q = qdisc_priv(sch);
203562306a36Sopenharmony_ci	struct net_device *dev = qdisc_dev(sch);
203662306a36Sopenharmony_ci	struct sched_gate_list *oper, *admin;
203762306a36Sopenharmony_ci	unsigned int i;
203862306a36Sopenharmony_ci
203962306a36Sopenharmony_ci	list_del(&q->taprio_list);
204062306a36Sopenharmony_ci
204162306a36Sopenharmony_ci	/* Note that taprio_reset() might not be called if an error
204262306a36Sopenharmony_ci	 * happens in qdisc_create(), after taprio_init() has been called.
204362306a36Sopenharmony_ci	 */
204462306a36Sopenharmony_ci	hrtimer_cancel(&q->advance_timer);
204562306a36Sopenharmony_ci	qdisc_synchronize(sch);
204662306a36Sopenharmony_ci
204762306a36Sopenharmony_ci	taprio_disable_offload(dev, q, NULL);
204862306a36Sopenharmony_ci
204962306a36Sopenharmony_ci	if (q->qdiscs) {
205062306a36Sopenharmony_ci		for (i = 0; i < dev->num_tx_queues; i++)
205162306a36Sopenharmony_ci			qdisc_put(q->qdiscs[i]);
205262306a36Sopenharmony_ci
205362306a36Sopenharmony_ci		kfree(q->qdiscs);
205462306a36Sopenharmony_ci	}
205562306a36Sopenharmony_ci	q->qdiscs = NULL;
205662306a36Sopenharmony_ci
205762306a36Sopenharmony_ci	netdev_reset_tc(dev);
205862306a36Sopenharmony_ci
205962306a36Sopenharmony_ci	oper = rtnl_dereference(q->oper_sched);
206062306a36Sopenharmony_ci	admin = rtnl_dereference(q->admin_sched);
206162306a36Sopenharmony_ci
206262306a36Sopenharmony_ci	if (oper)
206362306a36Sopenharmony_ci		call_rcu(&oper->rcu, taprio_free_sched_cb);
206462306a36Sopenharmony_ci
206562306a36Sopenharmony_ci	if (admin)
206662306a36Sopenharmony_ci		call_rcu(&admin->rcu, taprio_free_sched_cb);
206762306a36Sopenharmony_ci
206862306a36Sopenharmony_ci	taprio_cleanup_broken_mqprio(q);
206962306a36Sopenharmony_ci}
207062306a36Sopenharmony_ci
207162306a36Sopenharmony_cistatic int taprio_init(struct Qdisc *sch, struct nlattr *opt,
207262306a36Sopenharmony_ci		       struct netlink_ext_ack *extack)
207362306a36Sopenharmony_ci{
207462306a36Sopenharmony_ci	struct taprio_sched *q = qdisc_priv(sch);
207562306a36Sopenharmony_ci	struct net_device *dev = qdisc_dev(sch);
207662306a36Sopenharmony_ci	int i, tc;
207762306a36Sopenharmony_ci
207862306a36Sopenharmony_ci	spin_lock_init(&q->current_entry_lock);
207962306a36Sopenharmony_ci
208062306a36Sopenharmony_ci	hrtimer_init(&q->advance_timer, CLOCK_TAI, HRTIMER_MODE_ABS);
208162306a36Sopenharmony_ci	q->advance_timer.function = advance_sched;
208262306a36Sopenharmony_ci
208362306a36Sopenharmony_ci	q->root = sch;
208462306a36Sopenharmony_ci
208562306a36Sopenharmony_ci	/* We only support static clockids. Use an invalid value as default
208662306a36Sopenharmony_ci	 * and get the valid one on taprio_change().
208762306a36Sopenharmony_ci	 */
208862306a36Sopenharmony_ci	q->clockid = -1;
208962306a36Sopenharmony_ci	q->flags = TAPRIO_FLAGS_INVALID;
209062306a36Sopenharmony_ci
209162306a36Sopenharmony_ci	list_add(&q->taprio_list, &taprio_list);
209262306a36Sopenharmony_ci
209362306a36Sopenharmony_ci	if (sch->parent != TC_H_ROOT) {
209462306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Can only be attached as root qdisc");
209562306a36Sopenharmony_ci		return -EOPNOTSUPP;
209662306a36Sopenharmony_ci	}
209762306a36Sopenharmony_ci
209862306a36Sopenharmony_ci	if (!netif_is_multiqueue(dev)) {
209962306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Multi-queue device is required");
210062306a36Sopenharmony_ci		return -EOPNOTSUPP;
210162306a36Sopenharmony_ci	}
210262306a36Sopenharmony_ci
210362306a36Sopenharmony_ci	q->qdiscs = kcalloc(dev->num_tx_queues, sizeof(q->qdiscs[0]),
210462306a36Sopenharmony_ci			    GFP_KERNEL);
210562306a36Sopenharmony_ci	if (!q->qdiscs)
210662306a36Sopenharmony_ci		return -ENOMEM;
210762306a36Sopenharmony_ci
210862306a36Sopenharmony_ci	if (!opt)
210962306a36Sopenharmony_ci		return -EINVAL;
211062306a36Sopenharmony_ci
211162306a36Sopenharmony_ci	for (i = 0; i < dev->num_tx_queues; i++) {
211262306a36Sopenharmony_ci		struct netdev_queue *dev_queue;
211362306a36Sopenharmony_ci		struct Qdisc *qdisc;
211462306a36Sopenharmony_ci
211562306a36Sopenharmony_ci		dev_queue = netdev_get_tx_queue(dev, i);
211662306a36Sopenharmony_ci		qdisc = qdisc_create_dflt(dev_queue,
211762306a36Sopenharmony_ci					  &pfifo_qdisc_ops,
211862306a36Sopenharmony_ci					  TC_H_MAKE(TC_H_MAJ(sch->handle),
211962306a36Sopenharmony_ci						    TC_H_MIN(i + 1)),
212062306a36Sopenharmony_ci					  extack);
212162306a36Sopenharmony_ci		if (!qdisc)
212262306a36Sopenharmony_ci			return -ENOMEM;
212362306a36Sopenharmony_ci
212462306a36Sopenharmony_ci		if (i < dev->real_num_tx_queues)
212562306a36Sopenharmony_ci			qdisc_hash_add(qdisc, false);
212662306a36Sopenharmony_ci
212762306a36Sopenharmony_ci		q->qdiscs[i] = qdisc;
212862306a36Sopenharmony_ci	}
212962306a36Sopenharmony_ci
213062306a36Sopenharmony_ci	for (tc = 0; tc < TC_QOPT_MAX_QUEUE; tc++)
213162306a36Sopenharmony_ci		q->fp[tc] = TC_FP_EXPRESS;
213262306a36Sopenharmony_ci
213362306a36Sopenharmony_ci	taprio_detect_broken_mqprio(q);
213462306a36Sopenharmony_ci
213562306a36Sopenharmony_ci	return taprio_change(sch, opt, extack);
213662306a36Sopenharmony_ci}
213762306a36Sopenharmony_ci
213862306a36Sopenharmony_cistatic void taprio_attach(struct Qdisc *sch)
213962306a36Sopenharmony_ci{
214062306a36Sopenharmony_ci	struct taprio_sched *q = qdisc_priv(sch);
214162306a36Sopenharmony_ci	struct net_device *dev = qdisc_dev(sch);
214262306a36Sopenharmony_ci	unsigned int ntx;
214362306a36Sopenharmony_ci
214462306a36Sopenharmony_ci	/* Attach underlying qdisc */
214562306a36Sopenharmony_ci	for (ntx = 0; ntx < dev->num_tx_queues; ntx++) {
214662306a36Sopenharmony_ci		struct netdev_queue *dev_queue = netdev_get_tx_queue(dev, ntx);
214762306a36Sopenharmony_ci		struct Qdisc *old, *dev_queue_qdisc;
214862306a36Sopenharmony_ci
214962306a36Sopenharmony_ci		if (FULL_OFFLOAD_IS_ENABLED(q->flags)) {
215062306a36Sopenharmony_ci			struct Qdisc *qdisc = q->qdiscs[ntx];
215162306a36Sopenharmony_ci
215262306a36Sopenharmony_ci			/* In offload mode, the root taprio qdisc is bypassed
215362306a36Sopenharmony_ci			 * and the netdev TX queues see the children directly
215462306a36Sopenharmony_ci			 */
215562306a36Sopenharmony_ci			qdisc->flags |= TCQ_F_ONETXQUEUE | TCQ_F_NOPARENT;
215662306a36Sopenharmony_ci			dev_queue_qdisc = qdisc;
215762306a36Sopenharmony_ci		} else {
215862306a36Sopenharmony_ci			/* In software mode, attach the root taprio qdisc
215962306a36Sopenharmony_ci			 * to all netdev TX queues, so that dev_qdisc_enqueue()
216062306a36Sopenharmony_ci			 * goes through taprio_enqueue().
216162306a36Sopenharmony_ci			 */
216262306a36Sopenharmony_ci			dev_queue_qdisc = sch;
216362306a36Sopenharmony_ci		}
216462306a36Sopenharmony_ci		old = dev_graft_qdisc(dev_queue, dev_queue_qdisc);
216562306a36Sopenharmony_ci		/* The qdisc's refcount requires to be elevated once
216662306a36Sopenharmony_ci		 * for each netdev TX queue it is grafted onto
216762306a36Sopenharmony_ci		 */
216862306a36Sopenharmony_ci		qdisc_refcount_inc(dev_queue_qdisc);
216962306a36Sopenharmony_ci		if (old)
217062306a36Sopenharmony_ci			qdisc_put(old);
217162306a36Sopenharmony_ci	}
217262306a36Sopenharmony_ci}
217362306a36Sopenharmony_ci
217462306a36Sopenharmony_cistatic struct netdev_queue *taprio_queue_get(struct Qdisc *sch,
217562306a36Sopenharmony_ci					     unsigned long cl)
217662306a36Sopenharmony_ci{
217762306a36Sopenharmony_ci	struct net_device *dev = qdisc_dev(sch);
217862306a36Sopenharmony_ci	unsigned long ntx = cl - 1;
217962306a36Sopenharmony_ci
218062306a36Sopenharmony_ci	if (ntx >= dev->num_tx_queues)
218162306a36Sopenharmony_ci		return NULL;
218262306a36Sopenharmony_ci
218362306a36Sopenharmony_ci	return netdev_get_tx_queue(dev, ntx);
218462306a36Sopenharmony_ci}
218562306a36Sopenharmony_ci
218662306a36Sopenharmony_cistatic int taprio_graft(struct Qdisc *sch, unsigned long cl,
218762306a36Sopenharmony_ci			struct Qdisc *new, struct Qdisc **old,
218862306a36Sopenharmony_ci			struct netlink_ext_ack *extack)
218962306a36Sopenharmony_ci{
219062306a36Sopenharmony_ci	struct taprio_sched *q = qdisc_priv(sch);
219162306a36Sopenharmony_ci	struct net_device *dev = qdisc_dev(sch);
219262306a36Sopenharmony_ci	struct netdev_queue *dev_queue = taprio_queue_get(sch, cl);
219362306a36Sopenharmony_ci
219462306a36Sopenharmony_ci	if (!dev_queue)
219562306a36Sopenharmony_ci		return -EINVAL;
219662306a36Sopenharmony_ci
219762306a36Sopenharmony_ci	if (dev->flags & IFF_UP)
219862306a36Sopenharmony_ci		dev_deactivate(dev);
219962306a36Sopenharmony_ci
220062306a36Sopenharmony_ci	/* In offload mode, the child Qdisc is directly attached to the netdev
220162306a36Sopenharmony_ci	 * TX queue, and thus, we need to keep its refcount elevated in order
220262306a36Sopenharmony_ci	 * to counteract qdisc_graft()'s call to qdisc_put() once per TX queue.
220362306a36Sopenharmony_ci	 * However, save the reference to the new qdisc in the private array in
220462306a36Sopenharmony_ci	 * both software and offload cases, to have an up-to-date reference to
220562306a36Sopenharmony_ci	 * our children.
220662306a36Sopenharmony_ci	 */
220762306a36Sopenharmony_ci	*old = q->qdiscs[cl - 1];
220862306a36Sopenharmony_ci	if (FULL_OFFLOAD_IS_ENABLED(q->flags)) {
220962306a36Sopenharmony_ci		WARN_ON_ONCE(dev_graft_qdisc(dev_queue, new) != *old);
221062306a36Sopenharmony_ci		if (new)
221162306a36Sopenharmony_ci			qdisc_refcount_inc(new);
221262306a36Sopenharmony_ci		if (*old)
221362306a36Sopenharmony_ci			qdisc_put(*old);
221462306a36Sopenharmony_ci	}
221562306a36Sopenharmony_ci
221662306a36Sopenharmony_ci	q->qdiscs[cl - 1] = new;
221762306a36Sopenharmony_ci	if (new)
221862306a36Sopenharmony_ci		new->flags |= TCQ_F_ONETXQUEUE | TCQ_F_NOPARENT;
221962306a36Sopenharmony_ci
222062306a36Sopenharmony_ci	if (dev->flags & IFF_UP)
222162306a36Sopenharmony_ci		dev_activate(dev);
222262306a36Sopenharmony_ci
222362306a36Sopenharmony_ci	return 0;
222462306a36Sopenharmony_ci}
222562306a36Sopenharmony_ci
222662306a36Sopenharmony_cistatic int dump_entry(struct sk_buff *msg,
222762306a36Sopenharmony_ci		      const struct sched_entry *entry)
222862306a36Sopenharmony_ci{
222962306a36Sopenharmony_ci	struct nlattr *item;
223062306a36Sopenharmony_ci
223162306a36Sopenharmony_ci	item = nla_nest_start_noflag(msg, TCA_TAPRIO_SCHED_ENTRY);
223262306a36Sopenharmony_ci	if (!item)
223362306a36Sopenharmony_ci		return -ENOSPC;
223462306a36Sopenharmony_ci
223562306a36Sopenharmony_ci	if (nla_put_u32(msg, TCA_TAPRIO_SCHED_ENTRY_INDEX, entry->index))
223662306a36Sopenharmony_ci		goto nla_put_failure;
223762306a36Sopenharmony_ci
223862306a36Sopenharmony_ci	if (nla_put_u8(msg, TCA_TAPRIO_SCHED_ENTRY_CMD, entry->command))
223962306a36Sopenharmony_ci		goto nla_put_failure;
224062306a36Sopenharmony_ci
224162306a36Sopenharmony_ci	if (nla_put_u32(msg, TCA_TAPRIO_SCHED_ENTRY_GATE_MASK,
224262306a36Sopenharmony_ci			entry->gate_mask))
224362306a36Sopenharmony_ci		goto nla_put_failure;
224462306a36Sopenharmony_ci
224562306a36Sopenharmony_ci	if (nla_put_u32(msg, TCA_TAPRIO_SCHED_ENTRY_INTERVAL,
224662306a36Sopenharmony_ci			entry->interval))
224762306a36Sopenharmony_ci		goto nla_put_failure;
224862306a36Sopenharmony_ci
224962306a36Sopenharmony_ci	return nla_nest_end(msg, item);
225062306a36Sopenharmony_ci
225162306a36Sopenharmony_cinla_put_failure:
225262306a36Sopenharmony_ci	nla_nest_cancel(msg, item);
225362306a36Sopenharmony_ci	return -1;
225462306a36Sopenharmony_ci}
225562306a36Sopenharmony_ci
225662306a36Sopenharmony_cistatic int dump_schedule(struct sk_buff *msg,
225762306a36Sopenharmony_ci			 const struct sched_gate_list *root)
225862306a36Sopenharmony_ci{
225962306a36Sopenharmony_ci	struct nlattr *entry_list;
226062306a36Sopenharmony_ci	struct sched_entry *entry;
226162306a36Sopenharmony_ci
226262306a36Sopenharmony_ci	if (nla_put_s64(msg, TCA_TAPRIO_ATTR_SCHED_BASE_TIME,
226362306a36Sopenharmony_ci			root->base_time, TCA_TAPRIO_PAD))
226462306a36Sopenharmony_ci		return -1;
226562306a36Sopenharmony_ci
226662306a36Sopenharmony_ci	if (nla_put_s64(msg, TCA_TAPRIO_ATTR_SCHED_CYCLE_TIME,
226762306a36Sopenharmony_ci			root->cycle_time, TCA_TAPRIO_PAD))
226862306a36Sopenharmony_ci		return -1;
226962306a36Sopenharmony_ci
227062306a36Sopenharmony_ci	if (nla_put_s64(msg, TCA_TAPRIO_ATTR_SCHED_CYCLE_TIME_EXTENSION,
227162306a36Sopenharmony_ci			root->cycle_time_extension, TCA_TAPRIO_PAD))
227262306a36Sopenharmony_ci		return -1;
227362306a36Sopenharmony_ci
227462306a36Sopenharmony_ci	entry_list = nla_nest_start_noflag(msg,
227562306a36Sopenharmony_ci					   TCA_TAPRIO_ATTR_SCHED_ENTRY_LIST);
227662306a36Sopenharmony_ci	if (!entry_list)
227762306a36Sopenharmony_ci		goto error_nest;
227862306a36Sopenharmony_ci
227962306a36Sopenharmony_ci	list_for_each_entry(entry, &root->entries, list) {
228062306a36Sopenharmony_ci		if (dump_entry(msg, entry) < 0)
228162306a36Sopenharmony_ci			goto error_nest;
228262306a36Sopenharmony_ci	}
228362306a36Sopenharmony_ci
228462306a36Sopenharmony_ci	nla_nest_end(msg, entry_list);
228562306a36Sopenharmony_ci	return 0;
228662306a36Sopenharmony_ci
228762306a36Sopenharmony_cierror_nest:
228862306a36Sopenharmony_ci	nla_nest_cancel(msg, entry_list);
228962306a36Sopenharmony_ci	return -1;
229062306a36Sopenharmony_ci}
229162306a36Sopenharmony_ci
229262306a36Sopenharmony_cistatic int taprio_dump_tc_entries(struct sk_buff *skb,
229362306a36Sopenharmony_ci				  struct taprio_sched *q,
229462306a36Sopenharmony_ci				  struct sched_gate_list *sched)
229562306a36Sopenharmony_ci{
229662306a36Sopenharmony_ci	struct nlattr *n;
229762306a36Sopenharmony_ci	int tc;
229862306a36Sopenharmony_ci
229962306a36Sopenharmony_ci	for (tc = 0; tc < TC_MAX_QUEUE; tc++) {
230062306a36Sopenharmony_ci		n = nla_nest_start(skb, TCA_TAPRIO_ATTR_TC_ENTRY);
230162306a36Sopenharmony_ci		if (!n)
230262306a36Sopenharmony_ci			return -EMSGSIZE;
230362306a36Sopenharmony_ci
230462306a36Sopenharmony_ci		if (nla_put_u32(skb, TCA_TAPRIO_TC_ENTRY_INDEX, tc))
230562306a36Sopenharmony_ci			goto nla_put_failure;
230662306a36Sopenharmony_ci
230762306a36Sopenharmony_ci		if (nla_put_u32(skb, TCA_TAPRIO_TC_ENTRY_MAX_SDU,
230862306a36Sopenharmony_ci				sched->max_sdu[tc]))
230962306a36Sopenharmony_ci			goto nla_put_failure;
231062306a36Sopenharmony_ci
231162306a36Sopenharmony_ci		if (nla_put_u32(skb, TCA_TAPRIO_TC_ENTRY_FP, q->fp[tc]))
231262306a36Sopenharmony_ci			goto nla_put_failure;
231362306a36Sopenharmony_ci
231462306a36Sopenharmony_ci		nla_nest_end(skb, n);
231562306a36Sopenharmony_ci	}
231662306a36Sopenharmony_ci
231762306a36Sopenharmony_ci	return 0;
231862306a36Sopenharmony_ci
231962306a36Sopenharmony_cinla_put_failure:
232062306a36Sopenharmony_ci	nla_nest_cancel(skb, n);
232162306a36Sopenharmony_ci	return -EMSGSIZE;
232262306a36Sopenharmony_ci}
232362306a36Sopenharmony_ci
232462306a36Sopenharmony_cistatic int taprio_put_stat(struct sk_buff *skb, u64 val, u16 attrtype)
232562306a36Sopenharmony_ci{
232662306a36Sopenharmony_ci	if (val == TAPRIO_STAT_NOT_SET)
232762306a36Sopenharmony_ci		return 0;
232862306a36Sopenharmony_ci	if (nla_put_u64_64bit(skb, attrtype, val, TCA_TAPRIO_OFFLOAD_STATS_PAD))
232962306a36Sopenharmony_ci		return -EMSGSIZE;
233062306a36Sopenharmony_ci	return 0;
233162306a36Sopenharmony_ci}
233262306a36Sopenharmony_ci
233362306a36Sopenharmony_cistatic int taprio_dump_xstats(struct Qdisc *sch, struct gnet_dump *d,
233462306a36Sopenharmony_ci			      struct tc_taprio_qopt_offload *offload,
233562306a36Sopenharmony_ci			      struct tc_taprio_qopt_stats *stats)
233662306a36Sopenharmony_ci{
233762306a36Sopenharmony_ci	struct net_device *dev = qdisc_dev(sch);
233862306a36Sopenharmony_ci	const struct net_device_ops *ops;
233962306a36Sopenharmony_ci	struct sk_buff *skb = d->skb;
234062306a36Sopenharmony_ci	struct nlattr *xstats;
234162306a36Sopenharmony_ci	int err;
234262306a36Sopenharmony_ci
234362306a36Sopenharmony_ci	ops = qdisc_dev(sch)->netdev_ops;
234462306a36Sopenharmony_ci
234562306a36Sopenharmony_ci	/* FIXME I could use qdisc_offload_dump_helper(), but that messes
234662306a36Sopenharmony_ci	 * with sch->flags depending on whether the device reports taprio
234762306a36Sopenharmony_ci	 * stats, and I'm not sure whether that's a good idea, considering
234862306a36Sopenharmony_ci	 * that stats are optional to the offload itself
234962306a36Sopenharmony_ci	 */
235062306a36Sopenharmony_ci	if (!ops->ndo_setup_tc)
235162306a36Sopenharmony_ci		return 0;
235262306a36Sopenharmony_ci
235362306a36Sopenharmony_ci	memset(stats, 0xff, sizeof(*stats));
235462306a36Sopenharmony_ci
235562306a36Sopenharmony_ci	err = ops->ndo_setup_tc(dev, TC_SETUP_QDISC_TAPRIO, offload);
235662306a36Sopenharmony_ci	if (err == -EOPNOTSUPP)
235762306a36Sopenharmony_ci		return 0;
235862306a36Sopenharmony_ci	if (err)
235962306a36Sopenharmony_ci		return err;
236062306a36Sopenharmony_ci
236162306a36Sopenharmony_ci	xstats = nla_nest_start(skb, TCA_STATS_APP);
236262306a36Sopenharmony_ci	if (!xstats)
236362306a36Sopenharmony_ci		goto err;
236462306a36Sopenharmony_ci
236562306a36Sopenharmony_ci	if (taprio_put_stat(skb, stats->window_drops,
236662306a36Sopenharmony_ci			    TCA_TAPRIO_OFFLOAD_STATS_WINDOW_DROPS) ||
236762306a36Sopenharmony_ci	    taprio_put_stat(skb, stats->tx_overruns,
236862306a36Sopenharmony_ci			    TCA_TAPRIO_OFFLOAD_STATS_TX_OVERRUNS))
236962306a36Sopenharmony_ci		goto err_cancel;
237062306a36Sopenharmony_ci
237162306a36Sopenharmony_ci	nla_nest_end(skb, xstats);
237262306a36Sopenharmony_ci
237362306a36Sopenharmony_ci	return 0;
237462306a36Sopenharmony_ci
237562306a36Sopenharmony_cierr_cancel:
237662306a36Sopenharmony_ci	nla_nest_cancel(skb, xstats);
237762306a36Sopenharmony_cierr:
237862306a36Sopenharmony_ci	return -EMSGSIZE;
237962306a36Sopenharmony_ci}
238062306a36Sopenharmony_ci
238162306a36Sopenharmony_cistatic int taprio_dump_stats(struct Qdisc *sch, struct gnet_dump *d)
238262306a36Sopenharmony_ci{
238362306a36Sopenharmony_ci	struct tc_taprio_qopt_offload offload = {
238462306a36Sopenharmony_ci		.cmd = TAPRIO_CMD_STATS,
238562306a36Sopenharmony_ci	};
238662306a36Sopenharmony_ci
238762306a36Sopenharmony_ci	return taprio_dump_xstats(sch, d, &offload, &offload.stats);
238862306a36Sopenharmony_ci}
238962306a36Sopenharmony_ci
239062306a36Sopenharmony_cistatic int taprio_dump(struct Qdisc *sch, struct sk_buff *skb)
239162306a36Sopenharmony_ci{
239262306a36Sopenharmony_ci	struct taprio_sched *q = qdisc_priv(sch);
239362306a36Sopenharmony_ci	struct net_device *dev = qdisc_dev(sch);
239462306a36Sopenharmony_ci	struct sched_gate_list *oper, *admin;
239562306a36Sopenharmony_ci	struct tc_mqprio_qopt opt = { 0 };
239662306a36Sopenharmony_ci	struct nlattr *nest, *sched_nest;
239762306a36Sopenharmony_ci
239862306a36Sopenharmony_ci	oper = rtnl_dereference(q->oper_sched);
239962306a36Sopenharmony_ci	admin = rtnl_dereference(q->admin_sched);
240062306a36Sopenharmony_ci
240162306a36Sopenharmony_ci	mqprio_qopt_reconstruct(dev, &opt);
240262306a36Sopenharmony_ci
240362306a36Sopenharmony_ci	nest = nla_nest_start_noflag(skb, TCA_OPTIONS);
240462306a36Sopenharmony_ci	if (!nest)
240562306a36Sopenharmony_ci		goto start_error;
240662306a36Sopenharmony_ci
240762306a36Sopenharmony_ci	if (nla_put(skb, TCA_TAPRIO_ATTR_PRIOMAP, sizeof(opt), &opt))
240862306a36Sopenharmony_ci		goto options_error;
240962306a36Sopenharmony_ci
241062306a36Sopenharmony_ci	if (!FULL_OFFLOAD_IS_ENABLED(q->flags) &&
241162306a36Sopenharmony_ci	    nla_put_s32(skb, TCA_TAPRIO_ATTR_SCHED_CLOCKID, q->clockid))
241262306a36Sopenharmony_ci		goto options_error;
241362306a36Sopenharmony_ci
241462306a36Sopenharmony_ci	if (q->flags && nla_put_u32(skb, TCA_TAPRIO_ATTR_FLAGS, q->flags))
241562306a36Sopenharmony_ci		goto options_error;
241662306a36Sopenharmony_ci
241762306a36Sopenharmony_ci	if (q->txtime_delay &&
241862306a36Sopenharmony_ci	    nla_put_u32(skb, TCA_TAPRIO_ATTR_TXTIME_DELAY, q->txtime_delay))
241962306a36Sopenharmony_ci		goto options_error;
242062306a36Sopenharmony_ci
242162306a36Sopenharmony_ci	if (oper && taprio_dump_tc_entries(skb, q, oper))
242262306a36Sopenharmony_ci		goto options_error;
242362306a36Sopenharmony_ci
242462306a36Sopenharmony_ci	if (oper && dump_schedule(skb, oper))
242562306a36Sopenharmony_ci		goto options_error;
242662306a36Sopenharmony_ci
242762306a36Sopenharmony_ci	if (!admin)
242862306a36Sopenharmony_ci		goto done;
242962306a36Sopenharmony_ci
243062306a36Sopenharmony_ci	sched_nest = nla_nest_start_noflag(skb, TCA_TAPRIO_ATTR_ADMIN_SCHED);
243162306a36Sopenharmony_ci	if (!sched_nest)
243262306a36Sopenharmony_ci		goto options_error;
243362306a36Sopenharmony_ci
243462306a36Sopenharmony_ci	if (dump_schedule(skb, admin))
243562306a36Sopenharmony_ci		goto admin_error;
243662306a36Sopenharmony_ci
243762306a36Sopenharmony_ci	nla_nest_end(skb, sched_nest);
243862306a36Sopenharmony_ci
243962306a36Sopenharmony_cidone:
244062306a36Sopenharmony_ci	return nla_nest_end(skb, nest);
244162306a36Sopenharmony_ci
244262306a36Sopenharmony_ciadmin_error:
244362306a36Sopenharmony_ci	nla_nest_cancel(skb, sched_nest);
244462306a36Sopenharmony_ci
244562306a36Sopenharmony_cioptions_error:
244662306a36Sopenharmony_ci	nla_nest_cancel(skb, nest);
244762306a36Sopenharmony_ci
244862306a36Sopenharmony_cistart_error:
244962306a36Sopenharmony_ci	return -ENOSPC;
245062306a36Sopenharmony_ci}
245162306a36Sopenharmony_ci
245262306a36Sopenharmony_cistatic struct Qdisc *taprio_leaf(struct Qdisc *sch, unsigned long cl)
245362306a36Sopenharmony_ci{
245462306a36Sopenharmony_ci	struct taprio_sched *q = qdisc_priv(sch);
245562306a36Sopenharmony_ci	struct net_device *dev = qdisc_dev(sch);
245662306a36Sopenharmony_ci	unsigned int ntx = cl - 1;
245762306a36Sopenharmony_ci
245862306a36Sopenharmony_ci	if (ntx >= dev->num_tx_queues)
245962306a36Sopenharmony_ci		return NULL;
246062306a36Sopenharmony_ci
246162306a36Sopenharmony_ci	return q->qdiscs[ntx];
246262306a36Sopenharmony_ci}
246362306a36Sopenharmony_ci
246462306a36Sopenharmony_cistatic unsigned long taprio_find(struct Qdisc *sch, u32 classid)
246562306a36Sopenharmony_ci{
246662306a36Sopenharmony_ci	unsigned int ntx = TC_H_MIN(classid);
246762306a36Sopenharmony_ci
246862306a36Sopenharmony_ci	if (!taprio_queue_get(sch, ntx))
246962306a36Sopenharmony_ci		return 0;
247062306a36Sopenharmony_ci	return ntx;
247162306a36Sopenharmony_ci}
247262306a36Sopenharmony_ci
247362306a36Sopenharmony_cistatic int taprio_dump_class(struct Qdisc *sch, unsigned long cl,
247462306a36Sopenharmony_ci			     struct sk_buff *skb, struct tcmsg *tcm)
247562306a36Sopenharmony_ci{
247662306a36Sopenharmony_ci	struct Qdisc *child = taprio_leaf(sch, cl);
247762306a36Sopenharmony_ci
247862306a36Sopenharmony_ci	tcm->tcm_parent = TC_H_ROOT;
247962306a36Sopenharmony_ci	tcm->tcm_handle |= TC_H_MIN(cl);
248062306a36Sopenharmony_ci	tcm->tcm_info = child->handle;
248162306a36Sopenharmony_ci
248262306a36Sopenharmony_ci	return 0;
248362306a36Sopenharmony_ci}
248462306a36Sopenharmony_ci
248562306a36Sopenharmony_cistatic int taprio_dump_class_stats(struct Qdisc *sch, unsigned long cl,
248662306a36Sopenharmony_ci				   struct gnet_dump *d)
248762306a36Sopenharmony_ci	__releases(d->lock)
248862306a36Sopenharmony_ci	__acquires(d->lock)
248962306a36Sopenharmony_ci{
249062306a36Sopenharmony_ci	struct Qdisc *child = taprio_leaf(sch, cl);
249162306a36Sopenharmony_ci	struct tc_taprio_qopt_offload offload = {
249262306a36Sopenharmony_ci		.cmd = TAPRIO_CMD_QUEUE_STATS,
249362306a36Sopenharmony_ci		.queue_stats = {
249462306a36Sopenharmony_ci			.queue = cl - 1,
249562306a36Sopenharmony_ci		},
249662306a36Sopenharmony_ci	};
249762306a36Sopenharmony_ci
249862306a36Sopenharmony_ci	if (gnet_stats_copy_basic(d, NULL, &child->bstats, true) < 0 ||
249962306a36Sopenharmony_ci	    qdisc_qstats_copy(d, child) < 0)
250062306a36Sopenharmony_ci		return -1;
250162306a36Sopenharmony_ci
250262306a36Sopenharmony_ci	return taprio_dump_xstats(sch, d, &offload, &offload.queue_stats.stats);
250362306a36Sopenharmony_ci}
250462306a36Sopenharmony_ci
250562306a36Sopenharmony_cistatic void taprio_walk(struct Qdisc *sch, struct qdisc_walker *arg)
250662306a36Sopenharmony_ci{
250762306a36Sopenharmony_ci	struct net_device *dev = qdisc_dev(sch);
250862306a36Sopenharmony_ci	unsigned long ntx;
250962306a36Sopenharmony_ci
251062306a36Sopenharmony_ci	if (arg->stop)
251162306a36Sopenharmony_ci		return;
251262306a36Sopenharmony_ci
251362306a36Sopenharmony_ci	arg->count = arg->skip;
251462306a36Sopenharmony_ci	for (ntx = arg->skip; ntx < dev->num_tx_queues; ntx++) {
251562306a36Sopenharmony_ci		if (!tc_qdisc_stats_dump(sch, ntx + 1, arg))
251662306a36Sopenharmony_ci			break;
251762306a36Sopenharmony_ci	}
251862306a36Sopenharmony_ci}
251962306a36Sopenharmony_ci
252062306a36Sopenharmony_cistatic struct netdev_queue *taprio_select_queue(struct Qdisc *sch,
252162306a36Sopenharmony_ci						struct tcmsg *tcm)
252262306a36Sopenharmony_ci{
252362306a36Sopenharmony_ci	return taprio_queue_get(sch, TC_H_MIN(tcm->tcm_parent));
252462306a36Sopenharmony_ci}
252562306a36Sopenharmony_ci
252662306a36Sopenharmony_cistatic const struct Qdisc_class_ops taprio_class_ops = {
252762306a36Sopenharmony_ci	.graft		= taprio_graft,
252862306a36Sopenharmony_ci	.leaf		= taprio_leaf,
252962306a36Sopenharmony_ci	.find		= taprio_find,
253062306a36Sopenharmony_ci	.walk		= taprio_walk,
253162306a36Sopenharmony_ci	.dump		= taprio_dump_class,
253262306a36Sopenharmony_ci	.dump_stats	= taprio_dump_class_stats,
253362306a36Sopenharmony_ci	.select_queue	= taprio_select_queue,
253462306a36Sopenharmony_ci};
253562306a36Sopenharmony_ci
253662306a36Sopenharmony_cistatic struct Qdisc_ops taprio_qdisc_ops __read_mostly = {
253762306a36Sopenharmony_ci	.cl_ops		= &taprio_class_ops,
253862306a36Sopenharmony_ci	.id		= "taprio",
253962306a36Sopenharmony_ci	.priv_size	= sizeof(struct taprio_sched),
254062306a36Sopenharmony_ci	.init		= taprio_init,
254162306a36Sopenharmony_ci	.change		= taprio_change,
254262306a36Sopenharmony_ci	.destroy	= taprio_destroy,
254362306a36Sopenharmony_ci	.reset		= taprio_reset,
254462306a36Sopenharmony_ci	.attach		= taprio_attach,
254562306a36Sopenharmony_ci	.peek		= taprio_peek,
254662306a36Sopenharmony_ci	.dequeue	= taprio_dequeue,
254762306a36Sopenharmony_ci	.enqueue	= taprio_enqueue,
254862306a36Sopenharmony_ci	.dump		= taprio_dump,
254962306a36Sopenharmony_ci	.dump_stats	= taprio_dump_stats,
255062306a36Sopenharmony_ci	.owner		= THIS_MODULE,
255162306a36Sopenharmony_ci};
255262306a36Sopenharmony_ci
255362306a36Sopenharmony_cistatic struct notifier_block taprio_device_notifier = {
255462306a36Sopenharmony_ci	.notifier_call = taprio_dev_notifier,
255562306a36Sopenharmony_ci};
255662306a36Sopenharmony_ci
255762306a36Sopenharmony_cistatic int __init taprio_module_init(void)
255862306a36Sopenharmony_ci{
255962306a36Sopenharmony_ci	int err = register_netdevice_notifier(&taprio_device_notifier);
256062306a36Sopenharmony_ci
256162306a36Sopenharmony_ci	if (err)
256262306a36Sopenharmony_ci		return err;
256362306a36Sopenharmony_ci
256462306a36Sopenharmony_ci	return register_qdisc(&taprio_qdisc_ops);
256562306a36Sopenharmony_ci}
256662306a36Sopenharmony_ci
256762306a36Sopenharmony_cistatic void __exit taprio_module_exit(void)
256862306a36Sopenharmony_ci{
256962306a36Sopenharmony_ci	unregister_qdisc(&taprio_qdisc_ops);
257062306a36Sopenharmony_ci	unregister_netdevice_notifier(&taprio_device_notifier);
257162306a36Sopenharmony_ci}
257262306a36Sopenharmony_ci
257362306a36Sopenharmony_cimodule_init(taprio_module_init);
257462306a36Sopenharmony_cimodule_exit(taprio_module_exit);
257562306a36Sopenharmony_ciMODULE_LICENSE("GPL");
2576