| .. | .. |
|---|
| 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 | } |
|---|
| .. | .. |
|---|
| 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); |
|---|