diff options
-rw-r--r-- | include/net/act_api.h | 12 | ||||
-rw-r--r-- | include/net/pkt_cls.h | 16 | ||||
-rw-r--r-- | net/sched/act_api.c | 105 | ||||
-rw-r--r-- | net/sched/cls_api.c | 54 | ||||
-rw-r--r-- | net/sched/cls_basic.c | 1 | ||||
-rw-r--r-- | net/sched/cls_bpf.c | 1 | ||||
-rw-r--r-- | net/sched/cls_cgroup.c | 1 | ||||
-rw-r--r-- | net/sched/cls_flow.c | 1 | ||||
-rw-r--r-- | net/sched/cls_fw.c | 1 | ||||
-rw-r--r-- | net/sched/cls_route.c | 1 | ||||
-rw-r--r-- | net/sched/cls_rsvp.h | 1 | ||||
-rw-r--r-- | net/sched/cls_tcindex.c | 5 | ||||
-rw-r--r-- | net/sched/cls_u32.c | 1 |
13 files changed, 101 insertions, 99 deletions
diff --git a/include/net/act_api.h b/include/net/act_api.h index 04c6825e172b..a72642610790 100644 --- a/include/net/act_api.h +++ b/include/net/act_api.h | |||
@@ -60,7 +60,7 @@ struct tc_action { | |||
60 | const struct tc_action_ops *ops; | 60 | const struct tc_action_ops *ops; |
61 | __u32 type; /* for backward compat(TCA_OLD_COMPAT) */ | 61 | __u32 type; /* for backward compat(TCA_OLD_COMPAT) */ |
62 | __u32 order; | 62 | __u32 order; |
63 | struct tc_action *next; | 63 | struct list_head list; |
64 | }; | 64 | }; |
65 | 65 | ||
66 | #define TCA_CAP_NONE 0 | 66 | #define TCA_CAP_NONE 0 |
@@ -99,16 +99,16 @@ void tcf_hash_insert(struct tcf_common *p, struct tcf_hashinfo *hinfo); | |||
99 | 99 | ||
100 | int tcf_register_action(struct tc_action_ops *a); | 100 | int tcf_register_action(struct tc_action_ops *a); |
101 | int tcf_unregister_action(struct tc_action_ops *a); | 101 | int tcf_unregister_action(struct tc_action_ops *a); |
102 | void tcf_action_destroy(struct tc_action *a, int bind); | 102 | void tcf_action_destroy(struct list_head *actions, int bind); |
103 | int tcf_action_exec(struct sk_buff *skb, const struct tc_action *a, | 103 | int tcf_action_exec(struct sk_buff *skb, const struct list_head *actions, |
104 | struct tcf_result *res); | 104 | struct tcf_result *res); |
105 | struct tc_action *tcf_action_init(struct net *net, struct nlattr *nla, | 105 | int tcf_action_init(struct net *net, struct nlattr *nla, |
106 | struct nlattr *est, char *n, int ovr, | 106 | struct nlattr *est, char *n, int ovr, |
107 | int bind); | 107 | int bind, struct list_head *); |
108 | struct tc_action *tcf_action_init_1(struct net *net, struct nlattr *nla, | 108 | struct tc_action *tcf_action_init_1(struct net *net, struct nlattr *nla, |
109 | struct nlattr *est, char *n, int ovr, | 109 | struct nlattr *est, char *n, int ovr, |
110 | int bind); | 110 | int bind); |
111 | int tcf_action_dump(struct sk_buff *skb, struct tc_action *a, int, int); | 111 | int tcf_action_dump(struct sk_buff *skb, struct list_head *, int, int); |
112 | int tcf_action_dump_old(struct sk_buff *skb, struct tc_action *a, int, int); | 112 | int tcf_action_dump_old(struct sk_buff *skb, struct tc_action *a, int, int); |
113 | int tcf_action_dump_1(struct sk_buff *skb, struct tc_action *a, int, int); | 113 | int tcf_action_dump_1(struct sk_buff *skb, struct tc_action *a, int, int); |
114 | int tcf_action_copy_stats(struct sk_buff *, struct tc_action *, int); | 114 | int tcf_action_copy_stats(struct sk_buff *, struct tc_action *, int); |
diff --git a/include/net/pkt_cls.h b/include/net/pkt_cls.h index 2ebef77a2f9a..34fe693ddf9a 100644 --- a/include/net/pkt_cls.h +++ b/include/net/pkt_cls.h | |||
@@ -62,7 +62,8 @@ tcf_unbind_filter(struct tcf_proto *tp, struct tcf_result *r) | |||
62 | 62 | ||
63 | struct tcf_exts { | 63 | struct tcf_exts { |
64 | #ifdef CONFIG_NET_CLS_ACT | 64 | #ifdef CONFIG_NET_CLS_ACT |
65 | struct tc_action *action; | 65 | __u32 type; /* for backward compat(TCA_OLD_COMPAT) */ |
66 | struct list_head actions; | ||
66 | #endif | 67 | #endif |
67 | }; | 68 | }; |
68 | 69 | ||
@@ -74,6 +75,13 @@ struct tcf_ext_map { | |||
74 | int police; | 75 | int police; |
75 | }; | 76 | }; |
76 | 77 | ||
78 | static inline void tcf_exts_init(struct tcf_exts *exts) | ||
79 | { | ||
80 | #ifdef CONFIG_NET_CLS_ACT | ||
81 | INIT_LIST_HEAD(&exts->actions); | ||
82 | #endif | ||
83 | } | ||
84 | |||
77 | /** | 85 | /** |
78 | * tcf_exts_is_predicative - check if a predicative extension is present | 86 | * tcf_exts_is_predicative - check if a predicative extension is present |
79 | * @exts: tc filter extensions handle | 87 | * @exts: tc filter extensions handle |
@@ -85,7 +93,7 @@ static inline int | |||
85 | tcf_exts_is_predicative(struct tcf_exts *exts) | 93 | tcf_exts_is_predicative(struct tcf_exts *exts) |
86 | { | 94 | { |
87 | #ifdef CONFIG_NET_CLS_ACT | 95 | #ifdef CONFIG_NET_CLS_ACT |
88 | return !!exts->action; | 96 | return !list_empty(&exts->actions); |
89 | #else | 97 | #else |
90 | return 0; | 98 | return 0; |
91 | #endif | 99 | #endif |
@@ -120,8 +128,8 @@ tcf_exts_exec(struct sk_buff *skb, struct tcf_exts *exts, | |||
120 | struct tcf_result *res) | 128 | struct tcf_result *res) |
121 | { | 129 | { |
122 | #ifdef CONFIG_NET_CLS_ACT | 130 | #ifdef CONFIG_NET_CLS_ACT |
123 | if (exts->action) | 131 | if (!list_empty(&exts->actions)) |
124 | return tcf_action_exec(skb, exts->action, res); | 132 | return tcf_action_exec(skb, &exts->actions, res); |
125 | #endif | 133 | #endif |
126 | return 0; | 134 | return 0; |
127 | } | 135 | } |
diff --git a/net/sched/act_api.c b/net/sched/act_api.c index 51e28f7a4638..7d84183b633e 100644 --- a/net/sched/act_api.c +++ b/net/sched/act_api.c | |||
@@ -379,7 +379,7 @@ static struct tc_action_ops *tc_lookup_action_id(u32 type) | |||
379 | } | 379 | } |
380 | #endif | 380 | #endif |
381 | 381 | ||
382 | int tcf_action_exec(struct sk_buff *skb, const struct tc_action *act, | 382 | int tcf_action_exec(struct sk_buff *skb, const struct list_head *actions, |
383 | struct tcf_result *res) | 383 | struct tcf_result *res) |
384 | { | 384 | { |
385 | const struct tc_action *a; | 385 | const struct tc_action *a; |
@@ -390,7 +390,7 @@ int tcf_action_exec(struct sk_buff *skb, const struct tc_action *act, | |||
390 | ret = TC_ACT_OK; | 390 | ret = TC_ACT_OK; |
391 | goto exec_done; | 391 | goto exec_done; |
392 | } | 392 | } |
393 | while ((a = act) != NULL) { | 393 | list_for_each_entry(a, actions, list) { |
394 | repeat: | 394 | repeat: |
395 | if (a->ops) { | 395 | if (a->ops) { |
396 | ret = a->ops->act(skb, a, res); | 396 | ret = a->ops->act(skb, a, res); |
@@ -404,27 +404,26 @@ repeat: | |||
404 | if (ret != TC_ACT_PIPE) | 404 | if (ret != TC_ACT_PIPE) |
405 | goto exec_done; | 405 | goto exec_done; |
406 | } | 406 | } |
407 | act = a->next; | ||
408 | } | 407 | } |
409 | exec_done: | 408 | exec_done: |
410 | return ret; | 409 | return ret; |
411 | } | 410 | } |
412 | EXPORT_SYMBOL(tcf_action_exec); | 411 | EXPORT_SYMBOL(tcf_action_exec); |
413 | 412 | ||
414 | void tcf_action_destroy(struct tc_action *act, int bind) | 413 | void tcf_action_destroy(struct list_head *actions, int bind) |
415 | { | 414 | { |
416 | struct tc_action *a; | 415 | struct tc_action *a, *tmp; |
417 | 416 | ||
418 | for (a = act; a; a = act) { | 417 | list_for_each_entry_safe(a, tmp, actions, list) { |
419 | if (a->ops) { | 418 | if (a->ops) { |
420 | if (a->ops->cleanup(a, bind) == ACT_P_DELETED) | 419 | if (a->ops->cleanup(a, bind) == ACT_P_DELETED) |
421 | module_put(a->ops->owner); | 420 | module_put(a->ops->owner); |
422 | act = act->next; | 421 | list_del(&a->list); |
423 | kfree(a); | 422 | kfree(a); |
424 | } else { | 423 | } else { |
425 | /*FIXME: Remove later - catch insertion bugs*/ | 424 | /*FIXME: Remove later - catch insertion bugs*/ |
426 | WARN(1, "tcf_action_destroy: BUG? destroying NULL ops\n"); | 425 | WARN(1, "tcf_action_destroy: BUG? destroying NULL ops\n"); |
427 | act = act->next; | 426 | list_del(&a->list); |
428 | kfree(a); | 427 | kfree(a); |
429 | } | 428 | } |
430 | } | 429 | } |
@@ -470,14 +469,13 @@ nla_put_failure: | |||
470 | EXPORT_SYMBOL(tcf_action_dump_1); | 469 | EXPORT_SYMBOL(tcf_action_dump_1); |
471 | 470 | ||
472 | int | 471 | int |
473 | tcf_action_dump(struct sk_buff *skb, struct tc_action *act, int bind, int ref) | 472 | tcf_action_dump(struct sk_buff *skb, struct list_head *actions, int bind, int ref) |
474 | { | 473 | { |
475 | struct tc_action *a; | 474 | struct tc_action *a; |
476 | int err = -EINVAL; | 475 | int err = -EINVAL; |
477 | struct nlattr *nest; | 476 | struct nlattr *nest; |
478 | 477 | ||
479 | while ((a = act) != NULL) { | 478 | list_for_each_entry(a, actions, list) { |
480 | act = a->next; | ||
481 | nest = nla_nest_start(skb, a->order); | 479 | nest = nla_nest_start(skb, a->order); |
482 | if (nest == NULL) | 480 | if (nest == NULL) |
483 | goto nla_put_failure; | 481 | goto nla_put_failure; |
@@ -552,6 +550,7 @@ struct tc_action *tcf_action_init_1(struct net *net, struct nlattr *nla, | |||
552 | if (a == NULL) | 550 | if (a == NULL) |
553 | goto err_mod; | 551 | goto err_mod; |
554 | 552 | ||
553 | INIT_LIST_HEAD(&a->list); | ||
555 | /* backward compatibility for policer */ | 554 | /* backward compatibility for policer */ |
556 | if (name == NULL) | 555 | if (name == NULL) |
557 | err = a_o->init(net, tb[TCA_ACT_OPTIONS], est, a, ovr, bind); | 556 | err = a_o->init(net, tb[TCA_ACT_OPTIONS], est, a, ovr, bind); |
@@ -578,37 +577,33 @@ err_out: | |||
578 | return ERR_PTR(err); | 577 | return ERR_PTR(err); |
579 | } | 578 | } |
580 | 579 | ||
581 | struct tc_action *tcf_action_init(struct net *net, struct nlattr *nla, | 580 | int tcf_action_init(struct net *net, struct nlattr *nla, |
582 | struct nlattr *est, char *name, int ovr, | 581 | struct nlattr *est, char *name, int ovr, |
583 | int bind) | 582 | int bind, struct list_head *actions) |
584 | { | 583 | { |
585 | struct nlattr *tb[TCA_ACT_MAX_PRIO + 1]; | 584 | struct nlattr *tb[TCA_ACT_MAX_PRIO + 1]; |
586 | struct tc_action *head = NULL, *act, *act_prev = NULL; | 585 | struct tc_action *act; |
587 | int err; | 586 | int err; |
588 | int i; | 587 | int i; |
589 | 588 | ||
590 | err = nla_parse_nested(tb, TCA_ACT_MAX_PRIO, nla, NULL); | 589 | err = nla_parse_nested(tb, TCA_ACT_MAX_PRIO, nla, NULL); |
591 | if (err < 0) | 590 | if (err < 0) |
592 | return ERR_PTR(err); | 591 | return err; |
593 | 592 | ||
594 | for (i = 1; i <= TCA_ACT_MAX_PRIO && tb[i]; i++) { | 593 | for (i = 1; i <= TCA_ACT_MAX_PRIO && tb[i]; i++) { |
595 | act = tcf_action_init_1(net, tb[i], est, name, ovr, bind); | 594 | act = tcf_action_init_1(net, tb[i], est, name, ovr, bind); |
596 | if (IS_ERR(act)) | 595 | if (IS_ERR(act)) { |
596 | err = PTR_ERR(act); | ||
597 | goto err; | 597 | goto err; |
598 | } | ||
598 | act->order = i; | 599 | act->order = i; |
599 | 600 | list_add_tail(&act->list, actions); | |
600 | if (head == NULL) | ||
601 | head = act; | ||
602 | else | ||
603 | act_prev->next = act; | ||
604 | act_prev = act; | ||
605 | } | 601 | } |
606 | return head; | 602 | return 0; |
607 | 603 | ||
608 | err: | 604 | err: |
609 | if (head != NULL) | 605 | tcf_action_destroy(actions, bind); |
610 | tcf_action_destroy(head, bind); | 606 | return err; |
611 | return act; | ||
612 | } | 607 | } |
613 | 608 | ||
614 | int tcf_action_copy_stats(struct sk_buff *skb, struct tc_action *a, | 609 | int tcf_action_copy_stats(struct sk_buff *skb, struct tc_action *a, |
@@ -653,7 +648,7 @@ errout: | |||
653 | } | 648 | } |
654 | 649 | ||
655 | static int | 650 | static int |
656 | tca_get_fill(struct sk_buff *skb, struct tc_action *a, u32 portid, u32 seq, | 651 | tca_get_fill(struct sk_buff *skb, struct list_head *actions, u32 portid, u32 seq, |
657 | u16 flags, int event, int bind, int ref) | 652 | u16 flags, int event, int bind, int ref) |
658 | { | 653 | { |
659 | struct tcamsg *t; | 654 | struct tcamsg *t; |
@@ -673,7 +668,7 @@ tca_get_fill(struct sk_buff *skb, struct tc_action *a, u32 portid, u32 seq, | |||
673 | if (nest == NULL) | 668 | if (nest == NULL) |
674 | goto out_nlmsg_trim; | 669 | goto out_nlmsg_trim; |
675 | 670 | ||
676 | if (tcf_action_dump(skb, a, bind, ref) < 0) | 671 | if (tcf_action_dump(skb, actions, bind, ref) < 0) |
677 | goto out_nlmsg_trim; | 672 | goto out_nlmsg_trim; |
678 | 673 | ||
679 | nla_nest_end(skb, nest); | 674 | nla_nest_end(skb, nest); |
@@ -688,14 +683,14 @@ out_nlmsg_trim: | |||
688 | 683 | ||
689 | static int | 684 | static int |
690 | act_get_notify(struct net *net, u32 portid, struct nlmsghdr *n, | 685 | act_get_notify(struct net *net, u32 portid, struct nlmsghdr *n, |
691 | struct tc_action *a, int event) | 686 | struct list_head *actions, int event) |
692 | { | 687 | { |
693 | struct sk_buff *skb; | 688 | struct sk_buff *skb; |
694 | 689 | ||
695 | skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); | 690 | skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); |
696 | if (!skb) | 691 | if (!skb) |
697 | return -ENOBUFS; | 692 | return -ENOBUFS; |
698 | if (tca_get_fill(skb, a, portid, n->nlmsg_seq, 0, event, 0, 0) <= 0) { | 693 | if (tca_get_fill(skb, actions, portid, n->nlmsg_seq, 0, event, 0, 0) <= 0) { |
699 | kfree_skb(skb); | 694 | kfree_skb(skb); |
700 | return -EINVAL; | 695 | return -EINVAL; |
701 | } | 696 | } |
@@ -726,6 +721,7 @@ tcf_action_get_1(struct nlattr *nla, struct nlmsghdr *n, u32 portid) | |||
726 | if (a == NULL) | 721 | if (a == NULL) |
727 | goto err_out; | 722 | goto err_out; |
728 | 723 | ||
724 | INIT_LIST_HEAD(&a->list); | ||
729 | err = -EINVAL; | 725 | err = -EINVAL; |
730 | a->ops = tc_lookup_action(tb[TCA_ACT_KIND]); | 726 | a->ops = tc_lookup_action(tb[TCA_ACT_KIND]); |
731 | if (a->ops == NULL) | 727 | if (a->ops == NULL) |
@@ -745,12 +741,12 @@ err_out: | |||
745 | return ERR_PTR(err); | 741 | return ERR_PTR(err); |
746 | } | 742 | } |
747 | 743 | ||
748 | static void cleanup_a(struct tc_action *act) | 744 | static void cleanup_a(struct list_head *actions) |
749 | { | 745 | { |
750 | struct tc_action *a; | 746 | struct tc_action *a, *tmp; |
751 | 747 | ||
752 | for (a = act; a; a = act) { | 748 | list_for_each_entry_safe(a, tmp, actions, list) { |
753 | act = a->next; | 749 | list_del(&a->list); |
754 | kfree(a); | 750 | kfree(a); |
755 | } | 751 | } |
756 | } | 752 | } |
@@ -765,6 +761,7 @@ static struct tc_action *create_a(int i) | |||
765 | return NULL; | 761 | return NULL; |
766 | } | 762 | } |
767 | act->order = i; | 763 | act->order = i; |
764 | INIT_LIST_HEAD(&act->list); | ||
768 | return act; | 765 | return act; |
769 | } | 766 | } |
770 | 767 | ||
@@ -852,7 +849,8 @@ tca_action_gd(struct net *net, struct nlattr *nla, struct nlmsghdr *n, | |||
852 | { | 849 | { |
853 | int i, ret; | 850 | int i, ret; |
854 | struct nlattr *tb[TCA_ACT_MAX_PRIO + 1]; | 851 | struct nlattr *tb[TCA_ACT_MAX_PRIO + 1]; |
855 | struct tc_action *head = NULL, *act, *act_prev = NULL; | 852 | struct tc_action *act; |
853 | LIST_HEAD(actions); | ||
856 | 854 | ||
857 | ret = nla_parse_nested(tb, TCA_ACT_MAX_PRIO, nla, NULL); | 855 | ret = nla_parse_nested(tb, TCA_ACT_MAX_PRIO, nla, NULL); |
858 | if (ret < 0) | 856 | if (ret < 0) |
@@ -872,16 +870,11 @@ tca_action_gd(struct net *net, struct nlattr *nla, struct nlmsghdr *n, | |||
872 | goto err; | 870 | goto err; |
873 | } | 871 | } |
874 | act->order = i; | 872 | act->order = i; |
875 | 873 | list_add_tail(&act->list, &actions); | |
876 | if (head == NULL) | ||
877 | head = act; | ||
878 | else | ||
879 | act_prev->next = act; | ||
880 | act_prev = act; | ||
881 | } | 874 | } |
882 | 875 | ||
883 | if (event == RTM_GETACTION) | 876 | if (event == RTM_GETACTION) |
884 | ret = act_get_notify(net, portid, n, head, event); | 877 | ret = act_get_notify(net, portid, n, &actions, event); |
885 | else { /* delete */ | 878 | else { /* delete */ |
886 | struct sk_buff *skb; | 879 | struct sk_buff *skb; |
887 | 880 | ||
@@ -891,7 +884,7 @@ tca_action_gd(struct net *net, struct nlattr *nla, struct nlmsghdr *n, | |||
891 | goto err; | 884 | goto err; |
892 | } | 885 | } |
893 | 886 | ||
894 | if (tca_get_fill(skb, head, portid, n->nlmsg_seq, 0, event, | 887 | if (tca_get_fill(skb, &actions, portid, n->nlmsg_seq, 0, event, |
895 | 0, 1) <= 0) { | 888 | 0, 1) <= 0) { |
896 | kfree_skb(skb); | 889 | kfree_skb(skb); |
897 | ret = -EINVAL; | 890 | ret = -EINVAL; |
@@ -899,7 +892,7 @@ tca_action_gd(struct net *net, struct nlattr *nla, struct nlmsghdr *n, | |||
899 | } | 892 | } |
900 | 893 | ||
901 | /* now do the delete */ | 894 | /* now do the delete */ |
902 | tcf_action_destroy(head, 0); | 895 | tcf_action_destroy(&actions, 0); |
903 | ret = rtnetlink_send(skb, net, portid, RTNLGRP_TC, | 896 | ret = rtnetlink_send(skb, net, portid, RTNLGRP_TC, |
904 | n->nlmsg_flags & NLM_F_ECHO); | 897 | n->nlmsg_flags & NLM_F_ECHO); |
905 | if (ret > 0) | 898 | if (ret > 0) |
@@ -907,11 +900,11 @@ tca_action_gd(struct net *net, struct nlattr *nla, struct nlmsghdr *n, | |||
907 | return ret; | 900 | return ret; |
908 | } | 901 | } |
909 | err: | 902 | err: |
910 | cleanup_a(head); | 903 | cleanup_a(&actions); |
911 | return ret; | 904 | return ret; |
912 | } | 905 | } |
913 | 906 | ||
914 | static int tcf_add_notify(struct net *net, struct tc_action *a, | 907 | static int tcf_add_notify(struct net *net, struct list_head *actions, |
915 | u32 portid, u32 seq, int event, u16 flags) | 908 | u32 portid, u32 seq, int event, u16 flags) |
916 | { | 909 | { |
917 | struct tcamsg *t; | 910 | struct tcamsg *t; |
@@ -939,7 +932,7 @@ static int tcf_add_notify(struct net *net, struct tc_action *a, | |||
939 | if (nest == NULL) | 932 | if (nest == NULL) |
940 | goto out_kfree_skb; | 933 | goto out_kfree_skb; |
941 | 934 | ||
942 | if (tcf_action_dump(skb, a, 0, 0) < 0) | 935 | if (tcf_action_dump(skb, actions, 0, 0) < 0) |
943 | goto out_kfree_skb; | 936 | goto out_kfree_skb; |
944 | 937 | ||
945 | nla_nest_end(skb, nest); | 938 | nla_nest_end(skb, nest); |
@@ -963,26 +956,18 @@ tcf_action_add(struct net *net, struct nlattr *nla, struct nlmsghdr *n, | |||
963 | u32 portid, int ovr) | 956 | u32 portid, int ovr) |
964 | { | 957 | { |
965 | int ret = 0; | 958 | int ret = 0; |
966 | struct tc_action *act; | 959 | LIST_HEAD(actions); |
967 | struct tc_action *a; | ||
968 | u32 seq = n->nlmsg_seq; | 960 | u32 seq = n->nlmsg_seq; |
969 | 961 | ||
970 | act = tcf_action_init(net, nla, NULL, NULL, ovr, 0); | 962 | ret = tcf_action_init(net, nla, NULL, NULL, ovr, 0, &actions); |
971 | if (act == NULL) | 963 | if (ret) |
972 | goto done; | ||
973 | if (IS_ERR(act)) { | ||
974 | ret = PTR_ERR(act); | ||
975 | goto done; | 964 | goto done; |
976 | } | ||
977 | 965 | ||
978 | /* dump then free all the actions after update; inserted policy | 966 | /* dump then free all the actions after update; inserted policy |
979 | * stays intact | 967 | * stays intact |
980 | */ | 968 | */ |
981 | ret = tcf_add_notify(net, act, portid, seq, RTM_NEWACTION, n->nlmsg_flags); | 969 | ret = tcf_add_notify(net, &actions, portid, seq, RTM_NEWACTION, n->nlmsg_flags); |
982 | for (a = act; a; a = act) { | 970 | cleanup_a(&actions); |
983 | act = a->next; | ||
984 | kfree(a); | ||
985 | } | ||
986 | done: | 971 | done: |
987 | return ret; | 972 | return ret; |
988 | } | 973 | } |
diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c index 8e118af90973..3c056d73d394 100644 --- a/net/sched/cls_api.c +++ b/net/sched/cls_api.c | |||
@@ -500,10 +500,8 @@ out: | |||
500 | void tcf_exts_destroy(struct tcf_proto *tp, struct tcf_exts *exts) | 500 | void tcf_exts_destroy(struct tcf_proto *tp, struct tcf_exts *exts) |
501 | { | 501 | { |
502 | #ifdef CONFIG_NET_CLS_ACT | 502 | #ifdef CONFIG_NET_CLS_ACT |
503 | if (exts->action) { | 503 | tcf_action_destroy(&exts->actions, TCA_ACT_UNBIND); |
504 | tcf_action_destroy(exts->action, TCA_ACT_UNBIND); | 504 | INIT_LIST_HEAD(&exts->actions); |
505 | exts->action = NULL; | ||
506 | } | ||
507 | #endif | 505 | #endif |
508 | } | 506 | } |
509 | EXPORT_SYMBOL(tcf_exts_destroy); | 507 | EXPORT_SYMBOL(tcf_exts_destroy); |
@@ -518,6 +516,7 @@ int tcf_exts_validate(struct net *net, struct tcf_proto *tp, struct nlattr **tb, | |||
518 | { | 516 | { |
519 | struct tc_action *act; | 517 | struct tc_action *act; |
520 | 518 | ||
519 | INIT_LIST_HEAD(&exts->actions); | ||
521 | if (map->police && tb[map->police]) { | 520 | if (map->police && tb[map->police]) { |
522 | act = tcf_action_init_1(net, tb[map->police], rate_tlv, | 521 | act = tcf_action_init_1(net, tb[map->police], rate_tlv, |
523 | "police", TCA_ACT_NOREPLACE, | 522 | "police", TCA_ACT_NOREPLACE, |
@@ -525,16 +524,15 @@ int tcf_exts_validate(struct net *net, struct tcf_proto *tp, struct nlattr **tb, | |||
525 | if (IS_ERR(act)) | 524 | if (IS_ERR(act)) |
526 | return PTR_ERR(act); | 525 | return PTR_ERR(act); |
527 | 526 | ||
528 | act->type = TCA_OLD_COMPAT; | 527 | act->type = exts->type = TCA_OLD_COMPAT; |
529 | exts->action = act; | 528 | list_add(&act->list, &exts->actions); |
530 | } else if (map->action && tb[map->action]) { | 529 | } else if (map->action && tb[map->action]) { |
531 | act = tcf_action_init(net, tb[map->action], rate_tlv, | 530 | int err; |
531 | err = tcf_action_init(net, tb[map->action], rate_tlv, | ||
532 | NULL, TCA_ACT_NOREPLACE, | 532 | NULL, TCA_ACT_NOREPLACE, |
533 | TCA_ACT_BIND); | 533 | TCA_ACT_BIND, &exts->actions); |
534 | if (IS_ERR(act)) | 534 | if (err) |
535 | return PTR_ERR(act); | 535 | return err; |
536 | |||
537 | exts->action = act; | ||
538 | } | 536 | } |
539 | } | 537 | } |
540 | #else | 538 | #else |
@@ -551,43 +549,45 @@ void tcf_exts_change(struct tcf_proto *tp, struct tcf_exts *dst, | |||
551 | struct tcf_exts *src) | 549 | struct tcf_exts *src) |
552 | { | 550 | { |
553 | #ifdef CONFIG_NET_CLS_ACT | 551 | #ifdef CONFIG_NET_CLS_ACT |
554 | if (src->action) { | 552 | if (!list_empty(&src->actions)) { |
555 | struct tc_action *act; | 553 | LIST_HEAD(tmp); |
556 | tcf_tree_lock(tp); | 554 | tcf_tree_lock(tp); |
557 | act = dst->action; | 555 | list_splice_init(&dst->actions, &tmp); |
558 | dst->action = src->action; | 556 | list_splice(&src->actions, &dst->actions); |
559 | tcf_tree_unlock(tp); | 557 | tcf_tree_unlock(tp); |
560 | if (act) | 558 | tcf_action_destroy(&tmp, TCA_ACT_UNBIND); |
561 | tcf_action_destroy(act, TCA_ACT_UNBIND); | ||
562 | } | 559 | } |
563 | #endif | 560 | #endif |
564 | } | 561 | } |
565 | EXPORT_SYMBOL(tcf_exts_change); | 562 | EXPORT_SYMBOL(tcf_exts_change); |
566 | 563 | ||
564 | #define tcf_exts_first_act(ext) \ | ||
565 | list_first_entry(&(exts)->actions, struct tc_action, list) | ||
566 | |||
567 | int tcf_exts_dump(struct sk_buff *skb, struct tcf_exts *exts, | 567 | int tcf_exts_dump(struct sk_buff *skb, struct tcf_exts *exts, |
568 | const struct tcf_ext_map *map) | 568 | const struct tcf_ext_map *map) |
569 | { | 569 | { |
570 | #ifdef CONFIG_NET_CLS_ACT | 570 | #ifdef CONFIG_NET_CLS_ACT |
571 | if (map->action && exts->action) { | 571 | if (map->action && !list_empty(&exts->actions)) { |
572 | /* | 572 | /* |
573 | * again for backward compatible mode - we want | 573 | * again for backward compatible mode - we want |
574 | * to work with both old and new modes of entering | 574 | * to work with both old and new modes of entering |
575 | * tc data even if iproute2 was newer - jhs | 575 | * tc data even if iproute2 was newer - jhs |
576 | */ | 576 | */ |
577 | struct nlattr *nest; | 577 | struct nlattr *nest; |
578 | 578 | if (exts->type != TCA_OLD_COMPAT) { | |
579 | if (exts->action->type != TCA_OLD_COMPAT) { | ||
580 | nest = nla_nest_start(skb, map->action); | 579 | nest = nla_nest_start(skb, map->action); |
581 | if (nest == NULL) | 580 | if (nest == NULL) |
582 | goto nla_put_failure; | 581 | goto nla_put_failure; |
583 | if (tcf_action_dump(skb, exts->action, 0, 0) < 0) | 582 | if (tcf_action_dump(skb, &exts->actions, 0, 0) < 0) |
584 | goto nla_put_failure; | 583 | goto nla_put_failure; |
585 | nla_nest_end(skb, nest); | 584 | nla_nest_end(skb, nest); |
586 | } else if (map->police) { | 585 | } else if (map->police) { |
586 | struct tc_action *act = tcf_exts_first_act(exts); | ||
587 | nest = nla_nest_start(skb, map->police); | 587 | nest = nla_nest_start(skb, map->police); |
588 | if (nest == NULL) | 588 | if (nest == NULL) |
589 | goto nla_put_failure; | 589 | goto nla_put_failure; |
590 | if (tcf_action_dump_old(skb, exts->action, 0, 0) < 0) | 590 | if (tcf_action_dump_old(skb, act, 0, 0) < 0) |
591 | goto nla_put_failure; | 591 | goto nla_put_failure; |
592 | nla_nest_end(skb, nest); | 592 | nla_nest_end(skb, nest); |
593 | } | 593 | } |
@@ -604,13 +604,11 @@ int tcf_exts_dump_stats(struct sk_buff *skb, struct tcf_exts *exts, | |||
604 | const struct tcf_ext_map *map) | 604 | const struct tcf_ext_map *map) |
605 | { | 605 | { |
606 | #ifdef CONFIG_NET_CLS_ACT | 606 | #ifdef CONFIG_NET_CLS_ACT |
607 | if (exts->action) | 607 | struct tc_action *a = tcf_exts_first_act(exts); |
608 | if (tcf_action_copy_stats(skb, exts->action, 1) < 0) | 608 | if (tcf_action_copy_stats(skb, a, 1) < 0) |
609 | goto nla_put_failure; | 609 | return -1; |
610 | #endif | 610 | #endif |
611 | return 0; | 611 | return 0; |
612 | nla_put_failure: __attribute__ ((unused)) | ||
613 | return -1; | ||
614 | } | 612 | } |
615 | EXPORT_SYMBOL(tcf_exts_dump_stats); | 613 | EXPORT_SYMBOL(tcf_exts_dump_stats); |
616 | 614 | ||
diff --git a/net/sched/cls_basic.c b/net/sched/cls_basic.c index 636d9131d870..7b9b4602b5b7 100644 --- a/net/sched/cls_basic.c +++ b/net/sched/cls_basic.c | |||
@@ -191,6 +191,7 @@ static int basic_change(struct net *net, struct sk_buff *in_skb, | |||
191 | if (f == NULL) | 191 | if (f == NULL) |
192 | goto errout; | 192 | goto errout; |
193 | 193 | ||
194 | tcf_exts_init(&f->exts); | ||
194 | err = -EINVAL; | 195 | err = -EINVAL; |
195 | if (handle) | 196 | if (handle) |
196 | f->handle = handle; | 197 | f->handle = handle; |
diff --git a/net/sched/cls_bpf.c b/net/sched/cls_bpf.c index d7c72be121f3..90fe275e580c 100644 --- a/net/sched/cls_bpf.c +++ b/net/sched/cls_bpf.c | |||
@@ -271,6 +271,7 @@ static int cls_bpf_change(struct net *net, struct sk_buff *in_skb, | |||
271 | if (prog == NULL) | 271 | if (prog == NULL) |
272 | return -ENOBUFS; | 272 | return -ENOBUFS; |
273 | 273 | ||
274 | tcf_exts_init(&prog->exts); | ||
274 | if (handle == 0) | 275 | if (handle == 0) |
275 | prog->handle = cls_bpf_grab_new_handle(tp, head); | 276 | prog->handle = cls_bpf_grab_new_handle(tp, head); |
276 | else | 277 | else |
diff --git a/net/sched/cls_cgroup.c b/net/sched/cls_cgroup.c index 16006c92c3fd..e4fae032bcfb 100644 --- a/net/sched/cls_cgroup.c +++ b/net/sched/cls_cgroup.c | |||
@@ -203,6 +203,7 @@ static int cls_cgroup_change(struct net *net, struct sk_buff *in_skb, | |||
203 | if (head == NULL) | 203 | if (head == NULL) |
204 | return -ENOBUFS; | 204 | return -ENOBUFS; |
205 | 205 | ||
206 | tcf_exts_init(&head->exts); | ||
206 | head->handle = handle; | 207 | head->handle = handle; |
207 | 208 | ||
208 | tcf_tree_lock(tp); | 209 | tcf_tree_lock(tp); |
diff --git a/net/sched/cls_flow.c b/net/sched/cls_flow.c index 83a6322b7751..f05904e3848f 100644 --- a/net/sched/cls_flow.c +++ b/net/sched/cls_flow.c | |||
@@ -455,6 +455,7 @@ static int flow_change(struct net *net, struct sk_buff *in_skb, | |||
455 | 455 | ||
456 | f->handle = handle; | 456 | f->handle = handle; |
457 | f->mask = ~0U; | 457 | f->mask = ~0U; |
458 | tcf_exts_init(&f->exts); | ||
458 | 459 | ||
459 | get_random_bytes(&f->hashrnd, 4); | 460 | get_random_bytes(&f->hashrnd, 4); |
460 | f->perturb_timer.function = flow_perturbation; | 461 | f->perturb_timer.function = flow_perturbation; |
diff --git a/net/sched/cls_fw.c b/net/sched/cls_fw.c index 9b97172db84a..d1cebad19410 100644 --- a/net/sched/cls_fw.c +++ b/net/sched/cls_fw.c | |||
@@ -280,6 +280,7 @@ static int fw_change(struct net *net, struct sk_buff *in_skb, | |||
280 | if (f == NULL) | 280 | if (f == NULL) |
281 | return -ENOBUFS; | 281 | return -ENOBUFS; |
282 | 282 | ||
283 | tcf_exts_init(&f->exts); | ||
283 | f->id = handle; | 284 | f->id = handle; |
284 | 285 | ||
285 | err = fw_change_attrs(net, tp, f, tb, tca, base); | 286 | err = fw_change_attrs(net, tp, f, tb, tca, base); |
diff --git a/net/sched/cls_route.c b/net/sched/cls_route.c index 37da567d833e..f1f1dfdf4eb3 100644 --- a/net/sched/cls_route.c +++ b/net/sched/cls_route.c | |||
@@ -481,6 +481,7 @@ static int route4_change(struct net *net, struct sk_buff *in_skb, | |||
481 | if (f == NULL) | 481 | if (f == NULL) |
482 | goto errout; | 482 | goto errout; |
483 | 483 | ||
484 | tcf_exts_init(&f->exts); | ||
484 | err = route4_set_parms(net, tp, base, f, handle, head, tb, | 485 | err = route4_set_parms(net, tp, base, f, handle, head, tb, |
485 | tca[TCA_RATE], 1); | 486 | tca[TCA_RATE], 1); |
486 | if (err < 0) | 487 | if (err < 0) |
diff --git a/net/sched/cls_rsvp.h b/net/sched/cls_rsvp.h index 252d8b05872e..b1d3ce5c5ff8 100644 --- a/net/sched/cls_rsvp.h +++ b/net/sched/cls_rsvp.h | |||
@@ -471,6 +471,7 @@ static int rsvp_change(struct net *net, struct sk_buff *in_skb, | |||
471 | if (f == NULL) | 471 | if (f == NULL) |
472 | goto errout2; | 472 | goto errout2; |
473 | 473 | ||
474 | tcf_exts_init(&f->exts); | ||
474 | h2 = 16; | 475 | h2 = 16; |
475 | if (tb[TCA_RSVP_SRC]) { | 476 | if (tb[TCA_RSVP_SRC]) { |
476 | memcpy(f->src, nla_data(tb[TCA_RSVP_SRC]), sizeof(f->src)); | 477 | memcpy(f->src, nla_data(tb[TCA_RSVP_SRC]), sizeof(f->src)); |
diff --git a/net/sched/cls_tcindex.c b/net/sched/cls_tcindex.c index b86535a40169..c39bbfc0300a 100644 --- a/net/sched/cls_tcindex.c +++ b/net/sched/cls_tcindex.c | |||
@@ -215,11 +215,14 @@ tcindex_set_parms(struct net *net, struct tcf_proto *tp, unsigned long base, | |||
215 | 215 | ||
216 | memcpy(&cp, p, sizeof(cp)); | 216 | memcpy(&cp, p, sizeof(cp)); |
217 | memset(&new_filter_result, 0, sizeof(new_filter_result)); | 217 | memset(&new_filter_result, 0, sizeof(new_filter_result)); |
218 | tcf_exts_init(&new_filter_result.exts); | ||
218 | 219 | ||
219 | if (old_r) | 220 | if (old_r) |
220 | memcpy(&cr, r, sizeof(cr)); | 221 | memcpy(&cr, r, sizeof(cr)); |
221 | else | 222 | else { |
222 | memset(&cr, 0, sizeof(cr)); | 223 | memset(&cr, 0, sizeof(cr)); |
224 | tcf_exts_init(&cr.exts); | ||
225 | } | ||
223 | 226 | ||
224 | if (tb[TCA_TCINDEX_HASH]) | 227 | if (tb[TCA_TCINDEX_HASH]) |
225 | cp.hash = nla_get_u32(tb[TCA_TCINDEX_HASH]); | 228 | cp.hash = nla_get_u32(tb[TCA_TCINDEX_HASH]); |
diff --git a/net/sched/cls_u32.c b/net/sched/cls_u32.c index 59e546c2ac98..492d9a6401ce 100644 --- a/net/sched/cls_u32.c +++ b/net/sched/cls_u32.c | |||
@@ -646,6 +646,7 @@ static int u32_change(struct net *net, struct sk_buff *in_skb, | |||
646 | n->ht_up = ht; | 646 | n->ht_up = ht; |
647 | n->handle = handle; | 647 | n->handle = handle; |
648 | n->fshift = s->hmask ? ffs(ntohl(s->hmask)) - 1 : 0; | 648 | n->fshift = s->hmask ? ffs(ntohl(s->hmask)) - 1 : 0; |
649 | tcf_exts_init(&n->exts); | ||
649 | 650 | ||
650 | #ifdef CONFIG_CLS_U32_MARK | 651 | #ifdef CONFIG_CLS_U32_MARK |
651 | if (tb[TCA_U32_MARK]) { | 652 | if (tb[TCA_U32_MARK]) { |