Skip to content

Commit

Permalink
rxrpc: Use RCU to access a peer's service connection tree
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
dhowells committed Jul 6, 2016
1 parent 995f140 commit 8496af5
Show file tree
Hide file tree
Showing 6 changed files with 224 additions and 180 deletions.
21 changes: 12 additions & 9 deletions net/rxrpc/ar-internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
*/

#include <linux/atomic.h>
#include <linux/seqlock.h>
#include <net/sock.h>
#include <net/af_rxrpc.h>
#include <rxrpc/packet.h>
Expand Down Expand Up @@ -206,7 +207,7 @@ struct rxrpc_peer {
struct hlist_head error_targets; /* targets for net error distribution */
struct work_struct error_distributor;
struct rb_root service_conns; /* Service connections */
rwlock_t conn_lock;
seqlock_t service_conn_lock;
spinlock_t lock; /* access lock */
unsigned int if_mtu; /* interface MTU for this peer */
unsigned int mtu; /* network MTU for this peer */
Expand Down Expand Up @@ -559,12 +560,10 @@ extern unsigned int rxrpc_connection_expiry;
extern struct list_head rxrpc_connections;
extern rwlock_t rxrpc_connection_lock;

void rxrpc_conn_hash_proto_key(struct rxrpc_conn_proto *);
void rxrpc_extract_conn_params(struct rxrpc_conn_proto *,
struct rxrpc_local *, struct sk_buff *);
int rxrpc_extract_addr_from_skb(struct sockaddr_rxrpc *, struct sk_buff *);
struct rxrpc_connection *rxrpc_alloc_connection(gfp_t);
struct rxrpc_connection *rxrpc_find_connection(struct rxrpc_local *,
struct sk_buff *);
struct rxrpc_connection *rxrpc_find_connection_rcu(struct rxrpc_local *,
struct sk_buff *);
void __rxrpc_disconnect_call(struct rxrpc_call *);
void rxrpc_disconnect_call(struct rxrpc_call *);
void rxrpc_put_connection(struct rxrpc_connection *);
Expand All @@ -591,16 +590,20 @@ struct rxrpc_connection *rxrpc_get_connection_maybe(struct rxrpc_connection *con
return atomic_inc_not_zero(&conn->usage) ? conn : NULL;
}

static inline void rxrpc_queue_conn(struct rxrpc_connection *conn)
static inline bool rxrpc_queue_conn(struct rxrpc_connection *conn)
{
if (rxrpc_get_connection_maybe(conn) &&
!rxrpc_queue_work(&conn->processor))
if (!rxrpc_get_connection_maybe(conn))
return false;
if (!rxrpc_queue_work(&conn->processor))
rxrpc_put_connection(conn);
return true;
}

/*
* conn_service.c
*/
struct rxrpc_connection *rxrpc_find_service_conn_rcu(struct rxrpc_peer *,
struct sk_buff *);
struct rxrpc_connection *rxrpc_incoming_connection(struct rxrpc_local *,
struct sockaddr_rxrpc *,
struct sk_buff *);
Expand Down
2 changes: 0 additions & 2 deletions net/rxrpc/conn_client.c
Original file line number Diff line number Diff line change
Expand Up @@ -132,8 +132,6 @@ rxrpc_alloc_client_connection(struct rxrpc_conn_parameters *cp, gfp_t gfp)
}

conn->params = *cp;
conn->proto.epoch = rxrpc_epoch;
conn->proto.cid = 0;
conn->out_clientflag = RXRPC_CLIENT_INITIATED;
conn->state = RXRPC_CONN_CLIENT;

Expand Down
65 changes: 27 additions & 38 deletions net/rxrpc/conn_object.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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",
Expand All @@ -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;

Expand All @@ -143,20 +139,13 @@ struct rxrpc_connection *rxrpc_find_connection(struct rxrpc_local *local,
BUG();
}

conn = rxrpc_get_connection_maybe(conn);
_leave(" = %p", conn);
return conn;
}

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;
}

/*
Expand Down
Loading

0 comments on commit 8496af5

Please sign in to comment.