Blame view

fs/afs/server.c 15.2 KB
2874c5fd2   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-or-later
ec26815ad   David Howells   [AFS]: Clean up t...
2
  /* AFS server record management
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
3
   *
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
4
   * Copyright (C) 2002, 2007 Red Hat, Inc. All Rights Reserved.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
5
   * Written by David Howells (dhowells@redhat.com)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
6
7
8
9
   */
  
  #include <linux/sched.h>
  #include <linux/slab.h>
4d9df9868   David Howells   afs: Keep and pas...
10
  #include "afs_fs.h"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
11
  #include "internal.h"
30062bd13   David Howells   afs: Implement YF...
12
  #include "protocol_yfs.h"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
13

d2ddc776a   David Howells   afs: Overhaul vol...
14
15
  static unsigned afs_server_gc_delay = 10;	/* Server record timeout in seconds */
  static unsigned afs_server_update_delay = 30;	/* Time till VLDB recheck in secs */
452181936   David Howells   afs: Trace afs_se...
16
  static atomic_t afs_server_debug_id;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
17

59fa1c4a9   David Howells   afs: Fix server r...
18
19
20
21
22
23
24
25
  static void afs_inc_servers_outstanding(struct afs_net *net)
  {
  	atomic_inc(&net->servers_outstanding);
  }
  
  static void afs_dec_servers_outstanding(struct afs_net *net)
  {
  	if (atomic_dec_and_test(&net->servers_outstanding))
ab1fbe324   Peter Zijlstra   sched/wait, fs/af...
26
  		wake_up_var(&net->servers_outstanding);
59fa1c4a9   David Howells   afs: Fix server r...
27
  }
d2ddc776a   David Howells   afs: Overhaul vol...
28
29
30
31
32
  /*
   * Find a server by one of its addresses.
   */
  struct afs_server *afs_find_server(struct afs_net *net,
  				   const struct sockaddr_rxrpc *srx)
59fa1c4a9   David Howells   afs: Fix server r...
33
  {
d2ddc776a   David Howells   afs: Overhaul vol...
34
35
36
  	const struct afs_addr_list *alist;
  	struct afs_server *server = NULL;
  	unsigned int i;
d2ddc776a   David Howells   afs: Overhaul vol...
37
  	int seq = 0, diff;
d2ddc776a   David Howells   afs: Overhaul vol...
38
39
40
41
  	rcu_read_lock();
  
  	do {
  		if (server)
452181936   David Howells   afs: Trace afs_se...
42
  			afs_put_server(net, server, afs_server_trace_put_find_rsq);
d2ddc776a   David Howells   afs: Overhaul vol...
43
44
  		server = NULL;
  		read_seqbegin_or_lock(&net->fs_addr_lock, &seq);
79ce91d27   Marc Dionne   afs: Fix afs_find...
45
46
  		if (srx->transport.family == AF_INET6) {
  			const struct sockaddr_in6 *a = &srx->transport.sin6, *b;
d2ddc776a   David Howells   afs: Overhaul vol...
47
48
49
50
  			hlist_for_each_entry_rcu(server, &net->fs_addresses6, addr6_link) {
  				alist = rcu_dereference(server->addresses);
  				for (i = alist->nr_ipv4; i < alist->nr_addrs; i++) {
  					b = &alist->addrs[i].transport.sin6;
fe342cf77   David Howells   afs: Fix checker ...
51
52
  					diff = ((u16 __force)a->sin6_port -
  						(u16 __force)b->sin6_port);
d2ddc776a   David Howells   afs: Overhaul vol...
53
54
55
56
57
58
  					if (diff == 0)
  						diff = memcmp(&a->sin6_addr,
  							      &b->sin6_addr,
  							      sizeof(struct in6_addr));
  					if (diff == 0)
  						goto found;
d2ddc776a   David Howells   afs: Overhaul vol...
59
60
61
  				}
  			}
  		} else {
79ce91d27   Marc Dionne   afs: Fix afs_find...
62
  			const struct sockaddr_in *a = &srx->transport.sin, *b;
d2ddc776a   David Howells   afs: Overhaul vol...
63
64
65
  			hlist_for_each_entry_rcu(server, &net->fs_addresses4, addr4_link) {
  				alist = rcu_dereference(server->addresses);
  				for (i = 0; i < alist->nr_ipv4; i++) {
79ce91d27   Marc Dionne   afs: Fix afs_find...
66
67
68
  					b = &alist->addrs[i].transport.sin;
  					diff = ((u16 __force)a->sin_port -
  						(u16 __force)b->sin_port);
d2ddc776a   David Howells   afs: Overhaul vol...
69
  					if (diff == 0)
79ce91d27   Marc Dionne   afs: Fix afs_find...
70
71
  						diff = ((u32 __force)a->sin_addr.s_addr -
  							(u32 __force)b->sin_addr.s_addr);
d2ddc776a   David Howells   afs: Overhaul vol...
72
73
  					if (diff == 0)
  						goto found;
d2ddc776a   David Howells   afs: Overhaul vol...
74
75
76
  				}
  			}
  		}
59fa1c4a9   David Howells   afs: Fix server r...
77

d2ddc776a   David Howells   afs: Overhaul vol...
78
79
80
81
82
83
84
85
86
87
88
  		server = NULL;
  	found:
  		if (server && !atomic_inc_not_zero(&server->usage))
  			server = NULL;
  
  	} while (need_seqretry(&net->fs_addr_lock, seq));
  
  	done_seqretry(&net->fs_addr_lock, seq);
  
  	rcu_read_unlock();
  	return server;
59fa1c4a9   David Howells   afs: Fix server r...
89
  }
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
90
  /*
d2ddc776a   David Howells   afs: Overhaul vol...
91
   * Look up a server by its UUID
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
92
   */
