1b4d0d230SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
27877a4a4SDavid Howells /* Service connection management
37877a4a4SDavid Howells *
47877a4a4SDavid Howells * Copyright (C) 2016 Red Hat, Inc. All Rights Reserved.
57877a4a4SDavid Howells * Written by David Howells (dhowells@redhat.com)
67877a4a4SDavid Howells */
77877a4a4SDavid Howells
87877a4a4SDavid Howells #include <linux/slab.h>
97877a4a4SDavid Howells #include "ar-internal.h"
107877a4a4SDavid Howells
117877a4a4SDavid Howells /*
128496af50SDavid Howells * Find a service connection under RCU conditions.
138496af50SDavid Howells *
148496af50SDavid Howells * We could use a hash table, but that is subject to bucket stuffing by an
158496af50SDavid Howells * attacker as the client gets to pick the epoch and cid values and would know
168496af50SDavid Howells * the hash function. So, instead, we use a hash table for the peer and from
178496af50SDavid Howells * that an rbtree to find the service connection. Under ordinary circumstances
188496af50SDavid Howells * it might be slower than a large hash table, but it is at least limited in
198496af50SDavid Howells * depth.
208496af50SDavid Howells */
rxrpc_find_service_conn_rcu(struct rxrpc_peer * peer,struct sk_buff * skb)218496af50SDavid Howells struct rxrpc_connection *rxrpc_find_service_conn_rcu(struct rxrpc_peer *peer,
228496af50SDavid Howells struct sk_buff *skb)
238496af50SDavid Howells {
248496af50SDavid Howells struct rxrpc_connection *conn = NULL;
258496af50SDavid Howells struct rxrpc_conn_proto k;
268496af50SDavid Howells struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
278496af50SDavid Howells struct rb_node *p;
28*0cb7eafeSOleg Nesterov unsigned int seq = 1;
298496af50SDavid Howells
308496af50SDavid Howells k.epoch = sp->hdr.epoch;
318496af50SDavid Howells k.cid = sp->hdr.cid & RXRPC_CIDMASK;
328496af50SDavid Howells
338496af50SDavid Howells do {
348496af50SDavid Howells /* Unfortunately, rbtree walking doesn't give reliable results
358496af50SDavid Howells * under just the RCU read lock, so we have to check for
368496af50SDavid Howells * changes.
378496af50SDavid Howells */
38*0cb7eafeSOleg Nesterov seq++; /* 2 on the 1st/lockless path, otherwise odd */
398496af50SDavid Howells read_seqbegin_or_lock(&peer->service_conn_lock, &seq);
408496af50SDavid Howells
418496af50SDavid Howells p = rcu_dereference_raw(peer->service_conns.rb_node);
428496af50SDavid Howells while (p) {
438496af50SDavid Howells conn = rb_entry(p, struct rxrpc_connection, service_node);
448496af50SDavid Howells
458496af50SDavid Howells if (conn->proto.index_key < k.index_key)
468496af50SDavid Howells p = rcu_dereference_raw(p->rb_left);
478496af50SDavid Howells else if (conn->proto.index_key > k.index_key)
488496af50SDavid Howells p = rcu_dereference_raw(p->rb_right);
498496af50SDavid Howells else
50fdade4f6SDavid Howells break;
518496af50SDavid Howells conn = NULL;
528496af50SDavid Howells }
538496af50SDavid Howells } while (need_seqretry(&peer->service_conn_lock, seq));
548496af50SDavid Howells
558496af50SDavid Howells done_seqretry(&peer->service_conn_lock, seq);
568496af50SDavid Howells _leave(" = %d", conn ? conn->debug_id : -1);
578496af50SDavid Howells return conn;
588496af50SDavid Howells }
598496af50SDavid Howells
608496af50SDavid Howells /*
618496af50SDavid Howells * Insert a service connection into a peer's tree, thereby making it a target
628496af50SDavid Howells * for incoming packets.
638496af50SDavid Howells */
rxrpc_publish_service_conn(struct rxrpc_peer * peer,struct rxrpc_connection * conn)64248f219cSDavid Howells static void rxrpc_publish_service_conn(struct rxrpc_peer *peer,
658496af50SDavid Howells struct rxrpc_connection *conn)
668496af50SDavid Howells {
678496af50SDavid Howells struct rxrpc_connection *cursor = NULL;
688496af50SDavid Howells struct rxrpc_conn_proto k = conn->proto;
698496af50SDavid Howells struct rb_node **pp, *parent;
708496af50SDavid Howells
713dd9c8b5SDavid Howells write_seqlock(&peer->service_conn_lock);
728496af50SDavid Howells
738496af50SDavid Howells pp = &peer->service_conns.rb_node;
748496af50SDavid Howells parent = NULL;
758496af50SDavid Howells while (*pp) {
768496af50SDavid Howells parent = *pp;
778496af50SDavid Howells cursor = rb_entry(parent,
788496af50SDavid Howells struct rxrpc_connection, service_node);
798496af50SDavid Howells
808496af50SDavid Howells if (cursor->proto.index_key < k.index_key)
818496af50SDavid Howells pp = &(*pp)->rb_left;
828496af50SDavid Howells else if (cursor->proto.index_key > k.index_key)
838496af50SDavid Howells pp = &(*pp)->rb_right;
848496af50SDavid Howells else
858496af50SDavid Howells goto found_extant_conn;
868496af50SDavid Howells }
878496af50SDavid Howells
888496af50SDavid Howells rb_link_node_rcu(&conn->service_node, parent, pp);
898496af50SDavid Howells rb_insert_color(&conn->service_node, &peer->service_conns);
908496af50SDavid Howells conn_published:
918496af50SDavid Howells set_bit(RXRPC_CONN_IN_SERVICE_CONNS, &conn->flags);
923dd9c8b5SDavid Howells write_sequnlock(&peer->service_conn_lock);
938496af50SDavid Howells _leave(" = %d [new]", conn->debug_id);
94248f219cSDavid Howells return;
958496af50SDavid Howells
968496af50SDavid Howells found_extant_conn:
97a0575429SDavid Howells if (refcount_read(&cursor->ref) == 0)
988496af50SDavid Howells goto replace_old_connection;
993dd9c8b5SDavid Howells write_sequnlock(&peer->service_conn_lock);
1008496af50SDavid Howells /* We should not be able to get here. rxrpc_incoming_connection() is
1018496af50SDavid Howells * called in a non-reentrant context, so there can't be a race to
1028496af50SDavid Howells * insert a new connection.
1038496af50SDavid Howells */
1048496af50SDavid Howells BUG();
1058496af50SDavid Howells
1068496af50SDavid Howells replace_old_connection:
1078496af50SDavid Howells /* The old connection is from an outdated epoch. */
1088496af50SDavid Howells _debug("replace conn");
1098496af50SDavid Howells rb_replace_node_rcu(&cursor->service_node,
1108496af50SDavid Howells &conn->service_node,
1118496af50SDavid Howells &peer->service_conns);
1128496af50SDavid Howells clear_bit(RXRPC_CONN_IN_SERVICE_CONNS, &cursor->flags);
1138496af50SDavid Howells goto conn_published;
1148496af50SDavid Howells }
1158496af50SDavid Howells
1168496af50SDavid Howells /*
11700e90712SDavid Howells * Preallocate a service connection. The connection is placed on the proc and
11800e90712SDavid Howells * reap lists so that we don't have to get the lock from BH context.
11900e90712SDavid Howells */
rxrpc_prealloc_service_connection(struct rxrpc_net * rxnet,gfp_t gfp)1202baec2c3SDavid Howells struct rxrpc_connection *rxrpc_prealloc_service_connection(struct rxrpc_net *rxnet,
1212baec2c3SDavid Howells gfp_t gfp)
12200e90712SDavid Howells {
1233cec055cSDavid Howells struct rxrpc_connection *conn = rxrpc_alloc_connection(rxnet, gfp);
12400e90712SDavid Howells
12500e90712SDavid Howells if (conn) {
12600e90712SDavid Howells /* We maintain an extra ref on the connection whilst it is on
12700e90712SDavid Howells * the rxrpc_connections list.
12800e90712SDavid Howells */
12900e90712SDavid Howells conn->state = RXRPC_CONN_SERVICE_PREALLOC;
130a0575429SDavid Howells refcount_set(&conn->ref, 2);
13100e90712SDavid Howells
13231f5f9a1SDavid Howells atomic_inc(&rxnet->nr_conns);
1332baec2c3SDavid Howells write_lock(&rxnet->conn_lock);
1342baec2c3SDavid Howells list_add_tail(&conn->link, &rxnet->service_conns);
1352baec2c3SDavid Howells list_add_tail(&conn->proc_link, &rxnet->conn_proc_list);
1362baec2c3SDavid Howells write_unlock(&rxnet->conn_lock);
137363deeabSDavid Howells
1387fa25105SDavid Howells rxrpc_see_connection(conn, rxrpc_conn_new_service);
13900e90712SDavid Howells }
14000e90712SDavid Howells
14100e90712SDavid Howells return conn;
14200e90712SDavid Howells }
14300e90712SDavid Howells
14400e90712SDavid Howells /*
145248f219cSDavid Howells * Set up an incoming connection. This is called in BH context with the RCU
146248f219cSDavid Howells * read lock held.
1477877a4a4SDavid Howells */
rxrpc_new_incoming_connection(struct rxrpc_sock * rx,struct rxrpc_connection * conn,const struct rxrpc_security * sec,struct sk_buff * skb)1484722974dSDavid Howells void rxrpc_new_incoming_connection(struct rxrpc_sock *rx,
1494722974dSDavid Howells struct rxrpc_connection *conn,
150063c60d3SDavid Howells const struct rxrpc_security *sec,
1517877a4a4SDavid Howells struct sk_buff *skb)
1527877a4a4SDavid Howells {
1537877a4a4SDavid Howells struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
1547877a4a4SDavid Howells
1557877a4a4SDavid Howells _enter("");
1567877a4a4SDavid Howells
1578496af50SDavid Howells conn->proto.epoch = sp->hdr.epoch;
1588496af50SDavid Howells conn->proto.cid = sp->hdr.cid & RXRPC_CIDMASK;
1592cc80086SDavid Howells conn->orig_service_id = sp->hdr.serviceId;
16068d6d1aeSDavid Howells conn->service_id = sp->hdr.serviceId;
1618496af50SDavid Howells conn->security_ix = sp->hdr.securityIndex;
1628496af50SDavid Howells conn->out_clientflag = 0;
163063c60d3SDavid Howells conn->security = sec;
164248f219cSDavid Howells if (conn->security_ix)
1658496af50SDavid Howells conn->state = RXRPC_CONN_SERVICE_UNSECURED;
166248f219cSDavid Howells else
167248f219cSDavid Howells conn->state = RXRPC_CONN_SERVICE;
1687877a4a4SDavid Howells
1694722974dSDavid Howells /* See if we should upgrade the service. This can only happen on the
1704722974dSDavid Howells * first packet on a new connection. Once done, it applies to all
1714722974dSDavid Howells * subsequent calls on that connection.
1724722974dSDavid Howells */
1734722974dSDavid Howells if (sp->hdr.userStatus == RXRPC_USERSTATUS_SERVICE_UPGRADE &&
1744722974dSDavid Howells conn->service_id == rx->service_upgrade.from)
1754722974dSDavid Howells conn->service_id = rx->service_upgrade.to;
1764722974dSDavid Howells
1773cec055cSDavid Howells atomic_set(&conn->active, 1);
1783cec055cSDavid Howells
1798496af50SDavid Howells /* Make the connection a target for incoming packets. */
1802cc80086SDavid Howells rxrpc_publish_service_conn(conn->peer, conn);
1817877a4a4SDavid Howells }
182001c1122SDavid Howells
183001c1122SDavid Howells /*
184001c1122SDavid Howells * Remove the service connection from the peer's tree, thereby removing it as a
185001c1122SDavid Howells * target for incoming packets.
186001c1122SDavid Howells */
rxrpc_unpublish_service_conn(struct rxrpc_connection * conn)187001c1122SDavid Howells void rxrpc_unpublish_service_conn(struct rxrpc_connection *conn)
188001c1122SDavid Howells {
1892cc80086SDavid Howells struct rxrpc_peer *peer = conn->peer;
190001c1122SDavid Howells
1913dd9c8b5SDavid Howells write_seqlock(&peer->service_conn_lock);
192001c1122SDavid Howells if (test_and_clear_bit(RXRPC_CONN_IN_SERVICE_CONNS, &conn->flags))
193001c1122SDavid Howells rb_erase(&conn->service_node, &peer->service_conns);
1943dd9c8b5SDavid Howells write_sequnlock(&peer->service_conn_lock);
195001c1122SDavid Howells }
196