aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/net/act_api.h12
-rw-r--r--include/net/pkt_cls.h16
-rw-r--r--net/sched/act_api.c105
-rw-r--r--net/sched/cls_api.c54
-rw-r--r--net/sched/cls_basic.c1
-rw-r--r--net/sched/cls_bpf.c1
-rw-r--r--net/sched/cls_cgroup.c1
-rw-r--r--net/sched/cls_flow.c1
-rw-r--r--net/sched/cls_fw.c1
-rw-r--r--net/sched/cls_route.c1
-rw-r--r--net/sched/cls_rsvp.h1
-rw-r--r--net/sched/cls_tcindex.c5
-rw-r--r--net/sched/cls_u32.c1
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
100int tcf_register_action(struct tc_action_ops *a); 100int tcf_register_action(struct tc_action_ops *a);
101int tcf_unregister_action(struct tc_action_ops *a); 101int tcf_unregister_action(struct tc_action_ops *a);
102void tcf_action_destroy(struct tc_action *a, int bind); 102void tcf_action_destroy(struct list_head *actions, int bind);
103int tcf_action_exec(struct sk_buff *skb, const struct tc_action *a, 103int tcf_action_exec(struct sk_buff *skb, const struct list_head *actions,
104 struct tcf_result *res); 104 struct tcf_result *res);
105struct tc_action *tcf_action_init(struct net *net, struct nlattr *nla, 105int 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 *);
108struct tc_action *tcf_action_init_1(struct net *net, struct nlattr *nla, 108struct 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);
111int tcf_action_dump(struct sk_buff *skb, struct tc_action *a, int, int); 111int tcf_action_dump(struct sk_buff *skb, struct list_head *, int, int);
112int tcf_action_dump_old(struct sk_buff *skb, struct tc_action *a, int, int); 112int tcf_action_dump_old(struct sk_buff *skb, struct tc_action *a, int, int);
113int tcf_action_dump_1(struct sk_buff *skb, struct tc_action *a, int, int); 113int tcf_action_dump_1(struct sk_buff *skb, struct tc_action *a, int, int);
114int tcf_action_copy_stats(struct sk_buff *, struct tc_action *, int); 114int 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
63struct tcf_exts { 63struct 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
78static 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
85tcf_exts_is_predicative(struct tcf_exts *exts) 93tcf_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
382int tcf_action_exec(struct sk_buff *skb, const struct tc_action *act, 382int 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) {
394repeat: 394repeat:
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 }
409exec_done: 408exec_done:
410 return ret; 409 return ret;
411} 410}
412EXPORT_SYMBOL(tcf_action_exec); 411EXPORT_SYMBOL(tcf_action_exec);
413 412
414void tcf_action_destroy(struct tc_action *act, int bind) 413void 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:
470EXPORT_SYMBOL(tcf_action_dump_1); 469EXPORT_SYMBOL(tcf_action_dump_1);
471 470
472int 471int
473tcf_action_dump(struct sk_buff *skb, struct tc_action *act, int bind, int ref) 472tcf_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
581struct tc_action *tcf_action_init(struct net *net, struct nlattr *nla, 580int 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
608err: 604err:
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
614int tcf_action_copy_stats(struct sk_buff *skb, struct tc_action *a, 609int tcf_action_copy_stats(struct sk_buff *skb, struct tc_action *a,
@@ -653,7 +648,7 @@ errout:
653} 648}
654 649
655static int 650static int
656tca_get_fill(struct sk_buff *skb, struct tc_action *a, u32 portid, u32 seq, 651tca_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
689static int 684static int
690act_get_notify(struct net *net, u32 portid, struct nlmsghdr *n, 685act_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
748static void cleanup_a(struct tc_action *act) 744static 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 }
909err: 902err:
910 cleanup_a(head); 903 cleanup_a(&actions);
911 return ret; 904 return ret;
912} 905}
913 906
914static int tcf_add_notify(struct net *net, struct tc_action *a, 907static 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 }
986done: 971done:
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:
500void tcf_exts_destroy(struct tcf_proto *tp, struct tcf_exts *exts) 500void 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}
509EXPORT_SYMBOL(tcf_exts_destroy); 507EXPORT_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}
565EXPORT_SYMBOL(tcf_exts_change); 562EXPORT_SYMBOL(tcf_exts_change);
566 563
564#define tcf_exts_first_act(ext) \
565 list_first_entry(&(exts)->actions, struct tc_action, list)
566
567int tcf_exts_dump(struct sk_buff *skb, struct tcf_exts *exts, 567int 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;
612nla_put_failure: __attribute__ ((unused))
613 return -1;
614} 612}
615EXPORT_SYMBOL(tcf_exts_dump_stats); 613EXPORT_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]) {