diff options
Diffstat (limited to 'net/ipv6/ip6_flowlabel.c')
-rw-r--r-- | net/ipv6/ip6_flowlabel.c | 101 |
1 files changed, 74 insertions, 27 deletions
diff --git a/net/ipv6/ip6_flowlabel.c b/net/ipv6/ip6_flowlabel.c index 2b7d9ee9883..eb7a940310f 100644 --- a/net/ipv6/ip6_flowlabel.c +++ b/net/ipv6/ip6_flowlabel.c @@ -62,23 +62,23 @@ static DEFINE_RWLOCK(ip6_fl_lock); static DEFINE_RWLOCK(ip6_sk_fl_lock); -static __inline__ struct ip6_flowlabel * __fl_lookup(__be32 label) +static inline struct ip6_flowlabel *__fl_lookup(struct net *net, __be32 label) { struct ip6_flowlabel *fl; for (fl=fl_ht[FL_HASH(label)]; fl; fl = fl->next) { - if (fl->label == label) + if (fl->label == label && fl->fl_net == net) return fl; } return NULL; } -static struct ip6_flowlabel * fl_lookup(__be32 label) +static struct ip6_flowlabel *fl_lookup(struct net *net, __be32 label) { struct ip6_flowlabel *fl; read_lock_bh(&ip6_fl_lock); - fl = __fl_lookup(label); + fl = __fl_lookup(net, label); if (fl) atomic_inc(&fl->users); read_unlock_bh(&ip6_fl_lock); @@ -88,8 +88,10 @@ static struct ip6_flowlabel * fl_lookup(__be32 label) static void fl_free(struct ip6_flowlabel *fl) { - if (fl) + if (fl) { + release_net(fl->fl_net); kfree(fl->opt); + } kfree(fl); } @@ -112,7 +114,6 @@ static void fl_release(struct ip6_flowlabel *fl) time_after(ip6_fl_gc_timer.expires, ttd)) mod_timer(&ip6_fl_gc_timer, ttd); } - write_unlock_bh(&ip6_fl_lock); } @@ -148,13 +149,34 @@ static void ip6_fl_gc(unsigned long dummy) if (!sched && atomic_read(&fl_size)) sched = now + FL_MAX_LINGER; if (sched) { - ip6_fl_gc_timer.expires = sched; - add_timer(&ip6_fl_gc_timer); + mod_timer(&ip6_fl_gc_timer, sched); + } + write_unlock(&ip6_fl_lock); +} + +static void ip6_fl_purge(struct net *net) +{ + int i; + + write_lock(&ip6_fl_lock); + for (i = 0; i <= FL_HASH_MASK; i++) { + struct ip6_flowlabel *fl, **flp; + flp = &fl_ht[i]; + while ((fl = *flp) != NULL) { + if (fl->fl_net == net && atomic_read(&fl->users) == 0) { + *flp = fl->next; + fl_free(fl); + atomic_dec(&fl_size); + continue; + } + flp = &fl->next; + } } write_unlock(&ip6_fl_lock); } -static struct ip6_flowlabel *fl_intern(struct ip6_flowlabel *fl, __be32 label) +static struct ip6_flowlabel *fl_intern(struct net *net, + struct ip6_flowlabel *fl, __be32 label) { struct ip6_flowlabel *lfl; @@ -165,7 +187,7 @@ static struct ip6_flowlabel *fl_intern(struct ip6_flowlabel *fl, __be32 label) for (;;) { fl->label = htonl(net_random())&IPV6_FLOWLABEL_MASK; if (fl->label) { - lfl = __fl_lookup(fl->label); + lfl = __fl_lookup(net, fl->label); if (lfl == NULL) break; } @@ -179,7 +201,7 @@ static struct ip6_flowlabel *fl_intern(struct ip6_flowlabel *fl, __be32 label) * done in ipv6_flowlabel_opt - sock is locked, so new entry * with the same label can only appear on another sock */ - lfl = __fl_lookup(fl->label); + lfl = __fl_lookup(net, fl->label); if (lfl != NULL) { atomic_inc(&lfl->users); write_unlock_bh(&ip6_fl_lock); @@ -298,7 +320,8 @@ static int fl6_renew(struct ip6_flowlabel *fl, unsigned long linger, unsigned lo } static struct ip6_flowlabel * -fl_create(struct in6_flowlabel_req *freq, char __user *optval, int optlen, int *err_p) +fl_create(struct net *net, struct in6_flowlabel_req *freq, char __user *optval, + int optlen, int *err_p) { struct ip6_flowlabel *fl; int olen; @@ -343,6 +366,7 @@ fl_create(struct in6_flowlabel_req *freq, char __user *optval, int optlen, int * } } + fl->fl_net = hold_net(net); fl->expires = jiffies; err = fl6_renew(fl, freq->flr_linger, freq->flr_expires); if (err) @@ -441,6 +465,7 @@ static inline void fl_link(struct ipv6_pinfo *np, struct ipv6_fl_socklist *sfl, int ipv6_flowlabel_opt(struct sock *sk, char __user *optval, int optlen) { int err; + struct net *net = sock_net(sk); struct ipv6_pinfo *np = inet6_sk(sk); struct in6_flowlabel_req freq; struct ipv6_fl_socklist *sfl1=NULL; @@ -483,7 +508,7 @@ int ipv6_flowlabel_opt(struct sock *sk, char __user *optval, int optlen) read_unlock_bh(&ip6_sk_fl_lock); if (freq.flr_share == IPV6_FL_S_NONE && capable(CAP_NET_ADMIN)) { - fl = fl_lookup(freq.flr_label); + fl = fl_lookup(net, freq.flr_label); if (fl) { err = fl6_renew(fl, freq.flr_linger, freq.flr_expires); fl_release(fl); @@ -496,7 +521,7 @@ int ipv6_flowlabel_opt(struct sock *sk, char __user *optval, int optlen) if (freq.flr_label & ~IPV6_FLOWLABEL_MASK) return -EINVAL; - fl = fl_create(&freq, optval, optlen, &err); + fl = fl_create(net, &freq, optval, optlen, &err); if (fl == NULL) return err; sfl1 = kmalloc(sizeof(*sfl1), GFP_KERNEL); @@ -518,7 +543,7 @@ int ipv6_flowlabel_opt(struct sock *sk, char __user *optval, int optlen) read_unlock_bh(&ip6_sk_fl_lock); if (fl1 == NULL) - fl1 = fl_lookup(freq.flr_label); + fl1 = fl_lookup(net, freq.flr_label); if (fl1) { recheck: err = -EEXIST; @@ -559,7 +584,7 @@ release: if (sfl1 == NULL || (err = mem_check(sk)) != 0) goto done; - fl1 = fl_intern(fl, freq.flr_label); + fl1 = fl_intern(net, fl, freq.flr_label); if (fl1 != NULL) goto recheck; @@ -586,6 +611,7 @@ done: #ifdef CONFIG_PROC_FS struct ip6fl_iter_state { + struct seq_net_private p; int bucket; }; @@ -595,12 +621,15 @@ static struct ip6_flowlabel *ip6fl_get_first(struct seq_file *seq) { struct ip6_flowlabel *fl = NULL; struct ip6fl_iter_state *state = ip6fl_seq_private(seq); + struct net *net = seq_file_net(seq); for (state->bucket = 0; state->bucket <= FL_HASH_MASK; ++state->bucket) { - if (fl_ht[state->bucket]) { - fl = fl_ht[state->bucket]; + fl = fl_ht[state->bucket]; + + while (fl && fl->fl_net != net) + fl = fl->next; + if (fl) break; - } } return fl; } @@ -608,12 +637,18 @@ static struct ip6_flowlabel *ip6fl_get_first(struct seq_file *seq) static struct ip6_flowlabel *ip6fl_get_next(struct seq_file *seq, struct ip6_flowlabel *fl) { struct ip6fl_iter_state *state = ip6fl_seq_private(seq); + struct net *net = seq_file_net(seq); fl = fl->next; +try_again: + while (fl && fl->fl_net != net) + fl = fl->next; + while (!fl) { - if (++state->bucket <= FL_HASH_MASK) + if (++state->bucket <= FL_HASH_MASK) { fl = fl_ht[state->bucket]; - else + goto try_again; + } else break; } return fl; @@ -683,8 +718,8 @@ static const struct seq_operations ip6fl_seq_ops = { static int ip6fl_seq_open(struct inode *inode, struct file *file) { - return seq_open_private(file, &ip6fl_seq_ops, - sizeof(struct ip6fl_iter_state)); + return seq_open_net(inode, file, &ip6fl_seq_ops, + sizeof(struct ip6fl_iter_state)); } static const struct file_operations ip6fl_seq_fops = { @@ -692,12 +727,13 @@ static const struct file_operations ip6fl_seq_fops = { .open = ip6fl_seq_open, .read = seq_read, .llseek = seq_lseek, - .release = seq_release_private, + .release = seq_release_net, }; static int ip6_flowlabel_proc_init(struct net *net) { - if (!proc_net_fops_create(net, "ip6_flowlabel", S_IRUGO, &ip6fl_seq_fops)) + if (!proc_net_fops_create(net, "ip6_flowlabel", + S_IRUGO, &ip6fl_seq_fops)) return -ENOMEM; return 0; } @@ -717,13 +753,24 @@ static inline void ip6_flowlabel_proc_fini(struct net *net) } #endif +static inline void ip6_flowlabel_net_exit(struct net *net) +{ + ip6_fl_purge(net); + ip6_flowlabel_proc_fini(net); +} + +static struct pernet_operations ip6_flowlabel_net_ops = { + .init = ip6_flowlabel_proc_init, + .exit = ip6_flowlabel_net_exit, +}; + int ip6_flowlabel_init(void) { - return ip6_flowlabel_proc_init(&init_net); + return register_pernet_subsys(&ip6_flowlabel_net_ops); } void ip6_flowlabel_cleanup(void) { del_timer(&ip6_fl_gc_timer); - ip6_flowlabel_proc_fini(&init_net); + unregister_pernet_subsys(&ip6_flowlabel_net_ops); } |