diff options
author | David Howells <dhowells@redhat.com> | 2016-07-01 07:51:50 +0100 |
---|---|---|
committer | David Howells <dhowells@redhat.com> | 2016-07-06 10:51:14 +0100 |
commit | 8496af50eb385c1cadff9ad396fd5359e96b6c27 (patch) | |
tree | ea5adee605917e9b7b1d3b809dc2458cbd07c271 /net/rxrpc/conn_object.c | |
parent | 995f1405610bd8446c5be37d2ffc031a7729e406 (diff) |
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.c | 65 |
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; } /* |