diff options
Diffstat (limited to 'net/sched/cls_cgroup.c')
-rw-r--r-- | net/sched/cls_cgroup.c | 79 |
1 files changed, 48 insertions, 31 deletions
diff --git a/net/sched/cls_cgroup.c b/net/sched/cls_cgroup.c index cacf01bd04f0..d61a801222c1 100644 --- a/net/sched/cls_cgroup.c +++ b/net/sched/cls_cgroup.c | |||
@@ -22,17 +22,17 @@ struct cls_cgroup_head { | |||
22 | u32 handle; | 22 | u32 handle; |
23 | struct tcf_exts exts; | 23 | struct tcf_exts exts; |
24 | struct tcf_ematch_tree ematches; | 24 | struct tcf_ematch_tree ematches; |
25 | struct tcf_proto *tp; | ||
26 | struct rcu_head rcu; | ||
25 | }; | 27 | }; |
26 | 28 | ||
27 | static int cls_cgroup_classify(struct sk_buff *skb, const struct tcf_proto *tp, | 29 | static int cls_cgroup_classify(struct sk_buff *skb, const struct tcf_proto *tp, |
28 | struct tcf_result *res) | 30 | struct tcf_result *res) |
29 | { | 31 | { |
30 | struct cls_cgroup_head *head = tp->root; | 32 | struct cls_cgroup_head *head = rcu_dereference_bh(tp->root); |
31 | u32 classid; | 33 | u32 classid; |
32 | 34 | ||
33 | rcu_read_lock(); | ||
34 | classid = task_cls_state(current)->classid; | 35 | classid = task_cls_state(current)->classid; |
35 | rcu_read_unlock(); | ||
36 | 36 | ||
37 | /* | 37 | /* |
38 | * Due to the nature of the classifier it is required to ignore all | 38 | * Due to the nature of the classifier it is required to ignore all |
@@ -80,13 +80,25 @@ static const struct nla_policy cgroup_policy[TCA_CGROUP_MAX + 1] = { | |||
80 | [TCA_CGROUP_EMATCHES] = { .type = NLA_NESTED }, | 80 | [TCA_CGROUP_EMATCHES] = { .type = NLA_NESTED }, |
81 | }; | 81 | }; |
82 | 82 | ||
83 | static void cls_cgroup_destroy_rcu(struct rcu_head *root) | ||
84 | { | ||
85 | struct cls_cgroup_head *head = container_of(root, | ||
86 | struct cls_cgroup_head, | ||
87 | rcu); | ||
88 | |||
89 | tcf_exts_destroy(&head->exts); | ||
90 | tcf_em_tree_destroy(&head->ematches); | ||
91 | kfree(head); | ||
92 | } | ||
93 | |||
83 | static int cls_cgroup_change(struct net *net, struct sk_buff *in_skb, | 94 | static int cls_cgroup_change(struct net *net, struct sk_buff *in_skb, |
84 | struct tcf_proto *tp, unsigned long base, | 95 | struct tcf_proto *tp, unsigned long base, |
85 | u32 handle, struct nlattr **tca, | 96 | u32 handle, struct nlattr **tca, |
86 | unsigned long *arg, bool ovr) | 97 | unsigned long *arg, bool ovr) |
87 | { | 98 | { |
88 | struct nlattr *tb[TCA_CGROUP_MAX + 1]; | 99 | struct nlattr *tb[TCA_CGROUP_MAX + 1]; |
89 | struct cls_cgroup_head *head = tp->root; | 100 | struct cls_cgroup_head *head = rtnl_dereference(tp->root); |
101 | struct cls_cgroup_head *new; | ||
90 | struct tcf_ematch_tree t; | 102 | struct tcf_ematch_tree t; |
91 | struct tcf_exts e; | 103 | struct tcf_exts e; |
92 | int err; | 104 | int err; |
@@ -94,53 +106,58 @@ static int cls_cgroup_change(struct net *net, struct sk_buff *in_skb, | |||
94 | if (!tca[TCA_OPTIONS]) | 106 | if (!tca[TCA_OPTIONS]) |
95 | return -EINVAL; | 107 | return -EINVAL; |
96 | 108 | ||
97 | if (head == NULL) { | 109 | if (!head && !handle) |
98 | if (!handle) | 110 | return -EINVAL; |
99 | return -EINVAL; | ||
100 | |||
101 | head = kzalloc(sizeof(*head), GFP_KERNEL); | ||
102 | if (head == NULL) | ||
103 | return -ENOBUFS; | ||
104 | 111 | ||
105 | tcf_exts_init(&head->exts, TCA_CGROUP_ACT, TCA_CGROUP_POLICE); | 112 | if (head && handle != head->handle) |
106 | head->handle = handle; | 113 | return -ENOENT; |
107 | 114 | ||
108 | tcf_tree_lock(tp); | 115 | new = kzalloc(sizeof(*head), GFP_KERNEL); |
109 | tp->root = head; | 116 | if (!new) |
110 | tcf_tree_unlock(tp); | 117 | return -ENOBUFS; |
111 | } | ||
112 | 118 | ||
113 | if (handle != head->handle) | 119 | tcf_exts_init(&new->exts, TCA_CGROUP_ACT, TCA_CGROUP_POLICE); |
114 | return -ENOENT; | 120 | if (head) |
121 | new->handle = head->handle; | ||
122 | else | ||
123 | new->handle = handle; | ||
115 | 124 | ||
125 | new->tp = tp; | ||
116 | err = nla_parse_nested(tb, TCA_CGROUP_MAX, tca[TCA_OPTIONS], | 126 | err = nla_parse_nested(tb, TCA_CGROUP_MAX, tca[TCA_OPTIONS], |
117 | cgroup_policy); | 127 | cgroup_policy); |
118 | if (err < 0) | 128 | if (err < 0) |
119 | return err; | 129 | goto errout; |
120 | 130 | ||
121 | tcf_exts_init(&e, TCA_CGROUP_ACT, TCA_CGROUP_POLICE); | 131 | tcf_exts_init(&e, TCA_CGROUP_ACT, TCA_CGROUP_POLICE); |
122 | err = tcf_exts_validate(net, tp, tb, tca[TCA_RATE], &e, ovr); | 132 | err = tcf_exts_validate(net, tp, tb, tca[TCA_RATE], &e, ovr); |
123 | if (err < 0) | 133 | if (err < 0) |
124 | return err; | 134 | goto errout; |
125 | 135 | ||
126 | err = tcf_em_tree_validate(tp, tb[TCA_CGROUP_EMATCHES], &t); | 136 | err = tcf_em_tree_validate(tp, tb[TCA_CGROUP_EMATCHES], &t); |
127 | if (err < 0) | 137 | if (err < 0) { |
128 | return err; | 138 | tcf_exts_destroy(&e); |
139 | goto errout; | ||
140 | } | ||
129 | 141 | ||
130 | tcf_exts_change(tp, &head->exts, &e); | 142 | tcf_exts_change(tp, &new->exts, &e); |
131 | tcf_em_tree_change(tp, &head->ematches, &t); | 143 | tcf_em_tree_change(tp, &new->ematches, &t); |
132 | 144 | ||
145 | rcu_assign_pointer(tp->root, new); | ||
146 | if (head) | ||
147 | call_rcu(&head->rcu, cls_cgroup_destroy_rcu); | ||
133 | return 0; | 148 | return 0; |
149 | errout: | ||
150 | kfree(new); | ||
151 | return err; | ||
134 | } | 152 | } |
135 | 153 | ||
136 | static void cls_cgroup_destroy(struct tcf_proto *tp) | 154 | static void cls_cgroup_destroy(struct tcf_proto *tp) |
137 | { | 155 | { |
138 | struct cls_cgroup_head *head = tp->root; | 156 | struct cls_cgroup_head *head = rtnl_dereference(tp->root); |
139 | 157 | ||
140 | if (head) { | 158 | if (head) { |
141 | tcf_exts_destroy(tp, &head->exts); | 159 | RCU_INIT_POINTER(tp->root, NULL); |
142 | tcf_em_tree_destroy(tp, &head->ematches); | 160 | call_rcu(&head->rcu, cls_cgroup_destroy_rcu); |
143 | kfree(head); | ||
144 | } | 161 | } |
145 | } | 162 | } |
146 | 163 | ||
@@ -151,7 +168,7 @@ static int cls_cgroup_delete(struct tcf_proto *tp, unsigned long arg) | |||
151 | 168 | ||
152 | static void cls_cgroup_walk(struct tcf_proto *tp, struct tcf_walker *arg) | 169 | static void cls_cgroup_walk(struct tcf_proto *tp, struct tcf_walker *arg) |
153 | { | 170 | { |
154 | struct cls_cgroup_head *head = tp->root; | 171 | struct cls_cgroup_head *head = rtnl_dereference(tp->root); |
155 | 172 | ||
156 | if (arg->count < arg->skip) | 173 | if (arg->count < arg->skip) |
157 | goto skip; | 174 | goto skip; |
@@ -167,7 +184,7 @@ skip: | |||
167 | static int cls_cgroup_dump(struct net *net, struct tcf_proto *tp, unsigned long fh, | 184 | static int cls_cgroup_dump(struct net *net, struct tcf_proto *tp, unsigned long fh, |
168 | struct sk_buff *skb, struct tcmsg *t) | 185 | struct sk_buff *skb, struct tcmsg *t) |
169 | { | 186 | { |
170 | struct cls_cgroup_head *head = tp->root; | 187 | struct cls_cgroup_head *head = rtnl_dereference(tp->root); |
171 | unsigned char *b = skb_tail_pointer(skb); | 188 | unsigned char *b = skb_tail_pointer(skb); |
172 | struct nlattr *nest; | 189 | struct nlattr *nest; |
173 | 190 | ||