From be6e6707f6eec2048d9be608bc0ceecde5bd4cef Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 4 Apr 2016 14:00:32 +0100 Subject: 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 --- net/rxrpc/utils.c | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 net/rxrpc/utils.c (limited to 'net/rxrpc/utils.c') 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 @@ +/* Utility routines + * + * Copyright (C) 2015 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#include +#include +#include "ar-internal.h" + +/* + * Set up an RxRPC address from a socket buffer. + */ +void rxrpc_get_addr_from_skb(struct rxrpc_local *local, + const struct sk_buff *skb, + struct sockaddr_rxrpc *srx) +{ + memset(srx, 0, sizeof(*srx)); + srx->transport_type = local->srx.transport_type; + srx->transport.family = local->srx.transport.family; + + /* Can we see an ipv4 UDP packet on an ipv6 UDP socket? and vice + * versa? + */ + switch (srx->transport.family) { + case AF_INET: + srx->transport.sin.sin_port = udp_hdr(skb)->source; + srx->transport_len = sizeof(struct sockaddr_in); + memcpy(&srx->transport.sin.sin_addr, &ip_hdr(skb)->saddr, + sizeof(struct in_addr)); + break; + + default: + BUG(); + } +} -- cgit v1.2.3-55-g7522 From d991b4a32f65076efaf78739c4a46406ca8c7e79 Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 29 Jun 2016 14:40:39 +0100 Subject: rxrpc: Move peer lookup from call-accept to new-incoming-conn Move the lookup of a peer from a call that's being accepted into the function that creates a new incoming connection. This will allow us to avoid incrementing the peer's usage count in some cases in future. Note that I haven't bother to integrate rxrpc_get_addr_from_skb() with rxrpc_extract_addr_from_skb() as I'm going to delete the former in the very near future. Signed-off-by: David Howells --- net/rxrpc/ar-internal.h | 3 ++- net/rxrpc/call_accept.c | 31 +++++++------------------------ net/rxrpc/conn_service.c | 11 ++++++++++- net/rxrpc/utils.c | 32 ++++++++++++++++++++++++++++++++ 4 files changed, 51 insertions(+), 26 deletions(-) (limited to 'net/rxrpc/utils.c') diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index e1af258a7ac9..ad48f851b40c 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h @@ -607,7 +607,7 @@ static inline void rxrpc_queue_conn(struct rxrpc_connection *conn) * conn_service.c */ struct rxrpc_connection *rxrpc_incoming_connection(struct rxrpc_local *, - struct rxrpc_peer *, + struct sockaddr_rxrpc *, struct sk_buff *); /* @@ -773,6 +773,7 @@ static inline void rxrpc_sysctl_exit(void) {} */ void rxrpc_get_addr_from_skb(struct rxrpc_local *, const struct sk_buff *, struct sockaddr_rxrpc *); +int rxrpc_extract_addr_from_skb(struct sockaddr_rxrpc *, struct sk_buff *); /* * debug tracing diff --git a/net/rxrpc/call_accept.c b/net/rxrpc/call_accept.c index 5367dbe9b96f..0b2832141bd0 100644 --- a/net/rxrpc/call_accept.c +++ b/net/rxrpc/call_accept.c @@ -75,7 +75,6 @@ static int rxrpc_accept_incoming_call(struct rxrpc_local *local, { struct rxrpc_connection *conn; struct rxrpc_skb_priv *sp, *nsp; - struct rxrpc_peer *peer; struct rxrpc_call *call; struct sk_buff *notification; int ret; @@ -94,15 +93,7 @@ static int rxrpc_accept_incoming_call(struct rxrpc_local *local, rxrpc_new_skb(notification); notification->mark = RXRPC_SKB_MARK_NEW_CALL; - peer = rxrpc_lookup_peer(local, srx, GFP_NOIO); - if (!peer) { - _debug("no peer"); - ret = -EBUSY; - goto error; - } - - conn = rxrpc_incoming_connection(local, peer, skb); - rxrpc_put_peer(peer); + conn = rxrpc_incoming_connection(local, srx, skb); if (IS_ERR(conn)) { _debug("no conn"); ret = PTR_ERR(conn); @@ -226,20 +217,8 @@ void rxrpc_accept_incoming_calls(struct rxrpc_local *local) whdr._rsvd = 0; whdr.serviceId = htons(sp->hdr.serviceId); - /* determine the remote address */ - memset(&srx, 0, sizeof(srx)); - srx.srx_family = AF_RXRPC; - srx.transport.family = local->srx.transport.family; - srx.transport_type = local->srx.transport_type; - switch (srx.transport.family) { - case AF_INET: - srx.transport_len = sizeof(struct sockaddr_in); - srx.transport.sin.sin_port = udp_hdr(skb)->source; - srx.transport.sin.sin_addr.s_addr = ip_hdr(skb)->saddr; - break; - default: - goto busy; - } + if (rxrpc_extract_addr_from_skb(&srx, skb) < 0) + goto drop; /* get the socket providing the service */ read_lock_bh(&local->services_lock); @@ -285,6 +264,10 @@ busy: rxrpc_free_skb(skb); return; +drop: + rxrpc_free_skb(skb); + return; + invalid_service: skb->priority = RX_INVALID_OPERATION; rxrpc_reject_packet(local, skb); diff --git a/net/rxrpc/conn_service.c b/net/rxrpc/conn_service.c index cdcac50cd1a8..a42b210c40a5 100644 --- a/net/rxrpc/conn_service.c +++ b/net/rxrpc/conn_service.c @@ -16,17 +16,24 @@ * get a record of an incoming connection */ struct rxrpc_connection *rxrpc_incoming_connection(struct rxrpc_local *local, - struct rxrpc_peer *peer, + struct sockaddr_rxrpc *srx, struct sk_buff *skb) { struct rxrpc_connection *conn, *candidate = NULL; struct rxrpc_skb_priv *sp = rxrpc_skb(skb); + struct rxrpc_peer *peer; struct rb_node *p, **pp; const char *new = "old"; u32 epoch, cid; _enter(""); + peer = rxrpc_lookup_peer(local, srx, GFP_NOIO); + if (!peer) { + _debug("no peer"); + return ERR_PTR(-EBUSY); + } + ASSERT(sp->hdr.flags & RXRPC_CLIENT_INITIATED); epoch = sp->hdr.epoch; @@ -58,6 +65,7 @@ struct rxrpc_connection *rxrpc_incoming_connection(struct rxrpc_local *local, * redo the search */ candidate = rxrpc_alloc_connection(GFP_NOIO); if (!candidate) { + rxrpc_put_peer(peer); _leave(" = -ENOMEM"); return ERR_PTR(-ENOMEM); } @@ -114,6 +122,7 @@ struct rxrpc_connection *rxrpc_incoming_connection(struct rxrpc_local *local, success: _net("CONNECTION %s %d {%x}", new, conn->debug_id, conn->proto.cid); + rxrpc_put_peer(peer); _leave(" = %p {u=%d}", conn, atomic_read(&conn->usage)); return conn; diff --git a/net/rxrpc/utils.c b/net/rxrpc/utils.c index f28122a15a24..d3db02ecc37f 100644 --- a/net/rxrpc/utils.c +++ b/net/rxrpc/utils.c @@ -10,6 +10,7 @@ */ #include +#include #include #include "ar-internal.h" @@ -39,3 +40,34 @@ void rxrpc_get_addr_from_skb(struct rxrpc_local *local, BUG(); } } + +/* + * Fill out a peer address from a socket buffer containing a packet. + */ +int rxrpc_extract_addr_from_skb(struct sockaddr_rxrpc *srx, struct sk_buff *skb) +{ + memset(srx, 0, sizeof(*srx)); + + switch (ntohs(skb->protocol)) { + case ETH_P_IP: + srx->transport_type = SOCK_DGRAM; + srx->transport_len = sizeof(srx->transport.sin); + srx->transport.sin.sin_family = AF_INET; + srx->transport.sin.sin_port = udp_hdr(skb)->source; + srx->transport.sin.sin_addr.s_addr = ip_hdr(skb)->saddr; + return 0; + + case ETH_P_IPV6: + srx->transport_type = SOCK_DGRAM; + srx->transport_len = sizeof(srx->transport.sin6); + srx->transport.sin6.sin6_family = AF_INET6; + srx->transport.sin6.sin6_port = udp_hdr(skb)->source; + srx->transport.sin6.sin6_addr = ipv6_hdr(skb)->saddr; + return 0; + + default: + pr_warn_ratelimited("AF_RXRPC: Unknown eth protocol %u\n", + ntohs(skb->protocol)); + return -EAFNOSUPPORT; + } +} -- cgit v1.2.3-55-g7522 From 1291e9d1084506c5cba6313ce809d7516bb5868a Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 30 Jun 2016 12:02:53 +0100 Subject: rxrpc: Move data_ready peer lookup into rxrpc_find_connection() Move the peer lookup done in input.c by data_ready into rxrpc_find_connection(). Signed-off-by: David Howells --- net/rxrpc/ar-internal.h | 3 -- net/rxrpc/conn_object.c | 73 +++++++++++++++++++++++++++++++++++++------------ net/rxrpc/input.c | 30 ++------------------ net/rxrpc/utils.c | 27 ------------------ 4 files changed, 59 insertions(+), 74 deletions(-) (limited to 'net/rxrpc/utils.c') diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index 6fdee761dd0b..0fe63baf1286 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h @@ -564,7 +564,6 @@ void rxrpc_extract_conn_params(struct rxrpc_conn_proto *, struct rxrpc_local *, struct sk_buff *); struct rxrpc_connection *rxrpc_alloc_connection(gfp_t); struct rxrpc_connection *rxrpc_find_connection(struct rxrpc_local *, - struct rxrpc_peer *, struct sk_buff *); void __rxrpc_disconnect_call(struct rxrpc_call *); void rxrpc_disconnect_call(struct rxrpc_call *); @@ -768,8 +767,6 @@ static inline void rxrpc_sysctl_exit(void) {} /* * utils.c */ -void rxrpc_get_addr_from_skb(struct rxrpc_local *, const struct sk_buff *, - struct sockaddr_rxrpc *); int rxrpc_extract_addr_from_skb(struct sockaddr_rxrpc *, struct sk_buff *); /* diff --git a/net/rxrpc/conn_object.c b/net/rxrpc/conn_object.c index 89bc6480b4e2..130713869a16 100644 --- a/net/rxrpc/conn_object.c +++ b/net/rxrpc/conn_object.c @@ -68,52 +68,91 @@ struct rxrpc_connection *rxrpc_alloc_connection(gfp_t gfp) * packet */ struct rxrpc_connection *rxrpc_find_connection(struct rxrpc_local *local, - struct rxrpc_peer *peer, struct sk_buff *skb) { struct rxrpc_connection *conn; + struct rxrpc_conn_proto k; struct rxrpc_skb_priv *sp = rxrpc_skb(skb); + struct sockaddr_rxrpc srx; + struct rxrpc_peer *peer; struct rb_node *p; - u32 epoch, cid; _enter(",{%x,%x}", sp->hdr.cid, sp->hdr.flags); - read_lock_bh(&peer->conn_lock); + if (rxrpc_extract_addr_from_skb(&srx, skb) < 0) + goto not_found; - cid = sp->hdr.cid & RXRPC_CIDMASK; - epoch = sp->hdr.epoch; + /* We may have to handle mixing IPv4 and IPv6 */ + if (srx.transport.family != local->srx.transport.family) { + pr_warn_ratelimited("AF_RXRPC: Protocol mismatch %u not %u\n", + srx.transport.family, + local->srx.transport.family); + goto not_found; + } + + k.epoch = sp->hdr.epoch; + k.cid = sp->hdr.cid & RXRPC_CIDMASK; if (sp->hdr.flags & RXRPC_CLIENT_INITIATED) { + /* We need to look up service connections by the full protocol + * parameter set. We look up the peer first as an intermediate + * step and then the connection from the peer's tree. + */ + peer = rxrpc_lookup_peer_rcu(local, &srx); + if (!peer) + goto not_found; + + read_lock_bh(&peer->conn_lock); + p = peer->service_conns.rb_node; while (p) { conn = rb_entry(p, struct rxrpc_connection, service_node); _debug("maybe %x", conn->proto.cid); - if (epoch < conn->proto.epoch) + if (k.epoch < conn->proto.epoch) p = p->rb_left; - else if (epoch > conn->proto.epoch) + else if (k.epoch > conn->proto.epoch) p = p->rb_right; - else if (cid < conn->proto.cid) + else if (k.cid < conn->proto.cid) p = p->rb_left; - else if (cid > conn->proto.cid) + else if (k.cid > conn->proto.cid) p = p->rb_right; else - goto found; + goto found_service_conn; } + read_unlock_bh(&peer->conn_lock); } else { - conn = idr_find(&rxrpc_client_conn_ids, cid >> RXRPC_CIDSHIFT); - if (conn && - conn->proto.epoch == epoch && - conn->params.peer == peer) - goto found; + conn = idr_find(&rxrpc_client_conn_ids, + k.cid >> RXRPC_CIDSHIFT); + if (!conn || + conn->proto.epoch != k.epoch || + conn->params.local != local) + goto not_found; + + peer = conn->params.peer; + switch (srx.transport.family) { + case AF_INET: + if (peer->srx.transport.sin.sin_port != + srx.transport.sin.sin_port || + peer->srx.transport.sin.sin_addr.s_addr != + srx.transport.sin.sin_addr.s_addr) + goto not_found; + break; + default: + BUG(); + } + + conn = rxrpc_get_connection_maybe(conn); + _leave(" = %p", conn); + return conn; } - read_unlock_bh(&peer->conn_lock); +not_found: _leave(" = NULL"); return NULL; -found: +found_service_conn: conn = rxrpc_get_connection_maybe(conn); read_unlock_bh(&peer->conn_lock); _leave(" = %p", conn); diff --git a/net/rxrpc/input.c b/net/rxrpc/input.c index b993f2dc5a09..c2436476f793 100644 --- a/net/rxrpc/input.c +++ b/net/rxrpc/input.c @@ -626,32 +626,6 @@ int rxrpc_extract_header(struct rxrpc_skb_priv *sp, struct sk_buff *skb) return 0; } -static struct rxrpc_connection *rxrpc_conn_from_local(struct rxrpc_local *local, - struct sk_buff *skb) -{ - struct rxrpc_peer *peer; - struct rxrpc_connection *conn; - struct sockaddr_rxrpc srx; - - rxrpc_get_addr_from_skb(local, skb, &srx); - rcu_read_lock(); - peer = rxrpc_lookup_peer_rcu(local, &srx); - if (!peer) - goto cant_find_peer; - - conn = rxrpc_find_connection(local, peer, skb); - rcu_read_unlock(); - if (!conn) - goto cant_find_conn; - - return conn; - -cant_find_peer: - rcu_read_unlock(); -cant_find_conn: - return NULL; -} - /* * handle data received on the local endpoint * - may be called in interrupt context @@ -731,7 +705,9 @@ void rxrpc_data_ready(struct sock *sk) * old-fashioned way doesn't really hurt */ struct rxrpc_connection *conn; - conn = rxrpc_conn_from_local(local, skb); + rcu_read_lock(); + conn = rxrpc_find_connection(local, skb); + rcu_read_unlock(); if (!conn) goto cant_route_call; diff --git a/net/rxrpc/utils.c b/net/rxrpc/utils.c index d3db02ecc37f..b88914d53ca5 100644 --- a/net/rxrpc/utils.c +++ b/net/rxrpc/utils.c @@ -14,33 +14,6 @@ #include #include "ar-internal.h" -/* - * Set up an RxRPC address from a socket buffer. - */ -void rxrpc_get_addr_from_skb(struct rxrpc_local *local, - const struct sk_buff *skb, - struct sockaddr_rxrpc *srx) -{ - memset(srx, 0, sizeof(*srx)); - srx->transport_type = local->srx.transport_type; - srx->transport.family = local->srx.transport.family; - - /* Can we see an ipv4 UDP packet on an ipv6 UDP socket? and vice - * versa? - */ - switch (srx->transport.family) { - case AF_INET: - srx->transport.sin.sin_port = udp_hdr(skb)->source; - srx->transport_len = sizeof(struct sockaddr_in); - memcpy(&srx->transport.sin.sin_addr, &ip_hdr(skb)->saddr, - sizeof(struct in_addr)); - break; - - default: - BUG(); - } -} - /* * Fill out a peer address from a socket buffer containing a packet. */ -- cgit v1.2.3-55-g7522