diff options
| -rw-r--r-- | include/net/protocol.h | 2 | ||||
| -rw-r--r-- | net/ipv4/af_inet.c | 28 | ||||
| -rw-r--r-- | net/ipv4/icmp.c | 9 | ||||
| -rw-r--r-- | net/ipv4/ip_input.c | 6 | ||||
| -rw-r--r-- | net/ipv4/protocol.c | 13 | ||||
| -rw-r--r-- | net/ipv6/af_inet6.c | 31 | ||||
| -rw-r--r-- | net/ipv6/icmp.c | 3 | ||||
| -rw-r--r-- | net/ipv6/ip6_input.c | 11 | ||||
| -rw-r--r-- | net/ipv6/protocol.c | 11 |
9 files changed, 67 insertions, 47 deletions
diff --git a/include/net/protocol.h b/include/net/protocol.h index 6f0e4234a442..f67327486a9c 100644 --- a/include/net/protocol.h +++ b/include/net/protocol.h @@ -80,11 +80,9 @@ struct inet_protosw { extern struct inet_protocol *inet_protocol_base; extern struct inet_protocol *inet_protos[MAX_INET_PROTOS]; -extern struct list_head inetsw[SOCK_MAX]; #if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) extern struct inet6_protocol *inet6_protos[MAX_INET_PROTOS]; -extern struct list_head inetsw6[SOCK_MAX]; #endif extern int inet_add_protocol(struct inet_protocol *prot, unsigned char num); diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index 69cfc1691f4a..2ccf51403636 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -94,7 +94,6 @@ #include <linux/inet.h> #include <linux/igmp.h> #include <linux/netdevice.h> -#include <linux/brlock.h> #include <net/ip.h> #include <net/protocol.h> #include <net/arp.h> @@ -129,7 +128,8 @@ static kmem_cache_t *raw4_sk_cachep; /* The inetsw table contains everything that inet_create needs to * build a new socket. */ -struct list_head inetsw[SOCK_MAX]; +static struct list_head inetsw[SOCK_MAX]; +static spinlock_t inetsw_lock = SPIN_LOCK_UNLOCKED; /* New destruction routine */ @@ -337,8 +337,8 @@ static int inet_create(struct socket *sock, int protocol) /* Look for the requested type/protocol pair. */ answer = NULL; - br_read_lock_bh(BR_NETPROTO_LOCK); - list_for_each(p, &inetsw[sock->type]) { + rcu_read_lock(); + list_for_each_rcu(p, &inetsw[sock->type]) { answer = list_entry(p, struct inet_protosw, list); /* Check the non-wild match. */ @@ -356,7 +356,6 @@ static int inet_create(struct socket *sock, int protocol) } answer = NULL; } - br_read_unlock_bh(BR_NETPROTO_LOCK); err = -ESOCKTNOSUPPORT; if (!answer) @@ -373,6 +372,7 @@ static int inet_create(struct socket *sock, int protocol) sk->no_check = answer->no_check; if (INET_PROTOSW_REUSE & answer->flags) sk->reuse = 1; + rcu_read_unlock(); inet = inet_sk(sk); @@ -427,6 +427,7 @@ static int inet_create(struct socket *sock, int protocol) out: return err; out_sk_free: + rcu_read_unlock(); sk_free(sk); goto out; } @@ -979,7 +980,7 @@ void inet_register_protosw(struct inet_protosw *p) int protocol = p->protocol; struct list_head *last_perm; - br_write_lock_bh(BR_NETPROTO_LOCK); + spin_lock_bh(&inetsw_lock); if (p->type > SOCK_MAX) goto out_illegal; @@ -1008,9 +1009,12 @@ void inet_register_protosw(struct inet_protosw *p) * non-permanent entry. This means that when we remove this entry, the * system automatically returns to the old behavior. */ - list_add(&p->list, last_perm); + list_add_rcu(&p->list, last_perm); out: - br_write_unlock_bh(BR_NETPROTO_LOCK); + spin_unlock_bh(&inetsw_lock); + + synchronize_kernel(); + return; out_permanent: @@ -1032,9 +1036,11 @@ void inet_unregister_protosw(struct inet_protosw *p) "Attempt to unregister permanent protocol %d.\n", p->protocol); } else { - br_write_lock_bh(BR_NETPROTO_LOCK); - list_del(&p->list); - br_write_unlock_bh(BR_NETPROTO_LOCK); + spin_lock_bh(&inetsw_lock); + list_del_rcu(&p->list); + spin_unlock_bh(&inetsw_lock); + + synchronize_kernel(); } } diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c index 04e3361034d4..4589ca2d16b0 100644 --- a/net/ipv4/icmp.c +++ b/net/ipv4/icmp.c @@ -695,15 +695,12 @@ static void icmp_unreach(struct sk_buff *skb) } read_unlock(&raw_v4_lock); - /* - * This can't change while we are doing it. - * Callers have obtained BR_NETPROTO_LOCK so - * we are OK. - */ - + rcu_read_lock(); ipprot = inet_protos[hash]; + smp_read_barrier_depends(); if (ipprot && ipprot->err_handler) ipprot->err_handler(skb, info); + rcu_read_unlock(); out: return; diff --git a/net/ipv4/ip_input.c b/net/ipv4/ip_input.c index 6131e3babfca..fe561aa4a767 100644 --- a/net/ipv4/ip_input.c +++ b/net/ipv4/ip_input.c @@ -215,6 +215,7 @@ static inline int ip_local_deliver_finish(struct sk_buff *skb) /* Point into the IP datagram, just past the header. */ skb->h.raw = skb->data; + rcu_read_lock(); { /* Note: See raw.c and net/raw.h, RAWV4_HTABLE_SIZE==MAX_INET_PROTOS */ int protocol = skb->nh.iph->protocol; @@ -235,10 +236,11 @@ static inline int ip_local_deliver_finish(struct sk_buff *skb) if ((ipprot = inet_protos[hash]) != NULL) { int ret; + smp_read_barrier_depends(); if (!ipprot->no_policy && !xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) { kfree_skb(skb); - return 0; + goto out; } ret = ipprot->handler(skb); if (ret < 0) { @@ -258,6 +260,8 @@ static inline int ip_local_deliver_finish(struct sk_buff *skb) kfree_skb(skb); } } + out: + rcu_read_unlock(); return 0; } diff --git a/net/ipv4/protocol.c b/net/ipv4/protocol.c index 302e10a3ccfb..9735e45d5b86 100644 --- a/net/ipv4/protocol.c +++ b/net/ipv4/protocol.c @@ -37,7 +37,6 @@ #include <linux/inet.h> #include <linux/netdevice.h> #include <linux/timer.h> -#include <linux/brlock.h> #include <net/ip.h> #include <net/protocol.h> #include <net/tcp.h> @@ -49,6 +48,7 @@ #include <linux/igmp.h> struct inet_protocol *inet_protos[MAX_INET_PROTOS]; +static spinlock_t inet_proto_lock = SPIN_LOCK_UNLOCKED; /* * Add a protocol handler to the hash tables @@ -60,16 +60,14 @@ int inet_add_protocol(struct inet_protocol *prot, unsigned char protocol) hash = protocol & (MAX_INET_PROTOS - 1); - br_write_lock_bh(BR_NETPROTO_LOCK); - + spin_lock_bh(&inet_proto_lock); if (inet_protos[hash]) { ret = -1; } else { inet_protos[hash] = prot; ret = 0; } - - br_write_unlock_bh(BR_NETPROTO_LOCK); + spin_unlock_bh(&inet_proto_lock); return ret; } @@ -84,16 +82,15 @@ int inet_del_protocol(struct inet_protocol *prot, unsigned char protocol) hash = protocol & (MAX_INET_PROTOS - 1); - br_write_lock_bh(BR_NETPROTO_LOCK); - + spin_lock_bh(&inet_proto_lock); if (inet_protos[hash] == prot) { inet_protos[hash] = NULL; ret = 0; } else { ret = -1; } + spin_unlock_bh(&inet_proto_lock); - br_write_unlock_bh(BR_NETPROTO_LOCK); return ret; } diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index 8ae2dfaba7a5..aa799a503823 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -45,7 +45,6 @@ #include <linux/inet.h> #include <linux/netdevice.h> #include <linux/icmpv6.h> -#include <linux/brlock.h> #include <linux/smp_lock.h> #include <net/ip.h> @@ -102,7 +101,8 @@ kmem_cache_t *raw6_sk_cachep; /* The inetsw table contains everything that inet_create needs to * build a new socket. */ -struct list_head inetsw6[SOCK_MAX]; +static struct list_head inetsw6[SOCK_MAX]; +static spinlock_t inetsw6_lock = SPIN_LOCK_UNLOCKED; static void inet6_sock_destruct(struct sock *sk) { @@ -162,8 +162,8 @@ static int inet6_create(struct socket *sock, int protocol) /* Look for the requested type/protocol pair. */ answer = NULL; - br_read_lock_bh(BR_NETPROTO_LOCK); - list_for_each(p, &inetsw6[sock->type]) { + rcu_read_lock(); + list_for_each_rcu(p, &inetsw6[sock->type]) { answer = list_entry(p, struct inet_protosw, list); /* Check the non-wild match. */ @@ -181,7 +181,6 @@ static int inet6_create(struct socket *sock, int protocol) } answer = NULL; } - br_read_unlock_bh(BR_NETPROTO_LOCK); if (!answer) goto free_and_badtype; @@ -198,6 +197,7 @@ static int inet6_create(struct socket *sock, int protocol) sk->no_check = answer->no_check; if (INET_PROTOSW_REUSE & answer->flags) sk->reuse = 1; + rcu_read_unlock(); inet = inet_sk(sk); @@ -260,12 +260,15 @@ static int inet6_create(struct socket *sock, int protocol) return 0; free_and_badtype: + rcu_read_unlock(); sk_free(sk); return -ESOCKTNOSUPPORT; free_and_badperm: + rcu_read_unlock(); sk_free(sk); return -EPERM; free_and_noproto: + rcu_read_unlock(); sk_free(sk); return -EPROTONOSUPPORT; do_oom: @@ -574,7 +577,7 @@ inet6_register_protosw(struct inet_protosw *p) int protocol = p->protocol; struct list_head *last_perm; - br_write_lock_bh(BR_NETPROTO_LOCK); + spin_lock_bh(&inetsw6_lock); if (p->type > SOCK_MAX) goto out_illegal; @@ -603,9 +606,9 @@ inet6_register_protosw(struct inet_protosw *p) * non-permanent entry. This means that when we remove this entry, the * system automatically returns to the old behavior. */ - list_add(&p->list, last_perm); + list_add_rcu(&p->list, last_perm); out: - br_write_unlock_bh(BR_NETPROTO_LOCK); + spin_unlock_bh(&inetsw6_lock); return; out_permanent: @@ -623,7 +626,17 @@ out_illegal: void inet6_unregister_protosw(struct inet_protosw *p) { - inet_unregister_protosw(p); + if (INET_PROTOSW_PERMANENT & p->flags) { + printk(KERN_ERR + "Attempt to unregister permanent protocol %d.\n", + p->protocol); + } else { + spin_lock_bh(&inetsw6_lock); + list_del_rcu(&p->list); + spin_unlock_bh(&inetsw6_lock); + + synchronize_kernel(); + } } int diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c index 9f420ea0f94f..8a415c312de0 100644 --- a/net/ipv6/icmp.c +++ b/net/ipv6/icmp.c @@ -456,9 +456,12 @@ static void icmpv6_notify(struct sk_buff *skb, int type, int code, u32 info) hash = nexthdr & (MAX_INET_PROTOS - 1); + rcu_read_lock(); ipprot = inet6_protos[hash]; + smp_read_barrier_depends(); if (ipprot && ipprot->err_handler) ipprot->err_handler(skb, NULL, type, code, inner_offset, info); + rcu_read_unlock(); read_lock(&raw_v6_lock); if ((sk = raw_v6_htable[hash]) != NULL) { diff --git a/net/ipv6/ip6_input.c b/net/ipv6/ip6_input.c index c737d2f3d486..16feb45246a9 100644 --- a/net/ipv6/ip6_input.c +++ b/net/ipv6/ip6_input.c @@ -152,6 +152,7 @@ static inline int ip6_input_finish(struct sk_buff *skb) skb->h.raw += (skb->h.raw[1]+1)<<3; } + rcu_read_lock(); resubmit: if (!pskb_pull(skb, skb->h.raw - skb->data)) goto discard; @@ -165,6 +166,7 @@ resubmit: if ((ipprot = inet6_protos[hash]) != NULL) { int ret; + smp_read_barrier_depends(); if (ipprot->flags & INET6_PROTO_FINAL) { if (!cksum_sub && skb->ip_summed == CHECKSUM_HW) { skb->csum = csum_sub(skb->csum, @@ -173,10 +175,8 @@ resubmit: } } if (!(ipprot->flags & INET6_PROTO_NOPOLICY) && - !xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb)) { - kfree_skb(skb); - return 0; - } + !xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb)) + goto discard; ret = ipprot->handler(&skb, &nhoff); if (ret > 0) @@ -194,10 +194,11 @@ resubmit: kfree_skb(skb); } } - + rcu_read_unlock(); return 0; discard: + rcu_read_unlock(); kfree_skb(skb); return 0; } diff --git a/net/ipv6/protocol.c b/net/ipv6/protocol.c index d070acfd8e1b..d1dc7ac40ae2 100644 --- a/net/ipv6/protocol.c +++ b/net/ipv6/protocol.c @@ -32,7 +32,6 @@ #include <linux/in6.h> #include <linux/netdevice.h> #include <linux/if_arp.h> -#include <linux/brlock.h> #include <net/sock.h> #include <net/snmp.h> @@ -41,12 +40,14 @@ #include <net/protocol.h> struct inet6_protocol *inet6_protos[MAX_INET_PROTOS]; +static spinlock_t inet6_proto_lock = SPIN_LOCK_UNLOCKED; + int inet6_add_protocol(struct inet6_protocol *prot, unsigned char protocol) { int ret, hash = protocol & (MAX_INET_PROTOS - 1); - br_write_lock_bh(BR_NETPROTO_LOCK); + spin_lock_bh(&inet6_proto_lock); if (inet6_protos[hash]) { ret = -1; @@ -55,7 +56,7 @@ int inet6_add_protocol(struct inet6_protocol *prot, unsigned char protocol) ret = 0; } - br_write_unlock_bh(BR_NETPROTO_LOCK); + spin_unlock_bh(&inet6_proto_lock); return ret; } @@ -68,7 +69,7 @@ int inet6_del_protocol(struct inet6_protocol *prot, unsigned char protocol) { int ret, hash = protocol & (MAX_INET_PROTOS - 1); - br_write_lock_bh(BR_NETPROTO_LOCK); + spin_lock_bh(&inet6_proto_lock); if (inet6_protos[hash] != prot) { ret = -1; @@ -77,7 +78,7 @@ int inet6_del_protocol(struct inet6_protocol *prot, unsigned char protocol) ret = 0; } - br_write_unlock_bh(BR_NETPROTO_LOCK); + spin_unlock_bh(&inet6_proto_lock); return ret; } |
