summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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}