summaryrefslogtreecommitdiffstats
path: root/net/rxrpc/conn_object.c
diff options
context:
space:
mode:
authorDavid Howells2016-07-01 08:51:50 +0200
committerDavid Howells2016-07-06 11:51:14 +0200
commit8496af50eb385c1cadff9ad396fd5359e96b6c27 (patch)
treeea5adee605917e9b7b1d3b809dc2458cbd07c271 /net/rxrpc/conn_object.c
parentrcu: Suppress sparse warnings for rcu_dereference_raw() (diff)
downloadkernel-qcow2-linux-8496af50eb385c1cadff9ad396fd5359e96b6c27.tar.gz
kernel-qcow2-linux-8496af50eb385c1cadff9ad396fd5359e96b6c27.tar.xz
kernel-qcow2-linux-8496af50eb385c1cadff9ad396fd5359e96b6c27.zip
rxrpc: Use RCU to access a peer's service connection tree
Move to using RCU access to a peer's service connection tree when routing an incoming packet. This is done using a seqlock to trigger retrying of the tree walk if a change happened. Further, we no longer get a ref on the connection looked up in the data_ready handler unless we queue the connection's work item - and then only if the refcount > 0. Note that I'm avoiding the use of a hash table for service connections because each service connection is addressed by a 62-bit number (constructed from epoch and connection ID >> 2) that would allow the client to engage in bucket stuffing, given knowledge of the hash algorithm. Peers, however, are hashed as the network address is less controllable by the client. The total number of peers will also be limited in a future commit. Signed-off-by: David Howells <dhowells@redhat.com>
Diffstat (limited to 'net/rxrpc/conn_object.c')
-rw-r--r--net/rxrpc/conn_object.c65
1 files changed, 27 insertions, 38 deletions
diff --git a/net/rxrpc/conn_object.c b/net/rxrpc/conn_object.c
index 130713869a16..896d84493a05 100644
--- a/net/rxrpc/conn_object.c
+++ b/net/rxrpc/conn_object.c
@@ -15,7 +15,6 @@
#include <linux/slab.h>
#include <linux/net.h>
#include <linux/skbuff.h>
-#include <linux/crypto.h>
#include <net/sock.h>
#include <net/af_rxrpc.h>
#include "ar-internal.h"
@@ -64,24 +63,30 @@ struct rxrpc_connection *rxrpc_alloc_connection(gfp_t gfp)
}
/*
- * find a connection based on transport and RxRPC connection ID for an incoming
- * packet
+ * Look up a connection in the cache by protocol parameters.
+ *
+ * If successful, a pointer to the connection is returned, but no ref is taken.
+ * NULL is returned if there is no match.
+ *
+ * The caller must be holding the RCU read lock.
*/
-struct rxrpc_connection *rxrpc_find_connection(struct rxrpc_local *local,
- struct sk_buff *skb)
+struct rxrpc_connection *rxrpc_find_connection_rcu(struct rxrpc_local *local,
+ 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;
- _enter(",{%x,%x}", sp->hdr.cid, sp->hdr.flags);
+ _enter(",%x", sp->hdr.cid & RXRPC_CIDMASK);
if (rxrpc_extract_addr_from_skb(&srx, skb) < 0)
goto not_found;
+ k.epoch = sp->hdr.epoch;
+ k.cid = sp->hdr.cid & RXRPC_CIDMASK;
+
/* 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",
@@ -101,32 +106,23 @@ struct rxrpc_connection *rxrpc_find_connection(struct rxrpc_local *local,
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 (k.epoch < conn->proto.epoch)
- p = p->rb_left;
- else if (k.epoch > conn->proto.epoch)
- p = p->rb_right;
- else if (k.cid < conn->proto.cid)
- p = p->rb_left;
- else if (k.cid > conn->proto.cid)
- p = p->rb_right;
- else
- goto found_service_conn;
- }
- read_unlock_bh(&peer->conn_lock);
+ conn = rxrpc_find_service_conn_rcu(peer, skb);
+ if (!conn || atomic_read(&conn->usage) == 0)
+ goto not_found;
+ _leave(" = %p", conn);
+ return conn;
} else {
+ /* Look up client connections by connection ID alone as their
+ * IDs are unique for this machine.
+ */
conn = idr_find(&rxrpc_client_conn_ids,
- k.cid >> RXRPC_CIDSHIFT);
- if (!conn ||
- conn->proto.epoch != k.epoch ||
+ sp->hdr.cid >> RXRPC_CIDSHIFT);
+ if (!conn || atomic_read(&conn->usage) == 0) {
+ _debug("no conn");
+ goto not_found;
+ }
+
+ if (conn->proto.epoch != k.epoch ||
conn->params.local != local)
goto not_found;
@@ -143,7 +139,6 @@ struct rxrpc_connection *rxrpc_find_connection(struct rxrpc_local *local,
BUG();
}
- conn = rxrpc_get_connection_maybe(conn);
_leave(" = %p", conn);
return conn;
}
@@ -151,12 +146,6 @@ struct rxrpc_connection *rxrpc_find_connection(struct rxrpc_local *local,
not_found:
_leave(" = NULL");
return NULL;
-
-found_service_conn:
- conn = rxrpc_get_connection_maybe(conn);
- read_unlock_bh(&peer->conn_lock);
- _leave(" = %p", conn);
- return conn;
}
/*