diff options
Diffstat (limited to 'net/ipv6/ip6_flowlabel.c')
-rw-r--r-- | net/ipv6/ip6_flowlabel.c | 57 |
1 files changed, 40 insertions, 17 deletions
diff --git a/net/ipv6/ip6_flowlabel.c b/net/ipv6/ip6_flowlabel.c index 217d60f9fc80..b12cc22e7745 100644 --- a/net/ipv6/ip6_flowlabel.c +++ b/net/ipv6/ip6_flowlabel.c | |||
@@ -154,8 +154,10 @@ static void ip6_fl_gc(unsigned long dummy) | |||
154 | write_unlock(&ip6_fl_lock); | 154 | write_unlock(&ip6_fl_lock); |
155 | } | 155 | } |
156 | 156 | ||
157 | static int fl_intern(struct ip6_flowlabel *fl, __be32 label) | 157 | static struct ip6_flowlabel *fl_intern(struct ip6_flowlabel *fl, __be32 label) |
158 | { | 158 | { |
159 | struct ip6_flowlabel *lfl; | ||
160 | |||
159 | fl->label = label & IPV6_FLOWLABEL_MASK; | 161 | fl->label = label & IPV6_FLOWLABEL_MASK; |
160 | 162 | ||
161 | write_lock_bh(&ip6_fl_lock); | 163 | write_lock_bh(&ip6_fl_lock); |
@@ -163,12 +165,26 @@ static int fl_intern(struct ip6_flowlabel *fl, __be32 label) | |||
163 | for (;;) { | 165 | for (;;) { |
164 | fl->label = htonl(net_random())&IPV6_FLOWLABEL_MASK; | 166 | fl->label = htonl(net_random())&IPV6_FLOWLABEL_MASK; |
165 | if (fl->label) { | 167 | if (fl->label) { |
166 | struct ip6_flowlabel *lfl; | ||
167 | lfl = __fl_lookup(fl->label); | 168 | lfl = __fl_lookup(fl->label); |
168 | if (lfl == NULL) | 169 | if (lfl == NULL) |
169 | break; | 170 | break; |
170 | } | 171 | } |
171 | } | 172 | } |
173 | } else { | ||
174 | /* | ||
175 | * we dropper the ip6_fl_lock, so this entry could reappear | ||
176 | * and we need to recheck with it. | ||
177 | * | ||
178 | * OTOH no need to search the active socket first, like it is | ||
179 | * done in ipv6_flowlabel_opt - sock is locked, so new entry | ||
180 | * with the same label can only appear on another sock | ||
181 | */ | ||
182 | lfl = __fl_lookup(fl->label); | ||
183 | if (lfl != NULL) { | ||
184 | atomic_inc(&lfl->users); | ||
185 | write_unlock_bh(&ip6_fl_lock); | ||
186 | return lfl; | ||
187 | } | ||
172 | } | 188 | } |
173 | 189 | ||
174 | fl->lastuse = jiffies; | 190 | fl->lastuse = jiffies; |
@@ -176,7 +192,7 @@ static int fl_intern(struct ip6_flowlabel *fl, __be32 label) | |||
176 | fl_ht[FL_HASH(fl->label)] = fl; | 192 | fl_ht[FL_HASH(fl->label)] = fl; |
177 | atomic_inc(&fl_size); | 193 | atomic_inc(&fl_size); |
178 | write_unlock_bh(&ip6_fl_lock); | 194 | write_unlock_bh(&ip6_fl_lock); |
179 | return 0; | 195 | return NULL; |
180 | } | 196 | } |
181 | 197 | ||
182 | 198 | ||
@@ -190,14 +206,17 @@ struct ip6_flowlabel * fl6_sock_lookup(struct sock *sk, __be32 label) | |||
190 | 206 | ||
191 | label &= IPV6_FLOWLABEL_MASK; | 207 | label &= IPV6_FLOWLABEL_MASK; |
192 | 208 | ||
209 | read_lock_bh(&ip6_sk_fl_lock); | ||
193 | for (sfl=np->ipv6_fl_list; sfl; sfl = sfl->next) { | 210 | for (sfl=np->ipv6_fl_list; sfl; sfl = sfl->next) { |
194 | struct ip6_flowlabel *fl = sfl->fl; | 211 | struct ip6_flowlabel *fl = sfl->fl; |
195 | if (fl->label == label) { | 212 | if (fl->label == label) { |
196 | fl->lastuse = jiffies; | 213 | fl->lastuse = jiffies; |
197 | atomic_inc(&fl->users); | 214 | atomic_inc(&fl->users); |
215 | read_unlock_bh(&ip6_sk_fl_lock); | ||
198 | return fl; | 216 | return fl; |
199 | } | 217 | } |
200 | } | 218 | } |
219 | read_unlock_bh(&ip6_sk_fl_lock); | ||
201 | return NULL; | 220 | return NULL; |
202 | } | 221 | } |
203 | 222 | ||
@@ -409,6 +428,16 @@ static int ipv6_opt_cmp(struct ipv6_txoptions *o1, struct ipv6_txoptions *o2) | |||
409 | return 0; | 428 | return 0; |
410 | } | 429 | } |
411 | 430 | ||
431 | static inline void fl_link(struct ipv6_pinfo *np, struct ipv6_fl_socklist *sfl, | ||
432 | struct ip6_flowlabel *fl) | ||
433 | { | ||
434 | write_lock_bh(&ip6_sk_fl_lock); | ||
435 | sfl->fl = fl; | ||
436 | sfl->next = np->ipv6_fl_list; | ||
437 | np->ipv6_fl_list = sfl; | ||
438 | write_unlock_bh(&ip6_sk_fl_lock); | ||
439 | } | ||
440 | |||
412 | int ipv6_flowlabel_opt(struct sock *sk, char __user *optval, int optlen) | 441 | int ipv6_flowlabel_opt(struct sock *sk, char __user *optval, int optlen) |
413 | { | 442 | { |
414 | int err; | 443 | int err; |
@@ -416,7 +445,8 @@ int ipv6_flowlabel_opt(struct sock *sk, char __user *optval, int optlen) | |||
416 | struct in6_flowlabel_req freq; | 445 | struct in6_flowlabel_req freq; |
417 | struct ipv6_fl_socklist *sfl1=NULL; | 446 | struct ipv6_fl_socklist *sfl1=NULL; |
418 | struct ipv6_fl_socklist *sfl, **sflp; | 447 | struct ipv6_fl_socklist *sfl, **sflp; |
419 | struct ip6_flowlabel *fl; | 448 | struct ip6_flowlabel *fl, *fl1 = NULL; |
449 | |||
420 | 450 | ||
421 | if (optlen < sizeof(freq)) | 451 | if (optlen < sizeof(freq)) |
422 | return -EINVAL; | 452 | return -EINVAL; |
@@ -472,8 +502,6 @@ int ipv6_flowlabel_opt(struct sock *sk, char __user *optval, int optlen) | |||
472 | sfl1 = kmalloc(sizeof(*sfl1), GFP_KERNEL); | 502 | sfl1 = kmalloc(sizeof(*sfl1), GFP_KERNEL); |
473 | 503 | ||
474 | if (freq.flr_label) { | 504 | if (freq.flr_label) { |
475 | struct ip6_flowlabel *fl1 = NULL; | ||
476 | |||
477 | err = -EEXIST; | 505 | err = -EEXIST; |
478 | read_lock_bh(&ip6_sk_fl_lock); | 506 | read_lock_bh(&ip6_sk_fl_lock); |
479 | for (sfl = np->ipv6_fl_list; sfl; sfl = sfl->next) { | 507 | for (sfl = np->ipv6_fl_list; sfl; sfl = sfl->next) { |
@@ -492,6 +520,7 @@ int ipv6_flowlabel_opt(struct sock *sk, char __user *optval, int optlen) | |||
492 | if (fl1 == NULL) | 520 | if (fl1 == NULL) |
493 | fl1 = fl_lookup(freq.flr_label); | 521 | fl1 = fl_lookup(freq.flr_label); |
494 | if (fl1) { | 522 | if (fl1) { |
523 | recheck: | ||
495 | err = -EEXIST; | 524 | err = -EEXIST; |
496 | if (freq.flr_flags&IPV6_FL_F_EXCL) | 525 | if (freq.flr_flags&IPV6_FL_F_EXCL) |
497 | goto release; | 526 | goto release; |
@@ -513,11 +542,7 @@ int ipv6_flowlabel_opt(struct sock *sk, char __user *optval, int optlen) | |||
513 | fl1->linger = fl->linger; | 542 | fl1->linger = fl->linger; |
514 | if ((long)(fl->expires - fl1->expires) > 0) | 543 | if ((long)(fl->expires - fl1->expires) > 0) |
515 | fl1->expires = fl->expires; | 544 | fl1->expires = fl->expires; |
516 | write_lock_bh(&ip6_sk_fl_lock); | 545 | fl_link(np, sfl1, fl1); |
517 | sfl1->fl = fl1; | ||
518 | sfl1->next = np->ipv6_fl_list; | ||
519 | np->ipv6_fl_list = sfl1; | ||
520 | write_unlock_bh(&ip6_sk_fl_lock); | ||
521 | fl_free(fl); | 546 | fl_free(fl); |
522 | return 0; | 547 | return 0; |
523 | 548 | ||
@@ -534,9 +559,9 @@ release: | |||
534 | if (sfl1 == NULL || (err = mem_check(sk)) != 0) | 559 | if (sfl1 == NULL || (err = mem_check(sk)) != 0) |
535 | goto done; | 560 | goto done; |
536 | 561 | ||
537 | err = fl_intern(fl, freq.flr_label); | 562 | fl1 = fl_intern(fl, freq.flr_label); |
538 | if (err) | 563 | if (fl1 != NULL) |
539 | goto done; | 564 | goto recheck; |
540 | 565 | ||
541 | if (!freq.flr_label) { | 566 | if (!freq.flr_label) { |
542 | if (copy_to_user(&((struct in6_flowlabel_req __user *) optval)->flr_label, | 567 | if (copy_to_user(&((struct in6_flowlabel_req __user *) optval)->flr_label, |
@@ -545,9 +570,7 @@ release: | |||
545 | } | 570 | } |
546 | } | 571 | } |
547 | 572 | ||
548 | sfl1->fl = fl; | 573 | fl_link(np, sfl1, fl); |
549 | sfl1->next = np->ipv6_fl_list; | ||
550 | np->ipv6_fl_list = sfl1; | ||
551 | return 0; | 574 | return 0; |
552 | 575 | ||
553 | default: | 576 | default: |