12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
245025bceSDavid Howells /* RxRPC virtual connection handler, common bits.
38c3e34a4SDavid Howells *
445025bceSDavid Howells * Copyright (C) 2007, 2016 Red Hat, Inc. All Rights Reserved.
58c3e34a4SDavid Howells * Written by David Howells (dhowells@redhat.com)
68c3e34a4SDavid Howells */
78c3e34a4SDavid Howells
88c3e34a4SDavid Howells #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
98c3e34a4SDavid Howells
108c3e34a4SDavid Howells #include <linux/module.h>
118c3e34a4SDavid Howells #include <linux/slab.h>
128c3e34a4SDavid Howells #include <linux/net.h>
138c3e34a4SDavid Howells #include <linux/skbuff.h>
148c3e34a4SDavid Howells #include "ar-internal.h"
158c3e34a4SDavid Howells
168c3e34a4SDavid Howells /*
178c3e34a4SDavid Howells * Time till a connection expires after last use (in seconds).
188c3e34a4SDavid Howells */
19f859ab61SDavid Howells unsigned int __read_mostly rxrpc_connection_expiry = 10 * 60;
20f859ab61SDavid Howells unsigned int __read_mostly rxrpc_closed_conn_expiry = 10;
218c3e34a4SDavid Howells
223cec055cSDavid Howells static void rxrpc_clean_up_connection(struct work_struct *work);
233cec055cSDavid Howells static void rxrpc_set_service_reap_timer(struct rxrpc_net *rxnet,
243cec055cSDavid Howells unsigned long reap_at);
2545025bceSDavid Howells
rxrpc_poke_conn(struct rxrpc_connection * conn,enum rxrpc_conn_trace why)26f2cce89aSDavid Howells void rxrpc_poke_conn(struct rxrpc_connection *conn, enum rxrpc_conn_trace why)
27f2cce89aSDavid Howells {
28f2cce89aSDavid Howells struct rxrpc_local *local = conn->local;
29f2cce89aSDavid Howells bool busy;
30f2cce89aSDavid Howells
31f2cce89aSDavid Howells if (WARN_ON_ONCE(!local))
32f2cce89aSDavid Howells return;
33f2cce89aSDavid Howells
34f2cce89aSDavid Howells spin_lock_bh(&local->lock);
35f2cce89aSDavid Howells busy = !list_empty(&conn->attend_link);
36f2cce89aSDavid Howells if (!busy) {
37f2cce89aSDavid Howells rxrpc_get_connection(conn, why);
38f2cce89aSDavid Howells list_add_tail(&conn->attend_link, &local->conn_attend_q);
39f2cce89aSDavid Howells }
40f2cce89aSDavid Howells spin_unlock_bh(&local->lock);
41f2cce89aSDavid Howells rxrpc_wake_up_io_thread(local);
42f2cce89aSDavid Howells }
43f2cce89aSDavid Howells
rxrpc_connection_timer(struct timer_list * timer)443136ef49SDavid Howells static void rxrpc_connection_timer(struct timer_list *timer)
453136ef49SDavid Howells {
463136ef49SDavid Howells struct rxrpc_connection *conn =
473136ef49SDavid Howells container_of(timer, struct rxrpc_connection, timer);
483136ef49SDavid Howells
49f2cce89aSDavid Howells rxrpc_poke_conn(conn, rxrpc_conn_get_poke_timer);
503136ef49SDavid Howells }
513136ef49SDavid Howells
528c3e34a4SDavid Howells /*
538c3e34a4SDavid Howells * allocate a new connection
548c3e34a4SDavid Howells */
rxrpc_alloc_connection(struct rxrpc_net * rxnet,gfp_t gfp)553cec055cSDavid Howells struct rxrpc_connection *rxrpc_alloc_connection(struct rxrpc_net *rxnet,
563cec055cSDavid Howells gfp_t gfp)
578c3e34a4SDavid Howells {
588c3e34a4SDavid Howells struct rxrpc_connection *conn;
598c3e34a4SDavid Howells
608c3e34a4SDavid Howells _enter("");
618c3e34a4SDavid Howells
628c3e34a4SDavid Howells conn = kzalloc(sizeof(struct rxrpc_connection), gfp);
638c3e34a4SDavid Howells if (conn) {
6445025bceSDavid Howells INIT_LIST_HEAD(&conn->cache_link);
653136ef49SDavid Howells timer_setup(&conn->timer, &rxrpc_connection_timer, 0);
663cec055cSDavid Howells INIT_WORK(&conn->processor, rxrpc_process_connection);
673cec055cSDavid Howells INIT_WORK(&conn->destructor, rxrpc_clean_up_connection);
684d028b2cSDavid Howells INIT_LIST_HEAD(&conn->proc_link);
69999b69f8SDavid Howells INIT_LIST_HEAD(&conn->link);
70*f7627c81SDavid Howells INIT_LIST_HEAD(&conn->attend_link);
719d35d880SDavid Howells mutex_init(&conn->security_lock);
728c3e34a4SDavid Howells skb_queue_head_init(&conn->rx_queue);
733cec055cSDavid Howells conn->rxnet = rxnet;
748c3e34a4SDavid Howells conn->security = &rxrpc_no_security;
758c3e34a4SDavid Howells spin_lock_init(&conn->state_lock);
768c3e34a4SDavid Howells conn->debug_id = atomic_inc_return(&rxrpc_debug_id);
77f51b4480SDavid Howells conn->idle_timestamp = jiffies;
788c3e34a4SDavid Howells }
798c3e34a4SDavid Howells
808c3e34a4SDavid Howells _leave(" = %p{%d}", conn, conn ? conn->debug_id : 0);
818c3e34a4SDavid Howells return conn;
828c3e34a4SDavid Howells }
838c3e34a4SDavid Howells
848c3e34a4SDavid Howells /*
858496af50SDavid Howells * Look up a connection in the cache by protocol parameters.
868496af50SDavid Howells *
878496af50SDavid Howells * If successful, a pointer to the connection is returned, but no ref is taken.
888496af50SDavid Howells * NULL is returned if there is no match.
898496af50SDavid Howells *
900099dc58SDavid Howells * When searching for a service call, if we find a peer but no connection, we
910099dc58SDavid Howells * return that through *_peer in case we need to create a new service call.
920099dc58SDavid Howells *
938496af50SDavid Howells * The caller must be holding the RCU read lock.
948c3e34a4SDavid Howells */
rxrpc_find_client_connection_rcu(struct rxrpc_local * local,struct sockaddr_rxrpc * srx,struct sk_buff * skb)955e6ef4f1SDavid Howells struct rxrpc_connection *rxrpc_find_client_connection_rcu(struct rxrpc_local *local,
96393a2a20SDavid Howells struct sockaddr_rxrpc *srx,
975e6ef4f1SDavid Howells struct sk_buff *skb)
988c3e34a4SDavid Howells {
998c3e34a4SDavid Howells struct rxrpc_connection *conn;
10042886ffeSDavid Howells struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
1011291e9d1SDavid Howells struct rxrpc_peer *peer;
1028c3e34a4SDavid Howells
1038496af50SDavid Howells _enter(",%x", sp->hdr.cid & RXRPC_CIDMASK);
1048c3e34a4SDavid Howells
105f06cb291SDavid Howells /* Look up client connections by connection ID alone as their
106f06cb291SDavid Howells * IDs are unique for this machine.
1078496af50SDavid Howells */
108f06cb291SDavid Howells conn = idr_find(&local->conn_ids, sp->hdr.cid >> RXRPC_CIDSHIFT);
109a0575429SDavid Howells if (!conn || refcount_read(&conn->ref) == 0) {
1108496af50SDavid Howells _debug("no conn");
1118496af50SDavid Howells goto not_found;
1128496af50SDavid Howells }
1138496af50SDavid Howells
1145e6ef4f1SDavid Howells if (conn->proto.epoch != sp->hdr.epoch ||
1152cc80086SDavid Howells conn->local != local)
1161291e9d1SDavid Howells goto not_found;
1171291e9d1SDavid Howells
1182cc80086SDavid Howells peer = conn->peer;
119393a2a20SDavid Howells switch (srx->transport.family) {
1201291e9d1SDavid Howells case AF_INET:
1211291e9d1SDavid Howells if (peer->srx.transport.sin.sin_port !=
12273f81e5aSJeffrey Altman srx->transport.sin.sin_port)
1231291e9d1SDavid Howells goto not_found;
1241291e9d1SDavid Howells break;
125d1912747SDavid Howells #ifdef CONFIG_AF_RXRPC_IPV6
12675b54cb5SDavid Howells case AF_INET6:
12775b54cb5SDavid Howells if (peer->srx.transport.sin6.sin6_port !=
12873f81e5aSJeffrey Altman srx->transport.sin6.sin6_port)
12975b54cb5SDavid Howells goto not_found;
13075b54cb5SDavid Howells break;
131d1912747SDavid Howells #endif
1321291e9d1SDavid Howells default:
1331291e9d1SDavid Howells BUG();
1344a3388c8SDavid Howells }
1358c3e34a4SDavid Howells
1361291e9d1SDavid Howells _leave(" = %p", conn);
1371291e9d1SDavid Howells return conn;
1381291e9d1SDavid Howells
1391291e9d1SDavid Howells not_found:
1408c3e34a4SDavid Howells _leave(" = NULL");
1418c3e34a4SDavid Howells return NULL;
1428c3e34a4SDavid Howells }
1438c3e34a4SDavid Howells
1448c3e34a4SDavid Howells /*
145999b69f8SDavid Howells * Disconnect a call and clear any channel it occupies when that call
146a1399f8bSDavid Howells * terminates. The caller must hold the channel_lock and must release the
147a1399f8bSDavid Howells * call's ref on the connection.
148a1399f8bSDavid Howells */
__rxrpc_disconnect_call(struct rxrpc_connection * conn,struct rxrpc_call * call)14945025bceSDavid Howells void __rxrpc_disconnect_call(struct rxrpc_connection *conn,
15045025bceSDavid Howells struct rxrpc_call *call)
151a1399f8bSDavid Howells {
15201a90a45SDavid Howells struct rxrpc_channel *chan =
15301a90a45SDavid Howells &conn->channels[call->cid & RXRPC_CHANNELMASK];
154a1399f8bSDavid Howells
15501a90a45SDavid Howells _enter("%d,%x", conn->debug_id, call->cid);
156a1399f8bSDavid Howells
1579d35d880SDavid Howells if (chan->call == call) {
158a1399f8bSDavid Howells /* Save the result of the call so that we can repeat it if necessary
159a1399f8bSDavid Howells * through the channel, whilst disposing of the actual call record.
160a1399f8bSDavid Howells */
161b1d9f7fdSDavid Howells trace_rxrpc_disconnect_call(call);
16217e9e23bSDavid Howells switch (call->completion) {
16317e9e23bSDavid Howells case RXRPC_CALL_SUCCEEDED:
1645d7edbc9SDavid Howells chan->last_seq = call->rx_highest_seq;
16518bfeba5SDavid Howells chan->last_type = RXRPC_PACKET_TYPE_ACK;
16617e9e23bSDavid Howells break;
16717e9e23bSDavid Howells case RXRPC_CALL_LOCALLY_ABORTED:
16817e9e23bSDavid Howells chan->last_abort = call->abort_code;
16917e9e23bSDavid Howells chan->last_type = RXRPC_PACKET_TYPE_ABORT;
17017e9e23bSDavid Howells break;
17117e9e23bSDavid Howells default:
172de696c47SDavid Howells chan->last_abort = RX_CALL_DEAD;
17317e9e23bSDavid Howells chan->last_type = RXRPC_PACKET_TYPE_ABORT;
17417e9e23bSDavid Howells break;
17518bfeba5SDavid Howells }
17617e9e23bSDavid Howells
177a1399f8bSDavid Howells chan->last_call = chan->call_id;
178a1399f8bSDavid Howells chan->call_id = chan->call_counter;
1799d35d880SDavid Howells chan->call = NULL;
180a1399f8bSDavid Howells }
181a1399f8bSDavid Howells
182a1399f8bSDavid Howells _leave("");
183a1399f8bSDavid Howells }
184a1399f8bSDavid Howells
185a1399f8bSDavid Howells /*
186a1399f8bSDavid Howells * Disconnect a call and clear any channel it occupies when that call
187999b69f8SDavid Howells * terminates.
188999b69f8SDavid Howells */
rxrpc_disconnect_call(struct rxrpc_call * call)189999b69f8SDavid Howells void rxrpc_disconnect_call(struct rxrpc_call *call)
190999b69f8SDavid Howells {
191999b69f8SDavid Howells struct rxrpc_connection *conn = call->conn;
192999b69f8SDavid Howells
1935040011dSDavid Howells set_bit(RXRPC_CALL_DISCONNECTED, &call->flags);
1945040011dSDavid Howells rxrpc_see_call(call, rxrpc_call_see_disconnected);
1955040011dSDavid Howells
1961fc4fa2aSDavid Howells call->peer->cong_ssthresh = call->cong_ssthresh;
197f7aec129SDavid Howells
19865550098SDavid Howells if (!hlist_unhashed(&call->error_link)) {
19929fb4ec3SDavid Howells spin_lock(&call->peer->lock);
20029fb4ec3SDavid Howells hlist_del_init(&call->error_link);
20129fb4ec3SDavid Howells spin_unlock(&call->peer->lock);
20265550098SDavid Howells }
203248f219cSDavid Howells
2045040011dSDavid Howells if (rxrpc_is_client_call(call)) {
2051bab27afSDavid Howells rxrpc_disconnect_client_call(call->bundle, call);
2065040011dSDavid Howells } else {
20745025bceSDavid Howells __rxrpc_disconnect_call(conn, call);
208f51b4480SDavid Howells conn->idle_timestamp = jiffies;
2093cec055cSDavid Howells if (atomic_dec_and_test(&conn->active))
2103cec055cSDavid Howells rxrpc_set_service_reap_timer(conn->rxnet,
211b8a4f0a2SDavid Howells jiffies + rxrpc_connection_expiry * HZ);
21245025bceSDavid Howells }
21345025bceSDavid Howells
2145040011dSDavid Howells rxrpc_put_call(call, rxrpc_call_put_io_thread);
2155040011dSDavid Howells }
2165040011dSDavid Howells
21745025bceSDavid Howells /*
218363deeabSDavid Howells * Queue a connection's work processor, getting a ref to pass to the work
219363deeabSDavid Howells * queue.
2208c3e34a4SDavid Howells */
rxrpc_queue_conn(struct rxrpc_connection * conn,enum rxrpc_conn_trace why)2213cec055cSDavid Howells void rxrpc_queue_conn(struct rxrpc_connection *conn, enum rxrpc_conn_trace why)
2228c3e34a4SDavid Howells {
2233cec055cSDavid Howells if (atomic_read(&conn->active) >= 0 &&
2243cec055cSDavid Howells rxrpc_queue_work(&conn->processor))
2253cec055cSDavid Howells rxrpc_see_connection(conn, why);
226363deeabSDavid Howells }
227363deeabSDavid Howells
228363deeabSDavid Howells /*
229363deeabSDavid Howells * Note the re-emergence of a connection.
230363deeabSDavid Howells */
rxrpc_see_connection(struct rxrpc_connection * conn,enum rxrpc_conn_trace why)2317fa25105SDavid Howells void rxrpc_see_connection(struct rxrpc_connection *conn,
2327fa25105SDavid Howells enum rxrpc_conn_trace why)
233363deeabSDavid Howells {
234363deeabSDavid Howells if (conn) {
2357fa25105SDavid Howells int r = refcount_read(&conn->ref);
236363deeabSDavid Howells
2377fa25105SDavid Howells trace_rxrpc_conn(conn->debug_id, r, why);
238363deeabSDavid Howells }
239363deeabSDavid Howells }
240363deeabSDavid Howells
241363deeabSDavid Howells /*
242363deeabSDavid Howells * Get a ref on a connection.
243363deeabSDavid Howells */
rxrpc_get_connection(struct rxrpc_connection * conn,enum rxrpc_conn_trace why)2447fa25105SDavid Howells struct rxrpc_connection *rxrpc_get_connection(struct rxrpc_connection *conn,
2457fa25105SDavid Howells enum rxrpc_conn_trace why)
246363deeabSDavid Howells {
247a0575429SDavid Howells int r;
248363deeabSDavid Howells
249a0575429SDavid Howells __refcount_inc(&conn->ref, &r);
2507fa25105SDavid Howells trace_rxrpc_conn(conn->debug_id, r + 1, why);
251245500d8SDavid Howells return conn;
252363deeabSDavid Howells }
253363deeabSDavid Howells
254363deeabSDavid Howells /*
255363deeabSDavid Howells * Try to get a ref on a connection.
256363deeabSDavid Howells */
257363deeabSDavid Howells struct rxrpc_connection *
rxrpc_get_connection_maybe(struct rxrpc_connection * conn,enum rxrpc_conn_trace why)2587fa25105SDavid Howells rxrpc_get_connection_maybe(struct rxrpc_connection *conn,
2597fa25105SDavid Howells enum rxrpc_conn_trace why)
260363deeabSDavid Howells {
261a0575429SDavid Howells int r;
262363deeabSDavid Howells
263363deeabSDavid Howells if (conn) {
264a0575429SDavid Howells if (__refcount_inc_not_zero(&conn->ref, &r))
2657fa25105SDavid Howells trace_rxrpc_conn(conn->debug_id, r + 1, why);
266363deeabSDavid Howells else
267363deeabSDavid Howells conn = NULL;
268363deeabSDavid Howells }
269363deeabSDavid Howells return conn;
270363deeabSDavid Howells }
271363deeabSDavid Howells
272363deeabSDavid Howells /*
2733d18cbb7SDavid Howells * Set the service connection reap timer.
2743d18cbb7SDavid Howells */
rxrpc_set_service_reap_timer(struct rxrpc_net * rxnet,unsigned long reap_at)2753d18cbb7SDavid Howells static void rxrpc_set_service_reap_timer(struct rxrpc_net *rxnet,
2763d18cbb7SDavid Howells unsigned long reap_at)
2773d18cbb7SDavid Howells {
2783d18cbb7SDavid Howells if (rxnet->live)
2793d18cbb7SDavid Howells timer_reduce(&rxnet->service_conn_reap_timer, reap_at);
2803d18cbb7SDavid Howells }
2813d18cbb7SDavid Howells
2823d18cbb7SDavid Howells /*
2838c3e34a4SDavid Howells * destroy a virtual connection
2848c3e34a4SDavid Howells */
rxrpc_rcu_free_connection(struct rcu_head * rcu)2853cec055cSDavid Howells static void rxrpc_rcu_free_connection(struct rcu_head *rcu)
2868c3e34a4SDavid Howells {
287dee46364SDavid Howells struct rxrpc_connection *conn =
288dee46364SDavid Howells container_of(rcu, struct rxrpc_connection, rcu);
2893cec055cSDavid Howells struct rxrpc_net *rxnet = conn->rxnet;
290dee46364SDavid Howells
291a0575429SDavid Howells _enter("{%d,u=%d}", conn->debug_id, refcount_read(&conn->ref));
2928c3e34a4SDavid Howells
2937fa25105SDavid Howells trace_rxrpc_conn(conn->debug_id, refcount_read(&conn->ref),
2947fa25105SDavid Howells rxrpc_conn_free);
2953cec055cSDavid Howells kfree(conn);
2967fa25105SDavid Howells
2973cec055cSDavid Howells if (atomic_dec_and_test(&rxnet->nr_conns))
2983cec055cSDavid Howells wake_up_var(&rxnet->nr_conns);
2993cec055cSDavid Howells }
3003cec055cSDavid Howells
3013cec055cSDavid Howells /*
3023cec055cSDavid Howells * Clean up a dead connection.
3033cec055cSDavid Howells */
rxrpc_clean_up_connection(struct work_struct * work)3043cec055cSDavid Howells static void rxrpc_clean_up_connection(struct work_struct *work)
3053cec055cSDavid Howells {
3063cec055cSDavid Howells struct rxrpc_connection *conn =
3073cec055cSDavid Howells container_of(work, struct rxrpc_connection, destructor);
3083cec055cSDavid Howells struct rxrpc_net *rxnet = conn->rxnet;
3093cec055cSDavid Howells
3109d35d880SDavid Howells ASSERT(!conn->channels[0].call &&
3119d35d880SDavid Howells !conn->channels[1].call &&
3129d35d880SDavid Howells !conn->channels[2].call &&
3139d35d880SDavid Howells !conn->channels[3].call);
3143cec055cSDavid Howells ASSERT(list_empty(&conn->cache_link));
3158c3e34a4SDavid Howells
3163136ef49SDavid Howells del_timer_sync(&conn->timer);
3173cec055cSDavid Howells cancel_work_sync(&conn->processor); /* Processing may restart the timer */
3183cec055cSDavid Howells del_timer_sync(&conn->timer);
3193cec055cSDavid Howells
3203cec055cSDavid Howells write_lock(&rxnet->conn_lock);
3213cec055cSDavid Howells list_del_init(&conn->proc_link);
3223cec055cSDavid Howells write_unlock(&rxnet->conn_lock);
3233cec055cSDavid Howells
3248c3e34a4SDavid Howells rxrpc_purge_queue(&conn->rx_queue);
3258c3e34a4SDavid Howells
3263cec055cSDavid Howells rxrpc_kill_client_conn(conn);
3273cec055cSDavid Howells
3288c3e34a4SDavid Howells conn->security->clear(conn);
3292cc80086SDavid Howells key_put(conn->key);
330fa3492abSDavid Howells rxrpc_put_bundle(conn->bundle, rxrpc_bundle_put_conn);
33147c810a7SDavid Howells rxrpc_put_peer(conn->peer, rxrpc_peer_put_conn);
3320fde882fSDavid Howells rxrpc_put_local(conn->local, rxrpc_local_put_kill_conn);
3338c3e34a4SDavid Howells
3343cec055cSDavid Howells /* Drain the Rx queue. Note that even though we've unpublished, an
3353cec055cSDavid Howells * incoming packet could still be being added to our Rx queue, so we
3363cec055cSDavid Howells * will need to drain it again in the RCU cleanup handler.
3373cec055cSDavid Howells */
3383cec055cSDavid Howells rxrpc_purge_queue(&conn->rx_queue);
3393cec055cSDavid Howells
3403cec055cSDavid Howells call_rcu(&conn->rcu, rxrpc_rcu_free_connection);
3413cec055cSDavid Howells }
3423cec055cSDavid Howells
3433cec055cSDavid Howells /*
3443cec055cSDavid Howells * Drop a ref on a connection.
3453cec055cSDavid Howells */
rxrpc_put_connection(struct rxrpc_connection * conn,enum rxrpc_conn_trace why)3463cec055cSDavid Howells void rxrpc_put_connection(struct rxrpc_connection *conn,
3473cec055cSDavid Howells enum rxrpc_conn_trace why)
3483cec055cSDavid Howells {
3493cec055cSDavid Howells unsigned int debug_id;
3503cec055cSDavid Howells bool dead;
3513cec055cSDavid Howells int r;
3523cec055cSDavid Howells
3533cec055cSDavid Howells if (!conn)
3543cec055cSDavid Howells return;
3553cec055cSDavid Howells
3563cec055cSDavid Howells debug_id = conn->debug_id;
3573cec055cSDavid Howells dead = __refcount_dec_and_test(&conn->ref, &r);
3583cec055cSDavid Howells trace_rxrpc_conn(debug_id, r - 1, why);
3593cec055cSDavid Howells if (dead) {
3603cec055cSDavid Howells del_timer(&conn->timer);
3613cec055cSDavid Howells cancel_work(&conn->processor);
3623cec055cSDavid Howells
3633cec055cSDavid Howells if (in_softirq() || work_busy(&conn->processor) ||
3643cec055cSDavid Howells timer_pending(&conn->timer))
3653cec055cSDavid Howells /* Can't use the rxrpc workqueue as we need to cancel/flush
3663cec055cSDavid Howells * something that may be running/waiting there.
3673cec055cSDavid Howells */
3683cec055cSDavid Howells schedule_work(&conn->destructor);
3693cec055cSDavid Howells else
3703cec055cSDavid Howells rxrpc_clean_up_connection(&conn->destructor);
3713cec055cSDavid Howells }
3728c3e34a4SDavid Howells }
3738c3e34a4SDavid Howells
3748c3e34a4SDavid Howells /*
37545025bceSDavid Howells * reap dead service connections
3768c3e34a4SDavid Howells */
rxrpc_service_connection_reaper(struct work_struct * work)3772baec2c3SDavid Howells void rxrpc_service_connection_reaper(struct work_struct *work)
3788c3e34a4SDavid Howells {
3798c3e34a4SDavid Howells struct rxrpc_connection *conn, *_p;
3802baec2c3SDavid Howells struct rxrpc_net *rxnet =
3813d18cbb7SDavid Howells container_of(work, struct rxrpc_net, service_conn_reaper);
382f859ab61SDavid Howells unsigned long expire_at, earliest, idle_timestamp, now;
3833cec055cSDavid Howells int active;
3848c3e34a4SDavid Howells
3858c3e34a4SDavid Howells LIST_HEAD(graveyard);
3868c3e34a4SDavid Howells
3878c3e34a4SDavid Howells _enter("");
3888c3e34a4SDavid Howells
389f51b4480SDavid Howells now = jiffies;
390f859ab61SDavid Howells earliest = now + MAX_JIFFY_OFFSET;
3918c3e34a4SDavid Howells
3922baec2c3SDavid Howells write_lock(&rxnet->conn_lock);
3932baec2c3SDavid Howells list_for_each_entry_safe(conn, _p, &rxnet->service_conns, link) {
3943cec055cSDavid Howells ASSERTCMP(atomic_read(&conn->active), >=, 0);
3953cec055cSDavid Howells if (likely(atomic_read(&conn->active) > 0))
3968c3e34a4SDavid Howells continue;
39700e90712SDavid Howells if (conn->state == RXRPC_CONN_SERVICE_PREALLOC)
39800e90712SDavid Howells continue;
3998c3e34a4SDavid Howells
4002cc80086SDavid Howells if (rxnet->live && !conn->local->dead) {
401f51b4480SDavid Howells idle_timestamp = READ_ONCE(conn->idle_timestamp);
402f859ab61SDavid Howells expire_at = idle_timestamp + rxrpc_connection_expiry * HZ;
4032cc80086SDavid Howells if (conn->local->service_closed)
404f859ab61SDavid Howells expire_at = idle_timestamp + rxrpc_closed_conn_expiry * HZ;
405f859ab61SDavid Howells
4063cec055cSDavid Howells _debug("reap CONN %d { a=%d,t=%ld }",
4073cec055cSDavid Howells conn->debug_id, atomic_read(&conn->active),
408f859ab61SDavid Howells (long)expire_at - (long)now);
409f51b4480SDavid Howells
410f859ab61SDavid Howells if (time_before(now, expire_at)) {
411f859ab61SDavid Howells if (time_before(expire_at, earliest))
412f859ab61SDavid Howells earliest = expire_at;
413001c1122SDavid Howells continue;
414001c1122SDavid Howells }
415f859ab61SDavid Howells }
416999b69f8SDavid Howells
4173cec055cSDavid Howells /* The activity count sits at 0 whilst the conn is unused on
4183cec055cSDavid Howells * the list; we reduce that to -1 to make the conn unavailable.
419001c1122SDavid Howells */
4203cec055cSDavid Howells active = 0;
4213cec055cSDavid Howells if (!atomic_try_cmpxchg(&conn->active, &active, -1))
422001c1122SDavid Howells continue;
4237fa25105SDavid Howells rxrpc_see_connection(conn, rxrpc_conn_see_reap_service);
424001c1122SDavid Howells
425001c1122SDavid Howells if (rxrpc_conn_is_client(conn))
42645025bceSDavid Howells BUG();
427001c1122SDavid Howells else
428001c1122SDavid Howells rxrpc_unpublish_service_conn(conn);
429001c1122SDavid Howells
430999b69f8SDavid Howells list_move_tail(&conn->link, &graveyard);
4318c3e34a4SDavid Howells }
4322baec2c3SDavid Howells write_unlock(&rxnet->conn_lock);
4338c3e34a4SDavid Howells
434f859ab61SDavid Howells if (earliest != now + MAX_JIFFY_OFFSET) {
435f859ab61SDavid Howells _debug("reschedule reaper %ld", (long)earliest - (long)now);
436f51b4480SDavid Howells ASSERT(time_after(earliest, now));
4373d18cbb7SDavid Howells rxrpc_set_service_reap_timer(rxnet, earliest);
4388c3e34a4SDavid Howells }
4398c3e34a4SDavid Howells
4408c3e34a4SDavid Howells while (!list_empty(&graveyard)) {
4418c3e34a4SDavid Howells conn = list_entry(graveyard.next, struct rxrpc_connection,
4428c3e34a4SDavid Howells link);
4438c3e34a4SDavid Howells list_del_init(&conn->link);
4448c3e34a4SDavid Howells
4453cec055cSDavid Howells ASSERTCMP(atomic_read(&conn->active), ==, -1);
4463cec055cSDavid Howells rxrpc_put_connection(conn, rxrpc_conn_put_service_reaped);
4478c3e34a4SDavid Howells }
4488c3e34a4SDavid Howells
4498c3e34a4SDavid Howells _leave("");
4508c3e34a4SDavid Howells }
4518c3e34a4SDavid Howells
4528c3e34a4SDavid Howells /*
45345025bceSDavid Howells * preemptively destroy all the service connection records rather than
45445025bceSDavid Howells * waiting for them to time out
4558c3e34a4SDavid Howells */
rxrpc_destroy_all_connections(struct rxrpc_net * rxnet)4562baec2c3SDavid Howells void rxrpc_destroy_all_connections(struct rxrpc_net *rxnet)
4578c3e34a4SDavid Howells {
458dee46364SDavid Howells struct rxrpc_connection *conn, *_p;
459dee46364SDavid Howells bool leak = false;
460dee46364SDavid Howells
4618c3e34a4SDavid Howells _enter("");
4628c3e34a4SDavid Howells
46331f5f9a1SDavid Howells atomic_dec(&rxnet->nr_conns);
46445025bceSDavid Howells
4653d18cbb7SDavid Howells del_timer_sync(&rxnet->service_conn_reap_timer);
4663d18cbb7SDavid Howells rxrpc_queue_work(&rxnet->service_conn_reaper);
467dee46364SDavid Howells flush_workqueue(rxrpc_workqueue);
468dee46364SDavid Howells
4692baec2c3SDavid Howells write_lock(&rxnet->conn_lock);
4702baec2c3SDavid Howells list_for_each_entry_safe(conn, _p, &rxnet->service_conns, link) {
471dee46364SDavid Howells pr_err("AF_RXRPC: Leaked conn %p {%d}\n",
472a0575429SDavid Howells conn, refcount_read(&conn->ref));
473dee46364SDavid Howells leak = true;
474dee46364SDavid Howells }
4752baec2c3SDavid Howells write_unlock(&rxnet->conn_lock);
476dee46364SDavid Howells BUG_ON(leak);
477dee46364SDavid Howells
4782baec2c3SDavid Howells ASSERT(list_empty(&rxnet->conn_proc_list));
4798c3e34a4SDavid Howells
48031f5f9a1SDavid Howells /* We need to wait for the connections to be destroyed by RCU as they
48131f5f9a1SDavid Howells * pin things that we still need to get rid of.
48231f5f9a1SDavid Howells */
4835bb053beSLinus Torvalds wait_var_event(&rxnet->nr_conns, !atomic_read(&rxnet->nr_conns));
4848c3e34a4SDavid Howells _leave("");
4858c3e34a4SDavid Howells }
486