.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
---|
1 | 2 | /* |
---|
2 | 3 | * net/sched/sch_api.c Packet scheduler API. |
---|
3 | | - * |
---|
4 | | - * This program is free software; you can redistribute it and/or |
---|
5 | | - * modify it under the terms of the GNU General Public License |
---|
6 | | - * as published by the Free Software Foundation; either version |
---|
7 | | - * 2 of the License, or (at your option) any later version. |
---|
8 | 4 | * |
---|
9 | 5 | * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> |
---|
10 | 6 | * |
---|
.. | .. |
---|
27 | 23 | #include <linux/kmod.h> |
---|
28 | 24 | #include <linux/list.h> |
---|
29 | 25 | #include <linux/hrtimer.h> |
---|
30 | | -#include <linux/lockdep.h> |
---|
31 | 26 | #include <linux/slab.h> |
---|
32 | 27 | #include <linux/hashtable.h> |
---|
33 | 28 | |
---|
.. | .. |
---|
36 | 31 | #include <net/netlink.h> |
---|
37 | 32 | #include <net/pkt_sched.h> |
---|
38 | 33 | #include <net/pkt_cls.h> |
---|
| 34 | + |
---|
| 35 | +#include <trace/events/qdisc.h> |
---|
39 | 36 | |
---|
40 | 37 | /* |
---|
41 | 38 | |
---|
.. | .. |
---|
270 | 267 | root->handle == handle) |
---|
271 | 268 | return root; |
---|
272 | 269 | |
---|
273 | | - hash_for_each_possible_rcu(qdisc_dev(root)->qdisc_hash, q, hash, handle) { |
---|
| 270 | + hash_for_each_possible_rcu(qdisc_dev(root)->qdisc_hash, q, hash, handle, |
---|
| 271 | + lockdep_rtnl_is_held()) { |
---|
274 | 272 | if (q->handle == handle) |
---|
275 | 273 | return q; |
---|
276 | 274 | } |
---|
.. | .. |
---|
303 | 301 | |
---|
304 | 302 | if (!handle) |
---|
305 | 303 | return NULL; |
---|
306 | | - q = qdisc_match_from_root(dev->qdisc, handle); |
---|
| 304 | + q = qdisc_match_from_root(rtnl_dereference(dev->qdisc), handle); |
---|
307 | 305 | if (q) |
---|
308 | 306 | goto out; |
---|
309 | 307 | |
---|
.. | .. |
---|
322 | 320 | |
---|
323 | 321 | if (!handle) |
---|
324 | 322 | return NULL; |
---|
325 | | - q = qdisc_match_from_root(dev->qdisc, handle); |
---|
| 323 | + q = qdisc_match_from_root(rcu_dereference(dev->qdisc), handle); |
---|
326 | 324 | if (q) |
---|
327 | 325 | goto out; |
---|
328 | 326 | |
---|
.. | .. |
---|
336 | 334 | static struct Qdisc *qdisc_leaf(struct Qdisc *p, u32 classid) |
---|
337 | 335 | { |
---|
338 | 336 | unsigned long cl; |
---|
339 | | - struct Qdisc *leaf; |
---|
340 | 337 | const struct Qdisc_class_ops *cops = p->ops->cl_ops; |
---|
341 | 338 | |
---|
342 | 339 | if (cops == NULL) |
---|
.. | .. |
---|
345 | 342 | |
---|
346 | 343 | if (cl == 0) |
---|
347 | 344 | return NULL; |
---|
348 | | - leaf = cops->leaf(p, cl); |
---|
349 | | - return leaf; |
---|
| 345 | + return cops->leaf(p, cl); |
---|
350 | 346 | } |
---|
351 | 347 | |
---|
352 | 348 | /* Find queueing discipline by name */ |
---|
.. | .. |
---|
483 | 479 | u16 *tab = NULL; |
---|
484 | 480 | int err; |
---|
485 | 481 | |
---|
486 | | - err = nla_parse_nested(tb, TCA_STAB_MAX, opt, stab_policy, extack); |
---|
| 482 | + err = nla_parse_nested_deprecated(tb, TCA_STAB_MAX, opt, stab_policy, |
---|
| 483 | + extack); |
---|
487 | 484 | if (err < 0) |
---|
488 | 485 | return ERR_PTR(err); |
---|
489 | 486 | if (!tb[TCA_STAB_BASE]) { |
---|
.. | .. |
---|
536 | 533 | return stab; |
---|
537 | 534 | } |
---|
538 | 535 | |
---|
539 | | -static void stab_kfree_rcu(struct rcu_head *head) |
---|
540 | | -{ |
---|
541 | | - kfree(container_of(head, struct qdisc_size_table, rcu)); |
---|
542 | | -} |
---|
543 | | - |
---|
544 | 536 | void qdisc_put_stab(struct qdisc_size_table *tab) |
---|
545 | 537 | { |
---|
546 | 538 | if (!tab) |
---|
.. | .. |
---|
548 | 540 | |
---|
549 | 541 | if (--tab->refcnt == 0) { |
---|
550 | 542 | list_del(&tab->list); |
---|
551 | | - call_rcu_bh(&tab->rcu, stab_kfree_rcu); |
---|
| 543 | + kfree_rcu(tab, rcu); |
---|
552 | 544 | } |
---|
553 | 545 | } |
---|
554 | 546 | EXPORT_SYMBOL(qdisc_put_stab); |
---|
.. | .. |
---|
557 | 549 | { |
---|
558 | 550 | struct nlattr *nest; |
---|
559 | 551 | |
---|
560 | | - nest = nla_nest_start(skb, TCA_STAB); |
---|
| 552 | + nest = nla_nest_start_noflag(skb, TCA_STAB); |
---|
561 | 553 | if (nest == NULL) |
---|
562 | 554 | goto nla_put_failure; |
---|
563 | 555 | if (nla_put(skb, TCA_STAB_BASE, sizeof(stab->szopts), &stab->szopts)) |
---|
.. | .. |
---|
636 | 628 | } |
---|
637 | 629 | EXPORT_SYMBOL(qdisc_watchdog_init); |
---|
638 | 630 | |
---|
639 | | -void qdisc_watchdog_schedule_ns(struct qdisc_watchdog *wd, u64 expires) |
---|
| 631 | +void qdisc_watchdog_schedule_range_ns(struct qdisc_watchdog *wd, u64 expires, |
---|
| 632 | + u64 delta_ns) |
---|
640 | 633 | { |
---|
641 | 634 | if (test_bit(__QDISC_STATE_DEACTIVATED, |
---|
642 | 635 | &qdisc_root_sleeping(wd->qdisc)->state)) |
---|
643 | 636 | return; |
---|
644 | 637 | |
---|
645 | | - if (wd->last_expires == expires) |
---|
646 | | - return; |
---|
| 638 | + if (hrtimer_is_queued(&wd->timer)) { |
---|
| 639 | + /* If timer is already set in [expires, expires + delta_ns], |
---|
| 640 | + * do not reprogram it. |
---|
| 641 | + */ |
---|
| 642 | + if (wd->last_expires - expires <= delta_ns) |
---|
| 643 | + return; |
---|
| 644 | + } |
---|
647 | 645 | |
---|
648 | 646 | wd->last_expires = expires; |
---|
649 | | - hrtimer_start(&wd->timer, |
---|
650 | | - ns_to_ktime(expires), |
---|
651 | | - HRTIMER_MODE_ABS_PINNED); |
---|
| 647 | + hrtimer_start_range_ns(&wd->timer, |
---|
| 648 | + ns_to_ktime(expires), |
---|
| 649 | + delta_ns, |
---|
| 650 | + HRTIMER_MODE_ABS_PINNED); |
---|
652 | 651 | } |
---|
653 | | -EXPORT_SYMBOL(qdisc_watchdog_schedule_ns); |
---|
| 652 | +EXPORT_SYMBOL(qdisc_watchdog_schedule_range_ns); |
---|
654 | 653 | |
---|
655 | 654 | void qdisc_watchdog_cancel(struct qdisc_watchdog *wd) |
---|
656 | 655 | { |
---|
.. | .. |
---|
768 | 767 | return 0; |
---|
769 | 768 | } |
---|
770 | 769 | |
---|
771 | | -void qdisc_tree_reduce_backlog(struct Qdisc *sch, unsigned int n, |
---|
772 | | - unsigned int len) |
---|
| 770 | +void qdisc_tree_reduce_backlog(struct Qdisc *sch, int n, int len) |
---|
773 | 771 | { |
---|
774 | 772 | bool qdisc_is_offloaded = sch->flags & TCQ_F_OFFLOADED; |
---|
775 | 773 | const struct Qdisc_class_ops *cops; |
---|
.. | .. |
---|
817 | 815 | rcu_read_unlock(); |
---|
818 | 816 | } |
---|
819 | 817 | EXPORT_SYMBOL(qdisc_tree_reduce_backlog); |
---|
| 818 | + |
---|
| 819 | +int qdisc_offload_dump_helper(struct Qdisc *sch, enum tc_setup_type type, |
---|
| 820 | + void *type_data) |
---|
| 821 | +{ |
---|
| 822 | + struct net_device *dev = qdisc_dev(sch); |
---|
| 823 | + int err; |
---|
| 824 | + |
---|
| 825 | + sch->flags &= ~TCQ_F_OFFLOADED; |
---|
| 826 | + if (!tc_can_offload(dev) || !dev->netdev_ops->ndo_setup_tc) |
---|
| 827 | + return 0; |
---|
| 828 | + |
---|
| 829 | + err = dev->netdev_ops->ndo_setup_tc(dev, type, type_data); |
---|
| 830 | + if (err == -EOPNOTSUPP) |
---|
| 831 | + return 0; |
---|
| 832 | + |
---|
| 833 | + if (!err) |
---|
| 834 | + sch->flags |= TCQ_F_OFFLOADED; |
---|
| 835 | + |
---|
| 836 | + return err; |
---|
| 837 | +} |
---|
| 838 | +EXPORT_SYMBOL(qdisc_offload_dump_helper); |
---|
| 839 | + |
---|
| 840 | +void qdisc_offload_graft_helper(struct net_device *dev, struct Qdisc *sch, |
---|
| 841 | + struct Qdisc *new, struct Qdisc *old, |
---|
| 842 | + enum tc_setup_type type, void *type_data, |
---|
| 843 | + struct netlink_ext_ack *extack) |
---|
| 844 | +{ |
---|
| 845 | + bool any_qdisc_is_offloaded; |
---|
| 846 | + int err; |
---|
| 847 | + |
---|
| 848 | + if (!tc_can_offload(dev) || !dev->netdev_ops->ndo_setup_tc) |
---|
| 849 | + return; |
---|
| 850 | + |
---|
| 851 | + err = dev->netdev_ops->ndo_setup_tc(dev, type, type_data); |
---|
| 852 | + |
---|
| 853 | + /* Don't report error if the graft is part of destroy operation. */ |
---|
| 854 | + if (!err || !new || new == &noop_qdisc) |
---|
| 855 | + return; |
---|
| 856 | + |
---|
| 857 | + /* Don't report error if the parent, the old child and the new |
---|
| 858 | + * one are not offloaded. |
---|
| 859 | + */ |
---|
| 860 | + any_qdisc_is_offloaded = new->flags & TCQ_F_OFFLOADED; |
---|
| 861 | + any_qdisc_is_offloaded |= sch && sch->flags & TCQ_F_OFFLOADED; |
---|
| 862 | + any_qdisc_is_offloaded |= old && old->flags & TCQ_F_OFFLOADED; |
---|
| 863 | + |
---|
| 864 | + if (any_qdisc_is_offloaded) |
---|
| 865 | + NL_SET_ERR_MSG(extack, "Offloading graft operation failed."); |
---|
| 866 | +} |
---|
| 867 | +EXPORT_SYMBOL(qdisc_offload_graft_helper); |
---|
| 868 | + |
---|
| 869 | +static void qdisc_offload_graft_root(struct net_device *dev, |
---|
| 870 | + struct Qdisc *new, struct Qdisc *old, |
---|
| 871 | + struct netlink_ext_ack *extack) |
---|
| 872 | +{ |
---|
| 873 | + struct tc_root_qopt_offload graft_offload = { |
---|
| 874 | + .command = TC_ROOT_GRAFT, |
---|
| 875 | + .handle = new ? new->handle : 0, |
---|
| 876 | + .ingress = (new && new->flags & TCQ_F_INGRESS) || |
---|
| 877 | + (old && old->flags & TCQ_F_INGRESS), |
---|
| 878 | + }; |
---|
| 879 | + |
---|
| 880 | + qdisc_offload_graft_helper(dev, NULL, new, old, |
---|
| 881 | + TC_SETUP_ROOT_QDISC, &graft_offload, extack); |
---|
| 882 | +} |
---|
820 | 883 | |
---|
821 | 884 | static int tc_fill_qdisc(struct sk_buff *skb, struct Qdisc *q, u32 clid, |
---|
822 | 885 | u32 portid, u32 seq, u16 flags, int event) |
---|
.. | .. |
---|
949 | 1012 | qdisc_put(old); |
---|
950 | 1013 | } |
---|
951 | 1014 | |
---|
| 1015 | +static void qdisc_clear_nolock(struct Qdisc *sch) |
---|
| 1016 | +{ |
---|
| 1017 | + sch->flags &= ~TCQ_F_NOLOCK; |
---|
| 1018 | + if (!(sch->flags & TCQ_F_CPUSTATS)) |
---|
| 1019 | + return; |
---|
| 1020 | + |
---|
| 1021 | + free_percpu(sch->cpu_bstats); |
---|
| 1022 | + free_percpu(sch->cpu_qstats); |
---|
| 1023 | + sch->cpu_bstats = NULL; |
---|
| 1024 | + sch->cpu_qstats = NULL; |
---|
| 1025 | + sch->flags &= ~TCQ_F_CPUSTATS; |
---|
| 1026 | +} |
---|
| 1027 | + |
---|
952 | 1028 | /* Graft qdisc "new" to class "classid" of qdisc "parent" or |
---|
953 | 1029 | * to device "dev". |
---|
954 | 1030 | * |
---|
.. | .. |
---|
965 | 1041 | { |
---|
966 | 1042 | struct Qdisc *q = old; |
---|
967 | 1043 | struct net *net = dev_net(dev); |
---|
968 | | - int err = 0; |
---|
969 | 1044 | |
---|
970 | 1045 | if (parent == NULL) { |
---|
971 | 1046 | unsigned int i, num_q, ingress; |
---|
.. | .. |
---|
984 | 1059 | |
---|
985 | 1060 | if (dev->flags & IFF_UP) |
---|
986 | 1061 | dev_deactivate(dev); |
---|
| 1062 | + |
---|
| 1063 | + qdisc_offload_graft_root(dev, new, old, extack); |
---|
987 | 1064 | |
---|
988 | 1065 | if (new && new->ops->attach) |
---|
989 | 1066 | goto skip; |
---|
.. | .. |
---|
1004 | 1081 | |
---|
1005 | 1082 | skip: |
---|
1006 | 1083 | if (!ingress) { |
---|
1007 | | - notify_and_destroy(net, skb, n, classid, |
---|
1008 | | - dev->qdisc, new); |
---|
| 1084 | + old = rtnl_dereference(dev->qdisc); |
---|
1009 | 1085 | if (new && !new->ops->attach) |
---|
1010 | 1086 | qdisc_refcount_inc(new); |
---|
1011 | | - dev->qdisc = new ? : &noop_qdisc; |
---|
| 1087 | + rcu_assign_pointer(dev->qdisc, new ? : &noop_qdisc); |
---|
| 1088 | + |
---|
| 1089 | + notify_and_destroy(net, skb, n, classid, old, new); |
---|
1012 | 1090 | |
---|
1013 | 1091 | if (new && new->ops->attach) |
---|
1014 | 1092 | new->ops->attach(new); |
---|
.. | .. |
---|
1020 | 1098 | dev_activate(dev); |
---|
1021 | 1099 | } else { |
---|
1022 | 1100 | const struct Qdisc_class_ops *cops = parent->ops->cl_ops; |
---|
| 1101 | + unsigned long cl; |
---|
| 1102 | + int err; |
---|
1023 | 1103 | |
---|
1024 | 1104 | /* Only support running class lockless if parent is lockless */ |
---|
1025 | | - if (new && (new->flags & TCQ_F_NOLOCK) && |
---|
1026 | | - parent && !(parent->flags & TCQ_F_NOLOCK)) |
---|
1027 | | - new->flags &= ~TCQ_F_NOLOCK; |
---|
| 1105 | + if (new && (new->flags & TCQ_F_NOLOCK) && !(parent->flags & TCQ_F_NOLOCK)) |
---|
| 1106 | + qdisc_clear_nolock(new); |
---|
1028 | 1107 | |
---|
1029 | | - err = -EOPNOTSUPP; |
---|
1030 | | - if (cops && cops->graft) { |
---|
1031 | | - unsigned long cl = cops->find(parent, classid); |
---|
| 1108 | + if (!cops || !cops->graft) |
---|
| 1109 | + return -EOPNOTSUPP; |
---|
1032 | 1110 | |
---|
1033 | | - if (cl) { |
---|
1034 | | - err = cops->graft(parent, cl, new, &old, |
---|
1035 | | - extack); |
---|
1036 | | - } else { |
---|
1037 | | - NL_SET_ERR_MSG(extack, "Specified class not found"); |
---|
1038 | | - err = -ENOENT; |
---|
1039 | | - } |
---|
| 1111 | + cl = cops->find(parent, classid); |
---|
| 1112 | + if (!cl) { |
---|
| 1113 | + NL_SET_ERR_MSG(extack, "Specified class not found"); |
---|
| 1114 | + return -ENOENT; |
---|
1040 | 1115 | } |
---|
1041 | | - if (!err) |
---|
1042 | | - notify_and_destroy(net, skb, n, classid, old, new); |
---|
| 1116 | + |
---|
| 1117 | + err = cops->graft(parent, cl, new, &old, extack); |
---|
| 1118 | + if (err) |
---|
| 1119 | + return err; |
---|
| 1120 | + notify_and_destroy(net, skb, n, classid, old, new); |
---|
1043 | 1121 | } |
---|
1044 | | - return err; |
---|
| 1122 | + return 0; |
---|
1045 | 1123 | } |
---|
1046 | 1124 | |
---|
1047 | 1125 | static int qdisc_block_indexes_set(struct Qdisc *sch, struct nlattr **tca, |
---|
.. | .. |
---|
1077 | 1155 | } |
---|
1078 | 1156 | return 0; |
---|
1079 | 1157 | } |
---|
1080 | | - |
---|
1081 | | -/* lockdep annotation is needed for ingress; egress gets it only for name */ |
---|
1082 | | -static struct lock_class_key qdisc_tx_lock; |
---|
1083 | | -static struct lock_class_key qdisc_rx_lock; |
---|
1084 | 1158 | |
---|
1085 | 1159 | /* |
---|
1086 | 1160 | Allocate and initialize new qdisc. |
---|
.. | .. |
---|
1146 | 1220 | if (handle == TC_H_INGRESS) { |
---|
1147 | 1221 | sch->flags |= TCQ_F_INGRESS; |
---|
1148 | 1222 | handle = TC_H_MAKE(TC_H_INGRESS, 0); |
---|
1149 | | - lockdep_set_class(qdisc_lock(sch), &qdisc_rx_lock); |
---|
1150 | 1223 | } else { |
---|
1151 | 1224 | if (handle == 0) { |
---|
1152 | 1225 | handle = qdisc_alloc_handle(dev); |
---|
1153 | | - err = -ENOMEM; |
---|
1154 | | - if (handle == 0) |
---|
| 1226 | + if (handle == 0) { |
---|
| 1227 | + NL_SET_ERR_MSG(extack, "Maximum number of qdisc handles was exceeded"); |
---|
| 1228 | + err = -ENOSPC; |
---|
1155 | 1229 | goto err_out3; |
---|
| 1230 | + } |
---|
1156 | 1231 | } |
---|
1157 | | - lockdep_set_class(qdisc_lock(sch), &qdisc_tx_lock); |
---|
1158 | 1232 | if (!netif_is_multiqueue(dev)) |
---|
1159 | 1233 | sch->flags |= TCQ_F_ONETXQUEUE; |
---|
1160 | 1234 | } |
---|
.. | .. |
---|
1219 | 1293 | } |
---|
1220 | 1294 | |
---|
1221 | 1295 | qdisc_hash_add(sch, false); |
---|
| 1296 | + trace_qdisc_create(ops, dev, parent); |
---|
1222 | 1297 | |
---|
1223 | 1298 | return sch; |
---|
1224 | 1299 | |
---|
.. | .. |
---|
1333 | 1408 | } |
---|
1334 | 1409 | |
---|
1335 | 1410 | const struct nla_policy rtm_tca_policy[TCA_MAX + 1] = { |
---|
1336 | | - [TCA_KIND] = { .type = NLA_NUL_STRING, |
---|
1337 | | - .len = IFNAMSIZ - 1 }, |
---|
| 1411 | + [TCA_KIND] = { .type = NLA_STRING }, |
---|
1338 | 1412 | [TCA_RATE] = { .type = NLA_BINARY, |
---|
1339 | 1413 | .len = sizeof(struct tc_estimator) }, |
---|
1340 | 1414 | [TCA_STAB] = { .type = NLA_NESTED }, |
---|
.. | .. |
---|
1364 | 1438 | !netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN)) |
---|
1365 | 1439 | return -EPERM; |
---|
1366 | 1440 | |
---|
1367 | | - err = nlmsg_parse(n, sizeof(*tcm), tca, TCA_MAX, rtm_tca_policy, |
---|
1368 | | - extack); |
---|
| 1441 | + err = nlmsg_parse_deprecated(n, sizeof(*tcm), tca, TCA_MAX, |
---|
| 1442 | + rtm_tca_policy, extack); |
---|
1369 | 1443 | if (err < 0) |
---|
1370 | 1444 | return err; |
---|
1371 | 1445 | |
---|
.. | .. |
---|
1387 | 1461 | q = dev_ingress_queue(dev)->qdisc_sleeping; |
---|
1388 | 1462 | } |
---|
1389 | 1463 | } else { |
---|
1390 | | - q = dev->qdisc; |
---|
| 1464 | + q = rtnl_dereference(dev->qdisc); |
---|
1391 | 1465 | } |
---|
1392 | 1466 | if (!q) { |
---|
1393 | 1467 | NL_SET_ERR_MSG(extack, "Cannot find specified qdisc on specified device"); |
---|
.. | .. |
---|
1449 | 1523 | |
---|
1450 | 1524 | replay: |
---|
1451 | 1525 | /* Reinit, just in case something touches this. */ |
---|
1452 | | - err = nlmsg_parse(n, sizeof(*tcm), tca, TCA_MAX, rtm_tca_policy, |
---|
1453 | | - extack); |
---|
| 1526 | + err = nlmsg_parse_deprecated(n, sizeof(*tcm), tca, TCA_MAX, |
---|
| 1527 | + rtm_tca_policy, extack); |
---|
1454 | 1528 | if (err < 0) |
---|
1455 | 1529 | return err; |
---|
1456 | 1530 | |
---|
.. | .. |
---|
1476 | 1550 | q = dev_ingress_queue(dev)->qdisc_sleeping; |
---|
1477 | 1551 | } |
---|
1478 | 1552 | } else { |
---|
1479 | | - q = dev->qdisc; |
---|
| 1553 | + q = rtnl_dereference(dev->qdisc); |
---|
1480 | 1554 | } |
---|
1481 | 1555 | |
---|
1482 | 1556 | /* It may be default qdisc, ignore it */ |
---|
.. | .. |
---|
1684 | 1758 | idx = 0; |
---|
1685 | 1759 | ASSERT_RTNL(); |
---|
1686 | 1760 | |
---|
1687 | | - err = nlmsg_parse(nlh, sizeof(struct tcmsg), tca, TCA_MAX, |
---|
1688 | | - rtm_tca_policy, NULL); |
---|
| 1761 | + err = nlmsg_parse_deprecated(nlh, sizeof(struct tcmsg), tca, TCA_MAX, |
---|
| 1762 | + rtm_tca_policy, cb->extack); |
---|
1689 | 1763 | if (err < 0) |
---|
1690 | 1764 | return err; |
---|
1691 | 1765 | |
---|
.. | .. |
---|
1698 | 1772 | s_q_idx = 0; |
---|
1699 | 1773 | q_idx = 0; |
---|
1700 | 1774 | |
---|
1701 | | - if (tc_dump_qdisc_root(dev->qdisc, skb, cb, &q_idx, s_q_idx, |
---|
| 1775 | + if (tc_dump_qdisc_root(rtnl_dereference(dev->qdisc), |
---|
| 1776 | + skb, cb, &q_idx, s_q_idx, |
---|
1702 | 1777 | true, tca[TCA_DUMP_INVISIBLE]) < 0) |
---|
1703 | 1778 | goto done; |
---|
1704 | 1779 | |
---|
.. | .. |
---|
1778 | 1853 | { |
---|
1779 | 1854 | struct sk_buff *skb; |
---|
1780 | 1855 | u32 portid = oskb ? NETLINK_CB(oskb).portid : 0; |
---|
| 1856 | + int err = 0; |
---|
1781 | 1857 | |
---|
1782 | 1858 | skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); |
---|
1783 | 1859 | if (!skb) |
---|
.. | .. |
---|
1788 | 1864 | return -EINVAL; |
---|
1789 | 1865 | } |
---|
1790 | 1866 | |
---|
1791 | | - return rtnetlink_send(skb, net, portid, RTNLGRP_TC, |
---|
1792 | | - n->nlmsg_flags & NLM_F_ECHO); |
---|
| 1867 | + err = rtnetlink_send(skb, net, portid, RTNLGRP_TC, |
---|
| 1868 | + n->nlmsg_flags & NLM_F_ECHO); |
---|
| 1869 | + if (err > 0) |
---|
| 1870 | + err = 0; |
---|
| 1871 | + return err; |
---|
1793 | 1872 | } |
---|
1794 | 1873 | |
---|
1795 | 1874 | static int tclass_del_notify(struct net *net, |
---|
.. | .. |
---|
1820 | 1899 | return err; |
---|
1821 | 1900 | } |
---|
1822 | 1901 | |
---|
1823 | | - return rtnetlink_send(skb, net, portid, RTNLGRP_TC, |
---|
1824 | | - n->nlmsg_flags & NLM_F_ECHO); |
---|
| 1902 | + err = rtnetlink_send(skb, net, portid, RTNLGRP_TC, |
---|
| 1903 | + n->nlmsg_flags & NLM_F_ECHO); |
---|
| 1904 | + if (err > 0) |
---|
| 1905 | + err = 0; |
---|
| 1906 | + return err; |
---|
1825 | 1907 | } |
---|
1826 | 1908 | |
---|
1827 | 1909 | #ifdef CONFIG_NET_CLS |
---|
.. | .. |
---|
1847 | 1929 | return 0; |
---|
1848 | 1930 | } |
---|
1849 | 1931 | |
---|
| 1932 | +struct tc_bind_class_args { |
---|
| 1933 | + struct qdisc_walker w; |
---|
| 1934 | + unsigned long new_cl; |
---|
| 1935 | + u32 portid; |
---|
| 1936 | + u32 clid; |
---|
| 1937 | +}; |
---|
| 1938 | + |
---|
| 1939 | +static int tc_bind_class_walker(struct Qdisc *q, unsigned long cl, |
---|
| 1940 | + struct qdisc_walker *w) |
---|
| 1941 | +{ |
---|
| 1942 | + struct tc_bind_class_args *a = (struct tc_bind_class_args *)w; |
---|
| 1943 | + const struct Qdisc_class_ops *cops = q->ops->cl_ops; |
---|
| 1944 | + struct tcf_block *block; |
---|
| 1945 | + struct tcf_chain *chain; |
---|
| 1946 | + |
---|
| 1947 | + block = cops->tcf_block(q, cl, NULL); |
---|
| 1948 | + if (!block) |
---|
| 1949 | + return 0; |
---|
| 1950 | + for (chain = tcf_get_next_chain(block, NULL); |
---|
| 1951 | + chain; |
---|
| 1952 | + chain = tcf_get_next_chain(block, chain)) { |
---|
| 1953 | + struct tcf_proto *tp; |
---|
| 1954 | + |
---|
| 1955 | + for (tp = tcf_get_next_proto(chain, NULL, true); |
---|
| 1956 | + tp; tp = tcf_get_next_proto(chain, tp, true)) { |
---|
| 1957 | + struct tcf_bind_args arg = {}; |
---|
| 1958 | + |
---|
| 1959 | + arg.w.fn = tcf_node_bind; |
---|
| 1960 | + arg.classid = a->clid; |
---|
| 1961 | + arg.base = cl; |
---|
| 1962 | + arg.cl = a->new_cl; |
---|
| 1963 | + tp->ops->walk(tp, &arg.w, true); |
---|
| 1964 | + } |
---|
| 1965 | + } |
---|
| 1966 | + |
---|
| 1967 | + return 0; |
---|
| 1968 | +} |
---|
| 1969 | + |
---|
1850 | 1970 | static void tc_bind_tclass(struct Qdisc *q, u32 portid, u32 clid, |
---|
1851 | 1971 | unsigned long new_cl) |
---|
1852 | 1972 | { |
---|
1853 | 1973 | const struct Qdisc_class_ops *cops = q->ops->cl_ops; |
---|
1854 | | - struct tcf_block *block; |
---|
1855 | | - struct tcf_chain *chain; |
---|
1856 | | - unsigned long cl; |
---|
| 1974 | + struct tc_bind_class_args args = {}; |
---|
1857 | 1975 | |
---|
1858 | | - cl = cops->find(q, portid); |
---|
1859 | | - if (!cl) |
---|
1860 | | - return; |
---|
1861 | 1976 | if (!cops->tcf_block) |
---|
1862 | 1977 | return; |
---|
1863 | | - block = cops->tcf_block(q, cl, NULL); |
---|
1864 | | - if (!block) |
---|
1865 | | - return; |
---|
1866 | | - list_for_each_entry(chain, &block->chain_list, list) { |
---|
1867 | | - struct tcf_proto *tp; |
---|
1868 | | - |
---|
1869 | | - for (tp = rtnl_dereference(chain->filter_chain); |
---|
1870 | | - tp; tp = rtnl_dereference(tp->next)) { |
---|
1871 | | - struct tcf_bind_args arg = {}; |
---|
1872 | | - |
---|
1873 | | - arg.w.fn = tcf_node_bind; |
---|
1874 | | - arg.classid = clid; |
---|
1875 | | - arg.base = cl; |
---|
1876 | | - arg.cl = new_cl; |
---|
1877 | | - tp->ops->walk(tp, &arg.w); |
---|
1878 | | - } |
---|
1879 | | - } |
---|
| 1978 | + args.portid = portid; |
---|
| 1979 | + args.clid = clid; |
---|
| 1980 | + args.new_cl = new_cl; |
---|
| 1981 | + args.w.fn = tc_bind_class_walker; |
---|
| 1982 | + q->ops->cl_ops->walk(q, &args.w); |
---|
1880 | 1983 | } |
---|
1881 | 1984 | |
---|
1882 | 1985 | #else |
---|
.. | .. |
---|
1908 | 2011 | !netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN)) |
---|
1909 | 2012 | return -EPERM; |
---|
1910 | 2013 | |
---|
1911 | | - err = nlmsg_parse(n, sizeof(*tcm), tca, TCA_MAX, rtm_tca_policy, |
---|
1912 | | - extack); |
---|
| 2014 | + err = nlmsg_parse_deprecated(n, sizeof(*tcm), tca, TCA_MAX, |
---|
| 2015 | + rtm_tca_policy, extack); |
---|
1913 | 2016 | if (err < 0) |
---|
1914 | 2017 | return err; |
---|
1915 | 2018 | |
---|
.. | .. |
---|
1946 | 2049 | } else if (qid1) { |
---|
1947 | 2050 | qid = qid1; |
---|
1948 | 2051 | } else if (qid == 0) |
---|
1949 | | - qid = dev->qdisc->handle; |
---|
| 2052 | + qid = rtnl_dereference(dev->qdisc)->handle; |
---|
1950 | 2053 | |
---|
1951 | 2054 | /* Now qid is genuine qdisc handle consistent |
---|
1952 | 2055 | * both with parent and child. |
---|
.. | .. |
---|
1957 | 2060 | portid = TC_H_MAKE(qid, portid); |
---|
1958 | 2061 | } else { |
---|
1959 | 2062 | if (qid == 0) |
---|
1960 | | - qid = dev->qdisc->handle; |
---|
| 2063 | + qid = rtnl_dereference(dev->qdisc)->handle; |
---|
1961 | 2064 | } |
---|
1962 | 2065 | |
---|
1963 | 2066 | /* OK. Locate qdisc */ |
---|
.. | .. |
---|
2118 | 2221 | s_t = cb->args[0]; |
---|
2119 | 2222 | t = 0; |
---|
2120 | 2223 | |
---|
2121 | | - if (tc_dump_tclass_root(dev->qdisc, skb, tcm, cb, &t, s_t, true) < 0) |
---|
| 2224 | + if (tc_dump_tclass_root(rtnl_dereference(dev->qdisc), |
---|
| 2225 | + skb, tcm, cb, &t, s_t, true) < 0) |
---|
2122 | 2226 | goto done; |
---|
2123 | 2227 | |
---|
2124 | 2228 | dev_queue = dev_ingress_queue(dev); |
---|