aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv6/ip6_flowlabel.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/ipv6/ip6_flowlabel.c')
-rw-r--r--net/ipv6/ip6_flowlabel.c57
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
157static int fl_intern(struct ip6_flowlabel *fl, __be32 label) 157static 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
431static 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
412int ipv6_flowlabel_opt(struct sock *sk, char __user *optval, int optlen) 441int 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) {
523recheck:
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: