diff options
author | Eric Dumazet <dada1@cosmosbay.com> | 2008-11-20 03:40:07 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2008-11-20 03:40:07 -0500 |
commit | 5caea4ea7088e80ac5410d04660346094608b909 (patch) | |
tree | fad95133683c002d24ff5de7fb756dad806b41ed /net/ipv4/tcp_ipv4.c | |
parent | d8b83c57a7e497cba9b5cb156e63176323035785 (diff) |
net: listening_hash get a spinlock per bucket
This patch prepares RCU migration of listening_hash table for
TCP/DCCP protocols.
listening_hash table being small (32 slots per protocol), we add
a spinlock for each slot, instead of a single rwlock for whole table.
This should reduce hold time of readers, and writers concurrency.
Signed-off-by: Eric Dumazet <dada1@cosmosbay.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv4/tcp_ipv4.c')
-rw-r--r-- | net/ipv4/tcp_ipv4.c | 24 |
1 files changed, 12 insertions, 12 deletions
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 5559fea61e87..330b08a12274 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c | |||
@@ -97,11 +97,7 @@ struct tcp_md5sig_key *tcp_v4_md5_do_lookup(struct sock *sk, __be32 addr) | |||
97 | } | 97 | } |
98 | #endif | 98 | #endif |
99 | 99 | ||
100 | struct inet_hashinfo __cacheline_aligned tcp_hashinfo = { | 100 | struct inet_hashinfo tcp_hashinfo; |
101 | .lhash_lock = __RW_LOCK_UNLOCKED(tcp_hashinfo.lhash_lock), | ||
102 | .lhash_users = ATOMIC_INIT(0), | ||
103 | .lhash_wait = __WAIT_QUEUE_HEAD_INITIALIZER(tcp_hashinfo.lhash_wait), | ||
104 | }; | ||
105 | 101 | ||
106 | static inline __u32 tcp_v4_init_sequence(struct sk_buff *skb) | 102 | static inline __u32 tcp_v4_init_sequence(struct sk_buff *skb) |
107 | { | 103 | { |
@@ -1874,15 +1870,18 @@ static void *listening_get_next(struct seq_file *seq, void *cur) | |||
1874 | struct inet_connection_sock *icsk; | 1870 | struct inet_connection_sock *icsk; |
1875 | struct hlist_node *node; | 1871 | struct hlist_node *node; |
1876 | struct sock *sk = cur; | 1872 | struct sock *sk = cur; |
1873 | struct inet_listen_hashbucket *ilb; | ||
1877 | struct tcp_iter_state *st = seq->private; | 1874 | struct tcp_iter_state *st = seq->private; |
1878 | struct net *net = seq_file_net(seq); | 1875 | struct net *net = seq_file_net(seq); |
1879 | 1876 | ||
1880 | if (!sk) { | 1877 | if (!sk) { |
1881 | st->bucket = 0; | 1878 | st->bucket = 0; |
1882 | sk = sk_head(&tcp_hashinfo.listening_hash[0]); | 1879 | ilb = &tcp_hashinfo.listening_hash[0]; |
1880 | spin_lock_bh(&ilb->lock); | ||
1881 | sk = sk_head(&ilb->head); | ||
1883 | goto get_sk; | 1882 | goto get_sk; |
1884 | } | 1883 | } |
1885 | 1884 | ilb = &tcp_hashinfo.listening_hash[st->bucket]; | |
1886 | ++st->num; | 1885 | ++st->num; |
1887 | 1886 | ||
1888 | if (st->state == TCP_SEQ_STATE_OPENREQ) { | 1887 | if (st->state == TCP_SEQ_STATE_OPENREQ) { |
@@ -1932,8 +1931,11 @@ start_req: | |||
1932 | } | 1931 | } |
1933 | read_unlock_bh(&icsk->icsk_accept_queue.syn_wait_lock); | 1932 | read_unlock_bh(&icsk->icsk_accept_queue.syn_wait_lock); |
1934 | } | 1933 | } |
1934 | spin_unlock_bh(&ilb->lock); | ||
1935 | if (++st->bucket < INET_LHTABLE_SIZE) { | 1935 | if (++st->bucket < INET_LHTABLE_SIZE) { |
1936 | sk = sk_head(&tcp_hashinfo.listening_hash[st->bucket]); | 1936 | ilb = &tcp_hashinfo.listening_hash[st->bucket]; |
1937 | spin_lock_bh(&ilb->lock); | ||
1938 | sk = sk_head(&ilb->head); | ||
1937 | goto get_sk; | 1939 | goto get_sk; |
1938 | } | 1940 | } |
1939 | cur = NULL; | 1941 | cur = NULL; |
@@ -2066,12 +2068,10 @@ static void *tcp_get_idx(struct seq_file *seq, loff_t pos) | |||
2066 | void *rc; | 2068 | void *rc; |
2067 | struct tcp_iter_state *st = seq->private; | 2069 | struct tcp_iter_state *st = seq->private; |
2068 | 2070 | ||
2069 | inet_listen_lock(&tcp_hashinfo); | ||
2070 | st->state = TCP_SEQ_STATE_LISTENING; | 2071 | st->state = TCP_SEQ_STATE_LISTENING; |
2071 | rc = listening_get_idx(seq, &pos); | 2072 | rc = listening_get_idx(seq, &pos); |
2072 | 2073 | ||
2073 | if (!rc) { | 2074 | if (!rc) { |
2074 | inet_listen_unlock(&tcp_hashinfo); | ||
2075 | st->state = TCP_SEQ_STATE_ESTABLISHED; | 2075 | st->state = TCP_SEQ_STATE_ESTABLISHED; |
2076 | rc = established_get_idx(seq, pos); | 2076 | rc = established_get_idx(seq, pos); |
2077 | } | 2077 | } |
@@ -2103,7 +2103,6 @@ static void *tcp_seq_next(struct seq_file *seq, void *v, loff_t *pos) | |||
2103 | case TCP_SEQ_STATE_LISTENING: | 2103 | case TCP_SEQ_STATE_LISTENING: |
2104 | rc = listening_get_next(seq, v); | 2104 | rc = listening_get_next(seq, v); |
2105 | if (!rc) { | 2105 | if (!rc) { |
2106 | inet_listen_unlock(&tcp_hashinfo); | ||
2107 | st->state = TCP_SEQ_STATE_ESTABLISHED; | 2106 | st->state = TCP_SEQ_STATE_ESTABLISHED; |
2108 | rc = established_get_first(seq); | 2107 | rc = established_get_first(seq); |
2109 | } | 2108 | } |
@@ -2130,7 +2129,7 @@ static void tcp_seq_stop(struct seq_file *seq, void *v) | |||
2130 | } | 2129 | } |
2131 | case TCP_SEQ_STATE_LISTENING: | 2130 | case TCP_SEQ_STATE_LISTENING: |
2132 | if (v != SEQ_START_TOKEN) | 2131 | if (v != SEQ_START_TOKEN) |
2133 | inet_listen_unlock(&tcp_hashinfo); | 2132 | spin_unlock_bh(&tcp_hashinfo.listening_hash[st->bucket].lock); |
2134 | break; | 2133 | break; |
2135 | case TCP_SEQ_STATE_TIME_WAIT: | 2134 | case TCP_SEQ_STATE_TIME_WAIT: |
2136 | case TCP_SEQ_STATE_ESTABLISHED: | 2135 | case TCP_SEQ_STATE_ESTABLISHED: |
@@ -2405,6 +2404,7 @@ static struct pernet_operations __net_initdata tcp_sk_ops = { | |||
2405 | 2404 | ||
2406 | void __init tcp_v4_init(void) | 2405 | void __init tcp_v4_init(void) |
2407 | { | 2406 | { |
2407 | inet_hashinfo_init(&tcp_hashinfo); | ||
2408 | if (register_pernet_device(&tcp_sk_ops)) | 2408 | if (register_pernet_device(&tcp_sk_ops)) |
2409 | panic("Failed to create the TCP control socket.\n"); | 2409 | panic("Failed to create the TCP control socket.\n"); |
2410 | } | 2410 | } |