xref: /openbmc/linux/net/rxrpc/conn_service.c (revision 2baec2c3)
17877a4a4SDavid Howells /* Service connection management
27877a4a4SDavid Howells  *
37877a4a4SDavid Howells  * Copyright (C) 2016 Red Hat, Inc. All Rights Reserved.
47877a4a4SDavid Howells  * Written by David Howells (dhowells@redhat.com)
57877a4a4SDavid Howells  *
67877a4a4SDavid Howells  * This program is free software; you can redistribute it and/or
77877a4a4SDavid Howells  * modify it under the terms of the GNU General Public Licence
87877a4a4SDavid Howells  * as published by the Free Software Foundation; either version
97877a4a4SDavid Howells  * 2 of the Licence, or (at your option) any later version.
107877a4a4SDavid Howells  */
117877a4a4SDavid Howells 
127877a4a4SDavid Howells #include <linux/slab.h>
137877a4a4SDavid Howells #include "ar-internal.h"
147877a4a4SDavid Howells 
157877a4a4SDavid Howells /*
168496af50SDavid Howells  * Find a service connection under RCU conditions.
178496af50SDavid Howells  *
188496af50SDavid Howells  * We could use a hash table, but that is subject to bucket stuffing by an
198496af50SDavid Howells  * attacker as the client gets to pick the epoch and cid values and would know
208496af50SDavid Howells  * the hash function.  So, instead, we use a hash table for the peer and from
218496af50SDavid Howells  * that an rbtree to find the service connection.  Under ordinary circumstances
228496af50SDavid Howells  * it might be slower than a large hash table, but it is at least limited in
238496af50SDavid Howells  * depth.
248496af50SDavid Howells  */
258496af50SDavid Howells struct rxrpc_connection *rxrpc_find_service_conn_rcu(struct rxrpc_peer *peer,
268496af50SDavid Howells 						     struct sk_buff *skb)
278496af50SDavid Howells {
288496af50SDavid Howells 	struct rxrpc_connection *conn = NULL;
298496af50SDavid Howells 	struct rxrpc_conn_proto k;
308496af50SDavid Howells 	struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
318496af50SDavid Howells 	struct rb_node *p;
328496af50SDavid Howells 	unsigned int seq = 0;
338496af50SDavid Howells 
348496af50SDavid Howells 	k.epoch	= sp->hdr.epoch;
358496af50SDavid Howells 	k.cid	= sp->hdr.cid & RXRPC_CIDMASK;
368496af50SDavid Howells 
378496af50SDavid Howells 	do {
388496af50SDavid Howells 		/* Unfortunately, rbtree walking doesn't give reliable results
398496af50SDavid Howells 		 * under just the RCU read lock, so we have to check for
408496af50SDavid Howells 		 * changes.
418496af50SDavid Howells 		 */
428496af50SDavid Howells 		read_seqbegin_or_lock(&peer->service_conn_lock, &seq);
438496af50SDavid Howells 
448496af50SDavid Howells 		p = rcu_dereference_raw(peer->service_conns.rb_node);
458496af50SDavid Howells 		while (p) {
468496af50SDavid Howells 			conn = rb_entry(p, struct rxrpc_connection, service_node);
478496af50SDavid Howells 
488496af50SDavid Howells 			if (conn->proto.index_key < k.index_key)
498496af50SDavid Howells 				p = rcu_dereference_raw(p->rb_left);
508496af50SDavid Howells 			else if (conn->proto.index_key > k.index_key)
518496af50SDavid Howells 				p = rcu_dereference_raw(p->rb_right);
528496af50SDavid Howells 			else
538496af50SDavid Howells 				goto done;
548496af50SDavid Howells 			conn = NULL;
558496af50SDavid Howells 		}
568496af50SDavid Howells 	} while (need_seqretry(&peer->service_conn_lock, seq));
578496af50SDavid Howells 
588496af50SDavid Howells done:
598496af50SDavid Howells 	done_seqretry(&peer->service_conn_lock, seq);
608496af50SDavid Howells 	_leave(" = %d", conn ? conn->debug_id : -1);
618496af50SDavid Howells 	return conn;
628496af50SDavid Howells }
638496af50SDavid Howells 
648496af50SDavid Howells /*
658496af50SDavid Howells  * Insert a service connection into a peer's tree, thereby making it a target
668496af50SDavid Howells  * for incoming packets.
678496af50SDavid Howells  */
68248f219cSDavid Howells static void rxrpc_publish_service_conn(struct rxrpc_peer *peer,
698496af50SDavid Howells 				       struct rxrpc_connection *conn)
708496af50SDavid Howells {
718496af50SDavid Howells 	struct rxrpc_connection *cursor = NULL;
728496af50SDavid Howells 	struct rxrpc_conn_proto k = conn->proto;
738496af50SDavid Howells 	struct rb_node **pp, *parent;
748496af50SDavid Howells 
758496af50SDavid Howells 	write_seqlock_bh(&peer->service_conn_lock);
768496af50SDavid Howells 
778496af50SDavid Howells 	pp = &peer->service_conns.rb_node;
788496af50SDavid Howells 	parent = NULL;
798496af50SDavid Howells 	while (*pp) {
808496af50SDavid Howells 		parent = *pp;
818496af50SDavid Howells 		cursor = rb_entry(parent,
828496af50SDavid Howells 				  struct rxrpc_connection, service_node);
838496af50SDavid Howells 
848496af50SDavid Howells 		if (cursor->proto.index_key < k.index_key)
858496af50SDavid Howells 			pp = &(*pp)->rb_left;
868496af50SDavid Howells 		else if (cursor->proto.index_key > k.index_key)
878496af50SDavid Howells 			pp = &(*pp)->rb_right;
888496af50SDavid Howells 		else
898496af50SDavid Howells 			goto found_extant_conn;
908496af50SDavid Howells 	}
918496af50SDavid Howells 
928496af50SDavid Howells 	rb_link_node_rcu(&conn->service_node, parent, pp);
938496af50SDavid Howells 	rb_insert_color(&conn->service_node, &peer->service_conns);
948496af50SDavid Howells conn_published:
958496af50SDavid Howells 	set_bit(RXRPC_CONN_IN_SERVICE_CONNS, &conn->flags);
968496af50SDavid Howells 	write_sequnlock_bh(&peer->service_conn_lock);
978496af50SDavid Howells 	_leave(" = %d [new]", conn->debug_id);
98248f219cSDavid Howells 	return;
998496af50SDavid Howells 
1008496af50SDavid Howells found_extant_conn:
1018496af50SDavid Howells 	if (atomic_read(&cursor->usage) == 0)
1028496af50SDavid Howells 		goto replace_old_connection;
1038496af50SDavid Howells 	write_sequnlock_bh(&peer->service_conn_lock);
1048496af50SDavid Howells 	/* We should not be able to get here.  rxrpc_incoming_connection() is
1058496af50SDavid Howells 	 * called in a non-reentrant context, so there can't be a race to
1068496af50SDavid Howells 	 * insert a new connection.
1078496af50SDavid Howells 	 */
1088496af50SDavid Howells 	BUG();
1098496af50SDavid Howells 
1108496af50SDavid Howells replace_old_connection:
1118496af50SDavid Howells 	/* The old connection is from an outdated epoch. */
1128496af50SDavid Howells 	_debug("replace conn");
1138496af50SDavid Howells 	rb_replace_node_rcu(&cursor->service_node,
1148496af50SDavid Howells 			    &conn->service_node,
1158496af50SDavid Howells 			    &peer->service_conns);
1168496af50SDavid Howells 	clear_bit(RXRPC_CONN_IN_SERVICE_CONNS, &cursor->flags);
1178496af50SDavid Howells 	goto conn_published;
1188496af50SDavid Howells }
1198496af50SDavid Howells 
1208496af50SDavid Howells /*
12100e90712SDavid Howells  * Preallocate a service connection.  The connection is placed on the proc and
12200e90712SDavid Howells  * reap lists so that we don't have to get the lock from BH context.
12300e90712SDavid Howells  */
1242baec2c3SDavid Howells struct rxrpc_connection *rxrpc_prealloc_service_connection(struct rxrpc_net *rxnet,
1252baec2c3SDavid Howells 							   gfp_t gfp)
12600e90712SDavid Howells {
12700e90712SDavid Howells 	struct rxrpc_connection *conn = rxrpc_alloc_connection(gfp);
12800e90712SDavid Howells 
12900e90712SDavid Howells 	if (conn) {
13000e90712SDavid Howells 		/* We maintain an extra ref on the connection whilst it is on
13100e90712SDavid Howells 		 * the rxrpc_connections list.
13200e90712SDavid Howells 		 */
13300e90712SDavid Howells 		conn->state = RXRPC_CONN_SERVICE_PREALLOC;
13400e90712SDavid Howells 		atomic_set(&conn->usage, 2);
13500e90712SDavid Howells 
1362baec2c3SDavid Howells 		write_lock(&rxnet->conn_lock);
1372baec2c3SDavid Howells 		list_add_tail(&conn->link, &rxnet->service_conns);
1382baec2c3SDavid Howells 		list_add_tail(&conn->proc_link, &rxnet->conn_proc_list);
1392baec2c3SDavid Howells 		write_unlock(&rxnet->conn_lock);
140363deeabSDavid Howells 
141363deeabSDavid Howells 		trace_rxrpc_conn(conn, rxrpc_conn_new_service,
142363deeabSDavid Howells 				 atomic_read(&conn->usage),
143363deeabSDavid Howells 				 __builtin_return_address(0));
14400e90712SDavid Howells 	}
14500e90712SDavid Howells 
14600e90712SDavid Howells 	return conn;
14700e90712SDavid Howells }
14800e90712SDavid Howells 
14900e90712SDavid Howells /*
150248f219cSDavid Howells  * Set up an incoming connection.  This is called in BH context with the RCU
151248f219cSDavid Howells  * read lock held.
1527877a4a4SDavid Howells  */
153248f219cSDavid Howells void rxrpc_new_incoming_connection(struct rxrpc_connection *conn,
1547877a4a4SDavid Howells 				   struct sk_buff *skb)
1557877a4a4SDavid Howells {
1567877a4a4SDavid Howells 	struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
1577877a4a4SDavid Howells 
1587877a4a4SDavid Howells 	_enter("");
1597877a4a4SDavid Howells 
1608496af50SDavid Howells 	conn->proto.epoch	= sp->hdr.epoch;
1618496af50SDavid Howells 	conn->proto.cid		= sp->hdr.cid & RXRPC_CIDMASK;
1628496af50SDavid Howells 	conn->params.service_id	= sp->hdr.serviceId;
1638496af50SDavid Howells 	conn->security_ix	= sp->hdr.securityIndex;
1648496af50SDavid Howells 	conn->out_clientflag	= 0;
165248f219cSDavid Howells 	if (conn->security_ix)
1668496af50SDavid Howells 		conn->state	= RXRPC_CONN_SERVICE_UNSECURED;
167248f219cSDavid Howells 	else
168248f219cSDavid Howells 		conn->state	= RXRPC_CONN_SERVICE;
1697877a4a4SDavid Howells 
1708496af50SDavid Howells 	/* Make the connection a target for incoming packets. */
171248f219cSDavid Howells 	rxrpc_publish_service_conn(conn->params.peer, conn);
1728496af50SDavid Howells 
173248f219cSDavid Howells 	_net("CONNECTION new %d {%x}", conn->debug_id, conn->proto.cid);
1747877a4a4SDavid Howells }
175001c1122SDavid Howells 
176001c1122SDavid Howells /*
177001c1122SDavid Howells  * Remove the service connection from the peer's tree, thereby removing it as a
178001c1122SDavid Howells  * target for incoming packets.
179001c1122SDavid Howells  */
180001c1122SDavid Howells void rxrpc_unpublish_service_conn(struct rxrpc_connection *conn)
181001c1122SDavid Howells {
182001c1122SDavid Howells 	struct rxrpc_peer *peer = conn->params.peer;
183001c1122SDavid Howells 
1848496af50SDavid Howells 	write_seqlock_bh(&peer->service_conn_lock);
185001c1122SDavid Howells 	if (test_and_clear_bit(RXRPC_CONN_IN_SERVICE_CONNS, &conn->flags))
186001c1122SDavid Howells 		rb_erase(&conn->service_node, &peer->service_conns);
1878496af50SDavid Howells 	write_sequnlock_bh(&peer->service_conn_lock);
188001c1122SDavid Howells }
189