xref: /openbmc/linux/net/rxrpc/conn_service.c (revision 0cb7eafe)
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