Skip to content

Commit 8496af5

Browse files
committed
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 <[email protected]>
1 parent 995f140 commit 8496af5

File tree

6 files changed

+224
-180
lines changed

6 files changed

+224
-180
lines changed

net/rxrpc/ar-internal.h

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
*/
1111

1212
#include <linux/atomic.h>
13+
#include <linux/seqlock.h>
1314
#include <net/sock.h>
1415
#include <net/af_rxrpc.h>
1516
#include <rxrpc/packet.h>
@@ -206,7 +207,7 @@ struct rxrpc_peer {
206207
struct hlist_head error_targets; /* targets for net error distribution */
207208
struct work_struct error_distributor;
208209
struct rb_root service_conns; /* Service connections */
209-
rwlock_t conn_lock;
210+
seqlock_t service_conn_lock;
210211
spinlock_t lock; /* access lock */
211212
unsigned int if_mtu; /* interface MTU for this peer */
212213
unsigned int mtu; /* network MTU for this peer */
@@ -559,12 +560,10 @@ extern unsigned int rxrpc_connection_expiry;
559560
extern struct list_head rxrpc_connections;
560561
extern rwlock_t rxrpc_connection_lock;
561562

562-
void rxrpc_conn_hash_proto_key(struct rxrpc_conn_proto *);
563-
void rxrpc_extract_conn_params(struct rxrpc_conn_proto *,
564-
struct rxrpc_local *, struct sk_buff *);
563+
int rxrpc_extract_addr_from_skb(struct sockaddr_rxrpc *, struct sk_buff *);
565564
struct rxrpc_connection *rxrpc_alloc_connection(gfp_t);
566-
struct rxrpc_connection *rxrpc_find_connection(struct rxrpc_local *,
567-
struct sk_buff *);
565+
struct rxrpc_connection *rxrpc_find_connection_rcu(struct rxrpc_local *,
566+
struct sk_buff *);
568567
void __rxrpc_disconnect_call(struct rxrpc_call *);
569568
void rxrpc_disconnect_call(struct rxrpc_call *);
570569
void rxrpc_put_connection(struct rxrpc_connection *);
@@ -591,16 +590,20 @@ struct rxrpc_connection *rxrpc_get_connection_maybe(struct rxrpc_connection *con
591590
return atomic_inc_not_zero(&conn->usage) ? conn : NULL;
592591
}
593592

594-
static inline void rxrpc_queue_conn(struct rxrpc_connection *conn)
593+
static inline bool rxrpc_queue_conn(struct rxrpc_connection *conn)
595594
{
596-
if (rxrpc_get_connection_maybe(conn) &&
597-
!rxrpc_queue_work(&conn->processor))
595+
if (!rxrpc_get_connection_maybe(conn))
596+
return false;
597+
if (!rxrpc_queue_work(&conn->processor))
598598
rxrpc_put_connection(conn);
599+
return true;
599600
}
600601

601602
/*
602603
* conn_service.c
603604
*/
605+
struct rxrpc_connection *rxrpc_find_service_conn_rcu(struct rxrpc_peer *,
606+
struct sk_buff *);
604607
struct rxrpc_connection *rxrpc_incoming_connection(struct rxrpc_local *,
605608
struct sockaddr_rxrpc *,
606609
struct sk_buff *);

net/rxrpc/conn_client.c

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,8 +132,6 @@ rxrpc_alloc_client_connection(struct rxrpc_conn_parameters *cp, gfp_t gfp)
132132
}
133133

134134
conn->params = *cp;
135-
conn->proto.epoch = rxrpc_epoch;
136-
conn->proto.cid = 0;
137135
conn->out_clientflag = RXRPC_CLIENT_INITIATED;
138136
conn->state = RXRPC_CONN_CLIENT;
139137

net/rxrpc/conn_object.c

Lines changed: 27 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
#include <linux/slab.h>
1616
#include <linux/net.h>
1717
#include <linux/skbuff.h>
18-
#include <linux/crypto.h>
1918
#include <net/sock.h>
2019
#include <net/af_rxrpc.h>
2120
#include "ar-internal.h"
@@ -64,24 +63,30 @@ struct rxrpc_connection *rxrpc_alloc_connection(gfp_t gfp)
6463
}
6564

