History

taprio: Add support for txtime-assist mode

Currently, we are seeing non-critical packets being transmitted outside of their timeslice. We can confirm that the packets are being dequeued at the right time. So, the delay is induced in the hardware side. The most likely reason is the hardware queues are starving the lower priority queues. In order to improve the performance of taprio, we will be making use of the txtime feature provided by the ETF qdisc. For all the packets which do not have the SO_TXTIME option set, taprio will set the transmit timestamp (set in skb->tstamp) in this mode. TAPrio Qdisc will ensure that the transmit time for the packet is set to when the gate is open. If SO_TXTIME is set, the TAPrio qdisc will validate whether the timestamp (in skb->tstamp) occurs when the gate corresponding to skb's traffic class is open. Following two parameters added to support this mode: - flags: used to enable txtime-assist mode. Will also be used to enable other modes (like hardware offloading) later. - txtime-delay: This indicates the minimum time it will take for the packet to hit the wire. This is useful in determining whether we can transmit the packet in the remaining time if the gate corresponding to the packet is currently open. An example configuration for enabling txtime-assist: tc qdisc replace dev eth0 parent root handle 100 taprio \\ num_tc 3 \\ map 2 2 1 0 2 2 2 2 2 2 2 2 2 2 2 2 \\ queues 1@0 1@0 1@0 \\ base-time 1558653424279842568 \\ sched-entry S 01 300000 \\ sched-entry S 02 300000 \\ sched-entry S 04 400000 \\ flags 0x1 \\ txtime-delay 40000 \\ clockid CLOCK_TAI tc qdisc replace dev $IFACE parent 100:1 etf skip_sock_check \\ offload delta 200000 clockid CLOCK_TAI Note that all the traffic classes are mapped to the same queue. This is only possible in taprio when txtime-assist is enabled. Also, note that the ETF Qdisc is enabled with offload mode set. In this mode, if the packet's traffic class is open and the complete packet can be transmitted, taprio will try to transmit the packet immediately. This will be done by setting skb->tstamp to current_time + the time delta indicated in the txtime-delay parameter. This parameter indicates the time taken (in software) for packet to reach the network adapter. If the packet cannot be transmitted in the current interval or if the packet's traffic is not currently transmitting, the skb->tstamp is set to the next available timestamp value. This is tracked in the next_launchtime parameter in the struct sched_entry. The behaviour w.r.t admin and oper schedules is not changed from what is present in software mode. The transmit time is already known in advance. So, we do not need the HR timers to advance the schedule and wakeup the dequeue side of taprio. So, HR timer won't be run when this mode is enabled.

Vedang Patel committed 7 years ago

Commit 4cfd5771 parent 566af332 files · +328 −17Browse files

Unified · Split

2 files
  1. include/uapi/linux/pkt_sched.h +4 −0
  2. net/sched/sch_taprio.c +324 −17
