aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Howells <dhowells@redhat.com>2016-04-04 09:00:32 -0400
committerDavid Howells <dhowells@redhat.com>2016-06-15 05:12:33 -0400
commitbe6e6707f6eec2048d9be608bc0ceecde5bd4cef (patch)
treeda92ce7ff67efa365ba27ddb5742bdc3698e0836
parentd9fa17ef9f084c755332898c8243a396ea02d73e (diff)
rxrpc: Rework peer object handling to use hash table and RCU
Rework peer object handling to use a hash table instead of a flat list and to use RCU. Peer objects are no longer destroyed by passing them to a workqueue to process, but rather are just passed to the RCU garbage collector as kfree'able objects. The hash function uses the local endpoint plus all the components of the remote address, except for the RxRPC service ID. Peers thus represent a UDP port on the remote machine as contacted by a UDP port on this machine. The RCU read lock is used to handle non-creating lookups so that they can be called from bottom half context in the sk_error_report handler without having to lock the hash table against modification. rxrpc_lookup_peer_rcu() *does* take a reference on the peer object as in the future, this will be passed to a work item for error distribution in the error_report path and this function will cease being used in the data_ready path. Creating lookups are done under spinlock rather than mutex as they might be set up due to an external stimulus if the local endpoint is a server. Captured network error messages (ICMP) are handled with respect to this struct and MTU size and RTT are cached here. Signed-off-by: David Howells <dhowells@redhat.com>
-rw-r--r--net/rxrpc/Makefile3
-rw-r--r--net/rxrpc/af_rxrpc.c3
-rw-r--r--net/rxrpc/ar-internal.h46
-rw-r--r--net/rxrpc/call_accept.c2
-rw-r--r--net/rxrpc/input.c13
-rw-r--r--net/rxrpc/peer_event.c59
-rw-r--r--net/rxrpc/peer_object.c369
-rw-r--r--net/rxrpc/transport.c2
-rw-r--r--net/rxrpc/utils.c41
9 files changed, 335 insertions, 203 deletions
diff --git a/net/rxrpc/Makefile b/net/rxrpc/Makefile
index 7e1006a3bfa5..a6f6f21d8a59 100644
--- a/net/rxrpc/Makefile
+++ b/net/rxrpc/Makefile
@@ -20,7 +20,8 @@ af-rxrpc-y := \
20 recvmsg.o \ 20 recvmsg.o \
21 security.o \ 21 security.o \
22 skbuff.o \ 22 skbuff.o \
23 transport.o 23 transport.o \
24 utils.o
24 25
25af-rxrpc-$(CONFIG_PROC_FS) += proc.o 26af-rxrpc-$(CONFIG_PROC_FS) += proc.o
26af-rxrpc-$(CONFIG_RXKAD) += rxkad.o 27af-rxrpc-$(CONFIG_RXKAD) += rxkad.o
diff --git a/net/rxrpc/af_rxrpc.c b/net/rxrpc/af_rxrpc.c
index a1bcb0e17250..ba373caddbeb 100644
--- a/net/rxrpc/af_rxrpc.c
+++ b/net/rxrpc/af_rxrpc.c
@@ -244,7 +244,7 @@ struct rxrpc_transport *rxrpc_name_to_transport(struct rxrpc_sock *rx,
244 return ERR_PTR(-EAFNOSUPPORT); 244 return ERR_PTR(-EAFNOSUPPORT);
245 245
246 /* find a remote transport endpoint from the local one */ 246 /* find a remote transport endpoint from the local one */
247 peer = rxrpc_get_peer(srx, gfp); 247 peer = rxrpc_lookup_peer(rx->local, srx, gfp);
248 if (IS_ERR(peer)) 248 if (IS_ERR(peer))
249 return ERR_CAST(peer); 249 return ERR_CAST(peer);
250 250
@@ -835,7 +835,6 @@ static void __exit af_rxrpc_exit(void)
835 rxrpc_destroy_all_calls(); 835 rxrpc_destroy_all_calls();
836 rxrpc_destroy_all_connections(); 836 rxrpc_destroy_all_connections();
837 rxrpc_destroy_all_transports(); 837 rxrpc_destroy_all_transports();
838 rxrpc_destroy_all_peers();
839 rxrpc_destroy_all_locals(); 838 rxrpc_destroy_all_locals();
840 839
841 ASSERTCMP(atomic_read(&rxrpc_n_skbs), ==, 0); 840 ASSERTCMP(atomic_read(&rxrpc_n_skbs), ==, 0);
diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h
index 03919b9a8a31..7dba6677b9d5 100644
--- a/net/rxrpc/ar-internal.h
+++ b/net/rxrpc/ar-internal.h
@@ -9,7 +9,9 @@
9 * 2 of the License, or (at your option) any later version. 9 * 2 of the License, or (at your option) any later version.
10 */ 10 */
11 11
12#include <linux/atomic.h>
12#include <net/sock.h> 13#include <net/sock.h>
14#include <net/af_rxrpc.h>
13#include <rxrpc/packet.h> 15#include <rxrpc/packet.h>
14 16
15#if 0 17#if 0
@@ -193,15 +195,16 @@ struct rxrpc_local {
193 195
194/* 196/*
195 * RxRPC remote transport endpoint definition 197 * RxRPC remote transport endpoint definition
196 * - matched by remote port, address and protocol type 198 * - matched by local endpoint, remote port, address and protocol type
197 * - holds the connection ID counter for connections between the two endpoints
198 */ 199 */
199struct rxrpc_peer { 200struct rxrpc_peer {
200 struct work_struct destroyer; /* peer destroyer */ 201 struct rcu_head rcu; /* This must be first */
201 struct list_head link; /* link in master peer list */ 202 atomic_t usage;
203 unsigned long hash_key;
204 struct hlist_node hash_link;
205 struct rxrpc_local *local;
202 struct list_head error_targets; /* targets for net error distribution */ 206 struct list_head error_targets; /* targets for net error distribution */
203 spinlock_t lock; /* access lock */ 207 spinlock_t lock; /* access lock */
204 atomic_t usage;
205 unsigned int if_mtu; /* interface MTU for this peer */ 208 unsigned int if_mtu; /* interface MTU for this peer */
206 unsigned int mtu; /* network MTU for this peer */ 209 unsigned int mtu; /* network MTU for this peer */
207 unsigned int maxdata; /* data size (MTU - hdrsize) */ 210 unsigned int maxdata; /* data size (MTU - hdrsize) */
@@ -611,10 +614,29 @@ void rxrpc_UDP_error_handler(struct work_struct *);
611/* 614/*
612 * peer_object.c 615 * peer_object.c
613 */ 616 */
614struct rxrpc_peer *rxrpc_get_peer(struct sockaddr_rxrpc *, gfp_t); 617struct rxrpc_peer *rxrpc_lookup_peer_rcu(struct rxrpc_local *,
615void rxrpc_put_peer(struct rxrpc_peer *); 618 const struct sockaddr_rxrpc *);
616struct rxrpc_peer *rxrpc_find_peer(struct rxrpc_local *, __be32, __be16); 619struct rxrpc_peer *rxrpc_lookup_peer(struct rxrpc_local *,
617void __exit rxrpc_destroy_all_peers(void); 620 struct sockaddr_rxrpc *, gfp_t);
621struct rxrpc_peer *rxrpc_alloc_peer(struct rxrpc_local *, gfp_t);
622
623static inline void rxrpc_get_peer(struct rxrpc_peer *peer)
624{
625 atomic_inc(&peer->usage);
626}
627
628static inline
629struct rxrpc_peer *rxrpc_get_peer_maybe(struct rxrpc_peer *peer)
630{
631 return atomic_inc_not_zero(&peer->usage) ? peer : NULL;
632}
633
634extern void __rxrpc_put_peer(struct rxrpc_peer *peer);
635static inline void rxrpc_put_peer(struct rxrpc_peer *peer)
636{
637 if (atomic_dec_and_test(&peer->usage))
638 __rxrpc_put_peer(peer);
639}
618 640
619/* 641/*
620 * proc.c 642 * proc.c
@@ -673,6 +695,12 @@ struct rxrpc_transport *rxrpc_find_transport(struct rxrpc_local *,
673 struct rxrpc_peer *); 695 struct rxrpc_peer *);
674 696
675/* 697/*
698 * utils.c
699 */
700void rxrpc_get_addr_from_skb(struct rxrpc_local *, const struct sk_buff *,
701 struct sockaddr_rxrpc *);
702
703/*
676 * debug tracing 704 * debug tracing
677 */ 705 */
678extern unsigned int rxrpc_debug; 706extern unsigned int rxrpc_debug;
diff --git a/net/rxrpc/call_accept.c b/net/rxrpc/call_accept.c
index eea5f4a5d8b1..e5723f4dce89 100644
--- a/net/rxrpc/call_accept.c
+++ b/net/rxrpc/call_accept.c
@@ -95,7 +95,7 @@ static int rxrpc_accept_incoming_call(struct rxrpc_local *local,
95 rxrpc_new_skb(notification); 95 rxrpc_new_skb(notification);
96 notification->mark = RXRPC_SKB_MARK_NEW_CALL; 96 notification->mark = RXRPC_SKB_MARK_NEW_CALL;
97 97
98 peer = rxrpc_get_peer(srx, GFP_NOIO); 98 peer = rxrpc_lookup_peer(local, srx, GFP_NOIO);
99 if (IS_ERR(peer)) { 99 if (IS_ERR(peer)) {
100 _debug("no peer"); 100 _debug("no peer");
101 ret = -EBUSY; 101 ret = -EBUSY;
diff --git a/net/rxrpc/input.c b/net/rxrpc/input.c
index e0815a033999..3b405dbf3a05 100644
--- a/net/rxrpc/input.c
+++ b/net/rxrpc/input.c
@@ -635,14 +635,16 @@ static struct rxrpc_connection *rxrpc_conn_from_local(struct rxrpc_local *local,
635 struct rxrpc_peer *peer; 635 struct rxrpc_peer *peer;
636 struct rxrpc_transport *trans; 636 struct rxrpc_transport *trans;
637 struct rxrpc_connection *conn; 637 struct rxrpc_connection *conn;
638 struct sockaddr_rxrpc srx;
638 639
639 peer = rxrpc_find_peer(local, ip_hdr(skb)->saddr, 640 rxrpc_get_addr_from_skb(local, skb, &srx);
640 udp_hdr(skb)->source); 641 rcu_read_lock();
642 peer = rxrpc_lookup_peer_rcu(local, &srx);
641 if (IS_ERR(peer)) 643 if (IS_ERR(peer))
642 goto cant_find_conn; 644 goto cant_find_peer;
643 645
644 trans = rxrpc_find_transport(local, peer); 646 trans = rxrpc_find_transport(local, peer);
645 rxrpc_put_peer(peer); 647 rcu_read_unlock();
646 if (!trans) 648 if (!trans)
647 goto cant_find_conn; 649 goto cant_find_conn;
648 650
@@ -652,6 +654,9 @@ static struct rxrpc_connection *rxrpc_conn_from_local(struct rxrpc_local *local,
652 goto cant_find_conn; 654 goto cant_find_conn;
653 655
654 return conn; 656 return conn;
657
658cant_find_peer:
659 rcu_read_unlock();
655cant_find_conn: 660cant_find_conn:
656 return NULL; 661 return NULL;
657} 662}
diff --git a/net/rxrpc/peer_event.c b/net/rxrpc/peer_event.c
index 3e82d6f0313c..24f5ec0fcd20 100644
--- a/net/rxrpc/peer_event.c
+++ b/net/rxrpc/peer_event.c
@@ -23,6 +23,55 @@
23#include "ar-internal.h" 23#include "ar-internal.h"
24 24
25/* 25/*
26 * Find the peer associated with an ICMP packet.
27 */
28static struct rxrpc_peer *rxrpc_lookup_peer_icmp_rcu(struct rxrpc_local *local,
29 const struct sk_buff *skb)
30{
31 struct sock_exterr_skb *serr = SKB_EXT_ERR(skb);
32 struct sockaddr_rxrpc srx;
33
34 _enter("");
35
36 memset(&srx, 0, sizeof(srx));
37 srx.transport_type = local->srx.transport_type;
38 srx.transport.family = local->srx.transport.family;
39
40 /* Can we see an ICMP4 packet on an ICMP6 listening socket? and vice
41 * versa?
42 */
43 switch (srx.transport.family) {
44 case AF_INET:
45 srx.transport.sin.sin_port = serr->port;
46 srx.transport_len = sizeof(struct sockaddr_in);
47 switch (serr->ee.ee_origin) {
48 case SO_EE_ORIGIN_ICMP:
49 _net("Rx ICMP");
50 memcpy(&srx.transport.sin.sin_addr,
51 skb_network_header(skb) + serr->addr_offset,
52 sizeof(struct in_addr));
53 break;
54 case SO_EE_ORIGIN_ICMP6:
55 _net("Rx ICMP6 on v4 sock");
56 memcpy(&srx.transport.sin.sin_addr,
57 skb_network_header(skb) + serr->addr_offset + 12,
58 sizeof(struct in_addr));
59 break;
60 default:
61 memcpy(&srx.transport.sin.sin_addr, &ip_hdr(skb)->saddr,
62 sizeof(struct in_addr));
63 break;
64 }
65 break;
66
67 default:
68 BUG();
69 }
70
71 return rxrpc_lookup_peer_rcu(local, &srx);
72}
73
74/*
26 * handle an error received on the local endpoint 75 * handle an error received on the local endpoint
27 */ 76 */
28void rxrpc_UDP_error_report(struct sock *sk) 77void rxrpc_UDP_error_report(struct sock *sk)
@@ -57,8 +106,12 @@ void rxrpc_UDP_error_report(struct sock *sk)
57 _net("Rx UDP Error from %pI4:%hu", &addr, ntohs(port)); 106 _net("Rx UDP Error from %pI4:%hu", &addr, ntohs(port));
58 _debug("Msg l:%d d:%d", skb->len, skb->data_len); 107 _debug("Msg l:%d d:%d", skb->len, skb->data_len);
59 108
60 peer = rxrpc_find_peer(local, addr, port); 109 rcu_read_lock();
61 if (IS_ERR(peer)) { 110 peer = rxrpc_lookup_peer_icmp_rcu(local, skb);
111 if (peer && !rxrpc_get_peer_maybe(peer))
112 peer = NULL;
113 if (!peer) {
114 rcu_read_unlock();
62 rxrpc_free_skb(skb); 115 rxrpc_free_skb(skb);
63 _leave(" [no peer]"); 116 _leave(" [no peer]");
64 return; 117 return;
@@ -66,6 +119,7 @@ void rxrpc_UDP_error_report(struct sock *sk)
66 119
67 trans = rxrpc_find_transport(local, peer); 120 trans = rxrpc_find_transport(local, peer);
68 if (!trans) { 121 if (!trans) {
122 rcu_read_unlock();
69 rxrpc_put_peer(peer); 123 rxrpc_put_peer(peer);
70 rxrpc_free_skb(skb); 124 rxrpc_free_skb(skb);
71 _leave(" [no trans]"); 125 _leave(" [no trans]");
@@ -110,6 +164,7 @@ void rxrpc_UDP_error_report(struct sock *sk)
110 } 164 }
111 } 165 }
112 166
167 rcu_read_unlock();
113 rxrpc_put_peer(peer); 168 rxrpc_put_peer(peer);
114 169
115 /* pass the transport ref to error_handler to release */ 170 /* pass the transport ref to error_handler to release */
diff --git a/net/rxrpc/peer_object.c b/net/rxrpc/peer_object.c
index 0b54cda3d8e5..7fc50dc7d333 100644
--- a/net/rxrpc/peer_object.c
+++ b/net/rxrpc/peer_object.c
@@ -1,6 +1,6 @@
1/* RxRPC remote transport endpoint management 1/* RxRPC remote transport endpoint record management
2 * 2 *
3 * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. 3 * Copyright (C) 2007, 2016 Red Hat, Inc. All Rights Reserved.
4 * Written by David Howells (dhowells@redhat.com) 4 * Written by David Howells (dhowells@redhat.com)
5 * 5 *
6 * This program is free software; you can redistribute it and/or 6 * This program is free software; you can redistribute it and/or
@@ -16,20 +16,132 @@
16#include <linux/skbuff.h> 16#include <linux/skbuff.h>
17#include <linux/udp.h> 17#include <linux/udp.h>
18#include <linux/in.h> 18#include <linux/in.h>
19#include <linux/in6.h>
20#include <linux/icmp.h>
21#include <linux/slab.h> 19#include <linux/slab.h>
20#include <linux/hashtable.h>
22#include <net/sock.h> 21#include <net/sock.h>
23#include <net/af_rxrpc.h> 22#include <net/af_rxrpc.h>
24#include <net/ip.h> 23#include <net/ip.h>
25#include <net/route.h> 24#include <net/route.h>
26#include "ar-internal.h" 25#include "ar-internal.h"
27 26
28static LIST_HEAD(rxrpc_peers); 27static DEFINE_HASHTABLE(rxrpc_peer_hash, 10);
29static DEFINE_RWLOCK(rxrpc_peer_lock); 28static DEFINE_SPINLOCK(rxrpc_peer_hash_lock);
30static DECLARE_WAIT_QUEUE_HEAD(rxrpc_peer_wq);
31 29
32static void rxrpc_destroy_peer(struct work_struct *work); 30/*
31 * Hash a peer key.
32 */
33static unsigned long rxrpc_peer_hash_key(struct rxrpc_local *local,
34 const struct sockaddr_rxrpc *srx)
35{
36 const u16 *p;
37 unsigned int i, size;
38 unsigned long hash_key;
39
40 _enter("");
41
42 hash_key = (unsigned long)local / __alignof__(*local);
43 hash_key += srx->transport_type;
44 hash_key += srx->transport_len;
45 hash_key += srx->transport.family;
46
47 switch (srx->transport.family) {
48 case AF_INET:
49 hash_key += (u16 __force)srx->transport.sin.sin_port;
50 size = sizeof(srx->transport.sin.sin_addr);
51 p = (u16 *)&srx->transport.sin.sin_addr;
52 break;
53 }
54
55 /* Step through the peer address in 16-bit portions for speed */
56 for (i = 0; i < size; i += sizeof(*p), p++)
57 hash_key += *p;
58
59 _leave(" 0x%lx", hash_key);
60 return hash_key;
61}
62
63/*
64 * Compare a peer to a key. Return -ve, 0 or +ve to indicate less than, same
65 * or greater than.
66 *
67 * Unfortunately, the primitives in linux/hashtable.h don't allow for sorted
68 * buckets and mid-bucket insertion, so we don't make full use of this
69 * information at this point.
70 */
71static long rxrpc_peer_cmp_key(const struct rxrpc_peer *peer,
72 struct rxrpc_local *local,
73 const struct sockaddr_rxrpc *srx,
74 unsigned long hash_key)
75{
76 long diff;
77
78 diff = ((peer->hash_key - hash_key) ?:
79 ((unsigned long)peer->local - (unsigned long)local) ?:
80 (peer->srx.transport_type - srx->transport_type) ?:
81 (peer->srx.transport_len - srx->transport_len) ?:
82 (peer->srx.transport.family - srx->transport.family));
83 if (diff != 0)
84 return diff;
85
86 switch (srx->transport.family) {
87 case AF_INET:
88 return ((u16 __force)peer->srx.transport.sin.sin_port -
89 (u16 __force)srx->transport.sin.sin_port) ?:
90 memcmp(&peer->srx.transport.sin.sin_addr,
91 &srx->transport.sin.sin_addr,
92 sizeof(struct in_addr));
93 default:
94 BUG();
95 }
96}
97
98/*
99 * Look up a remote transport endpoint for the specified address using RCU.
100 */
101static struct rxrpc_peer *__rxrpc_lookup_peer_rcu(
102 struct rxrpc_local *local,
103 const struct sockaddr_rxrpc *srx,
104 unsigned long hash_key)
105{
106 struct rxrpc_peer *peer;
107
108 hash_for_each_possible_rcu(rxrpc_peer_hash, peer, hash_link, hash_key) {
109 if (rxrpc_peer_cmp_key(peer, local, srx, hash_key) == 0) {
110 if (atomic_read(&peer->usage) == 0)
111 return NULL;
112 return peer;
113 }
114 }
115
116 return NULL;
117}
118
119/*
120 * Look up a remote transport endpoint for the specified address using RCU.
121 */
122struct rxrpc_peer *rxrpc_lookup_peer_rcu(struct rxrpc_local *local,
123 const struct sockaddr_rxrpc *srx)
124{
125 struct rxrpc_peer *peer;
126 unsigned long hash_key = rxrpc_peer_hash_key(local, srx);
127
128 peer = __rxrpc_lookup_peer_rcu(local, srx, hash_key);
129 if (peer) {
130 switch (srx->transport.family) {
131 case AF_INET:
132 _net("PEER %d {%d,%u,%pI4+%hu}",
133 peer->debug_id,
134 peer->srx.transport_type,
135 peer->srx.transport.family,
136 &peer->srx.transport.sin.sin_addr,
137 ntohs(peer->srx.transport.sin.sin_port));
138 break;
139 }
140
141 _leave(" = %p {u=%d}", peer, atomic_read(&peer->usage));
142 }
143 return peer;
144}
33 145
34/* 146/*
35 * assess the MTU size for the network interface through which this peer is 147 * assess the MTU size for the network interface through which this peer is
@@ -58,10 +170,9 @@ static void rxrpc_assess_MTU_size(struct rxrpc_peer *peer)
58} 170}
59 171
60/* 172/*
61 * allocate a new peer 173 * Allocate a peer.
62 */ 174 */
63static struct rxrpc_peer *rxrpc_alloc_peer(struct sockaddr_rxrpc *srx, 175struct rxrpc_peer *rxrpc_alloc_peer(struct rxrpc_local *local, gfp_t gfp)
64 gfp_t gfp)
65{ 176{
66 struct rxrpc_peer *peer; 177 struct rxrpc_peer *peer;
67 178
@@ -69,12 +180,32 @@ static struct rxrpc_peer *rxrpc_alloc_peer(struct sockaddr_rxrpc *srx,
69 180
70 peer = kzalloc(sizeof(struct rxrpc_peer), gfp); 181 peer = kzalloc(sizeof(struct rxrpc_peer), gfp);
71 if (peer) { 182 if (peer) {
72 INIT_WORK(&peer->destroyer, &rxrpc_destroy_peer); 183 atomic_set(&peer->usage, 1);
73 INIT_LIST_HEAD(&peer->link); 184 peer->local = local;
74 INIT_LIST_HEAD(&peer->error_targets); 185 INIT_LIST_HEAD(&peer->error_targets);
75 spin_lock_init(&peer->lock); 186 spin_lock_init(&peer->lock);
76 atomic_set(&peer->usage, 1);
77 peer->debug_id = atomic_inc_return(&rxrpc_debug_id); 187 peer->debug_id = atomic_inc_return(&rxrpc_debug_id);
188 }
189
190 _leave(" = %p", peer);
191 return peer;
192}
193
194/*
195 * Set up a new peer.
196 */
197static struct rxrpc_peer *rxrpc_create_peer(struct rxrpc_local *local,
198 struct sockaddr_rxrpc *srx,
199 unsigned long hash_key,
200 gfp_t gfp)
201{
202 struct rxrpc_peer *peer;
203
204 _enter("");
205
206 peer = rxrpc_alloc_peer(local, gfp);
207 if (peer) {
208 peer->hash_key = hash_key;
78 memcpy(&peer->srx, srx, sizeof(*srx)); 209 memcpy(&peer->srx, srx, sizeof(*srx));
79 210
80 rxrpc_assess_MTU_size(peer); 211 rxrpc_assess_MTU_size(peer);
@@ -105,11 +236,11 @@ static struct rxrpc_peer *rxrpc_alloc_peer(struct sockaddr_rxrpc *srx,
105/* 236/*
106 * obtain a remote transport endpoint for the specified address 237 * obtain a remote transport endpoint for the specified address
107 */ 238 */
108struct rxrpc_peer *rxrpc_get_peer(struct sockaddr_rxrpc *srx, gfp_t gfp) 239struct rxrpc_peer *rxrpc_lookup_peer(struct rxrpc_local *local,
240 struct sockaddr_rxrpc *srx, gfp_t gfp)
109{ 241{
110 struct rxrpc_peer *peer, *candidate; 242 struct rxrpc_peer *peer, *candidate;
111 const char *new = "old"; 243 unsigned long hash_key = rxrpc_peer_hash_key(local, srx);
112 int usage;
113 244
114 _enter("{%d,%d,%pI4+%hu}", 245 _enter("{%d,%d,%pI4+%hu}",
115 srx->transport_type, 246 srx->transport_type,
@@ -118,188 +249,60 @@ struct rxrpc_peer *rxrpc_get_peer(struct sockaddr_rxrpc *srx, gfp_t gfp)
118 ntohs(srx->transport.sin.sin_port)); 249 ntohs(srx->transport.sin.sin_port));
119 250
120 /* search the peer list first */ 251 /* search the peer list first */
121 read_lock_bh(&rxrpc_peer_lock); 252 rcu_read_lock();
122 list_for_each_entry(peer, &rxrpc_peers, link) { 253 peer = __rxrpc_lookup_peer_rcu(local, srx, hash_key);
123 _debug("check PEER %d { u=%d t=%d l=%d }", 254 if (peer && !rxrpc_get_peer_maybe(peer))
124 peer->debug_id, 255 peer = NULL;
125 atomic_read(&peer->usage), 256 rcu_read_unlock();
126 peer->srx.transport_type, 257
127 peer->srx.transport_len); 258 if (!peer) {
128 259 /* The peer is not yet present in hash - create a candidate
129 if (atomic_read(&peer->usage) > 0 && 260 * for a new record and then redo the search.
130 peer->srx.transport_type == srx->transport_type && 261 */
131 peer->srx.transport_len == srx->transport_len && 262 candidate = rxrpc_create_peer(local, srx, hash_key, gfp);
132 memcmp(&peer->srx.transport, 263 if (!candidate) {
133 &srx->transport, 264 _leave(" = NULL [nomem]");
134 srx->transport_len) == 0) 265 return NULL;
135 goto found_extant_peer; 266 }
136 }
137 read_unlock_bh(&rxrpc_peer_lock);
138
139 /* not yet present - create a candidate for a new record and then
140 * redo the search */
141 candidate = rxrpc_alloc_peer(srx, gfp);
142 if (!candidate) {
143 _leave(" = -ENOMEM");
144 return ERR_PTR(-ENOMEM);
145 }
146 267
147 write_lock_bh(&rxrpc_peer_lock); 268 spin_lock(&rxrpc_peer_hash_lock);
148 269
149 list_for_each_entry(peer, &rxrpc_peers, link) { 270 /* Need to check that we aren't racing with someone else */
150 if (atomic_read(&peer->usage) > 0 && 271 peer = __rxrpc_lookup_peer_rcu(local, srx, hash_key);
151 peer->srx.transport_type == srx->transport_type && 272 if (peer && !rxrpc_get_peer_maybe(peer))
152 peer->srx.transport_len == srx->transport_len && 273 peer = NULL;
153 memcmp(&peer->srx.transport, 274 if (!peer)
154 &srx->transport, 275 hash_add_rcu(rxrpc_peer_hash,
155 srx->transport_len) == 0) 276 &candidate->hash_link, hash_key);
156 goto found_extant_second;
157 }
158 277
159 /* we can now add the new candidate to the list */ 278 spin_unlock(&rxrpc_peer_hash_lock);
160 peer = candidate;
161 candidate = NULL;
162 usage = atomic_read(&peer->usage);
163 279
164 list_add_tail(&peer->link, &rxrpc_peers); 280 if (peer)
165 write_unlock_bh(&rxrpc_peer_lock); 281 kfree(candidate);
166 new = "new"; 282 else
283 peer = candidate;
284 }
167 285
168success: 286 _net("PEER %d {%d,%pI4+%hu}",
169 _net("PEER %s %d {%d,%u,%pI4+%hu}",
170 new,
171 peer->debug_id, 287 peer->debug_id,
172 peer->srx.transport_type, 288 peer->srx.transport_type,
173 peer->srx.transport.family,
174 &peer->srx.transport.sin.sin_addr, 289 &peer->srx.transport.sin.sin_addr,
175 ntohs(peer->srx.transport.sin.sin_port)); 290 ntohs(peer->srx.transport.sin.sin_port));
176 291
177 _leave(" = %p {u=%d}", peer, usage); 292 _leave(" = %p {u=%d}", peer, atomic_read(&peer->usage));
178 return peer;
179
180 /* we found the peer in the list immediately */
181found_extant_peer:
182 usage = atomic_inc_return(&peer->usage);
183 read_unlock_bh(&rxrpc_peer_lock);
184 goto success;
185
186 /* we found the peer on the second time through the list */
187found_extant_second:
188 usage = atomic_inc_return(&peer->usage);
189 write_unlock_bh(&rxrpc_peer_lock);
190 kfree(candidate);
191 goto success;
192}
193
194/*
195 * find the peer associated with a packet
196 */
197struct rxrpc_peer *rxrpc_find_peer(struct rxrpc_local *local,
198 __be32 addr, __be16 port)
199{
200 struct rxrpc_peer *peer;
201
202 _enter("");
203
204 /* search the peer list */
205 read_lock_bh(&rxrpc_peer_lock);
206
207 if (local->srx.transport.family == AF_INET &&
208 local->srx.transport_type == SOCK_DGRAM
209 ) {
210 list_for_each_entry(peer, &rxrpc_peers, link) {
211 if (atomic_read(&peer->usage) > 0 &&
212 peer->srx.transport_type == SOCK_DGRAM &&
213 peer->srx.transport.family == AF_INET &&
214 peer->srx.transport.sin.sin_port == port &&
215 peer->srx.transport.sin.sin_addr.s_addr == addr)
216 goto found_UDP_peer;
217 }
218
219 goto new_UDP_peer;
220 }
221
222 read_unlock_bh(&rxrpc_peer_lock);
223 _leave(" = -EAFNOSUPPORT");
224 return ERR_PTR(-EAFNOSUPPORT);
225
226found_UDP_peer:
227 _net("Rx UDP DGRAM from peer %d", peer->debug_id);
228 atomic_inc(&peer->usage);
229 read_unlock_bh(&rxrpc_peer_lock);
230 _leave(" = %p", peer);
231 return peer; 293 return peer;
232
233new_UDP_peer:
234 _net("Rx UDP DGRAM from NEW peer");
235 read_unlock_bh(&rxrpc_peer_lock);
236 _leave(" = -EBUSY [new]");
237 return ERR_PTR(-EBUSY);
238} 294}
239 295
240/* 296/*
241 * release a remote transport endpoint 297 * Discard a ref on a remote peer record.
242 */ 298 */
243void rxrpc_put_peer(struct rxrpc_peer *peer) 299void __rxrpc_put_peer(struct rxrpc_peer *peer)
244{ 300{
245 _enter("%p{u=%d}", peer, atomic_read(&peer->usage)); 301 ASSERT(list_empty(&peer->error_targets));
246 302
247 ASSERTCMP(atomic_read(&peer->usage), >, 0); 303 spin_lock(&rxrpc_peer_hash_lock);
248 304 hash_del_rcu(&peer->hash_link);
249 if (likely(!atomic_dec_and_test(&peer->usage))) { 305 spin_unlock(&rxrpc_peer_hash_lock);
250 _leave(" [in use]");
251 return;
252 }
253
254 rxrpc_queue_work(&peer->destroyer);
255 _leave("");
256}
257
258/*
259 * destroy a remote transport endpoint
260 */
261static void rxrpc_destroy_peer(struct work_struct *work)
262{
263 struct rxrpc_peer *peer =
264 container_of(work, struct rxrpc_peer, destroyer);
265
266 _enter("%p{%d}", peer, atomic_read(&peer->usage));
267
268 write_lock_bh(&rxrpc_peer_lock);
269 list_del(&peer->link);
270 write_unlock_bh(&rxrpc_peer_lock);
271
272 _net("DESTROY PEER %d", peer->debug_id);
273 kfree(peer);
274
275 if (list_empty(&rxrpc_peers))
276 wake_up_all(&rxrpc_peer_wq);
277 _leave("");
278}
279
280/*
281 * preemptively destroy all the peer records from a transport endpoint rather
282 * than waiting for them to time out
283 */
284void __exit rxrpc_destroy_all_peers(void)
285{
286 DECLARE_WAITQUEUE(myself,current);
287
288 _enter("");
289
290 /* we simply have to wait for them to go away */
291 if (!list_empty(&rxrpc_peers)) {
292 set_current_state(TASK_UNINTERRUPTIBLE);
293 add_wait_queue(&rxrpc_peer_wq, &myself);
294
295 while (!list_empty(&rxrpc_peers)) {
296 schedule();
297 set_current_state(TASK_UNINTERRUPTIBLE);
298 }
299
300 remove_wait_queue(&rxrpc_peer_wq, &myself);
301 set_current_state(TASK_RUNNING);
302 }
303 306
304 _leave(""); 307 kfree_rcu(peer, rcu);
305} 308}
diff --git a/net/rxrpc/transport.c b/net/rxrpc/transport.c
index a1b65183b07d..d33387dec0ce 100644
--- a/net/rxrpc/transport.c
+++ b/net/rxrpc/transport.c
@@ -121,7 +121,7 @@ struct rxrpc_transport *rxrpc_get_transport(struct rxrpc_local *local,
121 usage = atomic_read(&trans->usage); 121 usage = atomic_read(&trans->usage);
122 122
123 rxrpc_get_local(trans->local); 123 rxrpc_get_local(trans->local);
124 atomic_inc(&trans->peer->usage); 124 rxrpc_get_peer(trans->peer);
125 list_add_tail(&trans->link, &rxrpc_transports); 125 list_add_tail(&trans->link, &rxrpc_transports);
126 write_unlock_bh(&rxrpc_transport_lock); 126 write_unlock_bh(&rxrpc_transport_lock);
127 new = "new"; 127 new = "new";
diff --git a/net/rxrpc/utils.c b/net/rxrpc/utils.c
new file mode 100644
index 000000000000..f28122a15a24
--- /dev/null
+++ b/net/rxrpc/utils.c
@@ -0,0 +1,41 @@
1/* Utility routines
2 *
3 * Copyright (C) 2015 Red Hat, Inc. All Rights Reserved.
4 * Written by David Howells (dhowells@redhat.com)
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public Licence
8 * as published by the Free Software Foundation; either version
9 * 2 of the Licence, or (at your option) any later version.
10 */
11
12#include <linux/ip.h>
13#include <linux/udp.h>
14#include "ar-internal.h"
15
16/*
17 * Set up an RxRPC address from a socket buffer.
18 */
19void rxrpc_get_addr_from_skb(struct rxrpc_local *local,
20 const struct sk_buff *skb,
21 struct sockaddr_rxrpc *srx)
22{
23 memset(srx, 0, sizeof(*srx));
24 srx->transport_type = local->srx.transport_type;
25 srx->transport.family = local->srx.transport.family;
26
27 /* Can we see an ipv4 UDP packet on an ipv6 UDP socket? and vice
28 * versa?
29 */
30 switch (srx->transport.family) {
31 case AF_INET:
32 srx->transport.sin.sin_port = udp_hdr(skb)->source;
33 srx->transport_len = sizeof(struct sockaddr_in);
34 memcpy(&srx->transport.sin.sin_addr, &ip_hdr(skb)->saddr,
35 sizeof(struct in_addr));
36 break;
37
38 default:
39 BUG();
40 }
41}