Commit c5adde94 authored by Ying Xue's avatar Ying Xue Committed by David S. Miller

netlink: eliminate nl_sk_hash_lock

As rhashtable_lookup_compare_insert() can guarantee the process
of search and insertion is atomic, it's safe to eliminate the
nl_sk_hash_lock. After this, object insertion or removal will
be protected with per bucket lock on write side while object
lookup is guarded with rcu read lock on read side.
Signed-off-by: default avatarYing Xue <ying.xue@windriver.com>
Cc: Thomas Graf <tgraf@suug.ch>
Acked-by: default avatarThomas Graf <tgraf@suug.ch>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 7a868d1e
...@@ -98,7 +98,7 @@ static void netlink_skb_destructor(struct sk_buff *skb); ...@@ -98,7 +98,7 @@ static void netlink_skb_destructor(struct sk_buff *skb);
/* nl_table locking explained: /* nl_table locking explained:
* Lookup and traversal are protected with an RCU read-side lock. Insertion * Lookup and traversal are protected with an RCU read-side lock. Insertion
* and removal are protected with nl_sk_hash_lock while using RCU list * and removal are protected with per bucket lock while using RCU list
* modification primitives and may run in parallel to RCU protected lookups. * modification primitives and may run in parallel to RCU protected lookups.
* Destruction of the Netlink socket may only occur *after* nl_table_lock has * Destruction of the Netlink socket may only occur *after* nl_table_lock has
* been acquired * either during or after the socket has been removed from * been acquired * either during or after the socket has been removed from
...@@ -110,10 +110,6 @@ static atomic_t nl_table_users = ATOMIC_INIT(0); ...@@ -110,10 +110,6 @@ static atomic_t nl_table_users = ATOMIC_INIT(0);
#define nl_deref_protected(X) rcu_dereference_protected(X, lockdep_is_held(&nl_table_lock)); #define nl_deref_protected(X) rcu_dereference_protected(X, lockdep_is_held(&nl_table_lock));
/* Protects netlink socket hash table mutations */
DEFINE_MUTEX(nl_sk_hash_lock);
EXPORT_SYMBOL_GPL(nl_sk_hash_lock);
static ATOMIC_NOTIFIER_HEAD(netlink_chain); static ATOMIC_NOTIFIER_HEAD(netlink_chain);
static DEFINE_SPINLOCK(netlink_tap_lock); static DEFINE_SPINLOCK(netlink_tap_lock);
...@@ -998,6 +994,19 @@ static struct sock *__netlink_lookup(struct netlink_table *table, u32 portid, ...@@ -998,6 +994,19 @@ static struct sock *__netlink_lookup(struct netlink_table *table, u32 portid,
&netlink_compare, &arg); &netlink_compare, &arg);
} }
static bool __netlink_insert(struct netlink_table *table, struct sock *sk,
struct net *net)
{
struct netlink_compare_arg arg = {
.net = net,
.portid = nlk_sk(sk)->portid,
};
return rhashtable_lookup_compare_insert(&table->hash,
&nlk_sk(sk)->node,
&netlink_compare, &arg);
}
static struct sock *netlink_lookup(struct net *net, int protocol, u32 portid) static struct sock *netlink_lookup(struct net *net, int protocol, u32 portid)
{ {
struct netlink_table *table = &nl_table[protocol]; struct netlink_table *table = &nl_table[protocol];
...@@ -1043,9 +1052,7 @@ static int netlink_insert(struct sock *sk, struct net *net, u32 portid) ...@@ -1043,9 +1052,7 @@ static int netlink_insert(struct sock *sk, struct net *net, u32 portid)
struct netlink_table *table = &nl_table[sk->sk_protocol]; struct netlink_table *table = &nl_table[sk->sk_protocol];
int err = -EADDRINUSE; int err = -EADDRINUSE;
mutex_lock(&nl_sk_hash_lock); lock_sock(sk);
if (__netlink_lookup(table, portid, net))
goto err;
err = -EBUSY; err = -EBUSY;
if (nlk_sk(sk)->portid) if (nlk_sk(sk)->portid)
...@@ -1058,10 +1065,12 @@ static int netlink_insert(struct sock *sk, struct net *net, u32 portid) ...@@ -1058,10 +1065,12 @@ static int netlink_insert(struct sock *sk, struct net *net, u32 portid)
nlk_sk(sk)->portid = portid; nlk_sk(sk)->portid = portid;
sock_hold(sk); sock_hold(sk);
rhashtable_insert(&table->hash, &nlk_sk(sk)->node); if (__netlink_insert(table, sk, net))
err = 0; err = 0;
else
sock_put(sk);
err: err:
mutex_unlock(&nl_sk_hash_lock); release_sock(sk);
return err; return err;
} }
...@@ -1069,13 +1078,11 @@ static void netlink_remove(struct sock *sk) ...@@ -1069,13 +1078,11 @@ static void netlink_remove(struct sock *sk)
{ {
struct netlink_table *table; struct netlink_table *table;
mutex_lock(&nl_sk_hash_lock);
table = &nl_table[sk->sk_protocol]; table = &nl_table[sk->sk_protocol];
if (rhashtable_remove(&table->hash, &nlk_sk(sk)->node)) { if (rhashtable_remove(&table->hash, &nlk_sk(sk)->node)) {
WARN_ON(atomic_read(&sk->sk_refcnt) == 1); WARN_ON(atomic_read(&sk->sk_refcnt) == 1);
__sock_put(sk); __sock_put(sk);
} }
mutex_unlock(&nl_sk_hash_lock);
netlink_table_grab(); netlink_table_grab();
if (nlk_sk(sk)->subscriptions) { if (nlk_sk(sk)->subscriptions) {
......
...@@ -74,6 +74,5 @@ struct netlink_table { ...@@ -74,6 +74,5 @@ struct netlink_table {
extern struct netlink_table *nl_table; extern struct netlink_table *nl_table;
extern rwlock_t nl_table_lock; extern rwlock_t nl_table_lock;
extern struct mutex nl_sk_hash_lock;
#endif #endif
...@@ -103,7 +103,7 @@ static int __netlink_diag_dump(struct sk_buff *skb, struct netlink_callback *cb, ...@@ -103,7 +103,7 @@ static int __netlink_diag_dump(struct sk_buff *skb, struct netlink_callback *cb,
{ {
struct netlink_table *tbl = &nl_table[protocol]; struct netlink_table *tbl = &nl_table[protocol];
struct rhashtable *ht = &tbl->hash; struct rhashtable *ht = &tbl->hash;
const struct bucket_table *htbl = rht_dereference(ht->tbl, ht); const struct bucket_table *htbl = rht_dereference_rcu(ht->tbl, ht);
struct net *net = sock_net(skb->sk); struct net *net = sock_net(skb->sk);
struct netlink_diag_req *req; struct netlink_diag_req *req;
struct netlink_sock *nlsk; struct netlink_sock *nlsk;
...@@ -115,7 +115,7 @@ static int __netlink_diag_dump(struct sk_buff *skb, struct netlink_callback *cb, ...@@ -115,7 +115,7 @@ static int __netlink_diag_dump(struct sk_buff *skb, struct netlink_callback *cb,
for (i = 0; i < htbl->size; i++) { for (i = 0; i < htbl->size; i++) {
struct rhash_head *pos; struct rhash_head *pos;
rht_for_each_entry(nlsk, pos, htbl, i, node) { rht_for_each_entry_rcu(nlsk, pos, htbl, i, node) {
sk = (struct sock *)nlsk; sk = (struct sock *)nlsk;
if (!net_eq(sock_net(sk), net)) if (!net_eq(sock_net(sk), net))
...@@ -172,7 +172,7 @@ static int netlink_diag_dump(struct sk_buff *skb, struct netlink_callback *cb) ...@@ -172,7 +172,7 @@ static int netlink_diag_dump(struct sk_buff *skb, struct netlink_callback *cb)
req = nlmsg_data(cb->nlh); req = nlmsg_data(cb->nlh);
mutex_lock(&nl_sk_hash_lock); rcu_read_lock();
read_lock(&nl_table_lock); read_lock(&nl_table_lock);
if (req->sdiag_protocol == NDIAG_PROTO_ALL) { if (req->sdiag_protocol == NDIAG_PROTO_ALL) {
...@@ -186,7 +186,7 @@ static int netlink_diag_dump(struct sk_buff *skb, struct netlink_callback *cb) ...@@ -186,7 +186,7 @@ static int netlink_diag_dump(struct sk_buff *skb, struct netlink_callback *cb)
} else { } else {
if (req->sdiag_protocol >= MAX_LINKS) { if (req->sdiag_protocol >= MAX_LINKS) {
read_unlock(&nl_table_lock); read_unlock(&nl_table_lock);
mutex_unlock(&nl_sk_hash_lock); rcu_read_unlock();
return -ENOENT; return -ENOENT;
} }
...@@ -194,7 +194,7 @@ static int netlink_diag_dump(struct sk_buff *skb, struct netlink_callback *cb) ...@@ -194,7 +194,7 @@ static int netlink_diag_dump(struct sk_buff *skb, struct netlink_callback *cb)
} }
read_unlock(&nl_table_lock); read_unlock(&nl_table_lock);
mutex_unlock(&nl_sk_hash_lock); rcu_read_unlock();
return skb->len; return skb->len;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment