diff options
author | David Howells <dhowells@redhat.com> | 2016-04-04 13:00:32 (GMT) |
---|---|---|
committer | David Howells <dhowells@redhat.com> | 2016-06-15 09:12:33 (GMT) |
commit | be6e6707f6eec2048d9be608bc0ceecde5bd4cef (patch) | |
tree | da92ce7ff67efa365ba27ddb5742bdc3698e0836 /net/rxrpc/peer_event.c | |
parent | d9fa17ef9f084c755332898c8243a396ea02d73e (diff) | |
download | linux-be6e6707f6eec2048d9be608bc0ceecde5bd4cef.tar.xz |
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>
Diffstat (limited to 'net/rxrpc/peer_event.c')
-rw-r--r-- | net/rxrpc/peer_event.c | 59 |
1 files changed, 57 insertions, 2 deletions
diff --git a/net/rxrpc/peer_event.c b/net/rxrpc/peer_event.c index 3e82d6f..24f5ec0 100644 --- a/net/rxrpc/peer_event.c +++ b/net/rxrpc/peer_event.c @@ -23,6 +23,55 @@ #include "ar-internal.h" /* + * Find the peer associated with an ICMP packet. + */ +static struct rxrpc_peer *rxrpc_lookup_peer_icmp_rcu(struct rxrpc_local *local, + const struct sk_buff *skb) +{ + struct sock_exterr_skb *serr = SKB_EXT_ERR(skb); + struct sockaddr_rxrpc srx; + + _enter(""); + + memset(&srx, 0, sizeof(srx)); + srx.transport_type = local->srx.transport_type; + srx.transport.family = local->srx.transport.family; + + /* Can we see an ICMP4 packet on an ICMP6 listening socket? and vice + * versa? + */ + switch (srx.transport.family) { + case AF_INET: + srx.transport.sin.sin_port = serr->port; + srx.transport_len = sizeof(struct sockaddr_in); + switch (serr->ee.ee_origin) { + case SO_EE_ORIGIN_ICMP: + _net("Rx ICMP"); + memcpy(&srx.transport.sin.sin_addr, + skb_network_header(skb) + serr->addr_offset, + sizeof(struct in_addr)); + break; + case SO_EE_ORIGIN_ICMP6: + _net("Rx ICMP6 on v4 sock"); + memcpy(&srx.transport.sin.sin_addr, + skb_network_header(skb) + serr->addr_offset + 12, + sizeof(struct in_addr)); + break; + default: + memcpy(&srx.transport.sin.sin_addr, &ip_hdr(skb)->saddr, + sizeof(struct in_addr)); + break; + } + break; + + default: + BUG(); + } + + return rxrpc_lookup_peer_rcu(local, &srx); +} + +/* * handle an error received on the local endpoint */ void rxrpc_UDP_error_report(struct sock *sk) @@ -57,8 +106,12 @@ void rxrpc_UDP_error_report(struct sock *sk) _net("Rx UDP Error from %pI4:%hu", &addr, ntohs(port)); _debug("Msg l:%d d:%d", skb->len, skb->data_len); - peer = rxrpc_find_peer(local, addr, port); - if (IS_ERR(peer)) { + rcu_read_lock(); + peer = rxrpc_lookup_peer_icmp_rcu(local, skb); + if (peer && !rxrpc_get_peer_maybe(peer)) + peer = NULL; + if (!peer) { + rcu_read_unlock(); rxrpc_free_skb(skb); _leave(" [no peer]"); return; @@ -66,6 +119,7 @@ void rxrpc_UDP_error_report(struct sock *sk) trans = rxrpc_find_transport(local, peer); if (!trans) { + rcu_read_unlock(); rxrpc_put_peer(peer); rxrpc_free_skb(skb); _leave(" [no trans]"); @@ -110,6 +164,7 @@ void rxrpc_UDP_error_report(struct sock *sk) } } + rcu_read_unlock(); rxrpc_put_peer(peer); /* pass the transport ref to error_handler to release */ |