6665
/*
67-
* find a connection based on transport and RxRPC connection ID for an incoming
68-
* packet
66+
* Look up a connection in the cache by protocol parameters.
67+
*
68+
* If successful, a pointer to the connection is returned, but no ref is taken.
69+
* NULL is returned if there is no match.
70+
*
71+
* The caller must be holding the RCU read lock.
6972
*/
70-
struct rxrpc_connection *rxrpc_find_connection(struct rxrpc_local *local,
71-
struct sk_buff *skb)
73+
struct rxrpc_connection *rxrpc_find_connection_rcu(struct rxrpc_local *local,
74+
struct sk_buff *skb)
7275
{
7376
struct rxrpc_connection *conn;
7477
struct rxrpc_conn_proto k;
7578
struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
7679
struct sockaddr_rxrpc srx;
7780
struct rxrpc_peer *peer;
78-
struct rb_node *p;
7981

80-
_enter(",{%x,%x}", sp->hdr.cid, sp->hdr.flags);
82+
_enter(",%x", sp->hdr.cid & RXRPC_CIDMASK);
8183

8284
if (rxrpc_extract_addr_from_skb(&srx, skb) < 0)
8385
goto not_found;
8486

87+
k.epoch = sp->hdr.epoch;
88+
k.cid = sp->hdr.cid & RXRPC_CIDMASK;
89+
8590
/* We may have to handle mixing IPv4 and IPv6 */
8691
if (srx.transport.family != local->srx.transport.family) {
8792
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,
101106
peer = rxrpc_lookup_peer_rcu(local, &srx);
102107
if (!peer)
103108
goto not_found;
104-
105-
read_lock_bh(&peer->conn_lock);
106-
107-
p = peer->service_conns.rb_node;
108-
while (p) {
109-
conn = rb_entry(p, struct rxrpc_connection, service_node);
110-
111-
_debug("maybe %x", conn->proto.cid);
112-
113-
if (k.epoch < conn->proto.epoch)
114-
p = p->rb_left;
115-
else if (k.epoch > conn->proto.epoch)
116-
p = p->rb_right;
117-
else if (k.cid < conn->proto.cid)
118-
p = p->rb_left;
119-
else if (k.cid > conn->proto.cid)
120-
p = p->rb_right;
121-
else
122-
goto found_service_conn;
123-
}
124-
read_unlock_bh(&peer->conn_lock);
109+
conn = rxrpc_find_service_conn_rcu(peer, skb);
110+
if (!conn || atomic_read(&conn->usage) == 0)
111+
goto not_found;
112+
_leave(" = %p", conn);
113+
return conn;
125114
} else {
115+
/* Look up client connections by connection ID alone as their
116+
* IDs are unique for this machine.
117+
*/
126118
conn = idr_find(&rxrpc_client_conn_ids,
127-
k.cid >> RXRPC_CIDSHIFT);
128-
if (!conn ||
129-
conn->proto.epoch != k.epoch ||
119+
sp->hdr.cid >> RXRPC_CIDSHIFT);
120+
if (!conn || atomic_read(&conn->usage) == 0) {
121+
_debug("no conn");
122+
goto not_found;
123+
}
124+
125+
if (conn->proto.epoch != k.epoch ||
130126
conn->params.local != local)
131127
goto not_found;
132128

@@ -143,20 +139,13 @@ struct rxrpc_connection *rxrpc_find_connection(struct rxrpc_local *local,
143139
BUG();
144140
}
145141

146-
conn = rxrpc_get_connection_maybe(conn);
147142
_leave(" = %p", conn);
148143
return conn;
149144
}
150145

151146
not_found:
152147
_leave(" = NULL");
153148
return NULL;
154-
155-
found_service_conn:
156-
conn = rxrpc_get_connection_maybe(conn);
157-
read_unlock_bh(&peer->conn_lock);
158-
_leave(" = %p", conn);
159-
return conn;
160149
}
161150

162151
/*

0 commit comments

Comments
 (0)