Skip to content

Commit be6e670

Browse files
committed
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 <[email protected]>
1 parent d9fa17e commit be6e670

File tree

9 files changed

+335
-203
lines changed

9 files changed

+335
-203
lines changed

net/rxrpc/Makefile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ af-rxrpc-y := \
2020
recvmsg.o \
2121
security.o \
2222
skbuff.o \
23-
transport.o
23+
transport.o \
24+
utils.o
2425

2526
af-rxrpc-$(CONFIG_PROC_FS) += proc.o
2627
af-rxrpc-$(CONFIG_RXKAD) += rxkad.o

net/rxrpc/af_rxrpc.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ struct rxrpc_transport *rxrpc_name_to_transport(struct rxrpc_sock *rx,
244244
return ERR_PTR(-EAFNOSUPPORT);
245245

246246
/* 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);
248248
if (IS_ERR(peer))
249249
return ERR_CAST(peer);
250250

@@ -835,7 +835,6 @@ static void __exit af_rxrpc_exit(void)
835835
rxrpc_destroy_all_calls();
836836
rxrpc_destroy_all_connections();
837837
rxrpc_destroy_all_transports();
838-
rxrpc_destroy_all_peers();
839838
rxrpc_destroy_all_locals();
840839

841840
ASSERTCMP(atomic_read(&rxrpc_n_skbs), ==, 0);

net/rxrpc/ar-internal.h

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@
99
* 2 of the License, or (at your option) any later version.
1010
*/
1111

12+
#include <linux/atomic.h>
1213
#include <net/sock.h>
14+
#include <net/af_rxrpc.h>
1315
#include <rxrpc/packet.h>
1416

1517
#if 0
@@ -193,15 +195,16 @@ struct rxrpc_local {
193195

194196
/*
195197
* RxRPC remote transport endpoint definition
196-
* - matched by remote port, address and protocol type
197-
* - holds the connection ID counter for connections between the two endpoints
198+
* - matched by local endpoint, remote port, address and protocol type
198199
*/
199200
struct rxrpc_peer {
200-
struct work_struct destroyer; /* peer destroyer */
201-
struct list_head link; /* link in master peer list */
201+
struct rcu_head rcu; /* This must be first */
202+
atomic_t usage;
203+
unsigned long hash_key;
204+
struct hlist_node hash_link;
205+
struct rxrpc_local *local;
202206
struct list_head error_targets; /* targets for net error distribution */
203207
spinlock_t lock; /* access lock */
204-
atomic_t usage;
205208
unsigned int if_mtu; /* interface MTU for this peer */
206209
unsigned int mtu; /* network MTU for this peer */
207210
unsigned int maxdata; /* data size (MTU - hdrsize) */
@@ -611,10 +614,29 @@ void rxrpc_UDP_error_handler(struct work_struct *);
611614
/*
612615
* peer_object.c
613616
*/
614-
struct rxrpc_peer *rxrpc_get_peer(struct sockaddr_rxrpc *, gfp_t);
615-
void rxrpc_put_peer(struct rxrpc_peer *);
616-
struct rxrpc_peer *rxrpc_find_peer(struct rxrpc_local *, __be32, __be16);
617-
void __exit rxrpc_destroy_all_peers(void);
617+
struct rxrpc_peer *rxrpc_lookup_peer_rcu(struct rxrpc_local *,
618+
const struct sockaddr_rxrpc *);
619+
struct rxrpc_peer *rxrpc_lookup_peer(struct rxrpc_local *,
620+
struct sockaddr_rxrpc *, gfp_t);
621+
struct rxrpc_peer *rxrpc_alloc_peer(struct rxrpc_local *, gfp_t);
622+
623+
static inline void rxrpc_get_peer(struct rxrpc_peer *peer)
624+
{
625+
atomic_inc(&peer->usage);
626+
}
627+
628+
static inline
629+
struct rxrpc_peer *rxrpc_get_peer_maybe(struct rxrpc_peer *peer)
630+
{
631+
return atomic_inc_not_zero(&peer->usage) ? peer : NULL;
632+
}
633+
634+
extern void __rxrpc_put_peer(struct rxrpc_peer *peer);
635+
static inline void rxrpc_put_peer(struct rxrpc_peer *peer)
636+
{
637+
if (atomic_dec_and_test(&peer->usage))
638+
__rxrpc_put_peer(peer);
639+
}
618640

619641
/*
620642
* proc.c
@@ -672,6 +694,12 @@ void __exit rxrpc_destroy_all_transports(void);
672694
struct rxrpc_transport *rxrpc_find_transport(struct rxrpc_local *,
673695
struct rxrpc_peer *);
674696

697+
/*
698+
* utils.c
699+
*/
700+
void rxrpc_get_addr_from_skb(struct rxrpc_local *, const struct sk_buff *,
701+
struct sockaddr_rxrpc *);
702+
675703
/*
676704
* debug tracing
677705
*/

net/rxrpc/call_accept.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ static int rxrpc_accept_incoming_call(struct rxrpc_local *local,
9595
rxrpc_new_skb(notification);
9696
notification->mark = RXRPC_SKB_MARK_NEW_CALL;
9797

98-
peer = rxrpc_get_peer(srx, GFP_NOIO);
98+
peer = rxrpc_lookup_peer(local, srx, GFP_NOIO);
9999
if (IS_ERR(peer)) {
100100
_debug("no peer");
101101
ret = -EBUSY;

net/rxrpc/input.c

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -635,14 +635,16 @@ static struct rxrpc_connection *rxrpc_conn_from_local(struct rxrpc_local *local,
635635
struct rxrpc_peer *peer;
636636
struct rxrpc_transport *trans;
637637
struct rxrpc_connection *conn;
638+
struct sockaddr_rxrpc srx;
638639

639-
peer = rxrpc_find_peer(local, ip_hdr(skb)->saddr,
640-
udp_hdr(skb)->source);
640+
rxrpc_get_addr_from_skb(local, skb, &srx);
641+
rcu_read_lock();
642+
peer = rxrpc_lookup_peer_rcu(local, &srx);
641643
if (IS_ERR(peer))
642-
goto cant_find_conn;
644+
goto cant_find_peer;
643645

644646
trans = rxrpc_find_transport(local, peer);
645-
rxrpc_put_peer(peer);
647+
rcu_read_unlock();
646648
if (!trans)
647649
goto cant_find_conn;
648650

@@ -652,6 +654,9 @@ static struct rxrpc_connection *rxrpc_conn_from_local(struct rxrpc_local *local,
652654
goto cant_find_conn;
653655

654656
return conn;
657+
658+
cant_find_peer:
659+
rcu_read_unlock();
655660
cant_find_conn:
656661
return NULL;
657662
}

net/rxrpc/peer_event.c

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,55 @@
2222
#include <net/ip.h>
2323
#include "ar-internal.h"
2424

25+
/*
26+
* Find the peer associated with an ICMP packet.
27+
*/
28+
static 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+
2574
/*
2675
* handle an error received on the local endpoint
2776
*/
@@ -57,15 +106,20 @@ void rxrpc_UDP_error_report(struct sock *sk)
57106
_net("Rx UDP Error from %pI4:%hu", &addr, ntohs(port));
58107
_debug("Msg l:%d d:%d", skb->len, skb->data_len);
59108

60-
peer = rxrpc_find_peer(local, addr, port);
61-
if (IS_ERR(peer)) {
109+
rcu_read_lock();
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();
62115
rxrpc_free_skb(skb);
63116
_leave(" [no peer]");
64117
return;
65118
}
66119

67120
trans = rxrpc_find_transport(local, peer);
68121
if (!trans) {
122+
rcu_read_unlock();
69123
rxrpc_put_peer(peer);
70124
rxrpc_free_skb(skb);
71125
_leave(" [no trans]");
@@ -110,6 +164,7 @@ void rxrpc_UDP_error_report(struct sock *sk)
110164
}
111165
}
112166

167+
rcu_read_unlock();
113168
rxrpc_put_peer(peer);
114169

115170
/* pass the transport ref to error_handler to release */

0 commit comments

Comments
 (0)