diff options
Diffstat (limited to 'net/rxrpc/call_object.c')
-rw-r--r-- | net/rxrpc/call_object.c | 273 |
1 files changed, 46 insertions, 227 deletions
diff --git a/net/rxrpc/call_object.c b/net/rxrpc/call_object.c index ad933daae13b..91287c9d01bb 100644 --- a/net/rxrpc/call_object.c +++ b/net/rxrpc/call_object.c @@ -14,7 +14,6 @@ #include <linux/slab.h> #include <linux/module.h> #include <linux/circ_buf.h> -#include <linux/hashtable.h> #include <linux/spinlock_types.h> #include <net/sock.h> #include <net/af_rxrpc.h> @@ -61,142 +60,6 @@ static void rxrpc_dead_call_expired(unsigned long _call); static void rxrpc_ack_time_expired(unsigned long _call); static void rxrpc_resend_time_expired(unsigned long _call); -static DEFINE_SPINLOCK(rxrpc_call_hash_lock); -static DEFINE_HASHTABLE(rxrpc_call_hash, 10); - -/* - * Hash function for rxrpc_call_hash - */ -static unsigned long rxrpc_call_hashfunc( - u8 in_clientflag, - u32 cid, - u32 call_id, - u32 epoch, - u16 service_id, - sa_family_t family, - void *localptr, - unsigned int addr_size, - const u8 *peer_addr) -{ - const u16 *p; - unsigned int i; - unsigned long key; - - _enter(""); - - key = (unsigned long)localptr; - /* We just want to add up the __be32 values, so forcing the - * cast should be okay. - */ - key += epoch; - key += service_id; - key += call_id; - key += (cid & RXRPC_CIDMASK) >> RXRPC_CIDSHIFT; - key += cid & RXRPC_CHANNELMASK; - key += in_clientflag; - key += family; - /* Step through the peer address in 16-bit portions for speed */ - for (i = 0, p = (const u16 *)peer_addr; i < addr_size >> 1; i++, p++) - key += *p; - _leave(" key = 0x%lx", key); - return key; -} - -/* - * Add a call to the hashtable - */ -static void rxrpc_call_hash_add(struct rxrpc_call *call) -{ - unsigned long key; - unsigned int addr_size = 0; - - _enter(""); - switch (call->family) { - case AF_INET: - addr_size = sizeof(call->peer_ip.ipv4_addr); - break; - case AF_INET6: - addr_size = sizeof(call->peer_ip.ipv6_addr); - break; - default: - break; - } - key = rxrpc_call_hashfunc(call->in_clientflag, call->cid, - call->call_id, call->epoch, - call->service_id, call->family, - call->conn->params.local, addr_size, - call->peer_ip.ipv6_addr); - /* Store the full key in the call */ - call->hash_key = key; - spin_lock(&rxrpc_call_hash_lock); - hash_add_rcu(rxrpc_call_hash, &call->hash_node, key); - spin_unlock(&rxrpc_call_hash_lock); - _leave(""); -} - -/* - * Remove a call from the hashtable - */ -static void rxrpc_call_hash_del(struct rxrpc_call *call) -{ - _enter(""); - spin_lock(&rxrpc_call_hash_lock); - hash_del_rcu(&call->hash_node); - spin_unlock(&rxrpc_call_hash_lock); - _leave(""); -} - -/* - * Find a call in the hashtable and return it, or NULL if it - * isn't there. - */ -struct rxrpc_call *rxrpc_find_call_hash( - struct rxrpc_host_header *hdr, - void *localptr, - sa_family_t family, - const void *peer_addr) -{ - unsigned long key; - unsigned int addr_size = 0; - struct rxrpc_call *call = NULL; - struct rxrpc_call *ret = NULL; - u8 in_clientflag = hdr->flags & RXRPC_CLIENT_INITIATED; - - _enter(""); - switch (family) { - case AF_INET: - addr_size = sizeof(call->peer_ip.ipv4_addr); - break; - case AF_INET6: - addr_size = sizeof(call->peer_ip.ipv6_addr); - break; - default: - break; - } - - key = rxrpc_call_hashfunc(in_clientflag, hdr->cid, hdr->callNumber, - hdr->epoch, hdr->serviceId, - family, localptr, addr_size, - peer_addr); - hash_for_each_possible_rcu(rxrpc_call_hash, call, hash_node, key) { - if (call->hash_key == key && - call->call_id == hdr->callNumber && - call->cid == hdr->cid && - call->in_clientflag == in_clientflag && - call->service_id == hdr->serviceId && - call->family == family && - call->local == localptr && - memcmp(call->peer_ip.ipv6_addr, peer_addr, - addr_size) == 0 && - call->epoch == hdr->epoch) { - ret = call; - break; - } - } - _leave(" = %p", ret); - return ret; -} - /* * find an extant server call * - called in process context with IRQs enabled @@ -305,20 +168,7 @@ static struct rxrpc_call *rxrpc_alloc_client_call(struct rxrpc_sock *rx, call->socket = rx; call->rx_data_post = 1; - /* Record copies of information for hashtable lookup */ - call->family = rx->family; call->local = rx->local; - switch (call->family) { - case AF_INET: - call->peer_ip.ipv4_addr = srx->transport.sin.sin_addr.s_addr; - break; - case AF_INET6: - memcpy(call->peer_ip.ipv6_addr, - srx->transport.sin6.sin6_addr.in6_u.u6_addr8, - sizeof(call->peer_ip.ipv6_addr)); - break; - } - call->service_id = srx->srx_service; call->in_clientflag = 0; @@ -345,9 +195,6 @@ static int rxrpc_begin_client_call(struct rxrpc_call *call, call->state = RXRPC_CALL_CLIENT_SEND_REQUEST; - /* Add the new call to the hashtable */ - rxrpc_call_hash_add(call); - spin_lock(&call->conn->params.peer->lock); hlist_add_head(&call->error_link, &call->conn->params.peer->error_targets); spin_unlock(&call->conn->params.peer->lock); @@ -425,9 +272,10 @@ error: rxrpc_put_call(call); write_lock_bh(&rxrpc_call_lock); - list_del(&call->link); + list_del_init(&call->link); write_unlock_bh(&rxrpc_call_lock); + call->state = RXRPC_CALL_DEAD; rxrpc_put_call(call); _leave(" = %d", ret); return ERR_PTR(ret); @@ -439,6 +287,7 @@ error: */ found_user_ID_now_present: write_unlock(&rx->call_lock); + call->state = RXRPC_CALL_DEAD; rxrpc_put_call(call); _leave(" = -EEXIST [%p]", call); return ERR_PTR(-EEXIST); @@ -454,8 +303,7 @@ struct rxrpc_call *rxrpc_incoming_call(struct rxrpc_sock *rx, { struct rxrpc_skb_priv *sp = rxrpc_skb(skb); struct rxrpc_call *call, *candidate; - struct rb_node **p, *parent; - u32 call_id; + u32 call_id, chan; _enter(",%d", conn->debug_id); @@ -465,20 +313,23 @@ struct rxrpc_call *rxrpc_incoming_call(struct rxrpc_sock *rx, if (!candidate) return ERR_PTR(-EBUSY); + chan = sp->hdr.cid & RXRPC_CHANNELMASK; candidate->socket = rx; candidate->conn = conn; candidate->cid = sp->hdr.cid; candidate->call_id = sp->hdr.callNumber; - candidate->channel = sp->hdr.cid & RXRPC_CHANNELMASK; + candidate->channel = chan; candidate->rx_data_post = 0; candidate->state = RXRPC_CALL_SERVER_ACCEPTING; if (conn->security_ix > 0) candidate->state = RXRPC_CALL_SERVER_SECURING; - write_lock_bh(&conn->lock); + spin_lock(&conn->channel_lock); /* set the channel for this call */ - call = conn->channels[candidate->channel]; + call = rcu_dereference_protected(conn->channels[chan].call, + lockdep_is_held(&conn->channel_lock)); + _debug("channel[%u] is %p", candidate->channel, call); if (call && call->call_id == sp->hdr.callNumber) { /* already set; must've been a duplicate packet */ @@ -507,9 +358,9 @@ struct rxrpc_call *rxrpc_incoming_call(struct rxrpc_sock *rx, call->debug_id, rxrpc_call_states[call->state]); if (call->state >= RXRPC_CALL_COMPLETE) { - conn->channels[call->channel] = NULL; + __rxrpc_disconnect_call(call); } else { - write_unlock_bh(&conn->lock); + spin_unlock(&conn->channel_lock); kmem_cache_free(rxrpc_call_jar, candidate); _leave(" = -EBUSY"); return ERR_PTR(-EBUSY); @@ -519,33 +370,22 @@ struct rxrpc_call *rxrpc_incoming_call(struct rxrpc_sock *rx, /* check the call number isn't duplicate */ _debug("check dup"); call_id = sp->hdr.callNumber; - p = &conn->calls.rb_node; - parent = NULL; - while (*p) { - parent = *p; - call = rb_entry(parent, struct rxrpc_call, conn_node); - - /* The tree is sorted in order of the __be32 value without - * turning it into host order. - */ - if (call_id < call->call_id) - p = &(*p)->rb_left; - else if (call_id > call->call_id) - p = &(*p)->rb_right; - else - goto old_call; - } + + /* We just ignore calls prior to the current call ID. Terminated calls + * are handled via the connection. + */ + if (call_id <= conn->channels[chan].call_counter) + goto old_call; /* TODO: Just drop packet */ /* make the call available */ _debug("new call"); call = candidate; candidate = NULL; - rb_link_node(&call->conn_node, parent, p); - rb_insert_color(&call->conn_node, &conn->calls); - conn->channels[call->channel] = call; + conn->channels[chan].call_counter = call_id; + rcu_assign_pointer(conn->channels[chan].call, call); sock_hold(&rx->sk); rxrpc_get_connection(conn); - write_unlock_bh(&conn->lock); + spin_unlock(&conn->channel_lock); spin_lock(&conn->params.peer->lock); hlist_add_head(&call->error_link, &conn->params.peer->error_targets); @@ -555,27 +395,10 @@ struct rxrpc_call *rxrpc_incoming_call(struct rxrpc_sock *rx, list_add_tail(&call->link, &rxrpc_calls); write_unlock_bh(&rxrpc_call_lock); - /* Record copies of information for hashtable lookup */ - call->family = rx->family; call->local = conn->params.local; - switch (call->family) { - case AF_INET: - call->peer_ip.ipv4_addr = - conn->params.peer->srx.transport.sin.sin_addr.s_addr; - break; - case AF_INET6: - memcpy(call->peer_ip.ipv6_addr, - conn->params.peer->srx.transport.sin6.sin6_addr.in6_u.u6_addr8, - sizeof(call->peer_ip.ipv6_addr)); - break; - default: - break; - } call->epoch = conn->proto.epoch; call->service_id = conn->params.service_id; - call->in_clientflag = conn->proto.in_clientflag; - /* Add the new call to the hashtable */ - rxrpc_call_hash_add(call); + call->in_clientflag = RXRPC_CLIENT_INITIATED; _net("CALL incoming %d on CONN %d", call->debug_id, call->conn->debug_id); @@ -585,19 +408,19 @@ struct rxrpc_call *rxrpc_incoming_call(struct rxrpc_sock *rx, return call; extant_call: - write_unlock_bh(&conn->lock); + spin_unlock(&conn->channel_lock); kmem_cache_free(rxrpc_call_jar, candidate); _leave(" = %p {%d} [extant]", call, call ? call->debug_id : -1); return call; aborted_call: - write_unlock_bh(&conn->lock); + spin_unlock(&conn->channel_lock); kmem_cache_free(rxrpc_call_jar, candidate); _leave(" = -ECONNABORTED"); return ERR_PTR(-ECONNABORTED); old_call: - write_unlock_bh(&conn->lock); + spin_unlock(&conn->channel_lock); kmem_cache_free(rxrpc_call_jar, candidate); _leave(" = -ECONNRESET [old]"); return ERR_PTR(-ECONNRESET); @@ -626,6 +449,10 @@ void rxrpc_release_call(struct rxrpc_call *call) */ _debug("RELEASE CALL %p (%d CONN %p)", call, call->debug_id, conn); + spin_lock(&conn->params.peer->lock); + hlist_del_init(&call->error_link); + spin_unlock(&conn->params.peer->lock); + write_lock_bh(&rx->call_lock); if (!list_empty(&call->accept_link)) { _debug("unlinking once-pending call %p { e=%lx f=%lx }", @@ -641,24 +468,17 @@ void rxrpc_release_call(struct rxrpc_call *call) write_unlock_bh(&rx->call_lock); /* free up the channel for reuse */ - spin_lock(&conn->channel_lock); - write_lock_bh(&conn->lock); - write_lock(&call->state_lock); - - rxrpc_disconnect_call(call); - - spin_unlock(&conn->channel_lock); + write_lock_bh(&call->state_lock); if (call->state < RXRPC_CALL_COMPLETE && call->state != RXRPC_CALL_CLIENT_FINAL_ACK) { _debug("+++ ABORTING STATE %d +++\n", call->state); call->state = RXRPC_CALL_LOCALLY_ABORTED; call->local_abort = RX_CALL_DEAD; - set_bit(RXRPC_CALL_EV_ABORT, &call->events); - rxrpc_queue_call(call); } - write_unlock(&call->state_lock); - write_unlock_bh(&conn->lock); + write_unlock_bh(&call->state_lock); + + rxrpc_disconnect_call(call); /* clean up the Rx queue */ if (!skb_queue_empty(&call->rx_queue) || @@ -792,6 +612,17 @@ void __rxrpc_put_call(struct rxrpc_call *call) } /* + * Final call destruction under RCU. + */ +static void rxrpc_rcu_destroy_call(struct rcu_head *rcu) +{ + struct rxrpc_call *call = container_of(rcu, struct rxrpc_call, rcu); + + rxrpc_purge_queue(&call->rx_queue); + kmem_cache_free(rxrpc_call_jar, call); +} + +/* * clean up a call */ static void rxrpc_cleanup_call(struct rxrpc_call *call) @@ -815,19 +646,7 @@ static void rxrpc_cleanup_call(struct rxrpc_call *call) return; } - if (call->conn) { - spin_lock(&call->conn->params.peer->lock); - hlist_del_init(&call->error_link); - spin_unlock(&call->conn->params.peer->lock); - - write_lock_bh(&call->conn->lock); - rb_erase(&call->conn_node, &call->conn->calls); - write_unlock_bh(&call->conn->lock); - rxrpc_put_connection(call->conn); - } - - /* Remove the call from the hash */ - rxrpc_call_hash_del(call); + ASSERTCMP(call->conn, ==, NULL); if (call->acks_window) { _debug("kill Tx window %d", @@ -855,7 +674,7 @@ static void rxrpc_cleanup_call(struct rxrpc_call *call) rxrpc_purge_queue(&call->rx_queue); ASSERT(skb_queue_empty(&call->rx_oos_queue)); sock_put(&call->socket->sk); - kmem_cache_free(rxrpc_call_jar, call); + call_rcu(&call->rcu, rxrpc_rcu_destroy_call); } /* |