include/uapi/linux/pkt_sched.h+4−0
@@ -1159,6 +1159,8 @@ enum { · 1158 unmodified lines
1159 * [TCA_TAPRIO_ATTR_SCHED_ENTRY_INTERVAL]1159 * [TCA_TAPRIO_ATTR_SCHED_ENTRY_INTERVAL]
1160 */1160 */
1161 1161
1162#define TCA_TAPRIO_ATTR_FLAG_TXTIME_ASSIST 0x1
1163
1162enum {1164enum {
1163 TCA_TAPRIO_ATTR_UNSPEC,1165 TCA_TAPRIO_ATTR_UNSPEC,
1164 TCA_TAPRIO_ATTR_PRIOMAP, /* struct tc_mqprio_qopt */1166 TCA_TAPRIO_ATTR_PRIOMAP, /* struct tc_mqprio_qopt */
@@ -1170,6 +1172,8 @@ enum { · 5 unmodified lines
1170 TCA_TAPRIO_ATTR_ADMIN_SCHED, /* The admin sched, only used in dump */1172 TCA_TAPRIO_ATTR_ADMIN_SCHED, /* The admin sched, only used in dump */
1171 TCA_TAPRIO_ATTR_SCHED_CYCLE_TIME, /* s64 */1173 TCA_TAPRIO_ATTR_SCHED_CYCLE_TIME, /* s64 */
1172 TCA_TAPRIO_ATTR_SCHED_CYCLE_TIME_EXTENSION, /* s64 */1174 TCA_TAPRIO_ATTR_SCHED_CYCLE_TIME_EXTENSION, /* s64 */
1175 TCA_TAPRIO_ATTR_FLAGS, /* u32 */
1176 TCA_TAPRIO_ATTR_TXTIME_DELAY, /* s32 */
1173 __TCA_TAPRIO_ATTR_MAX,1177 __TCA_TAPRIO_ATTR_MAX,
1174};1178};
1175 1179
net/sched/sch_taprio.c+324−17
@@ -21,12 +21,16 @@ · 20 unmodified lines
21#include <net/pkt_sched.h>21#include <net/pkt_sched.h>
22#include <net/pkt_cls.h>22#include <net/pkt_cls.h>
23#include <net/sch_generic.h>23#include <net/sch_generic.h>
24#include <net/sock.h>
24 25
25static LIST_HEAD(taprio_list);26static LIST_HEAD(taprio_list);
26static DEFINE_SPINLOCK(taprio_list_lock);27static DEFINE_SPINLOCK(taprio_list_lock);
27 28
28#define TAPRIO_ALL_GATES_OPEN -129#define TAPRIO_ALL_GATES_OPEN -1
29 30
31#define FLAGS_VALID(flags) (!((flags) & ~TCA_TAPRIO_ATTR_FLAG_TXTIME_ASSIST))
32#define TXTIME_ASSIST_IS_ENABLED(flags) ((flags) & TCA_TAPRIO_ATTR_FLAG_TXTIME_ASSIST)
33
30struct sched_entry {34struct sched_entry {
31 struct list_head list;35 struct list_head list;
32 36
@@ -35,6 +39,7 @@ struct sched_entry { · 2 unmodified lines
35 * packet leaves after this time.39 * packet leaves after this time.
36 */40 */
37 ktime_t close_time;41 ktime_t close_time;
42 ktime_t next_txtime;
38 atomic_t budget;43 atomic_t budget;
39 int index;44 int index;
40 u32 gate_mask;45 u32 gate_mask;
@@ -55,6 +60,7 @@ struct sched_gate_list { · 14 unmodified lines
55struct taprio_sched {60struct taprio_sched {
56 struct Qdisc **qdiscs;61 struct Qdisc **qdiscs;
57 struct Qdisc *root;62 struct Qdisc *root;
63 u32 flags;
58 int clockid;64 int clockid;
59 atomic64_t picos_per_byte; /* Using picoseconds because for 10Gbps+65 atomic64_t picos_per_byte; /* Using picoseconds because for 10Gbps+
60 * speeds it's sub-nanoseconds per byte66 * speeds it's sub-nanoseconds per byte
@@ -68,6 +74,7 @@ struct taprio_sched { · 7 unmodified lines
68 ktime_t (*get_time)(void);74 ktime_t (*get_time)(void);
69 struct hrtimer advance_timer;75 struct hrtimer advance_timer;
70 struct list_head taprio_list;76 struct list_head taprio_list;
77 int txtime_delay;
71};78};
72 79
73static ktime_t sched_base_time(const struct sched_gate_list *sched)80static ktime_t sched_base_time(const struct sched_gate_list *sched)
@@ -108,6 +115,227 @@ static void switch_schedules(struct taprio_sched *q, · 34 unmodified lines
108 *admin = NULL;115 *admin = NULL;
109}116}
110 117
118/* Get how much time has been already elapsed in the current cycle. */
119static s32 get_cycle_time_elapsed(struct sched_gate_list *sched, ktime_t time)
120{
121 ktime_t time_since_sched_start;
122 s32 time_elapsed;
123
124 time_since_sched_start = ktime_sub(time, sched->base_time);
125 div_s64_rem(time_since_sched_start, sched->cycle_time, &time_elapsed);
126
127 return time_elapsed;
128}
129
130static ktime_t get_interval_end_time(struct sched_gate_list *sched,
131 struct sched_gate_list *admin,
132 struct sched_entry *entry,
133 ktime_t intv_start)
134{
135 s32 cycle_elapsed = get_cycle_time_elapsed(sched, intv_start);
136 ktime_t intv_end, cycle_ext_end, cycle_end;
137
138 cycle_end = ktime_add_ns(intv_start, sched->cycle_time - cycle_elapsed);
139 intv_end = ktime_add_ns(intv_start, entry->interval);
140 cycle_ext_end = ktime_add(cycle_end, sched->cycle_time_extension);
141
142 if (ktime_before(intv_end, cycle_end))
143 return intv_end;
144 else if (admin && admin != sched &&
145 ktime_after(admin->base_time, cycle_end) &&
146 ktime_before(admin->base_time, cycle_ext_end))
147 return admin->base_time;
148 else
149 return cycle_end;
150}
151
152static int length_to_duration(struct taprio_sched *q, int len)
153{
154 return div_u64(len * atomic64_read(&q->picos_per_byte), 1000);
155}
156
157/* Returns the entry corresponding to next available interval. If
158 * validate_interval is set, it only validates whether the timestamp occurs
159 * when the gate corresponding to the skb's traffic class is open.
160 */
161static struct sched_entry *find_entry_to_transmit(struct sk_buff *skb,
162 struct Qdisc *sch,
163 struct sched_gate_list *sched,
164 struct sched_gate_list *admin,
165 ktime_t time,
166 ktime_t *interval_start,
167 ktime_t *interval_end,
168 bool validate_interval)
169{
170 ktime_t curr_intv_start, curr_intv_end, cycle_end, packet_transmit_time;
171 ktime_t earliest_txtime = KTIME_MAX, txtime, cycle, transmit_end_time;
172 struct sched_entry *entry = NULL, *entry_found = NULL;
173 struct taprio_sched *q = qdisc_priv(sch);
174 struct net_device *dev = qdisc_dev(sch);
175 bool entry_available = false;
176 s32 cycle_elapsed;
177 int tc, n;
178
179 tc = netdev_get_prio_tc_map(dev, skb->priority);
180 packet_transmit_time = length_to_duration(q, qdisc_pkt_len(skb));
181
182 *interval_start = 0;
183 *interval_end = 0;
184
185 if (!sched)
186 return NULL;
187
188 cycle = sched->cycle_time;
189 cycle_elapsed = get_cycle_time_elapsed(sched, time);
190 curr_intv_end = ktime_sub_ns(time, cycle_elapsed);
191 cycle_end = ktime_add_ns(curr_intv_end, cycle);
192
193 list_for_each_entry(entry, &sched->entries, list) {
194 curr_intv_start = curr_intv_end;
195 curr_intv_end = get_interval_end_time(sched, admin, entry,
196 curr_intv_start);
197
198 if (ktime_after(curr_intv_start, cycle_end))
199 break;
200
201 if (!(entry->gate_mask & BIT(tc)) ||
202 packet_transmit_time > entry->interval)
203 continue;
204
205 txtime = entry->next_txtime;
206
207 if (ktime_before(txtime, time) || validate_interval) {
208 transmit_end_time = ktime_add_ns(time, packet_transmit_time);
209 if ((ktime_before(curr_intv_start, time) &&
210 ktime_before(transmit_end_time, curr_intv_end)) ||
211 (ktime_after(curr_intv_start, time) && !validate_interval)) {
212 entry_found = entry;
213 *interval_start = curr_intv_start;
214 *interval_end = curr_intv_end;
215 break;
216 } else if (!entry_available && !validate_interval) {
217 /* Here, we are just trying to find out the
218 * first available interval in the next cycle.
219 */
220 entry_available = 1;
221 entry_found = entry;
222 *interval_start = ktime_add_ns(curr_intv_start, cycle);
223 *interval_end = ktime_add_ns(curr_intv_end, cycle);
224 }
225 } else if (ktime_before(txtime, earliest_txtime) &&
226 !entry_available) {
227 earliest_txtime = txtime;
228 entry_found = entry;
229 n = div_s64(ktime_sub(txtime, curr_intv_start), cycle);
230 *interval_start = ktime_add(curr_intv_start, n * cycle);
231 *interval_end = ktime_add(curr_intv_end, n * cycle);
232 }
233 }
234
235 return entry_found;
236}
237
238static bool is_valid_interval(struct sk_buff *skb, struct Qdisc *sch)
239{
240 struct taprio_sched *q = qdisc_priv(sch);
241 struct sched_gate_list *sched, *admin;
242 ktime_t interval_start, interval_end;
243 struct sched_entry *entry;
244
245 rcu_read_lock();
246 sched = rcu_dereference(q->oper_sched);
247 admin = rcu_dereference(q->admin_sched);
248
249 entry = find_entry_to_transmit(skb, sch, sched, admin, skb->tstamp,
250 &interval_start, &interval_end, true);
251 rcu_read_unlock();
252
253 return entry;
254}
255
256/* There are a few scenarios where we will have to modify the txtime from
257 * what is read from next_txtime in sched_entry. They are:
258 * 1. If txtime is in the past,
259 * a. The gate for the traffic class is currently open and packet can be
260 * transmitted before it closes, schedule the packet right away.
261 * b. If the gate corresponding to the traffic class is going to open later
262 * in the cycle, set the txtime of packet to the interval start.
263 * 2. If txtime is in the future, there are packets corresponding to the
264 * current traffic class waiting to be transmitted. So, the following
265 * possibilities exist:
266 * a. We can transmit the packet before the window containing the txtime
267 * closes.
268 * b. The window might close before the transmission can be completed
269 * successfully. So, schedule the packet in the next open window.
270 */
271static long get_packet_txtime(struct sk_buff *skb, struct Qdisc *sch)
272{
273 ktime_t transmit_end_time, interval_end, interval_start;
274 struct taprio_sched *q = qdisc_priv(sch);
275 struct sched_gate_list *sched, *admin;
276 ktime_t minimum_time, now, txtime;
277 int len, packet_transmit_time;
278 struct sched_entry *entry;
279 bool sched_changed;
280
281 now = q->get_time();
282 minimum_time = ktime_add_ns(now, q->txtime_delay);
283
284 rcu_read_lock();
285 admin = rcu_dereference(q->admin_sched);
286 sched = rcu_dereference(q->oper_sched);
287 if (admin && ktime_after(minimum_time, admin->base_time))
288 switch_schedules(q, &admin, &sched);
289
290 /* Until the schedule starts, all the queues are open */
291 if (!sched || ktime_before(minimum_time, sched->base_time)) {
292 txtime = minimum_time;
293 goto done;
294 }
295
296 len = qdisc_pkt_len(skb);
297 packet_transmit_time = length_to_duration(q, len);
298
299 do {
300 sched_changed = 0;
301
302 entry = find_entry_to_transmit(skb, sch, sched, admin,
303 minimum_time,
304 &interval_start, &interval_end,
305 false);
306 if (!entry) {
307 txtime = 0;
308 goto done;
309 }
310
311 txtime = entry->next_txtime;
312 txtime = max_t(ktime_t, txtime, minimum_time);
313 txtime = max_t(ktime_t, txtime, interval_start);
314
315 if (admin && admin != sched &&
316 ktime_after(txtime, admin->base_time)) {
317 sched = admin;
318 sched_changed = 1;
319 continue;
320 }
321
322 transmit_end_time = ktime_add(txtime, packet_transmit_time);
323 minimum_time = transmit_end_time;
324
325 /* Update the txtime of current entry to the next time it's
326 * interval starts.
327 */
328 if (ktime_after(transmit_end_time, interval_end))
329 entry->next_txtime = ktime_add(interval_start, sched->cycle_time);
330 } while (sched_changed || ktime_after(transmit_end_time, interval_end));
331
332 entry->next_txtime = transmit_end_time;
333
334done:
335 rcu_read_unlock();
336 return txtime;
337}
338
111static int taprio_enqueue(struct sk_buff *skb, struct Qdisc *sch,339static int taprio_enqueue(struct sk_buff *skb, struct Qdisc *sch,
112 struct sk_buff **to_free)340 struct sk_buff **to_free)
113{341{
@@ -121,6 +349,15 @@ static int taprio_enqueue(struct sk_buff *skb, struct Qdisc *sch, · 7 unmodified lines
121 if (unlikely(!child))349 if (unlikely(!child))
122 return qdisc_drop(skb, sch, to_free);350 return qdisc_drop(skb, sch, to_free);
123 351
352 if (skb->sk && sock_flag(skb->sk, SOCK_TXTIME)) {
353 if (!is_valid_interval(skb, sch))
354 return qdisc_drop(skb, sch, to_free);
355 } else if (TXTIME_ASSIST_IS_ENABLED(q->flags)) {
356 skb->tstamp = get_packet_txtime(skb, sch);
357 if (!skb->tstamp)
358 return qdisc_drop(skb, sch, to_free);
359 }
360
124 qdisc_qstats_backlog_inc(sch, skb);361 qdisc_qstats_backlog_inc(sch, skb);
125 sch->q.qlen++;362 sch->q.qlen++;
126 363
@@ -156,6 +393,9 @@ static struct sk_buff *taprio_peek(struct Qdisc *sch) · 29 unmodified lines
156 if (!skb)393 if (!skb)
157 continue;394 continue;
158 395
396 if (TXTIME_ASSIST_IS_ENABLED(q->flags))
397 return skb;
398
159 prio = skb->priority;399 prio = skb->priority;
160 tc = netdev_get_prio_tc_map(dev, prio);400 tc = netdev_get_prio_tc_map(dev, prio);
161 401
@@ -168,11 +408,6 @@ static struct sk_buff *taprio_peek(struct Qdisc *sch) · 6 unmodified lines
168 return NULL;408 return NULL;
169}409}
170 410
171static int length_to_duration(struct taprio_sched *q, int len)
172{
173 return div_u64(len * atomic64_read(&q->picos_per_byte), 1000);
174}
175
176static void taprio_set_budget(struct taprio_sched *q, struct sched_entry *entry)411static void taprio_set_budget(struct taprio_sched *q, struct sched_entry *entry)
177{412{
178 atomic_set(&entry->budget,413 atomic_set(&entry->budget,
@@ -216,6 +451,13 @@ static struct sk_buff *taprio_dequeue(struct Qdisc *sch) · 37 unmodified lines
216 if (unlikely(!child))451 if (unlikely(!child))
217 continue;452 continue;
218 453
454 if (TXTIME_ASSIST_IS_ENABLED(q->flags)) {
455 skb = child->ops->dequeue(child);
456 if (!skb)
457 continue;
458 goto skb_found;
459 }
460
219 skb = child->ops->peek(child);461 skb = child->ops->peek(child);
220 if (!skb)462 if (!skb)
221 continue;463 continue;
@@ -246,6 +488,7 @@ static struct sk_buff *taprio_dequeue(struct Qdisc *sch) · 24 unmodified lines
246 if (unlikely(!skb))488 if (unlikely(!skb))
247 goto done;489 goto done;
248 490
491skb_found:
249 qdisc_bstats_update(sch, skb);492 qdisc_bstats_update(sch, skb);
250 qdisc_qstats_backlog_dec(sch, skb);493 qdisc_qstats_backlog_dec(sch, skb);
251 sch->q.qlen--;494 sch->q.qlen--;
@@ -522,7 +765,8 @@ static int parse_taprio_schedule(struct nlattr **tb, · 270 unmodified lines
522 765
523static int taprio_parse_mqprio_opt(struct net_device *dev,766static int taprio_parse_mqprio_opt(struct net_device *dev,
524 struct tc_mqprio_qopt *qopt,767 struct tc_mqprio_qopt *qopt,
525 struct netlink_ext_ack *extack)768 struct netlink_ext_ack *extack,
769 u32 taprio_flags)
526{770{
527 int i, j;771 int i, j;
528 772
@@ -570,6 +814,9 @@ static int taprio_parse_mqprio_opt(struct net_device *dev, · 41 unmodified lines
570 return -EINVAL;814 return -EINVAL;
571 }815 }
572 816
817 if (TXTIME_ASSIST_IS_ENABLED(taprio_flags))
818 continue;
819
573 /* Verify that the offset and counts do not overlap */820 /* Verify that the offset and counts do not overlap */
574 for (j = i + 1; j < qopt->num_tc; j++) {821 for (j = i + 1; j < qopt->num_tc; j++) {
575 if (last > qopt->offset[j]) {822 if (last > qopt->offset[j]) {
@@ -700,6 +947,18 @@ static int taprio_dev_notifier(struct notifier_block *nb, unsigned long event, · 124 unmodified lines
700 return NOTIFY_DONE;947 return NOTIFY_DONE;
701}948}
702 949
950static void setup_txtime(struct taprio_sched *q,
951 struct sched_gate_list *sched, ktime_t base)
952{
953 struct sched_entry *entry;
954 u32 interval = 0;
955
956 list_for_each_entry(entry, &sched->entries, list) {
957 entry->next_txtime = ktime_add_ns(base, interval);
958 interval += entry->interval;
959 }
960}
961
703static int taprio_change(struct Qdisc *sch, struct nlattr *opt,962static int taprio_change(struct Qdisc *sch, struct nlattr *opt,
704 struct netlink_ext_ack *extack)963 struct netlink_ext_ack *extack)
705{964{
@@ -708,6 +967,7 @@ static int taprio_change(struct Qdisc *sch, struct nlattr *opt, · 2 unmodified lines
708 struct taprio_sched *q = qdisc_priv(sch);967 struct taprio_sched *q = qdisc_priv(sch);
709 struct net_device *dev = qdisc_dev(sch);968 struct net_device *dev = qdisc_dev(sch);
710 struct tc_mqprio_qopt *mqprio = NULL;969 struct tc_mqprio_qopt *mqprio = NULL;
970 u32 taprio_flags = 0;
711 int i, err, clockid;971 int i, err, clockid;
712 unsigned long flags;972 unsigned long flags;
713 ktime_t start;973 ktime_t start;
@@ -720,7 +980,21 @@ static int taprio_change(struct Qdisc *sch, struct nlattr *opt, · 6 unmodified lines
720 if (tb[TCA_TAPRIO_ATTR_PRIOMAP])980 if (tb[TCA_TAPRIO_ATTR_PRIOMAP])
721 mqprio = nla_data(tb[TCA_TAPRIO_ATTR_PRIOMAP]);981 mqprio = nla_data(tb[TCA_TAPRIO_ATTR_PRIOMAP]);
722 982
723 err = taprio_parse_mqprio_opt(dev, mqprio, extack);983 if (tb[TCA_TAPRIO_ATTR_FLAGS]) {
984 taprio_flags = nla_get_u32(tb[TCA_TAPRIO_ATTR_FLAGS]);
985
986 if (q->flags != 0 && q->flags != taprio_flags) {
987 NL_SET_ERR_MSG_MOD(extack, "Changing 'flags' of a running schedule is not supported");
988 return -EOPNOTSUPP;
989 } else if (!FLAGS_VALID(taprio_flags)) {
990 NL_SET_ERR_MSG_MOD(extack, "Specified 'flags' are not valid");
991 return -EINVAL;
992 }
993
994 q->flags = taprio_flags;
995 }
996
997 err = taprio_parse_mqprio_opt(dev, mqprio, extack, taprio_flags);
724 if (err < 0)998 if (err < 0)
725 return err;999 return err;
726 1000
@@ -779,7 +1053,18 @@ static int taprio_change(struct Qdisc *sch, struct nlattr *opt, · 52 unmodified lines
779 /* Protects against enqueue()/dequeue() */1053 /* Protects against enqueue()/dequeue() */
780 spin_lock_bh(qdisc_lock(sch));1054 spin_lock_bh(qdisc_lock(sch));
781 1055
782 if (!hrtimer_active(&q->advance_timer)) {1056 if (tb[TCA_TAPRIO_ATTR_TXTIME_DELAY]) {
1057 if (!TXTIME_ASSIST_IS_ENABLED(q->flags)) {
1058 NL_SET_ERR_MSG_MOD(extack, "txtime-delay can only be set when txtime-assist mode is enabled");
1059 err = -EINVAL;
1060 goto unlock;
1061 }
1062
1063 q->txtime_delay = nla_get_s32(tb[TCA_TAPRIO_ATTR_TXTIME_DELAY]);
1064 }
1065
1066 if (!TXTIME_ASSIST_IS_ENABLED(taprio_flags) &&
1067 !hrtimer_active(&q->advance_timer)) {
783 hrtimer_init(&q->advance_timer, q->clockid, HRTIMER_MODE_ABS);1068 hrtimer_init(&q->advance_timer, q->clockid, HRTIMER_MODE_ABS);
784 q->advance_timer.function = advance_sched;1069 q->advance_timer.function = advance_sched;
785 }1070 }
@@ -822,20 +1107,35 @@ static int taprio_change(struct Qdisc *sch, struct nlattr *opt, · 36 unmodified lines
822 goto unlock;1107 goto unlock;
823 }1108 }
824 1109
825 setup_first_close_time(q, new_admin, start);1110 if (TXTIME_ASSIST_IS_ENABLED(taprio_flags)) {
1111 setup_txtime(q, new_admin, start);
826 1112
827 /* Protects against advance_sched() */1113 if (!oper) {
828 spin_lock_irqsave(&q->current_entry_lock, flags);1114 rcu_assign_pointer(q->oper_sched, new_admin);
1115 err = 0;
1116 new_admin = NULL;
1117 goto unlock;
1118 }
829 1119
830 taprio_start_sched(sch, start, new_admin);1120 rcu_assign_pointer(q->admin_sched, new_admin);
1121 if (admin)
1122 call_rcu(&admin->rcu, taprio_free_sched_cb);
1123 } else {
1124 setup_first_close_time(q, new_admin, start);
831 1125
832 rcu_assign_pointer(q->admin_sched, new_admin);1126 /* Protects against advance_sched() */
833 if (admin)1127 spin_lock_irqsave(&q->current_entry_lock, flags);
834 call_rcu(&admin->rcu, taprio_free_sched_cb);1128
835 new_admin = NULL;1129 taprio_start_sched(sch, start, new_admin);
836 1130
837 spin_unlock_irqrestore(&q->current_entry_lock, flags);1131 rcu_assign_pointer(q->admin_sched, new_admin);
1132 if (admin)
1133 call_rcu(&admin->rcu, taprio_free_sched_cb);
838 1134
1135 spin_unlock_irqrestore(&q->current_entry_lock, flags);
1136 }
1137
1138 new_admin = NULL;
839 err = 0;1139 err = 0;
840 1140
841unlock:1141unlock:
@@ -1073,6 +1373,13 @@ static int taprio_dump(struct Qdisc *sch, struct sk_buff *skb) · 231 unmodified lines
1073 if (nla_put_s32(skb, TCA_TAPRIO_ATTR_SCHED_CLOCKID, q->clockid))1373 if (nla_put_s32(skb, TCA_TAPRIO_ATTR_SCHED_CLOCKID, q->clockid))
1074 goto options_error;1374 goto options_error;
1075 1375
1376 if (q->flags && nla_put_u32(skb, TCA_TAPRIO_ATTR_FLAGS, q->flags))
1377 goto options_error;
1378
1379 if (q->txtime_delay &&
1380 nla_put_s32(skb, TCA_TAPRIO_ATTR_TXTIME_DELAY, q->txtime_delay))
1381 goto options_error;
1382
1076 if (oper && dump_schedule(skb, oper))1383 if (oper && dump_schedule(skb, oper))
1077 goto options_error;1384 goto options_error;
1078 1385