diff options
author | Vinicius Costa Gomes <vinicius.gomes@intel.com> | 2018-07-23 20:08:00 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2018-07-26 16:58:30 -0400 |
commit | 990e35ecba1cb8ebee4ad4a028735e24f4615417 (patch) | |
tree | f6be1502e52c6da64ba897e4668ef49007b95484 | |
parent | 336a443bd9ddca319b99b5375e7756724a5545dd (diff) |
cbs: Add support for the graft function
This will allow to install a child qdisc under cbs. The main use case
is to install ETF (Earliest TxTime First) qdisc under cbs, so there's
another level of control for time-sensitive traffic.
Signed-off-by: Vinicius Costa Gomes <vinicius.gomes@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | net/sched/sch_cbs.c | 134 |
1 files changed, 125 insertions, 9 deletions
diff --git a/net/sched/sch_cbs.c b/net/sched/sch_cbs.c index cdd96b9a27bc..e26a24017faa 100644 --- a/net/sched/sch_cbs.c +++ b/net/sched/sch_cbs.c | |||
@@ -78,18 +78,42 @@ struct cbs_sched_data { | |||
78 | s64 sendslope; /* in bytes/s */ | 78 | s64 sendslope; /* in bytes/s */ |
79 | s64 idleslope; /* in bytes/s */ | 79 | s64 idleslope; /* in bytes/s */ |
80 | struct qdisc_watchdog watchdog; | 80 | struct qdisc_watchdog watchdog; |
81 | int (*enqueue)(struct sk_buff *skb, struct Qdisc *sch); | 81 | int (*enqueue)(struct sk_buff *skb, struct Qdisc *sch, |
82 | struct sk_buff **to_free); | ||
82 | struct sk_buff *(*dequeue)(struct Qdisc *sch); | 83 | struct sk_buff *(*dequeue)(struct Qdisc *sch); |
84 | struct Qdisc *qdisc; | ||
83 | }; | 85 | }; |
84 | 86 | ||
85 | static int cbs_enqueue_offload(struct sk_buff *skb, struct Qdisc *sch) | 87 | static int cbs_child_enqueue(struct sk_buff *skb, struct Qdisc *sch, |
88 | struct Qdisc *child, | ||
89 | struct sk_buff **to_free) | ||
86 | { | 90 | { |
87 | return qdisc_enqueue_tail(skb, sch); | 91 | int err; |
92 | |||
93 | err = child->ops->enqueue(skb, child, to_free); | ||
94 | if (err != NET_XMIT_SUCCESS) | ||
95 | return err; | ||
96 | |||
97 | qdisc_qstats_backlog_inc(sch, skb); | ||
98 | sch->q.qlen++; | ||
99 | |||
100 | return NET_XMIT_SUCCESS; | ||
88 | } | 101 | } |
89 | 102 | ||
90 | static int cbs_enqueue_soft(struct sk_buff *skb, struct Qdisc *sch) | 103 | static int cbs_enqueue_offload(struct sk_buff *skb, struct Qdisc *sch, |
104 | struct sk_buff **to_free) | ||
91 | { | 105 | { |
92 | struct cbs_sched_data *q = qdisc_priv(sch); | 106 | struct cbs_sched_data *q = qdisc_priv(sch); |
107 | struct Qdisc *qdisc = q->qdisc; | ||
108 | |||
109 | return cbs_child_enqueue(skb, sch, qdisc, to_free); | ||
110 | } | ||
111 | |||
112 | static int cbs_enqueue_soft(struct sk_buff *skb, struct Qdisc *sch, | ||
113 | struct sk_buff **to_free) | ||
114 | { | ||
115 | struct cbs_sched_data *q = qdisc_priv(sch); | ||
116 | struct Qdisc *qdisc = q->qdisc; | ||
93 | 117 | ||
94 | if (sch->q.qlen == 0 && q->credits > 0) { | 118 | if (sch->q.qlen == 0 && q->credits > 0) { |
95 | /* We need to stop accumulating credits when there's | 119 | /* We need to stop accumulating credits when there's |
@@ -99,7 +123,7 @@ static int cbs_enqueue_soft(struct sk_buff *skb, struct Qdisc *sch) | |||
99 | q->last = ktime_get_ns(); | 123 | q->last = ktime_get_ns(); |
100 | } | 124 | } |
101 | 125 | ||
102 | return qdisc_enqueue_tail(skb, sch); | 126 | return cbs_child_enqueue(skb, sch, qdisc, to_free); |
103 | } | 127 | } |
104 | 128 | ||
105 | static int cbs_enqueue(struct sk_buff *skb, struct Qdisc *sch, | 129 | static int cbs_enqueue(struct sk_buff *skb, struct Qdisc *sch, |
@@ -107,7 +131,7 @@ static int cbs_enqueue(struct sk_buff *skb, struct Qdisc *sch, | |||
107 | { | 131 | { |
108 | struct cbs_sched_data *q = qdisc_priv(sch); | 132 | struct cbs_sched_data *q = qdisc_priv(sch); |
109 | 133 | ||
110 | return q->enqueue(skb, sch); | 134 | return q->enqueue(skb, sch, to_free); |
111 | } | 135 | } |
112 | 136 | ||
113 | /* timediff is in ns, slope is in bytes/s */ | 137 | /* timediff is in ns, slope is in bytes/s */ |
@@ -132,9 +156,25 @@ static s64 credits_from_len(unsigned int len, s64 slope, s64 port_rate) | |||
132 | return div64_s64(len * slope, port_rate); | 156 | return div64_s64(len * slope, port_rate); |
133 | } | 157 | } |
134 | 158 | ||
159 | static struct sk_buff *cbs_child_dequeue(struct Qdisc *sch, struct Qdisc *child) | ||
160 | { | ||
161 | struct sk_buff *skb; | ||
162 | |||
163 | skb = child->ops->dequeue(child); | ||
164 | if (!skb) | ||
165 | return NULL; | ||
166 | |||
167 | qdisc_qstats_backlog_dec(sch, skb); | ||
168 | qdisc_bstats_update(sch, skb); | ||
169 | sch->q.qlen--; | ||
170 | |||
171 | return skb; | ||
172 | } | ||
173 | |||
135 | static struct sk_buff *cbs_dequeue_soft(struct Qdisc *sch) | 174 | static struct sk_buff *cbs_dequeue_soft(struct Qdisc *sch) |
136 | { | 175 | { |
137 | struct cbs_sched_data *q = qdisc_priv(sch); | 176 | struct cbs_sched_data *q = qdisc_priv(sch); |
177 | struct Qdisc *qdisc = q->qdisc; | ||
138 | s64 now = ktime_get_ns(); | 178 | s64 now = ktime_get_ns(); |
139 | struct sk_buff *skb; | 179 | struct sk_buff *skb; |
140 | s64 credits; | 180 | s64 credits; |
@@ -157,8 +197,7 @@ static struct sk_buff *cbs_dequeue_soft(struct Qdisc *sch) | |||
157 | return NULL; | 197 | return NULL; |
158 | } | 198 | } |
159 | } | 199 | } |
160 | 200 | skb = cbs_child_dequeue(sch, qdisc); | |
161 | skb = qdisc_dequeue_head(sch); | ||
162 | if (!skb) | 201 | if (!skb) |
163 | return NULL; | 202 | return NULL; |
164 | 203 | ||
@@ -178,7 +217,10 @@ static struct sk_buff *cbs_dequeue_soft(struct Qdisc *sch) | |||
178 | 217 | ||
179 | static struct sk_buff *cbs_dequeue_offload(struct Qdisc *sch) | 218 | static struct sk_buff *cbs_dequeue_offload(struct Qdisc *sch) |
180 | { | 219 | { |
181 | return qdisc_dequeue_head(sch); | 220 | struct cbs_sched_data *q = qdisc_priv(sch); |
221 | struct Qdisc *qdisc = q->qdisc; | ||
222 | |||
223 | return cbs_child_dequeue(sch, qdisc); | ||
182 | } | 224 | } |
183 | 225 | ||
184 | static struct sk_buff *cbs_dequeue(struct Qdisc *sch) | 226 | static struct sk_buff *cbs_dequeue(struct Qdisc *sch) |
@@ -310,6 +352,13 @@ static int cbs_init(struct Qdisc *sch, struct nlattr *opt, | |||
310 | return -EINVAL; | 352 | return -EINVAL; |
311 | } | 353 | } |
312 | 354 | ||
355 | q->qdisc = qdisc_create_dflt(sch->dev_queue, &pfifo_qdisc_ops, | ||
356 | sch->handle, extack); | ||
357 | if (!q->qdisc) | ||
358 | return -ENOMEM; | ||
359 | |||
360 | qdisc_hash_add(q->qdisc, false); | ||
361 | |||
313 | q->queue = sch->dev_queue - netdev_get_tx_queue(dev, 0); | 362 | q->queue = sch->dev_queue - netdev_get_tx_queue(dev, 0); |
314 | 363 | ||
315 | q->enqueue = cbs_enqueue_soft; | 364 | q->enqueue = cbs_enqueue_soft; |
@@ -328,6 +377,9 @@ static void cbs_destroy(struct Qdisc *sch) | |||
328 | qdisc_watchdog_cancel(&q->watchdog); | 377 | qdisc_watchdog_cancel(&q->watchdog); |
329 | 378 | ||
330 | cbs_disable_offload(dev, q); | 379 | cbs_disable_offload(dev, q); |
380 | |||
381 | if (q->qdisc) | ||
382 | qdisc_destroy(q->qdisc); | ||
331 | } | 383 | } |
332 | 384 | ||
333 | static int cbs_dump(struct Qdisc *sch, struct sk_buff *skb) | 385 | static int cbs_dump(struct Qdisc *sch, struct sk_buff *skb) |
@@ -356,8 +408,72 @@ nla_put_failure: | |||
356 | return -1; | 408 | return -1; |
357 | } | 409 | } |
358 | 410 | ||
411 | static int cbs_dump_class(struct Qdisc *sch, unsigned long cl, | ||
412 | struct sk_buff *skb, struct tcmsg *tcm) | ||
413 | { | ||
414 | struct cbs_sched_data *q = qdisc_priv(sch); | ||
415 | |||
416 | if (cl != 1 || !q->qdisc) /* only one class */ | ||
417 | return -ENOENT; | ||
418 | |||
419 | tcm->tcm_handle |= TC_H_MIN(1); | ||
420 | tcm->tcm_info = q->qdisc->handle; | ||
421 | |||
422 | return 0; | ||
423 | } | ||
424 | |||
425 | static int cbs_graft(struct Qdisc *sch, unsigned long arg, struct Qdisc *new, | ||
426 | struct Qdisc **old, struct netlink_ext_ack *extack) | ||
427 | { | ||
428 | struct cbs_sched_data *q = qdisc_priv(sch); | ||
429 | |||
430 | if (!new) { | ||
431 | new = qdisc_create_dflt(sch->dev_queue, &pfifo_qdisc_ops, | ||
432 | sch->handle, NULL); | ||
433 | if (!new) | ||
434 | new = &noop_qdisc; | ||
435 | } | ||
436 | |||
437 | *old = qdisc_replace(sch, new, &q->qdisc); | ||
438 | return 0; | ||
439 | } | ||
440 | |||
441 | static struct Qdisc *cbs_leaf(struct Qdisc *sch, unsigned long arg) | ||
442 | { | ||
443 | struct cbs_sched_data *q = qdisc_priv(sch); | ||
444 | |||
445 | return q->qdisc; | ||
446 | } | ||
447 | |||
448 | static unsigned long cbs_find(struct Qdisc *sch, u32 classid) | ||
449 | { | ||
450 | return 1; | ||
451 | } | ||
452 | |||
453 | static void cbs_walk(struct Qdisc *sch, struct qdisc_walker *walker) | ||
454 | { | ||
455 | if (!walker->stop) { | ||
456 | if (walker->count >= walker->skip) { | ||
457 | if (walker->fn(sch, 1, walker) < 0) { | ||
458 | walker->stop = 1; | ||
459 | return; | ||
460 | } | ||
461 | } | ||
462 | walker->count++; | ||
463 | } | ||
464 | } | ||
465 | |||
466 | static const struct Qdisc_class_ops cbs_class_ops = { | ||
467 | .graft = cbs_graft, | ||
468 | .leaf = cbs_leaf, | ||
469 | .find = cbs_find, | ||
470 | .walk = cbs_walk, | ||
471 | .dump = cbs_dump_class, | ||
472 | }; | ||
473 | |||
359 | static struct Qdisc_ops cbs_qdisc_ops __read_mostly = { | 474 | static struct Qdisc_ops cbs_qdisc_ops __read_mostly = { |
360 | .id = "cbs", | 475 | .id = "cbs", |
476 | .cl_ops = &cbs_class_ops, | ||
361 | .priv_size = sizeof(struct cbs_sched_data), | 477 | .priv_size = sizeof(struct cbs_sched_data), |
362 | .enqueue = cbs_enqueue, | 478 | .enqueue = cbs_enqueue, |
363 | .dequeue = cbs_dequeue, | 479 | .dequeue = cbs_dequeue, |