0001
0002
0003
0004
0005
0006
0007
0008 #include <linux/module.h>
0009 #include <linux/slab.h>
0010 #include <linux/types.h>
0011 #include <linux/kernel.h>
0012 #include <linux/string.h>
0013 #include <linux/errno.h>
0014 #include <linux/skbuff.h>
0015 #include <net/netlink.h>
0016 #include <net/pkt_sched.h>
0017 #include <net/pkt_cls.h>
0018
0019 struct multiq_sched_data {
0020 u16 bands;
0021 u16 max_bands;
0022 u16 curband;
0023 struct tcf_proto __rcu *filter_list;
0024 struct tcf_block *block;
0025 struct Qdisc **queues;
0026 };
0027
0028
0029 static struct Qdisc *
0030 multiq_classify(struct sk_buff *skb, struct Qdisc *sch, int *qerr)
0031 {
0032 struct multiq_sched_data *q = qdisc_priv(sch);
0033 u32 band;
0034 struct tcf_result res;
0035 struct tcf_proto *fl = rcu_dereference_bh(q->filter_list);
0036 int err;
0037
0038 *qerr = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;
0039 err = tcf_classify(skb, NULL, fl, &res, false);
0040 #ifdef CONFIG_NET_CLS_ACT
0041 switch (err) {
0042 case TC_ACT_STOLEN:
0043 case TC_ACT_QUEUED:
0044 case TC_ACT_TRAP:
0045 *qerr = NET_XMIT_SUCCESS | __NET_XMIT_STOLEN;
0046 fallthrough;
0047 case TC_ACT_SHOT:
0048 return NULL;
0049 }
0050 #endif
0051 band = skb_get_queue_mapping(skb);
0052
0053 if (band >= q->bands)
0054 return q->queues[0];
0055
0056 return q->queues[band];
0057 }
0058
0059 static int
0060 multiq_enqueue(struct sk_buff *skb, struct Qdisc *sch,
0061 struct sk_buff **to_free)
0062 {
0063 struct Qdisc *qdisc;
0064 int ret;
0065
0066 qdisc = multiq_classify(skb, sch, &ret);
0067 #ifdef CONFIG_NET_CLS_ACT
0068 if (qdisc == NULL) {
0069
0070 if (ret & __NET_XMIT_BYPASS)
0071 qdisc_qstats_drop(sch);
0072 __qdisc_drop(skb, to_free);
0073 return ret;
0074 }
0075 #endif
0076
0077 ret = qdisc_enqueue(skb, qdisc, to_free);
0078 if (ret == NET_XMIT_SUCCESS) {
0079 sch->q.qlen++;
0080 return NET_XMIT_SUCCESS;
0081 }
0082 if (net_xmit_drop_count(ret))
0083 qdisc_qstats_drop(sch);
0084 return ret;
0085 }
0086
0087 static struct sk_buff *multiq_dequeue(struct Qdisc *sch)
0088 {
0089 struct multiq_sched_data *q = qdisc_priv(sch);
0090 struct Qdisc *qdisc;
0091 struct sk_buff *skb;
0092 int band;
0093
0094 for (band = 0; band < q->bands; band++) {
0095
0096 q->curband++;
0097 if (q->curband >= q->bands)
0098 q->curband = 0;
0099
0100
0101
0102
0103 if (!netif_xmit_stopped(
0104 netdev_get_tx_queue(qdisc_dev(sch), q->curband))) {
0105 qdisc = q->queues[q->curband];
0106 skb = qdisc->dequeue(qdisc);
0107 if (skb) {
0108 qdisc_bstats_update(sch, skb);
0109 sch->q.qlen--;
0110 return skb;
0111 }
0112 }
0113 }
0114 return NULL;
0115
0116 }
0117
0118 static struct sk_buff *multiq_peek(struct Qdisc *sch)
0119 {
0120 struct multiq_sched_data *q = qdisc_priv(sch);
0121 unsigned int curband = q->curband;
0122 struct Qdisc *qdisc;
0123 struct sk_buff *skb;
0124 int band;
0125
0126 for (band = 0; band < q->bands; band++) {
0127
0128 curband++;
0129 if (curband >= q->bands)
0130 curband = 0;
0131
0132
0133
0134
0135 if (!netif_xmit_stopped(
0136 netdev_get_tx_queue(qdisc_dev(sch), curband))) {
0137 qdisc = q->queues[curband];
0138 skb = qdisc->ops->peek(qdisc);
0139 if (skb)
0140 return skb;
0141 }
0142 }
0143 return NULL;
0144
0145 }
0146
0147 static void
0148 multiq_reset(struct Qdisc *sch)
0149 {
0150 u16 band;
0151 struct multiq_sched_data *q = qdisc_priv(sch);
0152
0153 for (band = 0; band < q->bands; band++)
0154 qdisc_reset(q->queues[band]);
0155 sch->q.qlen = 0;
0156 q->curband = 0;
0157 }
0158
0159 static void
0160 multiq_destroy(struct Qdisc *sch)
0161 {
0162 int band;
0163 struct multiq_sched_data *q = qdisc_priv(sch);
0164
0165 tcf_block_put(q->block);
0166 for (band = 0; band < q->bands; band++)
0167 qdisc_put(q->queues[band]);
0168
0169 kfree(q->queues);
0170 }
0171
0172 static int multiq_tune(struct Qdisc *sch, struct nlattr *opt,
0173 struct netlink_ext_ack *extack)
0174 {
0175 struct multiq_sched_data *q = qdisc_priv(sch);
0176 struct tc_multiq_qopt *qopt;
0177 struct Qdisc **removed;
0178 int i, n_removed = 0;
0179
0180 if (!netif_is_multiqueue(qdisc_dev(sch)))
0181 return -EOPNOTSUPP;
0182 if (nla_len(opt) < sizeof(*qopt))
0183 return -EINVAL;
0184
0185 qopt = nla_data(opt);
0186
0187 qopt->bands = qdisc_dev(sch)->real_num_tx_queues;
0188
0189 removed = kmalloc(sizeof(*removed) * (q->max_bands - q->bands),
0190 GFP_KERNEL);
0191 if (!removed)
0192 return -ENOMEM;
0193
0194 sch_tree_lock(sch);
0195 q->bands = qopt->bands;
0196 for (i = q->bands; i < q->max_bands; i++) {
0197 if (q->queues[i] != &noop_qdisc) {
0198 struct Qdisc *child = q->queues[i];
0199
0200 q->queues[i] = &noop_qdisc;
0201 qdisc_purge_queue(child);
0202 removed[n_removed++] = child;
0203 }
0204 }
0205
0206 sch_tree_unlock(sch);
0207
0208 for (i = 0; i < n_removed; i++)
0209 qdisc_put(removed[i]);
0210 kfree(removed);
0211
0212 for (i = 0; i < q->bands; i++) {
0213 if (q->queues[i] == &noop_qdisc) {
0214 struct Qdisc *child, *old;
0215 child = qdisc_create_dflt(sch->dev_queue,
0216 &pfifo_qdisc_ops,
0217 TC_H_MAKE(sch->handle,
0218 i + 1), extack);
0219 if (child) {
0220 sch_tree_lock(sch);
0221 old = q->queues[i];
0222 q->queues[i] = child;
0223 if (child != &noop_qdisc)
0224 qdisc_hash_add(child, true);
0225
0226 if (old != &noop_qdisc)
0227 qdisc_purge_queue(old);
0228 sch_tree_unlock(sch);
0229 qdisc_put(old);
0230 }
0231 }
0232 }
0233 return 0;
0234 }
0235
0236 static int multiq_init(struct Qdisc *sch, struct nlattr *opt,
0237 struct netlink_ext_ack *extack)
0238 {
0239 struct multiq_sched_data *q = qdisc_priv(sch);
0240 int i, err;
0241
0242 q->queues = NULL;
0243
0244 if (!opt)
0245 return -EINVAL;
0246
0247 err = tcf_block_get(&q->block, &q->filter_list, sch, extack);
0248 if (err)
0249 return err;
0250
0251 q->max_bands = qdisc_dev(sch)->num_tx_queues;
0252
0253 q->queues = kcalloc(q->max_bands, sizeof(struct Qdisc *), GFP_KERNEL);
0254 if (!q->queues)
0255 return -ENOBUFS;
0256 for (i = 0; i < q->max_bands; i++)
0257 q->queues[i] = &noop_qdisc;
0258
0259 return multiq_tune(sch, opt, extack);
0260 }
0261
0262 static int multiq_dump(struct Qdisc *sch, struct sk_buff *skb)
0263 {
0264 struct multiq_sched_data *q = qdisc_priv(sch);
0265 unsigned char *b = skb_tail_pointer(skb);
0266 struct tc_multiq_qopt opt;
0267
0268 opt.bands = q->bands;
0269 opt.max_bands = q->max_bands;
0270
0271 if (nla_put(skb, TCA_OPTIONS, sizeof(opt), &opt))
0272 goto nla_put_failure;
0273
0274 return skb->len;
0275
0276 nla_put_failure:
0277 nlmsg_trim(skb, b);
0278 return -1;
0279 }
0280
0281 static int multiq_graft(struct Qdisc *sch, unsigned long arg, struct Qdisc *new,
0282 struct Qdisc **old, struct netlink_ext_ack *extack)
0283 {
0284 struct multiq_sched_data *q = qdisc_priv(sch);
0285 unsigned long band = arg - 1;
0286
0287 if (new == NULL)
0288 new = &noop_qdisc;
0289
0290 *old = qdisc_replace(sch, new, &q->queues[band]);
0291 return 0;
0292 }
0293
0294 static struct Qdisc *
0295 multiq_leaf(struct Qdisc *sch, unsigned long arg)
0296 {
0297 struct multiq_sched_data *q = qdisc_priv(sch);
0298 unsigned long band = arg - 1;
0299
0300 return q->queues[band];
0301 }
0302
0303 static unsigned long multiq_find(struct Qdisc *sch, u32 classid)
0304 {
0305 struct multiq_sched_data *q = qdisc_priv(sch);
0306 unsigned long band = TC_H_MIN(classid);
0307
0308 if (band - 1 >= q->bands)
0309 return 0;
0310 return band;
0311 }
0312
0313 static unsigned long multiq_bind(struct Qdisc *sch, unsigned long parent,
0314 u32 classid)
0315 {
0316 return multiq_find(sch, classid);
0317 }
0318
0319
0320 static void multiq_unbind(struct Qdisc *q, unsigned long cl)
0321 {
0322 }
0323
0324 static int multiq_dump_class(struct Qdisc *sch, unsigned long cl,
0325 struct sk_buff *skb, struct tcmsg *tcm)
0326 {
0327 struct multiq_sched_data *q = qdisc_priv(sch);
0328
0329 tcm->tcm_handle |= TC_H_MIN(cl);
0330 tcm->tcm_info = q->queues[cl - 1]->handle;
0331 return 0;
0332 }
0333
0334 static int multiq_dump_class_stats(struct Qdisc *sch, unsigned long cl,
0335 struct gnet_dump *d)
0336 {
0337 struct multiq_sched_data *q = qdisc_priv(sch);
0338 struct Qdisc *cl_q;
0339
0340 cl_q = q->queues[cl - 1];
0341 if (gnet_stats_copy_basic(d, cl_q->cpu_bstats, &cl_q->bstats, true) < 0 ||
0342 qdisc_qstats_copy(d, cl_q) < 0)
0343 return -1;
0344
0345 return 0;
0346 }
0347
0348 static void multiq_walk(struct Qdisc *sch, struct qdisc_walker *arg)
0349 {
0350 struct multiq_sched_data *q = qdisc_priv(sch);
0351 int band;
0352
0353 if (arg->stop)
0354 return;
0355
0356 for (band = 0; band < q->bands; band++) {
0357 if (arg->count < arg->skip) {
0358 arg->count++;
0359 continue;
0360 }
0361 if (arg->fn(sch, band + 1, arg) < 0) {
0362 arg->stop = 1;
0363 break;
0364 }
0365 arg->count++;
0366 }
0367 }
0368
0369 static struct tcf_block *multiq_tcf_block(struct Qdisc *sch, unsigned long cl,
0370 struct netlink_ext_ack *extack)
0371 {
0372 struct multiq_sched_data *q = qdisc_priv(sch);
0373
0374 if (cl)
0375 return NULL;
0376 return q->block;
0377 }
0378
0379 static const struct Qdisc_class_ops multiq_class_ops = {
0380 .graft = multiq_graft,
0381 .leaf = multiq_leaf,
0382 .find = multiq_find,
0383 .walk = multiq_walk,
0384 .tcf_block = multiq_tcf_block,
0385 .bind_tcf = multiq_bind,
0386 .unbind_tcf = multiq_unbind,
0387 .dump = multiq_dump_class,
0388 .dump_stats = multiq_dump_class_stats,
0389 };
0390
0391 static struct Qdisc_ops multiq_qdisc_ops __read_mostly = {
0392 .next = NULL,
0393 .cl_ops = &multiq_class_ops,
0394 .id = "multiq",
0395 .priv_size = sizeof(struct multiq_sched_data),
0396 .enqueue = multiq_enqueue,
0397 .dequeue = multiq_dequeue,
0398 .peek = multiq_peek,
0399 .init = multiq_init,
0400 .reset = multiq_reset,
0401 .destroy = multiq_destroy,
0402 .change = multiq_tune,
0403 .dump = multiq_dump,
0404 .owner = THIS_MODULE,
0405 };
0406
0407 static int __init multiq_module_init(void)
0408 {
0409 return register_qdisc(&multiq_qdisc_ops);
0410 }
0411
0412 static void __exit multiq_module_exit(void)
0413 {
0414 unregister_qdisc(&multiq_qdisc_ops);
0415 }
0416
0417 module_init(multiq_module_init)
0418 module_exit(multiq_module_exit)
0419
0420 MODULE_LICENSE("GPL");