aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVinicius Costa Gomes <vinicius.gomes@intel.com>2018-07-23 20:08:00 -0400
committerDavid S. Miller <davem@davemloft.net>2018-07-26 16:58:30 -0400
commit990e35ecba1cb8ebee4ad4a028735e24f4615417 (patch)
treef6be1502e52c6da64ba897e4668ef49007b95484
parent336a443bd9ddca319b99b5375e7756724a5545dd (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.c134
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
85static int cbs_enqueue_offload(struct sk_buff *skb, struct Qdisc *sch) 87static 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
90static int cbs_enqueue_soft(struct sk_buff *skb, struct Qdisc *sch) 103static 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
112static 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
105static int cbs_enqueue(struct sk_buff *skb, struct Qdisc *sch, 129static 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
159static 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
135static struct sk_buff *cbs_dequeue_soft(struct Qdisc *sch) 174static 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
179static struct sk_buff *cbs_dequeue_offload(struct Qdisc *sch) 218static 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
184static struct sk_buff *cbs_dequeue(struct Qdisc *sch) 226static 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
333static int cbs_dump(struct Qdisc *sch, struct sk_buff *skb) 385static 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
411static 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
425static 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
441static 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
448static unsigned long cbs_find(struct Qdisc *sch, u32 classid)
449{
450 return 1;
451}
452
453static 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
466static 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
359static struct Qdisc_ops cbs_qdisc_ops __read_mostly = { 474static 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,