summaryrefslogtreecommitdiffstats
path: root/net/rxrpc/net_ns.c
blob: 5d6a773db973180c646e61ddea9598dca6ff453a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
/* rxrpc network namespace handling.
 *
 * Copyright (C) 2017 Red Hat, Inc. All Rights Reserved.
 * Written by David Howells (dhowells@redhat.com)
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public Licence
 * as published by the Free Software Foundation; either version
 * 2 of the Licence, or (at your option) any later version.
 */

#include <linux/proc_fs.h>
#include "ar-internal.h"

unsigned int rxrpc_net_id;

static void rxrpc_client_conn_reap_timeout(struct timer_list *timer)
{
	struct rxrpc_net *rxnet =
		container_of(timer, struct rxrpc_net, client_conn_reap_timer);

	if (rxnet->live)
		rxrpc_queue_work(&rxnet->client_conn_reaper);
}

static void rxrpc_service_conn_reap_timeout(struct timer_list *timer)
{
	struct rxrpc_net *rxnet =
		container_of(timer, struct rxrpc_net, service_conn_reap_timer);

	if (rxnet->live)
		rxrpc_queue_work(&rxnet->service_conn_reaper);
}

static void rxrpc_peer_keepalive_timeout(struct timer_list *timer)
{
	struct rxrpc_net *rxnet =
		container_of(timer, struct rxrpc_net, peer_keepalive_timer);

	if (rxnet->live)
		rxrpc_queue_work(&rxnet->peer_keepalive_work);
}

/*
 * Initialise a per-network namespace record.
 */
static __net_init int rxrpc_init_net(struct net *net)
{
	struct rxrpc_net *rxnet = rxrpc_net(net);
	int ret, i;

	rxnet->live = true;
	get_random_bytes(&rxnet->epoch, sizeof(rxnet->epoch));
	rxnet->epoch |= RXRPC_RANDOM_EPOCH;

	INIT_LIST_HEAD(&rxnet->calls);
	rwlock_init(&rxnet->call_lock);
	atomic_set(&rxnet->nr_calls, 1);

	atomic_set(&rxnet->nr_conns, 1);
	INIT_LIST_HEAD(&rxnet->conn_proc_list);
	INIT_LIST_HEAD(&rxnet->service_conns);
	rwlock_init(&rxnet->conn_lock);
	INIT_WORK(&rxnet->service_conn_reaper,
		  rxrpc_service_connection_reaper);
	timer_setup(&rxnet->service_conn_reap_timer,
		    rxrpc_service_conn_reap_timeout, 0);

	rxnet->nr_client_conns = 0;
	rxnet->nr_active_client_conns = 0;
	rxnet->kill_all_client_conns = false;
	spin_lock_init(&rxnet->client_conn_cache_lock);
	spin_lock_init(&rxnet->client_conn_discard_lock);
	INIT_LIST_HEAD(&rxnet->waiting_client_conns);
	INIT_LIST_HEAD(&rxnet->active_client_conns);
	INIT_LIST_HEAD(&rxnet->idle_client_conns);
	INIT_WORK(&rxnet->client_conn_reaper,
		  rxrpc_discard_expired_client_conns);
	timer_setup(&rxnet->client_conn_reap_timer,
		    rxrpc_client_conn_reap_timeout, 0);

	INIT_LIST_HEAD(&rxnet->local_endpoints);
	mutex_init(&rxnet->local_mutex);

	hash_init(rxnet->peer_hash);
	spin_lock_init(&rxnet->peer_hash_lock);
	for (i = 0; i < ARRAY_SIZE(rxnet->peer_keepalive); i++)
		INIT_HLIST_HEAD(&rxnet->peer_keepalive[i]);
	INIT_HLIST_HEAD(&rxnet->peer_keepalive_new);
	timer_setup(&rxnet->peer_keepalive_timer,
		    rxrpc_peer_keepalive_timeout, 0);
	INIT_WORK(&rxnet->peer_keepalive_work, rxrpc_peer_keepalive_worker);
	rxnet->peer_keepalive_base = ktime_add(ktime_get_real(), NSEC_PER_SEC);

	ret = -ENOMEM;
	rxnet->proc_net = proc_net_mkdir(net, "rxrpc", net->proc_net);
	if (!rxnet->proc_net)
		goto err_proc;

	proc_create_net("calls", 0444, rxnet->proc_net, &rxrpc_call_seq_ops,
			sizeof(struct seq_net_private));
	proc_create_net("conns", 0444, rxnet->proc_net,
			&rxrpc_connection_seq_ops,
			sizeof(struct seq_net_private));
	return 0;

err_proc:
	rxnet->live = false;
	return ret;
}

/*
 * Clean up a per-network namespace record.
 */
static __net_exit void rxrpc_exit_net(struct net *net)
{
	struct rxrpc_net *rxnet = rxrpc_net(net);

	rxnet->live = false;
	del_timer_sync(&rxnet->peer_keepalive_timer);
	cancel_work_sync(&rxnet->peer_keepalive_work);
	rxrpc_destroy_all_calls(rxnet);
	rxrpc_destroy_all_connections(rxnet);
	rxrpc_destroy_all_peers(rxnet);
	rxrpc_destroy_all_locals(rxnet);
	proc_remove(rxnet->proc_net);
}

struct pernet_operations rxrpc_net_ops = {
	.init	= rxrpc_init_net,
	.exit	= rxrpc_exit_net,
	.id	= &rxrpc_net_id,
	.size	= sizeof(struct rxrpc_net),
};