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