Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit be6e6707 authored by David Howells's avatar David Howells
Browse files

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: default avatarDavid Howells <dhowells@redhat.com>
parent d9fa17ef
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -20,7 +20,8 @@ af-rxrpc-y := \
	recvmsg.o \
	security.o \
	skbuff.o \
	transport.o
	transport.o \
	utils.o

af-rxrpc-$(CONFIG_PROC_FS) += proc.o
af-rxrpc-$(CONFIG_RXKAD) += rxkad.o
+1 −2
Original line number Diff line number Diff line
@@ -244,7 +244,7 @@ struct rxrpc_transport *rxrpc_name_to_transport(struct rxrpc_sock *rx,
		return ERR_PTR(-EAFNOSUPPORT);

	/* find a remote transport endpoint from the local one */
	peer = rxrpc_get_peer(srx, gfp);
	peer = rxrpc_lookup_peer(rx->local, srx, gfp);
	if (IS_ERR(peer))
		return ERR_CAST(peer);

@@ -835,7 +835,6 @@ static void __exit af_rxrpc_exit(void)
	rxrpc_destroy_all_calls();
	rxrpc_destroy_all_connections();
	rxrpc_destroy_all_transports();
	rxrpc_destroy_all_peers();
	rxrpc_destroy_all_locals();

	ASSERTCMP(atomic_read(&rxrpc_n_skbs), ==, 0);
+37 −9
Original line number Diff line number Diff line
@@ -9,7 +9,9 @@
 * 2 of the License, or (at your option) any later version.
 */

#include <linux/atomic.h>
#include <net/sock.h>
#include <net/af_rxrpc.h>
#include <rxrpc/packet.h>

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

/*
 * RxRPC remote transport endpoint definition
 * - matched by remote port, address and protocol type
 * - holds the connection ID counter for connections between the two endpoints
 * - matched by local endpoint, remote port, address and protocol type
 */
struct rxrpc_peer {
	struct work_struct	destroyer;	/* peer destroyer */
	struct list_head	link;		/* link in master peer list */
	struct rcu_head		rcu;		/* This must be first */
	atomic_t		usage;
	unsigned long		hash_key;
	struct hlist_node	hash_link;
	struct rxrpc_local	*local;
	struct list_head	error_targets;	/* targets for net error distribution */
	spinlock_t		lock;		/* access lock */
	atomic_t		usage;
	unsigned int		if_mtu;		/* interface MTU for this peer */
	unsigned int		mtu;		/* network MTU for this peer */
	unsigned int		maxdata;	/* data size (MTU - hdrsize) */
@@ -611,10 +614,29 @@ void rxrpc_UDP_error_handler(struct work_struct *);
/*
 * peer_object.c
 */
struct rxrpc_peer *rxrpc_get_peer(struct sockaddr_rxrpc *, gfp_t);
void rxrpc_put_peer(struct rxrpc_peer *);
struct rxrpc_peer *rxrpc_find_peer(struct rxrpc_local *, __be32, __be16);
void __exit rxrpc_destroy_all_peers(void);
struct rxrpc_peer *rxrpc_lookup_peer_rcu(struct rxrpc_local *,
					 const struct sockaddr_rxrpc *);
struct rxrpc_peer *rxrpc_lookup_peer(struct rxrpc_local *,
				     struct sockaddr_rxrpc *, gfp_t);
struct rxrpc_peer *rxrpc_alloc_peer(struct rxrpc_local *, gfp_t);

static inline void rxrpc_get_peer(struct rxrpc_peer *peer)
{
	atomic_inc(&peer->usage);
}

static inline
struct rxrpc_peer *rxrpc_get_peer_maybe(struct rxrpc_peer *peer)
{
	return atomic_inc_not_zero(&peer->usage) ? peer : NULL;
}

extern void __rxrpc_put_peer(struct rxrpc_peer *peer);
static inline void rxrpc_put_peer(struct rxrpc_peer *peer)
{
	if (atomic_dec_and_test(&peer->usage))
		__rxrpc_put_peer(peer);
}

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

/*
 * utils.c
 */
void rxrpc_get_addr_from_skb(struct rxrpc_local *, const struct sk_buff *,
			     struct sockaddr_rxrpc *);

/*
 * debug tracing
 */
+1 −1
Original line number Diff line number Diff line
@@ -95,7 +95,7 @@ static int rxrpc_accept_incoming_call(struct rxrpc_local *local,
	rxrpc_new_skb(notification);
	notification->mark = RXRPC_SKB_MARK_NEW_CALL;

	peer = rxrpc_get_peer(srx, GFP_NOIO);
	peer = rxrpc_lookup_peer(local, srx, GFP_NOIO);
	if (IS_ERR(peer)) {
		_debug("no peer");
		ret = -EBUSY;
+9 −4
Original line number Diff line number Diff line
@@ -635,14 +635,16 @@ static struct rxrpc_connection *rxrpc_conn_from_local(struct rxrpc_local *local,
	struct rxrpc_peer *peer;
	struct rxrpc_transport *trans;
	struct rxrpc_connection *conn;
	struct sockaddr_rxrpc srx;

	peer = rxrpc_find_peer(local, ip_hdr(skb)->saddr,
				udp_hdr(skb)->source);
	rxrpc_get_addr_from_skb(local, skb, &srx);
	rcu_read_lock();
	peer = rxrpc_lookup_peer_rcu(local, &srx);
	if (IS_ERR(peer))
		goto cant_find_conn;
		goto cant_find_peer;

	trans = rxrpc_find_transport(local, peer);
	rxrpc_put_peer(peer);
	rcu_read_unlock();
	if (!trans)
		goto cant_find_conn;

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

	return conn;

cant_find_peer:
	rcu_read_unlock();
cant_find_conn:
	return NULL;
}
Loading