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 */ 12400e90712SDavid Howells struct rxrpc_connection *rxrpc_prealloc_service_connection(gfp_t gfp) 12500e90712SDavid Howells { 12600e90712SDavid Howells struct rxrpc_connection *conn = rxrpc_alloc_connection(gfp); 12700e90712SDavid Howells 12800e90712SDavid Howells if (conn) { 12900e90712SDavid Howells /* We maintain an extra ref on the connection whilst it is on 13000e90712SDavid Howells * the rxrpc_connections list. 13100e90712SDavid Howells */ 13200e90712SDavid Howells conn->state = RXRPC_CONN_SERVICE_PREALLOC; 13300e90712SDavid Howells atomic_set(&conn->usage, 2); 13400e90712SDavid Howells 13500e90712SDavid Howells write_lock(&rxrpc_connection_lock); 13600e90712SDavid Howells list_add_tail(&conn->link, &rxrpc_connections); 13700e90712SDavid Howells list_add_tail(&conn->proc_link, &rxrpc_connection_proc_list); 13800e90712SDavid Howells write_unlock(&rxrpc_connection_lock); 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 */ 148248f219cSDavid Howells void rxrpc_new_incoming_connection(struct rxrpc_connection *conn, 1497877a4a4SDavid Howells struct sk_buff *skb) 1507877a4a4SDavid Howells { 1517877a4a4SDavid Howells struct rxrpc_skb_priv *sp = rxrpc_skb(skb); 1527877a4a4SDavid Howells 1537877a4a4SDavid Howells _enter(""); 1547877a4a4SDavid Howells 1558496af50SDavid Howells conn->proto.epoch = sp->hdr.epoch; 1568496af50SDavid Howells conn->proto.cid = sp->hdr.cid & RXRPC_CIDMASK; 1578496af50SDavid Howells conn->params.service_id = sp->hdr.serviceId; 1588496af50SDavid Howells conn->security_ix = sp->hdr.securityIndex; 1598496af50SDavid Howells conn->out_clientflag = 0; 160248f219cSDavid Howells if (conn->security_ix) 1618496af50SDavid Howells conn->state = RXRPC_CONN_SERVICE_UNSECURED; 162248f219cSDavid Howells else 163248f219cSDavid Howells conn->state = RXRPC_CONN_SERVICE; 1647877a4a4SDavid Howells 1658496af50SDavid Howells /* Make the connection a target for incoming packets. */ 166248f219cSDavid Howells rxrpc_publish_service_conn(conn->params.peer, conn); 1678496af50SDavid Howells 168248f219cSDavid Howells _net("CONNECTION new %d {%x}", conn->debug_id, conn->proto.cid); 1697877a4a4SDavid Howells } 170001c1122SDavid Howells 171001c1122SDavid Howells /* 172001c1122SDavid Howells * Remove the service connection from the peer's tree, thereby removing it as a 173001c1122SDavid Howells * target for incoming packets. 174001c1122SDavid Howells */ 175001c1122SDavid Howells void rxrpc_unpublish_service_conn(struct rxrpc_connection *conn) 176001c1122SDavid Howells { 177001c1122SDavid Howells struct rxrpc_peer *peer = conn->params.peer; 178001c1122SDavid Howells 1798496af50SDavid Howells write_seqlock_bh(&peer->service_conn_lock); 180001c1122SDavid Howells if (test_and_clear_bit(RXRPC_CONN_IN_SERVICE_CONNS, &conn->flags)) 181001c1122SDavid Howells rb_erase(&conn->service_node, &peer->service_conns); 1828496af50SDavid Howells write_sequnlock_bh(&peer->service_conn_lock); 183001c1122SDavid Howells } 184