d2ddc776a   David Howells   afs: Overhaul vol...
93
  struct afs_server *afs_find_server_by_uuid(struct afs_net *net, const uuid_t *uuid)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
94
  {
d2ddc776a   David Howells   afs: Overhaul vol...
95
96
97
98
99
100
101
102
103
104
105
106
  	struct afs_server *server = NULL;
  	struct rb_node *p;
  	int diff, seq = 0;
  
  	_enter("%pU", uuid);
  
  	do {
  		/* Unfortunately, rbtree walking doesn't give reliable results
  		 * under just the RCU read lock, so we have to check for
  		 * changes.
  		 */
  		if (server)
452181936   David Howells   afs: Trace afs_se...
107
  			afs_put_server(net, server, afs_server_trace_put_uuid_rsq);
d2ddc776a   David Howells   afs: Overhaul vol...
108
109
110
111
112
113
114
115
116
117
118
119
120
121
  		server = NULL;
  
  		read_seqbegin_or_lock(&net->fs_lock, &seq);
  
  		p = net->fs_servers.rb_node;
  		while (p) {
  			server = rb_entry(p, struct afs_server, uuid_rb);
  
  			diff = memcmp(uuid, &server->uuid, sizeof(*uuid));
  			if (diff < 0) {
  				p = p->rb_left;
  			} else if (diff > 0) {
  				p = p->rb_right;
  			} else {
452181936   David Howells   afs: Trace afs_se...
122
  				afs_get_server(server, afs_server_trace_get_by_uuid);
d2ddc776a   David Howells   afs: Overhaul vol...
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
  				break;
  			}
  
  			server = NULL;
  		}
  	} while (need_seqretry(&net->fs_lock, seq));
  
  	done_seqretry(&net->fs_lock, seq);
  
  	_leave(" = %p", server);
  	return server;
  }
  
  /*
   * Install a server record in the namespace tree
   */
  static struct afs_server *afs_install_server(struct afs_net *net,
  					     struct afs_server *candidate)
  {
  	const struct afs_addr_list *alist;
  	struct afs_server *server;
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
144
  	struct rb_node **pp, *p;
d2ddc776a   David Howells   afs: Overhaul vol...
145
  	int ret = -EEXIST, diff;
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
146

d2ddc776a   David Howells   afs: Overhaul vol...
147
  	_enter("%p", candidate);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
148

d2ddc776a   David Howells   afs: Overhaul vol...
149
  	write_seqlock(&net->fs_lock);
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
150

d2ddc776a   David Howells   afs: Overhaul vol...
151
152
  	/* Firstly install the server in the UUID lookup tree */
  	pp = &net->fs_servers.rb_node;
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
153
154
155
156
  	p = NULL;
  	while (*pp) {
  		p = *pp;
  		_debug("- consider %p", p);
d2ddc776a   David Howells   afs: Overhaul vol...
157
158
  		server = rb_entry(p, struct afs_server, uuid_rb);
  		diff = memcmp(&candidate->uuid, &server->uuid, sizeof(uuid_t));
4d9df9868   David Howells   afs: Keep and pas...
159
  		if (diff < 0)
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
160
  			pp = &(*pp)->rb_left;
4d9df9868   David Howells   afs: Keep and pas...
161
  		else if (diff > 0)
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
162
163
  			pp = &(*pp)->rb_right;
  		else
d2ddc776a   David Howells   afs: Overhaul vol...
164
  			goto exists;
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
165
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
166

d2ddc776a   David Howells   afs: Overhaul vol...
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
  	server = candidate;
  	rb_link_node(&server->uuid_rb, p, pp);
  	rb_insert_color(&server->uuid_rb, &net->fs_servers);
  	hlist_add_head_rcu(&server->proc_link, &net->fs_proc);
  
  	write_seqlock(&net->fs_addr_lock);
  	alist = rcu_dereference_protected(server->addresses,
  					  lockdep_is_held(&net->fs_addr_lock.lock));
  
  	/* Secondly, if the server has any IPv4 and/or IPv6 addresses, install
  	 * it in the IPv4 and/or IPv6 reverse-map lists.
  	 *
  	 * TODO: For speed we want to use something other than a flat list
  	 * here; even sorting the list in terms of lowest address would help a
  	 * bit, but anything we might want to do gets messy and memory
  	 * intensive.
  	 */
  	if (alist->nr_ipv4 > 0)
  		hlist_add_head_rcu(&server->addr4_link, &net->fs_addresses4);
  	if (alist->nr_addrs > alist->nr_ipv4)
  		hlist_add_head_rcu(&server->addr6_link, &net->fs_addresses6);
  
  	write_sequnlock(&net->fs_addr_lock);
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
190
  	ret = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
191

d2ddc776a   David Howells   afs: Overhaul vol...
192
  exists:
452181936   David Howells   afs: Trace afs_se...
193
  	afs_get_server(server, afs_server_trace_get_install);
d2ddc776a   David Howells   afs: Overhaul vol...
194
195
  	write_sequnlock(&net->fs_lock);
  	return server;
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
196
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
197

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
198
  /*
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
199
   * allocate a new server record
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
200
   */
d2ddc776a   David Howells   afs: Overhaul vol...
201
202
203
  static struct afs_server *afs_alloc_server(struct afs_net *net,
  					   const uuid_t *uuid,
  					   struct afs_addr_list *alist)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
204
  {
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
205
  	struct afs_server *server;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
206

08e0e7c82   David Howells   [AF_RXRPC]: Make ...
207
  	_enter("");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
208

b593e48d2   Yan Burman   [PATCH] affs: rep...
209
  	server = kzalloc(sizeof(struct afs_server), GFP_KERNEL);
8b2a464ce   David Howells   afs: Add an addre...
210
211
  	if (!server)
  		goto enomem;
8b2a464ce   David Howells   afs: Add an addre...
212
213
  
  	atomic_set(&server->usage, 1);
452181936   David Howells   afs: Trace afs_se...
214
  	server->debug_id = atomic_inc_return(&afs_server_debug_id);
d2ddc776a   David Howells   afs: Overhaul vol...
215
216
217
  	RCU_INIT_POINTER(server->addresses, alist);
  	server->addr_version = alist->version;
  	server->uuid = *uuid;
d2ddc776a   David Howells   afs: Overhaul vol...
218
219
  	server->update_at = ktime_get_real_seconds() + afs_server_update_delay;
  	rwlock_init(&server->fs_lock);
47ea0f2eb   David Howells   afs: Optimise cal...
220
  	INIT_HLIST_HEAD(&server->cb_volumes);
8b2a464ce   David Howells   afs: Add an addre...
221
  	rwlock_init(&server->cb_break_lock);
3bf0fb6f3   David Howells   afs: Probe multip...
222
223
  	init_waitqueue_head(&server->probe_wq);
  	spin_lock_init(&server->probe_lock);
8b2a464ce   David Howells   afs: Add an addre...
224

d2ddc776a   David Howells   afs: Overhaul vol...
225
  	afs_inc_servers_outstanding(net);
452181936   David Howells   afs: Trace afs_se...
226
  	trace_afs_server(server, 1, afs_server_trace_alloc);
d2ddc776a   David Howells   afs: Overhaul vol...
227
  	_leave(" = %p", server);
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
228
  	return server;
8b2a464ce   David Howells   afs: Add an addre...
229

8b2a464ce   David Howells   afs: Add an addre...
230
231
232
  enomem:
  	_leave(" = NULL [nomem]");
  	return NULL;
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
233
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
234

08e0e7c82   David Howells   [AF_RXRPC]: Make ...
235
  /*
d2ddc776a   David Howells   afs: Overhaul vol...
236
   * Look up an address record for a server
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
237
   */
d2ddc776a   David Howells   afs: Overhaul vol...
238
239
  static struct afs_addr_list *afs_vl_lookup_addrs(struct afs_cell *cell,
  						 struct key *key, const uuid_t *uuid)
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
240
  {
0a5143f2f   David Howells   afs: Implement VL...
241
242
  	struct afs_vl_cursor vc;
  	struct afs_addr_list *alist = NULL;
d2ddc776a   David Howells   afs: Overhaul vol...
243
  	int ret;
0a5143f2f   David Howells   afs: Implement VL...
244
245
246
  	ret = -ERESTARTSYS;
  	if (afs_begin_vlserver_operation(&vc, cell, key)) {
  		while (afs_select_vlserver(&vc)) {
3bf0fb6f3   David Howells   afs: Probe multip...
247
  			if (test_bit(AFS_VLSERVER_FL_IS_YFS, &vc.server->flags))
0a5143f2f   David Howells   afs: Implement VL...
248
249
250
  				alist = afs_yfsvl_get_endpoints(&vc, uuid);
  			else
  				alist = afs_vl_get_addrs_u(&vc, uuid);
d2ddc776a   David Howells   afs: Overhaul vol...
251
  		}
0a5143f2f   David Howells   afs: Implement VL...
252
253
  
  		ret = afs_end_vlserver_operation(&vc);
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
254
  	}
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
255

0a5143f2f   David Howells   afs: Implement VL...
256
  	return ret < 0 ? ERR_PTR(ret) : alist;
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
257
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
258

08e0e7c82   David Howells   [AF_RXRPC]: Make ...
259
  /*
d2ddc776a   David Howells   afs: Overhaul vol...
260
   * Get or create a fileserver record.
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
261
   */
d2ddc776a   David Howells   afs: Overhaul vol...
262
263
  struct afs_server *afs_lookup_server(struct afs_cell *cell, struct key *key,
  				     const uuid_t *uuid)
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
264
  {
d2ddc776a   David Howells   afs: Overhaul vol...
265
266
  	struct afs_addr_list *alist;
  	struct afs_server *server, *candidate;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
267

d2ddc776a   David Howells   afs: Overhaul vol...
268
  	_enter("%p,%pU", cell->net, uuid);
8324f0bcf   David Howells   rxrpc: Provide a ...
269

d2ddc776a   David Howells   afs: Overhaul vol...
270
271
272
  	server = afs_find_server_by_uuid(cell->net, uuid);
  	if (server)
  		return server;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
273

d2ddc776a   David Howells   afs: Overhaul vol...
274
275
276
  	alist = afs_vl_lookup_addrs(cell, key, uuid);
  	if (IS_ERR(alist))
  		return ERR_CAST(alist);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
277

d2ddc776a   David Howells   afs: Overhaul vol...
278
279
280
281
282
  	candidate = afs_alloc_server(cell->net, uuid, alist);
  	if (!candidate) {
  		afs_put_addrlist(alist);
  		return ERR_PTR(-ENOMEM);
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
283

d2ddc776a   David Howells   afs: Overhaul vol...
284
285
286
287
  	server = afs_install_server(cell->net, candidate);
  	if (server != candidate) {
  		afs_put_addrlist(alist);
  		kfree(candidate);
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
288
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
289

d2ddc776a   David Howells   afs: Overhaul vol...
290
  	_leave(" = %p{%d}", server, atomic_read(&server->usage));
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
291
  	return server;
ec26815ad   David Howells   [AFS]: Clean up t...
292
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
293

d2ddc776a   David Howells   afs: Overhaul vol...
294
295
296
297
  /*
   * Set the server timer to fire after a given delay, assuming it's not already
   * set for an earlier time.
   */
59fa1c4a9   David Howells   afs: Fix server r...
298
299
  static void afs_set_server_timer(struct afs_net *net, time64_t delay)
  {
59fa1c4a9   David Howells   afs: Fix server r...
300
  	if (net->live) {
d2ddc776a   David Howells   afs: Overhaul vol...
301
302
  		afs_inc_servers_outstanding(net);
  		if (timer_reduce(&net->fs_timer, jiffies + delay * HZ))
59fa1c4a9   David Howells   afs: Fix server r...
303
304
305
  			afs_dec_servers_outstanding(net);
  	}
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
306
  /*
d2ddc776a   David Howells   afs: Overhaul vol...
307
308
309
310
311
312
313
314
315
316
317
318
319
   * Server management timer.  We have an increment on fs_outstanding that we
   * need to pass along to the work item.
   */
  void afs_servers_timer(struct timer_list *timer)
  {
  	struct afs_net *net = container_of(timer, struct afs_net, fs_timer);
  
  	_enter("");
  	if (!queue_work(afs_wq, &net->fs_manager))
  		afs_dec_servers_outstanding(net);
  }
  
  /*
452181936   David Howells   afs: Trace afs_se...
320
321
322
323
324
325
326
327
328
329
330
331
   * Get a reference on a server object.
   */
  struct afs_server *afs_get_server(struct afs_server *server,
  				  enum afs_server_trace reason)
  {
  	unsigned int u = atomic_inc_return(&server->usage);
  
  	trace_afs_server(server, u, reason);
  	return server;
  }
  
  /*
d2ddc776a   David Howells   afs: Overhaul vol...
332
   * Release a reference on a server record.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
333
   */
452181936   David Howells   afs: Trace afs_se...
334
335
  void afs_put_server(struct afs_net *net, struct afs_server *server,
  		    enum afs_server_trace reason)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
336
  {
d2ddc776a   David Howells   afs: Overhaul vol...
337
  	unsigned int usage;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
338
339
  	if (!server)
  		return;
d2ddc776a   David Howells   afs: Overhaul vol...
340
  	server->put_time = ktime_get_real_seconds();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
341

d2ddc776a   David Howells   afs: Overhaul vol...
342
  	usage = atomic_dec_return(&server->usage);
260a98031   David Howells   [AFS]: Add "direc...
343

452181936   David Howells   afs: Trace afs_se...
344
  	trace_afs_server(server, usage, reason);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
345

d2ddc776a   David Howells   afs: Overhaul vol...
346
  	if (likely(usage > 0))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
347
  		return;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
348

d2ddc776a   David Howells   afs: Overhaul vol...
349
350
351
352
353
354
  	afs_set_server_timer(net, afs_server_gc_delay);
  }
  
  static void afs_server_rcu(struct rcu_head *rcu)
  {
  	struct afs_server *server = container_of(rcu, struct afs_server, rcu);
452181936   David Howells   afs: Trace afs_se...
355
356
  	trace_afs_server(server, atomic_read(&server->usage),
  			 afs_server_trace_free);
fe342cf77   David Howells   afs: Fix checker ...
357
  	afs_put_addrlist(rcu_access_pointer(server->addresses));
d2ddc776a   David Howells   afs: Overhaul vol...
358
  	kfree(server);
ec26815ad   David Howells   [AFS]: Clean up t...
359
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
360

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
361
  /*
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
362
   * destroy a dead server
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
363
   */
59fa1c4a9   David Howells   afs: Fix server r...
364
  static void afs_destroy_server(struct afs_net *net, struct afs_server *server)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
365
  {
fe342cf77   David Howells   afs: Fix checker ...
366
  	struct afs_addr_list *alist = rcu_access_pointer(server->addresses);
8b2a464ce   David Howells   afs: Add an addre...
367
368
  	struct afs_addr_cursor ac = {
  		.alist	= alist,
3bf0fb6f3   David Howells   afs: Probe multip...
369
  		.index	= alist->preferred,
8b2a464ce   David Howells   afs: Add an addre...
370
371
  		.error	= 0,
  	};
452181936   David Howells   afs: Trace afs_se...
372
373
374
  
  	trace_afs_server(server, atomic_read(&server->usage),
  			 afs_server_trace_give_up_cb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
375

f2686b092   David Howells   afs: Fix giving u...
376
377
  	if (test_bit(AFS_SERVER_FL_MAY_HAVE_CB, &server->flags))
  		afs_fs_give_up_all_callbacks(net, server, &ac, NULL);
3bf0fb6f3   David Howells   afs: Probe multip...
378
379
  	wait_var_event(&server->probe_outstanding,
  		       atomic_read(&server->probe_outstanding) == 0);
452181936   David Howells   afs: Trace afs_se...
380
381
  	trace_afs_server(server, atomic_read(&server->usage),
  			 afs_server_trace_destroy);
d2ddc776a   David Howells   afs: Overhaul vol...
382
  	call_rcu(&server->rcu, afs_server_rcu);
59fa1c4a9   David Howells   afs: Fix server r...
383
  	afs_dec_servers_outstanding(net);
ec26815ad   David Howells   [AFS]: Clean up t...
384
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
385

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
386
  /*
d2ddc776a   David Howells   afs: Overhaul vol...
387
   * Garbage collect any expired servers.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
388
   */
d2ddc776a   David Howells   afs: Overhaul vol...
389
  static void afs_gc_servers(struct afs_net *net, struct afs_server *gc_list)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
390
  {
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
391
  	struct afs_server *server;
d2ddc776a   David Howells   afs: Overhaul vol...
392
393
394
395
396
397
398
399
400
  	bool deleted;
  	int usage;
  
  	while ((server = gc_list)) {
  		gc_list = server->gc_next;
  
  		write_seqlock(&net->fs_lock);
  		usage = 1;
  		deleted = atomic_try_cmpxchg(&server->usage, &usage, 0);
452181936   David Howells   afs: Trace afs_se...
401
  		trace_afs_server(server, usage, afs_server_trace_gc);
d2ddc776a   David Howells   afs: Overhaul vol...
402
403
404
  		if (deleted) {
  			rb_erase(&server->uuid_rb, &net->fs_servers);
  			hlist_del_rcu(&server->proc_link);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
405
  		}
d2ddc776a   David Howells   afs: Overhaul vol...
406
  		write_sequnlock(&net->fs_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
407

660625922   David Howells   afs: Fix server r...
408
409
410
411
412
413
414
  		if (deleted) {
  			write_seqlock(&net->fs_addr_lock);
  			if (!hlist_unhashed(&server->addr4_link))
  				hlist_del_rcu(&server->addr4_link);
  			if (!hlist_unhashed(&server->addr6_link))
  				hlist_del_rcu(&server->addr6_link);
  			write_sequnlock(&net->fs_addr_lock);
d2ddc776a   David Howells   afs: Overhaul vol...
415
  			afs_destroy_server(net, server);
660625922   David Howells   afs: Fix server r...
416
  		}
d2ddc776a   David Howells   afs: Overhaul vol...
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
  	}
  }
  
  /*
   * Manage the records of servers known to be within a network namespace.  This
   * includes garbage collecting unused servers.
   *
   * Note also that we were given an increment on net->servers_outstanding by
   * whoever queued us that we need to deal with before returning.
   */
  void afs_manage_servers(struct work_struct *work)
  {
  	struct afs_net *net = container_of(work, struct afs_net, fs_manager);
  	struct afs_server *gc_list = NULL;
  	struct rb_node *cursor;
  	time64_t now = ktime_get_real_seconds(), next_manage = TIME64_MAX;
  	bool purging = !net->live;
  
  	_enter("");
  
  	/* Trawl the server list looking for servers that have expired from
  	 * lack of use.
  	 */
  	read_seqlock_excl(&net->fs_lock);
  
  	for (cursor = rb_first(&net->fs_servers); cursor; cursor = rb_next(cursor)) {
  		struct afs_server *server =
  			rb_entry(cursor, struct afs_server, uuid_rb);
  		int usage = atomic_read(&server->usage);
  
  		_debug("manage %pU %u", &server->uuid, usage);
  
  		ASSERTCMP(usage, >=, 1);
  		ASSERTIFCMP(purging, usage, ==, 1);
  
  		if (usage == 1) {
  			time64_t expire_at = server->put_time;
  
  			if (!test_bit(AFS_SERVER_FL_VL_FAIL, &server->flags) &&
  			    !test_bit(AFS_SERVER_FL_NOT_FOUND, &server->flags))
  				expire_at += afs_server_gc_delay;
  			if (purging || expire_at <= now) {
  				server->gc_next = gc_list;
  				gc_list = server;
  			} else if (expire_at < next_manage) {
  				next_manage = expire_at;
  			}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
464
465
  		}
  	}
d2ddc776a   David Howells   afs: Overhaul vol...
466
  	read_sequnlock_excl(&net->fs_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
467

d2ddc776a   David Howells   afs: Overhaul vol...
468
469
470
471
472
473
474
475
476
477
478
479
480
  	/* Update the timer on the way out.  We have to pass an increment on
  	 * servers_outstanding in the namespace that we are in to the timer or
  	 * the work scheduler.
  	 */
  	if (!purging && next_manage < TIME64_MAX) {
  		now = ktime_get_real_seconds();
  
  		if (next_manage - now <= 0) {
  			if (queue_work(afs_wq, &net->fs_manager))
  				afs_inc_servers_outstanding(net);
  		} else {
  			afs_set_server_timer(net, next_manage - now);
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
481
  	}
59fa1c4a9   David Howells   afs: Fix server r...
482

d2ddc776a   David Howells   afs: Overhaul vol...
483
  	afs_gc_servers(net, gc_list);
59fa1c4a9   David Howells   afs: Fix server r...
484
  	afs_dec_servers_outstanding(net);
d2ddc776a   David Howells   afs: Overhaul vol...
485
486
487
488
489
490
491
492
  	_leave(" [%d]", atomic_read(&net->servers_outstanding));
  }
  
  static void afs_queue_server_manager(struct afs_net *net)
  {
  	afs_inc_servers_outstanding(net);
  	if (!queue_work(afs_wq, &net->fs_manager))
  		afs_dec_servers_outstanding(net);
ec26815ad   David Howells   [AFS]: Clean up t...
493
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
494

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
495
  /*
d2ddc776a   David Howells   afs: Overhaul vol...
496
   * Purge list of servers.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
497
   */
d2ddc776a   David Howells   afs: Overhaul vol...
498
  void afs_purge_servers(struct afs_net *net)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
499
  {
d2ddc776a   David Howells   afs: Overhaul vol...
500
501
502
  	_enter("");
  
  	if (del_timer_sync(&net->fs_timer))
59fa1c4a9   David Howells   afs: Fix server r...
503
  		atomic_dec(&net->servers_outstanding);
d2ddc776a   David Howells   afs: Overhaul vol...
504
  	afs_queue_server_manager(net);
59fa1c4a9   David Howells   afs: Fix server r...
505

d2ddc776a   David Howells   afs: Overhaul vol...
506
  	_debug("wait");
ab1fbe324   Peter Zijlstra   sched/wait, fs/af...
507
508
  	wait_var_event(&net->servers_outstanding,
  		       !atomic_read(&net->servers_outstanding));
d2ddc776a   David Howells   afs: Overhaul vol...
509
510
511
512
  	_leave("");
  }
  
  /*
d2ddc776a   David Howells   afs: Overhaul vol...
513
514
515
516
517
518
519
   * Get an update for a server's address list.
   */
  static noinline bool afs_update_server_record(struct afs_fs_cursor *fc, struct afs_server *server)
  {
  	struct afs_addr_list *alist, *discard;
  
  	_enter("");
452181936   David Howells   afs: Trace afs_se...
520
  	trace_afs_server(server, atomic_read(&server->usage), afs_server_trace_update);
d2ddc776a   David Howells   afs: Overhaul vol...
521
522
523
  	alist = afs_vl_lookup_addrs(fc->vnode->volume->cell, fc->key,
  				    &server->uuid);
  	if (IS_ERR(alist)) {
20b8391ff   David Howells   afs: Make some RP...
524
525
526
527
528
529
530
  		if ((PTR_ERR(alist) == -ERESTARTSYS ||
  		     PTR_ERR(alist) == -EINTR) &&
  		    !(fc->flags & AFS_FS_CURSOR_INTR) &&
  		    server->addresses) {
  			_leave(" = t [intr]");
  			return true;
  		}
0ab4c9594   David Howells   afs: Fix error pr...
531
532
  		fc->error = PTR_ERR(alist);
  		_leave(" = f [%d]", fc->error);
d2ddc776a   David Howells   afs: Overhaul vol...
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
  		return false;
  	}
  
  	discard = alist;
  	if (server->addr_version != alist->version) {
  		write_lock(&server->fs_lock);
  		discard = rcu_dereference_protected(server->addresses,
  						    lockdep_is_held(&server->fs_lock));
  		rcu_assign_pointer(server->addresses, alist);
  		server->addr_version = alist->version;
  		write_unlock(&server->fs_lock);
  	}
  
  	server->update_at = ktime_get_real_seconds() + afs_server_update_delay;
  	afs_put_addrlist(discard);
  	_leave(" = t");
  	return true;
  }
  
  /*
   * See if a server's address list needs updating.
   */
  bool afs_check_server_record(struct afs_fs_cursor *fc, struct afs_server *server)
  {
  	time64_t now = ktime_get_real_seconds();
  	long diff;
  	bool success;
  	int ret, retries = 0;
  
  	_enter("");
  
  	ASSERT(server);
  
  retry:
  	diff = READ_ONCE(server->update_at) - now;
  	if (diff > 0) {
  		_leave(" = t [not now %ld]", diff);
  		return true;
  	}
  
  	if (!test_and_set_bit_lock(AFS_SERVER_FL_UPDATING, &server->flags)) {
  		success = afs_update_server_record(fc, server);
  		clear_bit_unlock(AFS_SERVER_FL_UPDATING, &server->flags);
  		wake_up_bit(&server->flags, AFS_SERVER_FL_UPDATING);
  		_leave(" = %d", success);
  		return success;
  	}
  
  	ret = wait_on_bit(&server->flags, AFS_SERVER_FL_UPDATING,
c2bdc86ec   David Howells   afs: Make record ...
582
583
  			  (fc->flags & AFS_FS_CURSOR_INTR) ?
  			  TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE);
d2ddc776a   David Howells   afs: Overhaul vol...
584
  	if (ret == -ERESTARTSYS) {
0ab4c9594   David Howells   afs: Fix error pr...
585
  		fc->error = ret;
d2ddc776a   David Howells   afs: Overhaul vol...
586
587
588
589
590
591
592
593
594
595
596
  		_leave(" = f [intr]");
  		return false;
  	}
  
  	retries++;
  	if (retries == 4) {
  		_leave(" = f [stale]");
  		ret = -ESTALE;
  		return false;
  	}
  	goto retry;
ec26815ad   David Howells   [AFS]: Clean up t...
597
  }