.. | .. |
---|
| 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; |
---|
| 1047 | + struct netdev_queue *dev_queue; |
---|
972 | 1048 | |
---|
973 | 1049 | ingress = 0; |
---|
974 | 1050 | num_q = dev->num_tx_queues; |
---|
975 | 1051 | if ((q && q->flags & TCQ_F_INGRESS) || |
---|
976 | 1052 | (new && new->flags & TCQ_F_INGRESS)) { |
---|
977 | | - num_q = 1; |
---|
978 | 1053 | ingress = 1; |
---|
979 | 1054 | if (!dev_ingress_queue(dev)) { |
---|
980 | 1055 | NL_SET_ERR_MSG(extack, "Device does not have an ingress queue"); |
---|
.. | .. |
---|
985 | 1060 | if (dev->flags & IFF_UP) |
---|
986 | 1061 | dev_deactivate(dev); |
---|
987 | 1062 | |
---|
| 1063 | + qdisc_offload_graft_root(dev, new, old, extack); |
---|
| 1064 | + |
---|
988 | 1065 | if (new && new->ops->attach) |
---|
989 | 1066 | goto skip; |
---|
990 | 1067 | |
---|
991 | | - for (i = 0; i < num_q; i++) { |
---|
992 | | - struct netdev_queue *dev_queue = dev_ingress_queue(dev); |
---|
993 | | - |
---|
994 | | - if (!ingress) |
---|
| 1068 | + if (!ingress) { |
---|
| 1069 | + for (i = 0; i < num_q; i++) { |
---|
995 | 1070 | dev_queue = netdev_get_tx_queue(dev, i); |
---|
| 1071 | + old = dev_graft_qdisc(dev_queue, new); |
---|
996 | 1072 | |
---|
997 | | - old = dev_graft_qdisc(dev_queue, new); |
---|
998 | | - if (new && i > 0) |
---|
999 | | - qdisc_refcount_inc(new); |
---|
1000 | | - |
---|
1001 | | - if (!ingress) |
---|
| 1073 | + if (new && i > 0) |
---|
| 1074 | + qdisc_refcount_inc(new); |
---|
1002 | 1075 | qdisc_put(old); |
---|
| 1076 | + } |
---|
| 1077 | + } else { |
---|
| 1078 | + dev_queue = dev_ingress_queue(dev); |
---|
| 1079 | + old = dev_graft_qdisc(dev_queue, new); |
---|
1003 | 1080 | } |
---|
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 | + if (new && new->ops == &noqueue_qdisc_ops) { |
---|
| 1118 | + NL_SET_ERR_MSG(extack, "Cannot assign noqueue to a class"); |
---|
| 1119 | + return -EINVAL; |
---|
| 1120 | + } |
---|
| 1121 | + |
---|
| 1122 | + err = cops->graft(parent, cl, new, &old, extack); |
---|
| 1123 | + if (err) |
---|
| 1124 | + return err; |
---|
| 1125 | + notify_and_destroy(net, skb, n, classid, old, new); |
---|
1043 | 1126 | } |
---|
1044 | | - return err; |
---|
| 1127 | + return 0; |
---|
1045 | 1128 | } |
---|
1046 | 1129 | |
---|
1047 | 1130 | static int qdisc_block_indexes_set(struct Qdisc *sch, struct nlattr **tca, |
---|
.. | .. |
---|
1077 | 1160 | } |
---|
1078 | 1161 | return 0; |
---|
1079 | 1162 | } |
---|
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 | 1163 | |
---|
1085 | 1164 | /* |
---|
1086 | 1165 | Allocate and initialize new qdisc. |
---|
.. | .. |
---|
1144 | 1223 | sch->parent = parent; |
---|
1145 | 1224 | |
---|
1146 | 1225 | if (handle == TC_H_INGRESS) { |
---|
1147 | | - sch->flags |= TCQ_F_INGRESS; |
---|
| 1226 | + if (!(sch->flags & TCQ_F_INGRESS)) { |
---|
| 1227 | + NL_SET_ERR_MSG(extack, |
---|
| 1228 | + "Specified parent ID is reserved for ingress and clsact Qdiscs"); |
---|
| 1229 | + err = -EINVAL; |
---|
| 1230 | + goto err_out3; |
---|
| 1231 | + } |
---|
1148 | 1232 | handle = TC_H_MAKE(TC_H_INGRESS, 0); |
---|
1149 | | - lockdep_set_class(qdisc_lock(sch), &qdisc_rx_lock); |
---|
1150 | 1233 | } else { |
---|
1151 | 1234 | if (handle == 0) { |
---|
1152 | 1235 | handle = qdisc_alloc_handle(dev); |
---|
1153 | | - err = -ENOMEM; |
---|
1154 | | - if (handle == 0) |
---|
| 1236 | + if (handle == 0) { |
---|
| 1237 | + NL_SET_ERR_MSG(extack, "Maximum number of qdisc handles was exceeded"); |
---|
| 1238 | + err = -ENOSPC; |
---|
1155 | 1239 | goto err_out3; |
---|
| 1240 | + } |
---|
1156 | 1241 | } |
---|
1157 | | - lockdep_set_class(qdisc_lock(sch), &qdisc_tx_lock); |
---|
1158 | 1242 | if (!netif_is_multiqueue(dev)) |
---|
1159 | 1243 | sch->flags |= TCQ_F_ONETXQUEUE; |
---|
1160 | 1244 | } |
---|
.. | .. |
---|
1191 | 1275 | rcu_assign_pointer(sch->stab, stab); |
---|
1192 | 1276 | } |
---|
1193 | 1277 | if (tca[TCA_RATE]) { |
---|
1194 | | - net_seqlock_t *running; |
---|
| 1278 | + seqcount_t *running; |
---|
1195 | 1279 | |
---|
1196 | 1280 | err = -EOPNOTSUPP; |
---|
1197 | 1281 | if (sch->flags & TCQ_F_MQROOT) { |
---|
.. | .. |
---|
1219 | 1303 | } |
---|
1220 | 1304 | |
---|
1221 | 1305 | qdisc_hash_add(sch, false); |
---|
| 1306 | + trace_qdisc_create(ops, dev, parent); |
---|
1222 | 1307 | |
---|
1223 | 1308 | return sch; |
---|
1224 | 1309 | |
---|
.. | .. |
---|
1333 | 1418 | } |
---|
1334 | 1419 | |
---|
1335 | 1420 | const struct nla_policy rtm_tca_policy[TCA_MAX + 1] = { |
---|
1336 | | - [TCA_KIND] = { .type = NLA_NUL_STRING, |
---|
1337 | | - .len = IFNAMSIZ - 1 }, |
---|
| 1421 | + [TCA_KIND] = { .type = NLA_STRING }, |
---|
1338 | 1422 | [TCA_RATE] = { .type = NLA_BINARY, |
---|
1339 | 1423 | .len = sizeof(struct tc_estimator) }, |
---|
1340 | 1424 | [TCA_STAB] = { .type = NLA_NESTED }, |
---|
.. | .. |
---|
1364 | 1448 | !netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN)) |
---|
1365 | 1449 | return -EPERM; |
---|
1366 | 1450 | |
---|
1367 | | - err = nlmsg_parse(n, sizeof(*tcm), tca, TCA_MAX, rtm_tca_policy, |
---|
1368 | | - extack); |
---|
| 1451 | + err = nlmsg_parse_deprecated(n, sizeof(*tcm), tca, TCA_MAX, |
---|
| 1452 | + rtm_tca_policy, extack); |
---|
1369 | 1453 | if (err < 0) |
---|
1370 | 1454 | return err; |
---|
1371 | 1455 | |
---|
.. | .. |
---|
1387 | 1471 | q = dev_ingress_queue(dev)->qdisc_sleeping; |
---|
1388 | 1472 | } |
---|
1389 | 1473 | } else { |
---|
1390 | | - q = dev->qdisc; |
---|
| 1474 | + q = rtnl_dereference(dev->qdisc); |
---|
1391 | 1475 | } |
---|
1392 | 1476 | if (!q) { |
---|
1393 | 1477 | NL_SET_ERR_MSG(extack, "Cannot find specified qdisc on specified device"); |
---|
.. | .. |
---|
1429 | 1513 | return 0; |
---|
1430 | 1514 | } |
---|
1431 | 1515 | |
---|
| 1516 | +static bool req_create_or_replace(struct nlmsghdr *n) |
---|
| 1517 | +{ |
---|
| 1518 | + return (n->nlmsg_flags & NLM_F_CREATE && |
---|
| 1519 | + n->nlmsg_flags & NLM_F_REPLACE); |
---|
| 1520 | +} |
---|
| 1521 | + |
---|
| 1522 | +static bool req_create_exclusive(struct nlmsghdr *n) |
---|
| 1523 | +{ |
---|
| 1524 | + return (n->nlmsg_flags & NLM_F_CREATE && |
---|
| 1525 | + n->nlmsg_flags & NLM_F_EXCL); |
---|
| 1526 | +} |
---|
| 1527 | + |
---|
| 1528 | +static bool req_change(struct nlmsghdr *n) |
---|
| 1529 | +{ |
---|
| 1530 | + return (!(n->nlmsg_flags & NLM_F_CREATE) && |
---|
| 1531 | + !(n->nlmsg_flags & NLM_F_REPLACE) && |
---|
| 1532 | + !(n->nlmsg_flags & NLM_F_EXCL)); |
---|
| 1533 | +} |
---|
| 1534 | + |
---|
1432 | 1535 | /* |
---|
1433 | 1536 | * Create/change qdisc. |
---|
1434 | 1537 | */ |
---|
1435 | | - |
---|
1436 | 1538 | static int tc_modify_qdisc(struct sk_buff *skb, struct nlmsghdr *n, |
---|
1437 | 1539 | struct netlink_ext_ack *extack) |
---|
1438 | 1540 | { |
---|
.. | .. |
---|
1449 | 1551 | |
---|
1450 | 1552 | replay: |
---|
1451 | 1553 | /* Reinit, just in case something touches this. */ |
---|
1452 | | - err = nlmsg_parse(n, sizeof(*tcm), tca, TCA_MAX, rtm_tca_policy, |
---|
1453 | | - extack); |
---|
| 1554 | + err = nlmsg_parse_deprecated(n, sizeof(*tcm), tca, TCA_MAX, |
---|
| 1555 | + rtm_tca_policy, extack); |
---|
1454 | 1556 | if (err < 0) |
---|
1455 | 1557 | return err; |
---|
1456 | 1558 | |
---|
.. | .. |
---|
1476 | 1578 | q = dev_ingress_queue(dev)->qdisc_sleeping; |
---|
1477 | 1579 | } |
---|
1478 | 1580 | } else { |
---|
1479 | | - q = dev->qdisc; |
---|
| 1581 | + q = rtnl_dereference(dev->qdisc); |
---|
1480 | 1582 | } |
---|
1481 | 1583 | |
---|
1482 | 1584 | /* It may be default qdisc, ignore it */ |
---|
.. | .. |
---|
1505 | 1607 | NL_SET_ERR_MSG(extack, "Invalid qdisc name"); |
---|
1506 | 1608 | return -EINVAL; |
---|
1507 | 1609 | } |
---|
| 1610 | + if (q->flags & TCQ_F_INGRESS) { |
---|
| 1611 | + NL_SET_ERR_MSG(extack, |
---|
| 1612 | + "Cannot regraft ingress or clsact Qdiscs"); |
---|
| 1613 | + return -EINVAL; |
---|
| 1614 | + } |
---|
1508 | 1615 | if (q == p || |
---|
1509 | 1616 | (p && check_loop(q, p, 0))) { |
---|
1510 | 1617 | NL_SET_ERR_MSG(extack, "Qdisc parent/child loop detected"); |
---|
1511 | 1618 | return -ELOOP; |
---|
| 1619 | + } |
---|
| 1620 | + if (clid == TC_H_INGRESS) { |
---|
| 1621 | + NL_SET_ERR_MSG(extack, "Ingress cannot graft directly"); |
---|
| 1622 | + return -EINVAL; |
---|
1512 | 1623 | } |
---|
1513 | 1624 | qdisc_refcount_inc(q); |
---|
1514 | 1625 | goto graft; |
---|
.. | .. |
---|
1520 | 1631 | * |
---|
1521 | 1632 | * We know, that some child q is already |
---|
1522 | 1633 | * attached to this parent and have choice: |
---|
1523 | | - * either to change it or to create/graft new one. |
---|
| 1634 | + * 1) change it or 2) create/graft new one. |
---|
| 1635 | + * If the requested qdisc kind is different |
---|
| 1636 | + * than the existing one, then we choose graft. |
---|
| 1637 | + * If they are the same then this is "change" |
---|
| 1638 | + * operation - just let it fallthrough.. |
---|
1524 | 1639 | * |
---|
1525 | 1640 | * 1. We are allowed to create/graft only |
---|
1526 | | - * if CREATE and REPLACE flags are set. |
---|
| 1641 | + * if the request is explicitly stating |
---|
| 1642 | + * "please create if it doesn't exist". |
---|
1527 | 1643 | * |
---|
1528 | | - * 2. If EXCL is set, requestor wanted to say, |
---|
1529 | | - * that qdisc tcm_handle is not expected |
---|
| 1644 | + * 2. If the request is to exclusive create |
---|
| 1645 | + * then the qdisc tcm_handle is not expected |
---|
1530 | 1646 | * to exist, so that we choose create/graft too. |
---|
1531 | 1647 | * |
---|
1532 | 1648 | * 3. The last case is when no flags are set. |
---|
| 1649 | + * This will happen when for example tc |
---|
| 1650 | + * utility issues a "change" command. |
---|
1533 | 1651 | * Alas, it is sort of hole in API, we |
---|
1534 | 1652 | * cannot decide what to do unambiguously. |
---|
1535 | | - * For now we select create/graft, if |
---|
1536 | | - * user gave KIND, which does not match existing. |
---|
| 1653 | + * For now we select create/graft. |
---|
1537 | 1654 | */ |
---|
1538 | | - if ((n->nlmsg_flags & NLM_F_CREATE) && |
---|
1539 | | - (n->nlmsg_flags & NLM_F_REPLACE) && |
---|
1540 | | - ((n->nlmsg_flags & NLM_F_EXCL) || |
---|
1541 | | - (tca[TCA_KIND] && |
---|
1542 | | - nla_strcmp(tca[TCA_KIND], q->ops->id)))) |
---|
1543 | | - goto create_n_graft; |
---|
| 1655 | + if (tca[TCA_KIND] && |
---|
| 1656 | + nla_strcmp(tca[TCA_KIND], q->ops->id)) { |
---|
| 1657 | + if (req_create_or_replace(n) || |
---|
| 1658 | + req_create_exclusive(n)) |
---|
| 1659 | + goto create_n_graft; |
---|
| 1660 | + else if (req_change(n)) |
---|
| 1661 | + goto create_n_graft2; |
---|
| 1662 | + } |
---|
1544 | 1663 | } |
---|
1545 | 1664 | } |
---|
1546 | 1665 | } else { |
---|
.. | .. |
---|
1574 | 1693 | NL_SET_ERR_MSG(extack, "Qdisc not found. To create specify NLM_F_CREATE flag"); |
---|
1575 | 1694 | return -ENOENT; |
---|
1576 | 1695 | } |
---|
| 1696 | +create_n_graft2: |
---|
1577 | 1697 | if (clid == TC_H_INGRESS) { |
---|
1578 | 1698 | if (dev_ingress_queue(dev)) { |
---|
1579 | 1699 | q = qdisc_create(dev, dev_ingress_queue(dev), p, |
---|
.. | .. |
---|
1684 | 1804 | idx = 0; |
---|
1685 | 1805 | ASSERT_RTNL(); |
---|
1686 | 1806 | |
---|
1687 | | - err = nlmsg_parse(nlh, sizeof(struct tcmsg), tca, TCA_MAX, |
---|
1688 | | - rtm_tca_policy, NULL); |
---|
| 1807 | + err = nlmsg_parse_deprecated(nlh, sizeof(struct tcmsg), tca, TCA_MAX, |
---|
| 1808 | + rtm_tca_policy, cb->extack); |
---|
1689 | 1809 | if (err < 0) |
---|
1690 | 1810 | return err; |
---|
1691 | 1811 | |
---|
.. | .. |
---|
1698 | 1818 | s_q_idx = 0; |
---|
1699 | 1819 | q_idx = 0; |
---|
1700 | 1820 | |
---|
1701 | | - if (tc_dump_qdisc_root(dev->qdisc, skb, cb, &q_idx, s_q_idx, |
---|
| 1821 | + if (tc_dump_qdisc_root(rtnl_dereference(dev->qdisc), |
---|
| 1822 | + skb, cb, &q_idx, s_q_idx, |
---|
1702 | 1823 | true, tca[TCA_DUMP_INVISIBLE]) < 0) |
---|
1703 | 1824 | goto done; |
---|
1704 | 1825 | |
---|
.. | .. |
---|
1778 | 1899 | { |
---|
1779 | 1900 | struct sk_buff *skb; |
---|
1780 | 1901 | u32 portid = oskb ? NETLINK_CB(oskb).portid : 0; |
---|
| 1902 | + int err = 0; |
---|
1781 | 1903 | |
---|
1782 | 1904 | skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); |
---|
1783 | 1905 | if (!skb) |
---|
.. | .. |
---|
1788 | 1910 | return -EINVAL; |
---|
1789 | 1911 | } |
---|
1790 | 1912 | |
---|
1791 | | - return rtnetlink_send(skb, net, portid, RTNLGRP_TC, |
---|
1792 | | - n->nlmsg_flags & NLM_F_ECHO); |
---|
| 1913 | + err = rtnetlink_send(skb, net, portid, RTNLGRP_TC, |
---|
| 1914 | + n->nlmsg_flags & NLM_F_ECHO); |
---|
| 1915 | + if (err > 0) |
---|
| 1916 | + err = 0; |
---|
| 1917 | + return err; |
---|
1793 | 1918 | } |
---|
1794 | 1919 | |
---|
1795 | 1920 | static int tclass_del_notify(struct net *net, |
---|
.. | .. |
---|
1820 | 1945 | return err; |
---|
1821 | 1946 | } |
---|
1822 | 1947 | |
---|
1823 | | - return rtnetlink_send(skb, net, portid, RTNLGRP_TC, |
---|
1824 | | - n->nlmsg_flags & NLM_F_ECHO); |
---|
| 1948 | + err = rtnetlink_send(skb, net, portid, RTNLGRP_TC, |
---|
| 1949 | + n->nlmsg_flags & NLM_F_ECHO); |
---|
| 1950 | + if (err > 0) |
---|
| 1951 | + err = 0; |
---|
| 1952 | + return err; |
---|
1825 | 1953 | } |
---|
1826 | 1954 | |
---|
1827 | 1955 | #ifdef CONFIG_NET_CLS |
---|
.. | .. |
---|
1847 | 1975 | return 0; |
---|
1848 | 1976 | } |
---|
1849 | 1977 | |
---|
| 1978 | +struct tc_bind_class_args { |
---|
| 1979 | + struct qdisc_walker w; |
---|
| 1980 | + unsigned long new_cl; |
---|
| 1981 | + u32 portid; |
---|
| 1982 | + u32 clid; |
---|
| 1983 | +}; |
---|
| 1984 | + |
---|
| 1985 | +static int tc_bind_class_walker(struct Qdisc *q, unsigned long cl, |
---|
| 1986 | + struct qdisc_walker *w) |
---|
| 1987 | +{ |
---|
| 1988 | + struct tc_bind_class_args *a = (struct tc_bind_class_args *)w; |
---|
| 1989 | + const struct Qdisc_class_ops *cops = q->ops->cl_ops; |
---|
| 1990 | + struct tcf_block *block; |
---|
| 1991 | + struct tcf_chain *chain; |
---|
| 1992 | + |
---|
| 1993 | + block = cops->tcf_block(q, cl, NULL); |
---|
| 1994 | + if (!block) |
---|
| 1995 | + return 0; |
---|
| 1996 | + for (chain = tcf_get_next_chain(block, NULL); |
---|
| 1997 | + chain; |
---|
| 1998 | + chain = tcf_get_next_chain(block, chain)) { |
---|
| 1999 | + struct tcf_proto *tp; |
---|
| 2000 | + |
---|
| 2001 | + for (tp = tcf_get_next_proto(chain, NULL, true); |
---|
| 2002 | + tp; tp = tcf_get_next_proto(chain, tp, true)) { |
---|
| 2003 | + struct tcf_bind_args arg = {}; |
---|
| 2004 | + |
---|
| 2005 | + arg.w.fn = tcf_node_bind; |
---|
| 2006 | + arg.classid = a->clid; |
---|
| 2007 | + arg.base = cl; |
---|
| 2008 | + arg.cl = a->new_cl; |
---|
| 2009 | + tp->ops->walk(tp, &arg.w, true); |
---|
| 2010 | + } |
---|
| 2011 | + } |
---|
| 2012 | + |
---|
| 2013 | + return 0; |
---|
| 2014 | +} |
---|
| 2015 | + |
---|
1850 | 2016 | static void tc_bind_tclass(struct Qdisc *q, u32 portid, u32 clid, |
---|
1851 | 2017 | unsigned long new_cl) |
---|
1852 | 2018 | { |
---|
1853 | 2019 | const struct Qdisc_class_ops *cops = q->ops->cl_ops; |
---|
1854 | | - struct tcf_block *block; |
---|
1855 | | - struct tcf_chain *chain; |
---|
1856 | | - unsigned long cl; |
---|
| 2020 | + struct tc_bind_class_args args = {}; |
---|
1857 | 2021 | |
---|
1858 | | - cl = cops->find(q, portid); |
---|
1859 | | - if (!cl) |
---|
1860 | | - return; |
---|
1861 | 2022 | if (!cops->tcf_block) |
---|
1862 | 2023 | 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 | | - } |
---|
| 2024 | + args.portid = portid; |
---|
| 2025 | + args.clid = clid; |
---|
| 2026 | + args.new_cl = new_cl; |
---|
| 2027 | + args.w.fn = tc_bind_class_walker; |
---|
| 2028 | + q->ops->cl_ops->walk(q, &args.w); |
---|
1880 | 2029 | } |
---|
1881 | 2030 | |
---|
1882 | 2031 | #else |
---|
.. | .. |
---|
1908 | 2057 | !netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN)) |
---|
1909 | 2058 | return -EPERM; |
---|
1910 | 2059 | |
---|
1911 | | - err = nlmsg_parse(n, sizeof(*tcm), tca, TCA_MAX, rtm_tca_policy, |
---|
1912 | | - extack); |
---|
| 2060 | + err = nlmsg_parse_deprecated(n, sizeof(*tcm), tca, TCA_MAX, |
---|
| 2061 | + rtm_tca_policy, extack); |
---|
1913 | 2062 | if (err < 0) |
---|
1914 | 2063 | return err; |
---|
1915 | 2064 | |
---|
.. | .. |
---|
1946 | 2095 | } else if (qid1) { |
---|
1947 | 2096 | qid = qid1; |
---|
1948 | 2097 | } else if (qid == 0) |
---|
1949 | | - qid = dev->qdisc->handle; |
---|
| 2098 | + qid = rtnl_dereference(dev->qdisc)->handle; |
---|
1950 | 2099 | |
---|
1951 | 2100 | /* Now qid is genuine qdisc handle consistent |
---|
1952 | 2101 | * both with parent and child. |
---|
.. | .. |
---|
1957 | 2106 | portid = TC_H_MAKE(qid, portid); |
---|
1958 | 2107 | } else { |
---|
1959 | 2108 | if (qid == 0) |
---|
1960 | | - qid = dev->qdisc->handle; |
---|
| 2109 | + qid = rtnl_dereference(dev->qdisc)->handle; |
---|
1961 | 2110 | } |
---|
1962 | 2111 | |
---|
1963 | 2112 | /* OK. Locate qdisc */ |
---|
.. | .. |
---|
2118 | 2267 | s_t = cb->args[0]; |
---|
2119 | 2268 | t = 0; |
---|
2120 | 2269 | |
---|
2121 | | - if (tc_dump_tclass_root(dev->qdisc, skb, tcm, cb, &t, s_t, true) < 0) |
---|
| 2270 | + if (tc_dump_tclass_root(rtnl_dereference(dev->qdisc), |
---|
| 2271 | + skb, tcm, cb, &t, s_t, true) < 0) |
---|
2122 | 2272 | goto done; |
---|
2123 | 2273 | |
---|
2124 | 2274 | dev_queue = dev_ingress_queue(dev); |
---|