diff options
| -rw-r--r-- | include/linux/netdevice.h | 9 | ||||
| -rw-r--r-- | include/net/sch_generic.h | 32 | ||||
| -rw-r--r-- | net/core/dev.c | 21 | ||||
| -rw-r--r-- | net/sched/sch_generic.c | 46 | ||||
| -rw-r--r-- | net/sched/sch_ingress.c | 19 |
5 files changed, 109 insertions, 18 deletions
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 5e02f79b2110..7de7656550c2 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h | |||
| @@ -1559,6 +1559,8 @@ enum netdev_priv_flags { | |||
| 1559 | * | 1559 | * |
| 1560 | * @rx_handler: handler for received packets | 1560 | * @rx_handler: handler for received packets |
| 1561 | * @rx_handler_data: XXX: need comments on this one | 1561 | * @rx_handler_data: XXX: need comments on this one |
| 1562 | * @miniq_ingress: ingress/clsact qdisc specific data for | ||
| 1563 | * ingress processing | ||
| 1562 | * @ingress_queue: XXX: need comments on this one | 1564 | * @ingress_queue: XXX: need comments on this one |
| 1563 | * @broadcast: hw bcast address | 1565 | * @broadcast: hw bcast address |
| 1564 | * | 1566 | * |
| @@ -1576,7 +1578,8 @@ enum netdev_priv_flags { | |||
| 1576 | * @tx_global_lock: XXX: need comments on this one | 1578 | * @tx_global_lock: XXX: need comments on this one |
| 1577 | * | 1579 | * |
| 1578 | * @xps_maps: XXX: need comments on this one | 1580 | * @xps_maps: XXX: need comments on this one |
| 1579 | * | 1581 | * @miniq_egress: clsact qdisc specific data for |
| 1582 | * egress processing | ||
| 1580 | * @watchdog_timeo: Represents the timeout that is used by | 1583 | * @watchdog_timeo: Represents the timeout that is used by |
| 1581 | * the watchdog (see dev_watchdog()) | 1584 | * the watchdog (see dev_watchdog()) |
| 1582 | * @watchdog_timer: List of timers | 1585 | * @watchdog_timer: List of timers |
| @@ -1795,7 +1798,7 @@ struct net_device { | |||
| 1795 | void __rcu *rx_handler_data; | 1798 | void __rcu *rx_handler_data; |
| 1796 | 1799 | ||
| 1797 | #ifdef CONFIG_NET_CLS_ACT | 1800 | #ifdef CONFIG_NET_CLS_ACT |
| 1798 | struct tcf_proto __rcu *ingress_cl_list; | 1801 | struct mini_Qdisc __rcu *miniq_ingress; |
| 1799 | #endif | 1802 | #endif |
| 1800 | struct netdev_queue __rcu *ingress_queue; | 1803 | struct netdev_queue __rcu *ingress_queue; |
| 1801 | #ifdef CONFIG_NETFILTER_INGRESS | 1804 | #ifdef CONFIG_NETFILTER_INGRESS |
| @@ -1826,7 +1829,7 @@ struct net_device { | |||
| 1826 | struct xps_dev_maps __rcu *xps_maps; | 1829 | struct xps_dev_maps __rcu *xps_maps; |
| 1827 | #endif | 1830 | #endif |
| 1828 | #ifdef CONFIG_NET_CLS_ACT | 1831 | #ifdef CONFIG_NET_CLS_ACT |
| 1829 | struct tcf_proto __rcu *egress_cl_list; | 1832 | struct mini_Qdisc __rcu *miniq_egress; |
| 1830 | #endif | 1833 | #endif |
| 1831 | 1834 | ||
| 1832 | /* These may be needed for future network-power-down code. */ | 1835 | /* These may be needed for future network-power-down code. */ |
diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h index f230269e0bfb..c64e62c9450a 100644 --- a/include/net/sch_generic.h +++ b/include/net/sch_generic.h | |||
| @@ -904,4 +904,36 @@ static inline void psched_ratecfg_getrate(struct tc_ratespec *res, | |||
| 904 | res->linklayer = (r->linklayer & TC_LINKLAYER_MASK); | 904 | res->linklayer = (r->linklayer & TC_LINKLAYER_MASK); |
| 905 | } | 905 | } |
| 906 | 906 | ||
| 907 | /* Mini Qdisc serves for specific needs of ingress/clsact Qdisc. | ||
| 908 | * The fast path only needs to access filter list and to update stats | ||
| 909 | */ | ||
| 910 | struct mini_Qdisc { | ||
| 911 | struct tcf_proto *filter_list; | ||
| 912 | struct gnet_stats_basic_cpu __percpu *cpu_bstats; | ||
| 913 | struct gnet_stats_queue __percpu *cpu_qstats; | ||
| 914 | struct rcu_head rcu; | ||
| 915 | }; | ||
| 916 | |||
| 917 | static inline void mini_qdisc_bstats_cpu_update(struct mini_Qdisc *miniq, | ||
| 918 | const struct sk_buff *skb) | ||
| 919 | { | ||
| 920 | bstats_cpu_update(this_cpu_ptr(miniq->cpu_bstats), skb); | ||
| 921 | } | ||
| 922 | |||
| 923 | static inline void mini_qdisc_qstats_cpu_drop(struct mini_Qdisc *miniq) | ||
| 924 | { | ||
| 925 | this_cpu_inc(miniq->cpu_qstats->drops); | ||
| 926 | } | ||
| 927 | |||
| 928 | struct mini_Qdisc_pair { | ||
| 929 | struct mini_Qdisc miniq1; | ||
| 930 | struct mini_Qdisc miniq2; | ||
| 931 | struct mini_Qdisc __rcu **p_miniq; | ||
| 932 | }; | ||
| 933 | |||
| 934 | void mini_qdisc_pair_swap(struct mini_Qdisc_pair *miniqp, | ||
| 935 | struct tcf_proto *tp_head); | ||
| 936 | void mini_qdisc_pair_init(struct mini_Qdisc_pair *miniqp, struct Qdisc *qdisc, | ||
| 937 | struct mini_Qdisc __rcu **p_miniq); | ||
| 938 | |||
| 907 | #endif | 939 | #endif |
diff --git a/net/core/dev.c b/net/core/dev.c index 24ac9083bc13..1423cf4d695c 100644 --- a/net/core/dev.c +++ b/net/core/dev.c | |||
| @@ -3274,22 +3274,22 @@ EXPORT_SYMBOL(dev_loopback_xmit); | |||
| 3274 | static struct sk_buff * | 3274 | static struct sk_buff * |
| 3275 | sch_handle_egress(struct sk_buff *skb, int *ret, struct net_device *dev) | 3275 | sch_handle_egress(struct sk_buff *skb, int *ret, struct net_device *dev) |
| 3276 | { | 3276 | { |
| 3277 | struct tcf_proto *cl = rcu_dereference_bh(dev->egress_cl_list); | 3277 | struct mini_Qdisc *miniq = rcu_dereference_bh(dev->miniq_egress); |
| 3278 | struct tcf_result cl_res; | 3278 | struct tcf_result cl_res; |
| 3279 | 3279 | ||
| 3280 | if (!cl) | 3280 | if (!miniq) |
| 3281 | return skb; | 3281 | return skb; |
| 3282 | 3282 | ||
| 3283 | /* qdisc_skb_cb(skb)->pkt_len was already set by the caller. */ | 3283 | /* qdisc_skb_cb(skb)->pkt_len was already set by the caller. */ |
| 3284 | qdisc_bstats_cpu_update(cl->q, skb); | 3284 | mini_qdisc_bstats_cpu_update(miniq, skb); |
| 3285 | 3285 | ||
| 3286 | switch (tcf_classify(skb, cl, &cl_res, false)) { | 3286 | switch (tcf_classify(skb, miniq->filter_list, &cl_res, false)) { |
| 3287 | case TC_ACT_OK: | 3287 | case TC_ACT_OK: |
| 3288 | case TC_ACT_RECLASSIFY: | 3288 | case TC_ACT_RECLASSIFY: |
| 3289 | skb->tc_index = TC_H_MIN(cl_res.classid); | 3289 | skb->tc_index = TC_H_MIN(cl_res.classid); |
| 3290 | break; | 3290 | break; |
| 3291 | case TC_ACT_SHOT: | 3291 | case TC_ACT_SHOT: |
| 3292 | qdisc_qstats_cpu_drop(cl->q); | 3292 | mini_qdisc_qstats_cpu_drop(miniq); |
| 3293 | *ret = NET_XMIT_DROP; | 3293 | *ret = NET_XMIT_DROP; |
| 3294 | kfree_skb(skb); | 3294 | kfree_skb(skb); |
| 3295 | return NULL; | 3295 | return NULL; |
| @@ -4189,7 +4189,7 @@ sch_handle_ingress(struct sk_buff *skb, struct packet_type **pt_prev, int *ret, | |||
| 4189 | struct net_device *orig_dev) | 4189 | struct net_device *orig_dev) |
| 4190 | { | 4190 | { |
| 4191 | #ifdef CONFIG_NET_CLS_ACT | 4191 | #ifdef CONFIG_NET_CLS_ACT |
| 4192 | struct tcf_proto *cl = rcu_dereference_bh(skb->dev->ingress_cl_list); | 4192 | struct mini_Qdisc *miniq = rcu_dereference_bh(skb->dev->miniq_ingress); |
| 4193 | struct tcf_result cl_res; | 4193 | struct tcf_result cl_res; |
| 4194 | 4194 | ||
| 4195 | /* If there's at least one ingress present somewhere (so | 4195 | /* If there's at least one ingress present somewhere (so |
| @@ -4197,8 +4197,9 @@ sch_handle_ingress(struct sk_buff *skb, struct packet_type **pt_prev, int *ret, | |||
| 4197 | * that are not configured with an ingress qdisc will bail | 4197 | * that are not configured with an ingress qdisc will bail |
| 4198 | * out here. | 4198 | * out here. |
| 4199 | */ | 4199 | */ |
| 4200 | if (!cl) | 4200 | if (!miniq) |
| 4201 | return skb; | 4201 | return skb; |
| 4202 | |||
| 4202 | if (*pt_prev) { | 4203 | if (*pt_prev) { |
| 4203 | *ret = deliver_skb(skb, *pt_prev, orig_dev); | 4204 | *ret = deliver_skb(skb, *pt_prev, orig_dev); |
| 4204 | *pt_prev = NULL; | 4205 | *pt_prev = NULL; |
| @@ -4206,15 +4207,15 @@ sch_handle_ingress(struct sk_buff *skb, struct packet_type **pt_prev, int *ret, | |||
| 4206 | 4207 | ||
| 4207 | qdisc_skb_cb(skb)->pkt_len = skb->len; | 4208 | qdisc_skb_cb(skb)->pkt_len = skb->len; |
| 4208 | skb->tc_at_ingress = 1; | 4209 | skb->tc_at_ingress = 1; |
| 4209 | qdisc_bstats_cpu_update(cl->q, skb); | 4210 | mini_qdisc_bstats_cpu_update(miniq, skb); |
| 4210 | 4211 | ||
| 4211 | switch (tcf_classify(skb, cl, &cl_res, false)) { | 4212 | switch (tcf_classify(skb, miniq->filter_list, &cl_res, false)) { |
| 4212 | case TC_ACT_OK: | 4213 | case TC_ACT_OK: |
| 4213 | case TC_ACT_RECLASSIFY: | 4214 | case TC_ACT_RECLASSIFY: |
| 4214 | skb->tc_index = TC_H_MIN(cl_res.classid); | 4215 | skb->tc_index = TC_H_MIN(cl_res.classid); |
| 4215 | break; | 4216 | break; |
| 4216 | case TC_ACT_SHOT: | 4217 | case TC_ACT_SHOT: |
| 4217 | qdisc_qstats_cpu_drop(cl->q); | 4218 | mini_qdisc_qstats_cpu_drop(miniq); |
| 4218 | kfree_skb(skb); | 4219 | kfree_skb(skb); |
| 4219 | return NULL; | 4220 | return NULL; |
| 4220 | case TC_ACT_STOLEN: | 4221 | case TC_ACT_STOLEN: |
diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index aa74aa42b5d7..3839cbbdc32b 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c | |||
| @@ -1024,3 +1024,49 @@ void psched_ratecfg_precompute(struct psched_ratecfg *r, | |||
| 1024 | } | 1024 | } |
| 1025 | } | 1025 | } |
| 1026 | EXPORT_SYMBOL(psched_ratecfg_precompute); | 1026 | EXPORT_SYMBOL(psched_ratecfg_precompute); |
| 1027 | |||
| 1028 | static void mini_qdisc_rcu_func(struct rcu_head *head) | ||
| 1029 | { | ||
| 1030 | } | ||
| 1031 | |||
| 1032 | void mini_qdisc_pair_swap(struct mini_Qdisc_pair *miniqp, | ||
| 1033 | struct tcf_proto *tp_head) | ||
| 1034 | { | ||
| 1035 | struct mini_Qdisc *miniq_old = rtnl_dereference(*miniqp->p_miniq); | ||
| 1036 | struct mini_Qdisc *miniq; | ||
| 1037 | |||
| 1038 | if (!tp_head) { | ||
| 1039 | RCU_INIT_POINTER(*miniqp->p_miniq, NULL); | ||
| 1040 | return; | ||
| 1041 | } | ||
| 1042 | |||
| 1043 | miniq = !miniq_old || miniq_old == &miniqp->miniq2 ? | ||
| 1044 | &miniqp->miniq1 : &miniqp->miniq2; | ||
| 1045 | |||
| 1046 | /* We need to make sure that readers won't see the miniq | ||
| 1047 | * we are about to modify. So wait until previous call_rcu_bh callback | ||
| 1048 | * is done. | ||
| 1049 | */ | ||
| 1050 | rcu_barrier_bh(); | ||
| 1051 | miniq->filter_list = tp_head; | ||
| 1052 | rcu_assign_pointer(*miniqp->p_miniq, miniq); | ||
| 1053 | |||
| 1054 | if (miniq_old) | ||
| 1055 | /* This is counterpart of the rcu barrier above. We need to | ||
| 1056 | * block potential new user of miniq_old until all readers | ||
| 1057 | * are not seeing it. | ||
| 1058 | */ | ||
| 1059 | call_rcu_bh(&miniq_old->rcu, mini_qdisc_rcu_func); | ||
| 1060 | } | ||
| 1061 | EXPORT_SYMBOL(mini_qdisc_pair_swap); | ||
| 1062 | |||
| 1063 | void mini_qdisc_pair_init(struct mini_Qdisc_pair *miniqp, struct Qdisc *qdisc, | ||
| 1064 | struct mini_Qdisc __rcu **p_miniq) | ||
| 1065 | { | ||
| 1066 | miniqp->miniq1.cpu_bstats = qdisc->cpu_bstats; | ||
| 1067 | miniqp->miniq1.cpu_qstats = qdisc->cpu_qstats; | ||
| 1068 | miniqp->miniq2.cpu_bstats = qdisc->cpu_bstats; | ||
| 1069 | miniqp->miniq2.cpu_qstats = qdisc->cpu_qstats; | ||
| 1070 | miniqp->p_miniq = p_miniq; | ||
| 1071 | } | ||
| 1072 | EXPORT_SYMBOL(mini_qdisc_pair_init); | ||
diff --git a/net/sched/sch_ingress.c b/net/sched/sch_ingress.c index 811845815b8c..5ecc38f35d47 100644 --- a/net/sched/sch_ingress.c +++ b/net/sched/sch_ingress.c | |||
| @@ -21,6 +21,7 @@ | |||
| 21 | struct ingress_sched_data { | 21 | struct ingress_sched_data { |
| 22 | struct tcf_block *block; | 22 | struct tcf_block *block; |
| 23 | struct tcf_block_ext_info block_info; | 23 | struct tcf_block_ext_info block_info; |
| 24 | struct mini_Qdisc_pair miniqp; | ||
| 24 | }; | 25 | }; |
| 25 | 26 | ||
| 26 | static struct Qdisc *ingress_leaf(struct Qdisc *sch, unsigned long arg) | 27 | static struct Qdisc *ingress_leaf(struct Qdisc *sch, unsigned long arg) |
| @@ -56,9 +57,9 @@ static struct tcf_block *ingress_tcf_block(struct Qdisc *sch, unsigned long cl) | |||
| 56 | 57 | ||
| 57 | static void clsact_chain_head_change(struct tcf_proto *tp_head, void *priv) | 58 | static void clsact_chain_head_change(struct tcf_proto *tp_head, void *priv) |
| 58 | { | 59 | { |
| 59 | struct tcf_proto __rcu **p_filter_chain = priv; | 60 | struct mini_Qdisc_pair *miniqp = priv; |
| 60 | 61 | ||
| 61 | rcu_assign_pointer(*p_filter_chain, tp_head); | 62 | mini_qdisc_pair_swap(miniqp, tp_head); |
| 62 | } | 63 | } |
| 63 | 64 | ||
| 64 | static int ingress_init(struct Qdisc *sch, struct nlattr *opt) | 65 | static int ingress_init(struct Qdisc *sch, struct nlattr *opt) |
| @@ -67,9 +68,11 @@ static int ingress_init(struct Qdisc *sch, struct nlattr *opt) | |||
| 67 | struct net_device *dev = qdisc_dev(sch); | 68 | struct net_device *dev = qdisc_dev(sch); |
| 68 | int err; | 69 | int err; |
| 69 | 70 | ||
| 71 | mini_qdisc_pair_init(&q->miniqp, sch, &dev->miniq_ingress); | ||
| 72 | |||
| 70 | q->block_info.binder_type = TCF_BLOCK_BINDER_TYPE_CLSACT_INGRESS; | 73 | q->block_info.binder_type = TCF_BLOCK_BINDER_TYPE_CLSACT_INGRESS; |
| 71 | q->block_info.chain_head_change = clsact_chain_head_change; | 74 | q->block_info.chain_head_change = clsact_chain_head_change; |
| 72 | q->block_info.chain_head_change_priv = &dev->ingress_cl_list; | 75 | q->block_info.chain_head_change_priv = &q->miniqp; |
| 73 | 76 | ||
| 74 | err = tcf_block_get_ext(&q->block, sch, &q->block_info); | 77 | err = tcf_block_get_ext(&q->block, sch, &q->block_info); |
| 75 | if (err) | 78 | if (err) |
| @@ -128,6 +131,8 @@ struct clsact_sched_data { | |||
| 128 | struct tcf_block *egress_block; | 131 | struct tcf_block *egress_block; |
| 129 | struct tcf_block_ext_info ingress_block_info; | 132 | struct tcf_block_ext_info ingress_block_info; |
| 130 | struct tcf_block_ext_info egress_block_info; | 133 | struct tcf_block_ext_info egress_block_info; |
| 134 | struct mini_Qdisc_pair miniqp_ingress; | ||
| 135 | struct mini_Qdisc_pair miniqp_egress; | ||
| 131 | }; | 136 | }; |
| 132 | 137 | ||
| 133 | static unsigned long clsact_find(struct Qdisc *sch, u32 classid) | 138 | static unsigned long clsact_find(struct Qdisc *sch, u32 classid) |
| @@ -167,17 +172,21 @@ static int clsact_init(struct Qdisc *sch, struct nlattr *opt) | |||
| 167 | struct net_device *dev = qdisc_dev(sch); | 172 | struct net_device *dev = qdisc_dev(sch); |
| 168 | int err; | 173 | int err; |
| 169 | 174 | ||
| 175 | mini_qdisc_pair_init(&q->miniqp_ingress, sch, &dev->miniq_ingress); | ||
| 176 | |||
| 170 | q->ingress_block_info.binder_type = TCF_BLOCK_BINDER_TYPE_CLSACT_INGRESS; | 177 | q->ingress_block_info.binder_type = TCF_BLOCK_BINDER_TYPE_CLSACT_INGRESS; |
| 171 | q->ingress_block_info.chain_head_change = clsact_chain_head_change; | 178 | q->ingress_block_info.chain_head_change = clsact_chain_head_change; |
| 172 | q->ingress_block_info.chain_head_change_priv = &dev->ingress_cl_list; | 179 | q->ingress_block_info.chain_head_change_priv = &q->miniqp_ingress; |
| 173 | 180 | ||
| 174 | err = tcf_block_get_ext(&q->ingress_block, sch, &q->ingress_block_info); | 181 | err = tcf_block_get_ext(&q->ingress_block, sch, &q->ingress_block_info); |
| 175 | if (err) | 182 | if (err) |
| 176 | return err; | 183 | return err; |
| 177 | 184 | ||
| 185 | mini_qdisc_pair_init(&q->miniqp_egress, sch, &dev->miniq_egress); | ||
| 186 | |||
| 178 | q->egress_block_info.binder_type = TCF_BLOCK_BINDER_TYPE_CLSACT_EGRESS; | 187 | q->egress_block_info.binder_type = TCF_BLOCK_BINDER_TYPE_CLSACT_EGRESS; |
| 179 | q->egress_block_info.chain_head_change = clsact_chain_head_change; | 188 | q->egress_block_info.chain_head_change = clsact_chain_head_change; |
| 180 | q->egress_block_info.chain_head_change_priv = &dev->egress_cl_list; | 189 | q->egress_block_info.chain_head_change_priv = &q->miniqp_egress; |
| 181 | 190 | ||
| 182 | err = tcf_block_get_ext(&q->egress_block, sch, &q->egress_block_info); | 191 | err = tcf_block_get_ext(&q->egress_block, sch, &q->egress_block_info); |
| 183 | if (err) | 192 | if (err) |
