rxrpc: Make service call handling more robust
Make the following changes to improve the robustness of the code that sets
up a new service call:
(1) Cache the rxrpc_sock struct obtained in rxrpc_data_ready() to do a
service ID check and pass that along to rxrpc_new_incoming_call().
This means that I can remove the check from rxrpc_new_incoming_call()
without the need to worry about the socket attached to the local
endpoint getting replaced - which would invalidate the check.
(2) Cache the rxrpc_peer struct, thereby allowing the peer search to be
done once. The peer is passed to rxrpc_new_incoming_call(), thereby
saving the need to repeat the search.
This also reduces the possibility of rxrpc_publish_service_conn()
BUG()'ing due to the detection of a duplicate connection, despite the
initial search done by rxrpc_find_connection_rcu() having turned up
nothing.
This BUG() shouldn't ever get hit since rxrpc_data_ready() *should* be
non-reentrant and the result of the initial search should still hold
true, but it has proven possible to hit.
I *think* this may be due to __rxrpc_lookup_peer_rcu() cutting short
the iteration over the hash table if it finds a matching peer with a
zero usage count, but I don't know for sure since it's only ever been
hit once that I know of.
Another possibility is that a bug in rxrpc_data_ready() that checked
the wrong byte in the header for the RXRPC_CLIENT_INITIATED flag
might've let through a packet that caused a spurious and invalid call
to be set up. That is addressed in another patch.
(3) Fix __rxrpc_lookup_peer_rcu() to skip peer records that have a zero
usage count rather than stopping and returning not found, just in case
there's another peer record behind it in the bucket.
(4) Don't search the peer records in rxrpc_alloc_incoming_call(), but
rather either use the peer cached in (2) or, if one wasn't found,
preemptively install a new one.
Fixes: 8496af50eb
("rxrpc: Use RCU to access a peer's service connection tree")
Signed-off-by: David Howells <dhowells@redhat.com>
This commit is contained in:
@@ -124,11 +124,9 @@ static struct rxrpc_peer *__rxrpc_lookup_peer_rcu(
|
||||
struct rxrpc_net *rxnet = local->rxnet;
|
||||
|
||||
hash_for_each_possible_rcu(rxnet->peer_hash, peer, hash_link, hash_key) {
|
||||
if (rxrpc_peer_cmp_key(peer, local, srx, hash_key) == 0) {
|
||||
if (atomic_read(&peer->usage) == 0)
|
||||
return NULL;
|
||||
if (rxrpc_peer_cmp_key(peer, local, srx, hash_key) == 0 &&
|
||||
atomic_read(&peer->usage) > 0)
|
||||
return peer;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
@@ -299,34 +297,23 @@ static struct rxrpc_peer *rxrpc_create_peer(struct rxrpc_local *local,
|
||||
}
|
||||
|
||||
/*
|
||||
* Set up a new incoming peer. The address is prestored in the preallocated
|
||||
* peer.
|
||||
* Set up a new incoming peer. There shouldn't be any other matching peers
|
||||
* since we've already done a search in the list from the non-reentrant context
|
||||
* (the data_ready handler) that is the only place we can add new peers.
|
||||
*/
|
||||
struct rxrpc_peer *rxrpc_lookup_incoming_peer(struct rxrpc_local *local,
|
||||
struct rxrpc_peer *prealloc)
|
||||
void rxrpc_new_incoming_peer(struct rxrpc_local *local, struct rxrpc_peer *peer)
|
||||
{
|
||||
struct rxrpc_peer *peer;
|
||||
struct rxrpc_net *rxnet = local->rxnet;
|
||||
unsigned long hash_key;
|
||||
|
||||
hash_key = rxrpc_peer_hash_key(local, &prealloc->srx);
|
||||
prealloc->local = local;
|
||||
rxrpc_init_peer(prealloc, hash_key);
|
||||
hash_key = rxrpc_peer_hash_key(local, &peer->srx);
|
||||
peer->local = local;
|
||||
rxrpc_init_peer(peer, hash_key);
|
||||
|
||||
spin_lock(&rxnet->peer_hash_lock);
|
||||
|
||||
/* Need to check that we aren't racing with someone else */
|
||||
peer = __rxrpc_lookup_peer_rcu(local, &prealloc->srx, hash_key);
|
||||
if (peer && !rxrpc_get_peer_maybe(peer))
|
||||
peer = NULL;
|
||||
if (!peer) {
|
||||
peer = prealloc;
|
||||
hash_add_rcu(rxnet->peer_hash, &peer->hash_link, hash_key);
|
||||
list_add_tail(&peer->keepalive_link, &rxnet->peer_keepalive_new);
|
||||
}
|
||||
|
||||
hash_add_rcu(rxnet->peer_hash, &peer->hash_link, hash_key);
|
||||
list_add_tail(&peer->keepalive_link, &rxnet->peer_keepalive_new);
|
||||
spin_unlock(&rxnet->peer_hash_lock);
|
||||
return peer;
|
||||
}
|
||||
|
||||
/*
|
||||
|
Reference in New Issue
Block a user