Blame view

fs/afs/cell.c 23.7 KB
2874c5fd2   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-or-later
ec26815ad   David Howells   [AFS]: Clean up t...
2
  /* AFS cell and server record management
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
3
   *
989782dcd   David Howells   afs: Overhaul cel...
4
   * Copyright (C) 2002, 2017 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
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
7
  #include <linux/slab.h>
00d3b7a45   David Howells   [AFS]: Add securi...
8
9
  #include <linux/key.h>
  #include <linux/ctype.h>
07567a550   Wang Lei   DNS: Make AFS go ...
10
  #include <linux/dns_resolver.h>
e8edc6e03   Alexey Dobriyan   Detach sched.h fr...
11
  #include <linux/sched.h>
3838d3ecd   David Howells   afs: Allow IPv6 a...
12
  #include <linux/inet.h>
0da0b7fd7   David Howells   afs: Display manu...
13
  #include <linux/namei.h>
00d3b7a45   David Howells   [AFS]: Add securi...
14
  #include <keys/rxrpc-type.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
15
  #include "internal.h"
fe342cf77   David Howells   afs: Fix checker ...
16
  static unsigned __read_mostly afs_cell_gc_delay = 10;
ded2f4c58   David Howells   afs: Fix TTL on V...
17
18
  static unsigned __read_mostly afs_cell_min_ttl = 10 * 60;
  static unsigned __read_mostly afs_cell_max_ttl = 24 * 60 * 60;
dca54a7bb   David Howells   afs: Add tracing ...
19
  static atomic_t cell_debug_id;
989782dcd   David Howells   afs: Overhaul cel...
20

286377f6b   David Howells   afs: Fix cell pur...
21
  static void afs_queue_cell_manager(struct afs_net *);
88c853c3f   David Howells   afs: Fix cell ref...
22
  static void afs_manage_cell_work(struct work_struct *);
989782dcd   David Howells   afs: Overhaul cel...
23
24
25
26
  
  static void afs_dec_cells_outstanding(struct afs_net *net)
  {
  	if (atomic_dec_and_test(&net->cells_outstanding))
ab1fbe324   Peter Zijlstra   sched/wait, fs/af...
27
  		wake_up_var(&net->cells_outstanding);
989782dcd   David Howells   afs: Overhaul cel...
28
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
29
  /*
989782dcd   David Howells   afs: Overhaul cel...
30
31
   * Set the cell timer to fire after a given delay, assuming it's not already
   * set for an earlier time.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
32
   */
989782dcd   David Howells   afs: Overhaul cel...
33
  static void afs_set_cell_timer(struct afs_net *net, time64_t delay)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
34
  {
989782dcd   David Howells   afs: Overhaul cel...
35
36
37
38
  	if (net->live) {
  		atomic_inc(&net->cells_outstanding);
  		if (timer_reduce(&net->cells_timer, jiffies + delay * HZ))
  			afs_dec_cells_outstanding(net);
286377f6b   David Howells   afs: Fix cell pur...
39
40
  	} else {
  		afs_queue_cell_manager(net);
989782dcd   David Howells   afs: Overhaul cel...
41
42
43
44
  	}
  }
  
  /*
92e3cc91d   David Howells   afs: Fix rapid ce...
45
46
   * Look up and get an activation reference on a cell record.  The caller must
   * hold net->cells_lock at least read-locked.
989782dcd   David Howells   afs: Overhaul cel...
47
   */
92e3cc91d   David Howells   afs: Fix rapid ce...
48
  static struct afs_cell *afs_find_cell_locked(struct afs_net *net,
dca54a7bb   David Howells   afs: Add tracing ...
49
50
  					     const char *name, unsigned int namesz,
  					     enum afs_cell_trace reason)
989782dcd   David Howells   afs: Overhaul cel...
51
52
53
  {
  	struct afs_cell *cell = NULL;
  	struct rb_node *p;
92e3cc91d   David Howells   afs: Fix rapid ce...
54
  	int n;
989782dcd   David Howells   afs: Overhaul cel...
55
56
57
58
59
60
61
  
  	_enter("%*.*s", namesz, namesz, name);
  
  	if (name && namesz == 0)
  		return ERR_PTR(-EINVAL);
  	if (namesz > AFS_MAXCELLNAME)
  		return ERR_PTR(-ENAMETOOLONG);
92e3cc91d   David Howells   afs: Fix rapid ce...
62
63
64
65
  	if (!name) {
  		cell = net->ws_cell;
  		if (!cell)
  			return ERR_PTR(-EDESTADDRREQ);
88c853c3f   David Howells   afs: Fix cell ref...
66
  		goto found;
92e3cc91d   David Howells   afs: Fix rapid ce...
67
  	}
989782dcd   David Howells   afs: Overhaul cel...
68

92e3cc91d   David Howells   afs: Fix rapid ce...
69
70
71
  	p = net->cells.rb_node;
  	while (p) {
  		cell = rb_entry(p, struct afs_cell, net_node);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
72

92e3cc91d   David Howells   afs: Fix rapid ce...
73
74
75
76
77
78
79
80
81
82
83
84
85
  		n = strncasecmp(cell->name, name,
  				min_t(size_t, cell->name_len, namesz));
  		if (n == 0)
  			n = cell->name_len - namesz;
  		if (n < 0)
  			p = p->rb_left;
  		else if (n > 0)
  			p = p->rb_right;
  		else
  			goto found;
  	}
  
  	return ERR_PTR(-ENOENT);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
86

92e3cc91d   David Howells   afs: Fix rapid ce...
87
  found:
dca54a7bb   David Howells   afs: Add tracing ...
88
  	return afs_use_cell(cell, reason);
92e3cc91d   David Howells   afs: Fix rapid ce...
89
  }
a5fb8e6c0   David Howells   afs: Fix leak in ...
90

88c853c3f   David Howells   afs: Fix cell ref...
91
92
93
  /*
   * Look up and get an activation reference on a cell record.
   */
92e3cc91d   David Howells   afs: Fix rapid ce...
94
  struct afs_cell *afs_find_cell(struct afs_net *net,
dca54a7bb   David Howells   afs: Add tracing ...
95
96
  			       const char *name, unsigned int namesz,
  			       enum afs_cell_trace reason)
92e3cc91d   David Howells   afs: Fix rapid ce...
97
98
99
100
  {
  	struct afs_cell *cell;
  
  	down_read(&net->cells_lock);
dca54a7bb   David Howells   afs: Add tracing ...
101
  	cell = afs_find_cell_locked(net, name, namesz, reason);
92e3cc91d   David Howells   afs: Fix rapid ce...
102
103
  	up_read(&net->cells_lock);
  	return cell;
989782dcd   David Howells   afs: Overhaul cel...
104
105
106
107
108
109
110
111
  }
  
  /*
   * Set up a cell record and fill in its name, VL server address list and
   * allocate an anonymous key
   */
  static struct afs_cell *afs_alloc_cell(struct afs_net *net,
  				       const char *name, unsigned int namelen,
0a5143f2f   David Howells   afs: Implement VL...
112
  				       const char *addresses)
989782dcd   David Howells   afs: Overhaul cel...
113
  {
ca1cbbdce   David Howells   afs: Fix afs_cell...
114
  	struct afs_vlserver_list *vllist;
989782dcd   David Howells   afs: Overhaul cel...
115
116
117
118
119
120
  	struct afs_cell *cell;
  	int i, ret;
  
  	ASSERT(name);
  	if (namelen == 0)
  		return ERR_PTR(-EINVAL);
07567a550   Wang Lei   DNS: Make AFS go ...
121
122
  	if (namelen > AFS_MAXCELLNAME) {
  		_leave(" = -ENAMETOOLONG");
00d3b7a45   David Howells   [AFS]: Add securi...
123
  		return ERR_PTR(-ENAMETOOLONG);
07567a550   Wang Lei   DNS: Make AFS go ...
124
  	}
a45ea48e2   David Howells   afs: Fix characte...
125
126
127
128
129
  
  	/* Prohibit cell names that contain unprintable chars, '/' and '@' or
  	 * that begin with a dot.  This also precludes "@cell".
  	 */
  	if (name[0] == '.')
37ab63688   David Howells   afs: Implement @c...
130
  		return ERR_PTR(-EINVAL);
a45ea48e2   David Howells   afs: Fix characte...
131
132
133
134
135
  	for (i = 0; i < namelen; i++) {
  		char ch = name[i];
  		if (!isprint(ch) || ch == '/' || ch == '@')
  			return ERR_PTR(-EINVAL);
  	}
00d3b7a45   David Howells   [AFS]: Add securi...
136

0a5143f2f   David Howells   afs: Implement VL...
137
  	_enter("%*.*s,%s", namelen, namelen, name, addresses);
989782dcd   David Howells   afs: Overhaul cel...
138
139
  
  	cell = kzalloc(sizeof(struct afs_cell), GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
140
141
  	if (!cell) {
  		_leave(" = -ENOMEM");
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
142
  		return ERR_PTR(-ENOMEM);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
143
  	}
719fdd329   David Howells   afs: Fix storage ...
144
145
146
147
148
  	cell->name = kmalloc(namelen + 1, GFP_KERNEL);
  	if (!cell->name) {
  		kfree(cell);
  		return ERR_PTR(-ENOMEM);
  	}
f044c8847   David Howells   afs: Lay the grou...
149
  	cell->net = net;
989782dcd   David Howells   afs: Overhaul cel...
150
151
152
  	cell->name_len = namelen;
  	for (i = 0; i < namelen; i++)
  		cell->name[i] = tolower(name[i]);
719fdd329   David Howells   afs: Fix storage ...
153
  	cell->name[i] = 0;
989782dcd   David Howells   afs: Overhaul cel...
154

88c853c3f   David Howells   afs: Fix cell ref...
155
156
157
  	atomic_set(&cell->ref, 1);
  	atomic_set(&cell->active, 0);
  	INIT_WORK(&cell->manager, afs_manage_cell_work);
20325960f   David Howells   afs: Reorganise v...
158
159
160
161
162
  	cell->volumes = RB_ROOT;
  	INIT_HLIST_HEAD(&cell->proc_volumes);
  	seqlock_init(&cell->volume_lock);
  	cell->fs_servers = RB_ROOT;
  	seqlock_init(&cell->fs_lock);
0a5143f2f   David Howells   afs: Implement VL...
163
  	rwlock_init(&cell->vl_servers_lock);
8a070a964   David Howells   afs: Detect cell ...
164
  	cell->flags = (1 << AFS_CELL_FL_CHECK_ALIAS);
4d9df9868   David Howells   afs: Keep and pas...
165

ca1cbbdce   David Howells   afs: Fix afs_cell...
166
167
  	/* Provide a VL server list, filling it in if we were given a list of
  	 * addresses to use.
989782dcd   David Howells   afs: Overhaul cel...
168
  	 */
0a5143f2f   David Howells   afs: Implement VL...
169
  	if (addresses) {
0a5143f2f   David Howells   afs: Implement VL...
170
171
172
173
174
  		vllist = afs_parse_text_addrs(net,
  					      addresses, strlen(addresses), ':',
  					      VL_SERVICE, AFS_VL_PORT);
  		if (IS_ERR(vllist)) {
  			ret = PTR_ERR(vllist);
8b2a464ce   David Howells   afs: Add an addre...
175
176
  			goto parse_failed;
  		}
00d3b7a45   David Howells   [AFS]: Add securi...
177

d5c32c89b   David Howells   afs: Fix cell DNS...
178
179
  		vllist->source = DNS_RECORD_FROM_CONFIG;
  		vllist->status = DNS_LOOKUP_NOT_DONE;
989782dcd   David Howells   afs: Overhaul cel...
180
  		cell->dns_expiry = TIME64_MAX;
ded2f4c58   David Howells   afs: Fix TTL on V...
181
  	} else {
ca1cbbdce   David Howells   afs: Fix afs_cell...
182
183
184
185
  		ret = -ENOMEM;
  		vllist = afs_alloc_vlserver_list(0);
  		if (!vllist)
  			goto error;
d5c32c89b   David Howells   afs: Fix cell DNS...
186
187
  		vllist->source = DNS_RECORD_UNAVAILABLE;
  		vllist->status = DNS_LOOKUP_NOT_DONE;
ded2f4c58   David Howells   afs: Fix TTL on V...
188
  		cell->dns_expiry = ktime_get_real_seconds();
00d3b7a45   David Howells   [AFS]: Add securi...
189
  	}
00d3b7a45   David Howells   [AFS]: Add securi...
190

ca1cbbdce   David Howells   afs: Fix afs_cell...
191
  	rcu_assign_pointer(cell->vl_servers, vllist);
d5c32c89b   David Howells   afs: Fix cell DNS...
192
193
194
  	cell->dns_source = vllist->source;
  	cell->dns_status = vllist->status;
  	smp_store_release(&cell->dns_lookup_count, 1); /* vs source/status */
88c853c3f   David Howells   afs: Fix cell ref...
195
  	atomic_inc(&net->cells_outstanding);
dca54a7bb   David Howells   afs: Add tracing ...
196
197
  	cell->debug_id = atomic_inc_return(&cell_debug_id);
  	trace_afs_cell(cell->debug_id, 1, 0, afs_cell_trace_alloc);
d5c32c89b   David Howells   afs: Fix cell DNS...
198

00d3b7a45   David Howells   [AFS]: Add securi...
199
200
  	_leave(" = %p", cell);
  	return cell;
8b2a464ce   David Howells   afs: Add an addre...
201
202
203
204
  parse_failed:
  	if (ret == -EINVAL)
  		printk(KERN_ERR "kAFS: bad VL server IP address
  ");
ca1cbbdce   David Howells   afs: Fix afs_cell...
205
  error:
719fdd329   David Howells   afs: Fix storage ...
206
  	kfree(cell->name);
00d3b7a45   David Howells   [AFS]: Add securi...
207
208
209
210
  	kfree(cell);
  	_leave(" = %d", ret);
  	return ERR_PTR(ret);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
211

00d3b7a45   David Howells   [AFS]: Add securi...
212
  /*
989782dcd   David Howells   afs: Overhaul cel...
213
   * afs_lookup_cell - Look up or create a cell record.
f044c8847   David Howells   afs: Lay the grou...
214
   * @net:	The network namespace
989782dcd   David Howells   afs: Overhaul cel...
215
216
217
218
219
220
221
222
223
   * @name:	The name of the cell.
   * @namesz:	The strlen of the cell name.
   * @vllist:	A colon/comma separated list of numeric IP addresses or NULL.
   * @excl:	T if an error should be given if the cell name already exists.
   *
   * Look up a cell record by name and query the DNS for VL server addresses if
   * needed.  Note that that actual DNS query is punted off to the manager thread
   * so that this function can return immediately if interrupted whilst allowing
   * cell records to be shared even if not yet fully constructed.
00d3b7a45   David Howells   [AFS]: Add securi...
224
   */
989782dcd   David Howells   afs: Overhaul cel...
225
226
227
  struct afs_cell *afs_lookup_cell(struct afs_net *net,
  				 const char *name, unsigned int namesz,
  				 const char *vllist, bool excl)
00d3b7a45   David Howells   [AFS]: Add securi...
228
  {
989782dcd   David Howells   afs: Overhaul cel...
229
230
  	struct afs_cell *cell, *candidate, *cursor;
  	struct rb_node *parent, **pp;
d5c32c89b   David Howells   afs: Fix cell DNS...
231
  	enum afs_cell_state state;
989782dcd   David Howells   afs: Overhaul cel...
232
233
234
235
236
  	int ret, n;
  
  	_enter("%s,%s", name, vllist);
  
  	if (!excl) {
dca54a7bb   David Howells   afs: Add tracing ...
237
  		cell = afs_find_cell(net, name, namesz, afs_cell_trace_use_lookup);
683279516   Gustavo A. R. Silva   afs: cell: Remove...
238
  		if (!IS_ERR(cell))
989782dcd   David Howells   afs: Overhaul cel...
239
  			goto wait_for_cell;
989782dcd   David Howells   afs: Overhaul cel...
240
  	}
00d3b7a45   David Howells   [AFS]: Add securi...
241

989782dcd   David Howells   afs: Overhaul cel...
242
243
244
245
246
247
248
249
250
251
252
  	/* Assume we're probably going to create a cell and preallocate and
  	 * mostly set up a candidate record.  We can then use this to stash the
  	 * name, the net namespace and VL server addresses.
  	 *
  	 * We also want to do this before we hold any locks as it may involve
  	 * upcalling to userspace to make DNS queries.
  	 */
  	candidate = afs_alloc_cell(net, name, namesz, vllist);
  	if (IS_ERR(candidate)) {
  		_leave(" = %ld", PTR_ERR(candidate));
  		return candidate;
5214b729e   Sven Schnelle   afs: prevent doub...
253
  	}
5214b729e   Sven Schnelle   afs: prevent doub...
254

989782dcd   David Howells   afs: Overhaul cel...
255
256
257
  	/* Find the insertion point and check to see if someone else added a
  	 * cell whilst we were allocating.
  	 */
92e3cc91d   David Howells   afs: Fix rapid ce...
258
  	down_write(&net->cells_lock);
989782dcd   David Howells   afs: Overhaul cel...
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
  
  	pp = &net->cells.rb_node;
  	parent = NULL;
  	while (*pp) {
  		parent = *pp;
  		cursor = rb_entry(parent, struct afs_cell, net_node);
  
  		n = strncasecmp(cursor->name, name,
  				min_t(size_t, cursor->name_len, namesz));
  		if (n == 0)
  			n = cursor->name_len - namesz;
  		if (n < 0)
  			pp = &(*pp)->rb_left;
  		else if (n > 0)
  			pp = &(*pp)->rb_right;
  		else
  			goto cell_already_exists;
00d3b7a45   David Howells   [AFS]: Add securi...
276
  	}
989782dcd   David Howells   afs: Overhaul cel...
277
278
  	cell = candidate;
  	candidate = NULL;
88c853c3f   David Howells   afs: Fix cell ref...
279
  	atomic_set(&cell->active, 2);
dca54a7bb   David Howells   afs: Add tracing ...
280
  	trace_afs_cell(cell->debug_id, atomic_read(&cell->ref), 2, afs_cell_trace_insert);
989782dcd   David Howells   afs: Overhaul cel...
281
282
  	rb_link_node_rcu(&cell->net_node, parent, pp);
  	rb_insert_color(&cell->net_node, &net->cells);
92e3cc91d   David Howells   afs: Fix rapid ce...
283
  	up_write(&net->cells_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
284

dca54a7bb   David Howells   afs: Add tracing ...
285
  	afs_queue_cell(cell, afs_cell_trace_get_queue_new);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
286

989782dcd   David Howells   afs: Overhaul cel...
287
  wait_for_cell:
dca54a7bb   David Howells   afs: Add tracing ...
288
289
  	trace_afs_cell(cell->debug_id, atomic_read(&cell->ref), atomic_read(&cell->active),
  		       afs_cell_trace_wait);
989782dcd   David Howells   afs: Overhaul cel...
290
  	_debug("wait_for_cell");
d5c32c89b   David Howells   afs: Fix cell DNS...
291
292
293
  	wait_var_event(&cell->state,
  		       ({
  			       state = smp_load_acquire(&cell->state); /* vs error */
1d0e850a4   David Howells   afs: Fix cell rem...
294
  			       state == AFS_CELL_ACTIVE || state == AFS_CELL_REMOVED;
d5c32c89b   David Howells   afs: Fix cell DNS...
295
296
297
  		       }));
  
  	/* Check the state obtained from the wait check. */
1d0e850a4   David Howells   afs: Fix cell rem...
298
  	if (state == AFS_CELL_REMOVED) {
989782dcd   David Howells   afs: Overhaul cel...
299
300
  		ret = cell->error;
  		goto error;
989782dcd   David Howells   afs: Overhaul cel...
301
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
302

989782dcd   David Howells   afs: Overhaul cel...
303
  	_leave(" = %p [cell]", cell);
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
304
  	return cell;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
305

989782dcd   David Howells   afs: Overhaul cel...
306
307
308
309
310
311
  cell_already_exists:
  	_debug("cell exists");
  	cell = cursor;
  	if (excl) {
  		ret = -EEXIST;
  	} else {
dca54a7bb   David Howells   afs: Add tracing ...
312
  		afs_use_cell(cursor, afs_cell_trace_use_lookup);
989782dcd   David Howells   afs: Overhaul cel...
313
314
  		ret = 0;
  	}
92e3cc91d   David Howells   afs: Fix rapid ce...
315
  	up_write(&net->cells_lock);
88c853c3f   David Howells   afs: Fix cell ref...
316
  	if (candidate)
dca54a7bb   David Howells   afs: Add tracing ...
317
  		afs_put_cell(candidate, afs_cell_trace_put_candidate);
989782dcd   David Howells   afs: Overhaul cel...
318
319
  	if (ret == 0)
  		goto wait_for_cell;
8b2a464ce   David Howells   afs: Add an addre...
320
  	goto error_noput;
ec26815ad   David Howells   [AFS]: Clean up t...
321
  error:
dca54a7bb   David Howells   afs: Add tracing ...
322
  	afs_unuse_cell(net, cell, afs_cell_trace_unuse_lookup);
8b2a464ce   David Howells   afs: Add an addre...
323
  error_noput:
989782dcd   David Howells   afs: Overhaul cel...
324
  	_leave(" = %d [error]", ret);
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
325
  	return ERR_PTR(ret);
ec26815ad   David Howells   [AFS]: Clean up t...
326
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
327

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
328
  /*
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
329
330
331
   * set the root cell information
   * - can be called with a module parameter string
   * - can be called from a write to /proc/fs/afs/rootcell
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
332
   */
989782dcd   David Howells   afs: Overhaul cel...
333
  int afs_cell_init(struct afs_net *net, const char *rootcell)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
334
335
  {
  	struct afs_cell *old_root, *new_root;
989782dcd   David Howells   afs: Overhaul cel...
336
337
  	const char *cp, *vllist;
  	size_t len;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
338
339
340
341
342
343
344
  
  	_enter("");
  
  	if (!rootcell) {
  		/* module is loaded with no parameters, or built statically.
  		 * - in the future we might initialize cell DB here.
  		 */
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
345
  		_leave(" = 0 [no root]");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
346
347
348
349
  		return 0;
  	}
  
  	cp = strchr(rootcell, ':');
989782dcd   David Howells   afs: Overhaul cel...
350
  	if (!cp) {
07567a550   Wang Lei   DNS: Make AFS go ...
351
  		_debug("kAFS: no VL server IP addresses specified");
989782dcd   David Howells   afs: Overhaul cel...
352
353
354
355
356
357
  		vllist = NULL;
  		len = strlen(rootcell);
  	} else {
  		vllist = cp + 1;
  		len = cp - rootcell;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
358
359
  
  	/* allocate a cell record for the root cell */
989782dcd   David Howells   afs: Overhaul cel...
360
  	new_root = afs_lookup_cell(net, rootcell, len, vllist, false);
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
361
362
363
  	if (IS_ERR(new_root)) {
  		_leave(" = %ld", PTR_ERR(new_root));
  		return PTR_ERR(new_root);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
364
  	}
17814aef5   David Howells   afs: Don't over-i...
365
  	if (!test_and_set_bit(AFS_CELL_FL_NO_GC, &new_root->flags))
dca54a7bb   David Howells   afs: Add tracing ...
366
  		afs_use_cell(new_root, afs_cell_trace_use_pin);
989782dcd   David Howells   afs: Overhaul cel...
367

08e0e7c82   David Howells   [AF_RXRPC]: Make ...
368
  	/* install the new cell */
92e3cc91d   David Howells   afs: Fix rapid ce...
369
  	down_write(&net->cells_lock);
dca54a7bb   David Howells   afs: Add tracing ...
370
  	afs_see_cell(new_root, afs_cell_trace_see_ws);
92e3cc91d   David Howells   afs: Fix rapid ce...
371
372
373
  	old_root = net->ws_cell;
  	net->ws_cell = new_root;
  	up_write(&net->cells_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
374

dca54a7bb   David Howells   afs: Add tracing ...
375
  	afs_unuse_cell(net, old_root, afs_cell_trace_unuse_ws);
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
376
377
  	_leave(" = 0");
  	return 0;
ec26815ad   David Howells   [AFS]: Clean up t...
378
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
379

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
380
  /*
989782dcd   David Howells   afs: Overhaul cel...
381
   * Update a cell's VL server address list from the DNS.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
382
   */
d5c32c89b   David Howells   afs: Fix cell DNS...
383
  static int afs_update_cell(struct afs_cell *cell)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
384
  {
d5c32c89b   David Howells   afs: Fix cell DNS...
385
  	struct afs_vlserver_list *vllist, *old = NULL, *p;
ded2f4c58   David Howells   afs: Fix TTL on V...
386
387
388
  	unsigned int min_ttl = READ_ONCE(afs_cell_min_ttl);
  	unsigned int max_ttl = READ_ONCE(afs_cell_max_ttl);
  	time64_t now, expiry = 0;
d5c32c89b   David Howells   afs: Fix cell DNS...
389
  	int ret = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
390

989782dcd   David Howells   afs: Overhaul cel...
391
  	_enter("%s", cell->name);
0a5143f2f   David Howells   afs: Implement VL...
392
  	vllist = afs_dns_query(cell, &expiry);
d5c32c89b   David Howells   afs: Fix cell DNS...
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
  	if (IS_ERR(vllist)) {
  		ret = PTR_ERR(vllist);
  
  		_debug("%s: fail %d", cell->name, ret);
  		if (ret == -ENOMEM)
  			goto out_wake;
  
  		ret = -ENOMEM;
  		vllist = afs_alloc_vlserver_list(0);
  		if (!vllist)
  			goto out_wake;
  
  		switch (ret) {
  		case -ENODATA:
  		case -EDESTADDRREQ:
  			vllist->status = DNS_LOOKUP_GOT_NOT_FOUND;
  			break;
  		case -EAGAIN:
  		case -ECONNREFUSED:
  			vllist->status = DNS_LOOKUP_GOT_TEMP_FAILURE;
  			break;
  		default:
  			vllist->status = DNS_LOOKUP_GOT_LOCAL_FAILURE;
  			break;
  		}
  	}
  
  	_debug("%s: got list %d %d", cell->name, vllist->source, vllist->status);
  	cell->dns_status = vllist->status;
ded2f4c58   David Howells   afs: Fix TTL on V...
422
423
424
425
426
427
428
429
  
  	now = ktime_get_real_seconds();
  	if (min_ttl > max_ttl)
  		max_ttl = min_ttl;
  	if (expiry < now + min_ttl)
  		expiry = now + min_ttl;
  	else if (expiry > now + max_ttl)
  		expiry = now + max_ttl;
d5c32c89b   David Howells   afs: Fix cell DNS...
430
431
432
433
  	_debug("%s: status %d", cell->name, vllist->status);
  	if (vllist->source == DNS_RECORD_UNAVAILABLE) {
  		switch (vllist->status) {
  		case DNS_LOOKUP_GOT_NOT_FOUND:
ded2f4c58   David Howells   afs: Fix TTL on V...
434
435
436
  			/* The DNS said that the cell does not exist or there
  			 * weren't any addresses to be had.
  			 */
ded2f4c58   David Howells   afs: Fix TTL on V...
437
  			cell->dns_expiry = expiry;
8b2a464ce   David Howells   afs: Add an addre...
438
  			break;
989782dcd   David Howells   afs: Overhaul cel...
439

d5c32c89b   David Howells   afs: Fix cell DNS...
440
441
442
443
  		case DNS_LOOKUP_BAD:
  		case DNS_LOOKUP_GOT_LOCAL_FAILURE:
  		case DNS_LOOKUP_GOT_TEMP_FAILURE:
  		case DNS_LOOKUP_GOT_NS_FAILURE:
8b2a464ce   David Howells   afs: Add an addre...
444
  		default:
ded2f4c58   David Howells   afs: Fix TTL on V...
445
  			cell->dns_expiry = now + 10;
8b2a464ce   David Howells   afs: Add an addre...
446
447
  			break;
  		}
8b2a464ce   David Howells   afs: Add an addre...
448
  	} else {
8b2a464ce   David Howells   afs: Add an addre...
449
  		cell->dns_expiry = expiry;
8b2a464ce   David Howells   afs: Add an addre...
450
  	}
bec5eb614   wanglei   AFS: Implement an...
451

d5c32c89b   David Howells   afs: Fix cell DNS...
452
453
454
455
456
457
458
459
460
461
462
463
  	/* Replace the VL server list if the new record has servers or the old
  	 * record doesn't.
  	 */
  	write_lock(&cell->vl_servers_lock);
  	p = rcu_dereference_protected(cell->vl_servers, true);
  	if (vllist->nr_servers > 0 || p->nr_servers == 0) {
  		rcu_assign_pointer(cell->vl_servers, vllist);
  		cell->dns_source = vllist->source;
  		old = p;
  	}
  	write_unlock(&cell->vl_servers_lock);
  	afs_put_vlserverlist(cell->net, old);
bec5eb614   wanglei   AFS: Implement an...
464

d5c32c89b   David Howells   afs: Fix cell DNS...
465
466
467
468
469
470
  out_wake:
  	smp_store_release(&cell->dns_lookup_count,
  			  cell->dns_lookup_count + 1); /* vs source/status */
  	wake_up_var(&cell->dns_lookup_count);
  	_leave(" = %d", ret);
  	return ret;
ec26815ad   David Howells   [AFS]: Clean up t...
471
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
472

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
473
  /*
989782dcd   David Howells   afs: Overhaul cel...
474
   * Destroy a cell record
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
475
   */
989782dcd   David Howells   afs: Overhaul cel...
476
  static void afs_cell_destroy(struct rcu_head *rcu)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
477
  {
989782dcd   David Howells   afs: Overhaul cel...
478
  	struct afs_cell *cell = container_of(rcu, struct afs_cell, rcu);
88c853c3f   David Howells   afs: Fix cell ref...
479
480
  	struct afs_net *net = cell->net;
  	int u;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
481

989782dcd   David Howells   afs: Overhaul cel...
482
  	_enter("%p{%s}", cell, cell->name);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
483

88c853c3f   David Howells   afs: Fix cell ref...
484
485
  	u = atomic_read(&cell->ref);
  	ASSERTCMP(u, ==, 0);
dca54a7bb   David Howells   afs: Add tracing ...
486
  	trace_afs_cell(cell->debug_id, u, atomic_read(&cell->active), afs_cell_trace_free);
989782dcd   David Howells   afs: Overhaul cel...
487

88c853c3f   David Howells   afs: Fix cell ref...
488
  	afs_put_vlserverlist(net, rcu_access_pointer(cell->vl_servers));
dca54a7bb   David Howells   afs: Add tracing ...
489
  	afs_unuse_cell(net, cell->alias_of, afs_cell_trace_unuse_alias);
989782dcd   David Howells   afs: Overhaul cel...
490
  	key_put(cell->anonymous_key);
719fdd329   David Howells   afs: Fix storage ...
491
  	kfree(cell->name);
989782dcd   David Howells   afs: Overhaul cel...
492
  	kfree(cell);
88c853c3f   David Howells   afs: Fix cell ref...
493
  	afs_dec_cells_outstanding(net);
989782dcd   David Howells   afs: Overhaul cel...
494
  	_leave(" [destroyed]");
ec26815ad   David Howells   [AFS]: Clean up t...
495
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
496

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
497
  /*
989782dcd   David Howells   afs: Overhaul cel...
498
   * Queue the cell manager.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
499
   */
989782dcd   David Howells   afs: Overhaul cel...
500
  static void afs_queue_cell_manager(struct afs_net *net)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
501
  {
989782dcd   David Howells   afs: Overhaul cel...
502
  	int outstanding = atomic_inc_return(&net->cells_outstanding);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
503

989782dcd   David Howells   afs: Overhaul cel...
504
  	_enter("%d", outstanding);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
505

989782dcd   David Howells   afs: Overhaul cel...
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
  	if (!queue_work(afs_wq, &net->cells_manager))
  		afs_dec_cells_outstanding(net);
  }
  
  /*
   * Cell management timer.  We have an increment on cells_outstanding that we
   * need to pass along to the work item.
   */
  void afs_cells_timer(struct timer_list *timer)
  {
  	struct afs_net *net = container_of(timer, struct afs_net, cells_timer);
  
  	_enter("");
  	if (!queue_work(afs_wq, &net->cells_manager))
  		afs_dec_cells_outstanding(net);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
522

989782dcd   David Howells   afs: Overhaul cel...
523
  /*
8b2a464ce   David Howells   afs: Add an addre...
524
525
   * Get a reference on a cell record.
   */
dca54a7bb   David Howells   afs: Add tracing ...
526
  struct afs_cell *afs_get_cell(struct afs_cell *cell, enum afs_cell_trace reason)
8b2a464ce   David Howells   afs: Add an addre...
527
  {
dca54a7bb   David Howells   afs: Add tracing ...
528
  	int u;
88c853c3f   David Howells   afs: Fix cell ref...
529
530
  	if (atomic_read(&cell->ref) <= 0)
  		BUG();
dca54a7bb   David Howells   afs: Add tracing ...
531
532
  	u = atomic_inc_return(&cell->ref);
  	trace_afs_cell(cell->debug_id, u, atomic_read(&cell->active), reason);
8b2a464ce   David Howells   afs: Add an addre...
533
534
535
536
  	return cell;
  }
  
  /*
989782dcd   David Howells   afs: Overhaul cel...
537
538
   * Drop a reference on a cell record.
   */
dca54a7bb   David Howells   afs: Add tracing ...
539
  void afs_put_cell(struct afs_cell *cell, enum afs_cell_trace reason)
88c853c3f   David Howells   afs: Fix cell ref...
540
541
  {
  	if (cell) {
dca54a7bb   David Howells   afs: Add tracing ...
542
  		unsigned int debug_id = cell->debug_id;
88c853c3f   David Howells   afs: Fix cell ref...
543
  		unsigned int u, a;
dca54a7bb   David Howells   afs: Add tracing ...
544
  		a = atomic_read(&cell->active);
88c853c3f   David Howells   afs: Fix cell ref...
545
  		u = atomic_dec_return(&cell->ref);
dca54a7bb   David Howells   afs: Add tracing ...
546
  		trace_afs_cell(debug_id, u, a, reason);
88c853c3f   David Howells   afs: Fix cell ref...
547
548
549
550
551
552
553
554
555
556
557
558
  		if (u == 0) {
  			a = atomic_read(&cell->active);
  			WARN(a != 0, "Cell active count %u > 0
  ", a);
  			call_rcu(&cell->rcu, afs_cell_destroy);
  		}
  	}
  }
  
  /*
   * Note a cell becoming more active.
   */
dca54a7bb   David Howells   afs: Add tracing ...
559
  struct afs_cell *afs_use_cell(struct afs_cell *cell, enum afs_cell_trace reason)
88c853c3f   David Howells   afs: Fix cell ref...
560
  {
dca54a7bb   David Howells   afs: Add tracing ...
561
  	int u, a;
88c853c3f   David Howells   afs: Fix cell ref...
562
563
  	if (atomic_read(&cell->ref) <= 0)
  		BUG();
dca54a7bb   David Howells   afs: Add tracing ...
564
565
566
  	u = atomic_read(&cell->ref);
  	a = atomic_inc_return(&cell->active);
  	trace_afs_cell(cell->debug_id, u, a, reason);
88c853c3f   David Howells   afs: Fix cell ref...
567
568
569
570
571
572
573
  	return cell;
  }
  
  /*
   * Record a cell becoming less active.  When the active counter reaches 1, it
   * is scheduled for destruction, but may get reactivated.
   */
dca54a7bb   David Howells   afs: Add tracing ...
574
  void afs_unuse_cell(struct afs_net *net, struct afs_cell *cell, enum afs_cell_trace reason)
989782dcd   David Howells   afs: Overhaul cel...
575
  {
acc080d15   David Howells   afs: Fix tracing ...
576
  	unsigned int debug_id;
989782dcd   David Howells   afs: Overhaul cel...
577
  	time64_t now, expire_delay;
dca54a7bb   David Howells   afs: Add tracing ...
578
  	int u, a;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
579

989782dcd   David Howells   afs: Overhaul cel...
580
  	if (!cell)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
581
  		return;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
582

989782dcd   David Howells   afs: Overhaul cel...
583
  	_enter("%s", cell->name);
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
584

989782dcd   David Howells   afs: Overhaul cel...
585
586
587
  	now = ktime_get_real_seconds();
  	cell->last_inactive = now;
  	expire_delay = 0;
d5c32c89b   David Howells   afs: Fix cell DNS...
588
  	if (cell->vl_servers->nr_servers)
989782dcd   David Howells   afs: Overhaul cel...
589
  		expire_delay = afs_cell_gc_delay;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
590

acc080d15   David Howells   afs: Fix tracing ...
591
  	debug_id = cell->debug_id;
dca54a7bb   David Howells   afs: Add tracing ...
592
  	u = atomic_read(&cell->ref);
88c853c3f   David Howells   afs: Fix cell ref...
593
  	a = atomic_dec_return(&cell->active);
dca54a7bb   David Howells   afs: Add tracing ...
594
  	trace_afs_cell(debug_id, u, a, reason);
88c853c3f   David Howells   afs: Fix cell ref...
595
596
597
598
599
  	WARN_ON(a == 0);
  	if (a == 1)
  		/* 'cell' may now be garbage collected. */
  		afs_set_cell_timer(net, expire_delay);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
600

88c853c3f   David Howells   afs: Fix cell ref...
601
  /*
dca54a7bb   David Howells   afs: Add tracing ...
602
603
604
605
606
607
608
609
610
611
612
613
   * Note that a cell has been seen.
   */
  void afs_see_cell(struct afs_cell *cell, enum afs_cell_trace reason)
  {
  	int u, a;
  
  	u = atomic_read(&cell->ref);
  	a = atomic_read(&cell->active);
  	trace_afs_cell(cell->debug_id, u, a, reason);
  }
  
  /*
88c853c3f   David Howells   afs: Fix cell ref...
614
615
   * Queue a cell for management, giving the workqueue a ref to hold.
   */
dca54a7bb   David Howells   afs: Add tracing ...
616
  void afs_queue_cell(struct afs_cell *cell, enum afs_cell_trace reason)
88c853c3f   David Howells   afs: Fix cell ref...
617
  {
dca54a7bb   David Howells   afs: Add tracing ...
618
  	afs_get_cell(cell, reason);
88c853c3f   David Howells   afs: Fix cell ref...
619
  	if (!queue_work(afs_wq, &cell->manager))
dca54a7bb   David Howells   afs: Add tracing ...
620
  		afs_put_cell(cell, afs_cell_trace_put_queue_fail);
ec26815ad   David Howells   [AFS]: Clean up t...
621
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
622

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
623
  /*
989782dcd   David Howells   afs: Overhaul cel...
624
   * Allocate a key to use as a placeholder for anonymous user security.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
625
   */
989782dcd   David Howells   afs: Overhaul cel...
626
  static int afs_alloc_anon_key(struct afs_cell *cell)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
627
  {
989782dcd   David Howells   afs: Overhaul cel...
628
629
  	struct key *key;
  	char keyname[4 + AFS_MAXCELLNAME + 1], *cp, *dp;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
630

989782dcd   David Howells   afs: Overhaul cel...
631
632
633
634
635
636
637
  	/* Create a key to represent an anonymous user. */
  	memcpy(keyname, "afs@", 4);
  	dp = keyname + 4;
  	cp = cell->name;
  	do {
  		*dp++ = tolower(*cp);
  	} while (*cp++);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
638

989782dcd   David Howells   afs: Overhaul cel...
639
640
641
  	key = rxrpc_get_null_key(keyname);
  	if (IS_ERR(key))
  		return PTR_ERR(key);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
642

989782dcd   David Howells   afs: Overhaul cel...
643
  	cell->anonymous_key = key;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
644

989782dcd   David Howells   afs: Overhaul cel...
645
646
647
648
  	_debug("anon key %p{%x}",
  	       cell->anonymous_key, key_serial(cell->anonymous_key));
  	return 0;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
649

989782dcd   David Howells   afs: Overhaul cel...
650
651
652
653
654
  /*
   * Activate a cell.
   */
  static int afs_activate_cell(struct afs_net *net, struct afs_cell *cell)
  {
6b3944e42   David Howells   afs: Fix cell pro...
655
656
  	struct hlist_node **p;
  	struct afs_cell *pcell;
989782dcd   David Howells   afs: Overhaul cel...
657
658
659
660
661
662
  	int ret;
  
  	if (!cell->anonymous_key) {
  		ret = afs_alloc_anon_key(cell);
  		if (ret < 0)
  			return ret;
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
663
  	}
989782dcd   David Howells   afs: Overhaul cel...
664
665
666
  #ifdef CONFIG_AFS_FSCACHE
  	cell->cache = fscache_acquire_cookie(afs_cache_netfs.primary_index,
  					     &afs_cell_cache_index_def,
402cb8dda   David Howells   fscache: Attach t...
667
668
  					     cell->name, strlen(cell->name),
  					     NULL, 0,
ee1235a9a   David Howells   fscache: Pass obj...
669
  					     cell, 0, true);
989782dcd   David Howells   afs: Overhaul cel...
670
  #endif
5b86d4ff5   David Howells   afs: Implement ne...
671
  	ret = afs_proc_cell_setup(cell);
989782dcd   David Howells   afs: Overhaul cel...
672
673
  	if (ret < 0)
  		return ret;
0da0b7fd7   David Howells   afs: Display manu...
674
675
  
  	mutex_lock(&net->proc_cells_lock);
6b3944e42   David Howells   afs: Fix cell pro...
676
677
678
679
680
681
682
683
684
685
686
  	for (p = &net->proc_cells.first; *p; p = &(*p)->next) {
  		pcell = hlist_entry(*p, struct afs_cell, proc_link);
  		if (strcmp(cell->name, pcell->name) < 0)
  			break;
  	}
  
  	cell->proc_link.pprev = p;
  	cell->proc_link.next = *p;
  	rcu_assign_pointer(*p, &cell->proc_link.next);
  	if (cell->proc_link.next)
  		cell->proc_link.next->pprev = &cell->proc_link.next;
0da0b7fd7   David Howells   afs: Display manu...
687
688
  	afs_dynroot_mkdir(net, cell);
  	mutex_unlock(&net->proc_cells_lock);
989782dcd   David Howells   afs: Overhaul cel...
689
690
691
692
693
694
695
696
697
  	return 0;
  }
  
  /*
   * Deactivate a cell.
   */
  static void afs_deactivate_cell(struct afs_net *net, struct afs_cell *cell)
  {
  	_enter("%s", cell->name);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
698

5b86d4ff5   David Howells   afs: Implement ne...
699
  	afs_proc_cell_remove(cell);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
700

0da0b7fd7   David Howells   afs: Display manu...
701
  	mutex_lock(&net->proc_cells_lock);
6b3944e42   David Howells   afs: Fix cell pro...
702
  	hlist_del_rcu(&cell->proc_link);
0da0b7fd7   David Howells   afs: Display manu...
703
704
  	afs_dynroot_rmdir(net, cell);
  	mutex_unlock(&net->proc_cells_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
705

9b3f26c91   David Howells   FS-Cache: Make kA...
706
  #ifdef CONFIG_AFS_FSCACHE
402cb8dda   David Howells   fscache: Attach t...
707
  	fscache_relinquish_cookie(cell->cache, NULL, false);
989782dcd   David Howells   afs: Overhaul cel...
708
  	cell->cache = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
709
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
710

989782dcd   David Howells   afs: Overhaul cel...
711
  	_leave("");
ec26815ad   David Howells   [AFS]: Clean up t...
712
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
713

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
714
  /*
989782dcd   David Howells   afs: Overhaul cel...
715
716
   * Manage a cell record, initialising and destroying it, maintaining its DNS
   * records.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
717
   */
88c853c3f   David Howells   afs: Fix cell ref...
718
  static void afs_manage_cell(struct afs_cell *cell)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
719
  {
989782dcd   David Howells   afs: Overhaul cel...
720
  	struct afs_net *net = cell->net;
88c853c3f   David Howells   afs: Fix cell ref...
721
  	int ret, active;
989782dcd   David Howells   afs: Overhaul cel...
722
723
724
725
726
727
728
729
  
  	_enter("%s", cell->name);
  
  again:
  	_debug("state %u", cell->state);
  	switch (cell->state) {
  	case AFS_CELL_INACTIVE:
  	case AFS_CELL_FAILED:
92e3cc91d   David Howells   afs: Fix rapid ce...
730
  		down_write(&net->cells_lock);
88c853c3f   David Howells   afs: Fix cell ref...
731
  		active = 1;
1d0e850a4   David Howells   afs: Fix cell rem...
732
  		if (atomic_try_cmpxchg_relaxed(&cell->active, &active, 0)) {
989782dcd   David Howells   afs: Overhaul cel...
733
  			rb_erase(&cell->net_node, &net->cells);
dca54a7bb   David Howells   afs: Add tracing ...
734
735
  			trace_afs_cell(cell->debug_id, atomic_read(&cell->ref), 0,
  				       afs_cell_trace_unuse_delete);
1d0e850a4   David Howells   afs: Fix cell rem...
736
  			smp_store_release(&cell->state, AFS_CELL_REMOVED);
88c853c3f   David Howells   afs: Fix cell ref...
737
  		}
92e3cc91d   David Howells   afs: Fix rapid ce...
738
  		up_write(&net->cells_lock);
1d0e850a4   David Howells   afs: Fix cell rem...
739
740
  		if (cell->state == AFS_CELL_REMOVED) {
  			wake_up_var(&cell->state);
989782dcd   David Howells   afs: Overhaul cel...
741
  			goto final_destruction;
1d0e850a4   David Howells   afs: Fix cell rem...
742
  		}
989782dcd   David Howells   afs: Overhaul cel...
743
744
  		if (cell->state == AFS_CELL_FAILED)
  			goto done;
d5c32c89b   David Howells   afs: Fix cell DNS...
745
746
  		smp_store_release(&cell->state, AFS_CELL_UNSET);
  		wake_up_var(&cell->state);
989782dcd   David Howells   afs: Overhaul cel...
747
748
749
  		goto again;
  
  	case AFS_CELL_UNSET:
d5c32c89b   David Howells   afs: Fix cell DNS...
750
751
  		smp_store_release(&cell->state, AFS_CELL_ACTIVATING);
  		wake_up_var(&cell->state);
989782dcd   David Howells   afs: Overhaul cel...
752
753
754
755
756
757
  		goto again;
  
  	case AFS_CELL_ACTIVATING:
  		ret = afs_activate_cell(net, cell);
  		if (ret < 0)
  			goto activation_failed;
d5c32c89b   David Howells   afs: Fix cell DNS...
758
759
  		smp_store_release(&cell->state, AFS_CELL_ACTIVE);
  		wake_up_var(&cell->state);
989782dcd   David Howells   afs: Overhaul cel...
760
761
762
  		goto again;
  
  	case AFS_CELL_ACTIVE:
88c853c3f   David Howells   afs: Fix cell ref...
763
  		if (atomic_read(&cell->active) > 1) {
d5c32c89b   David Howells   afs: Fix cell DNS...
764
765
766
767
768
  			if (test_and_clear_bit(AFS_CELL_FL_DO_LOOKUP, &cell->flags)) {
  				ret = afs_update_cell(cell);
  				if (ret < 0)
  					cell->error = ret;
  			}
989782dcd   David Howells   afs: Overhaul cel...
769
770
  			goto done;
  		}
d5c32c89b   David Howells   afs: Fix cell DNS...
771
772
  		smp_store_release(&cell->state, AFS_CELL_DEACTIVATING);
  		wake_up_var(&cell->state);
989782dcd   David Howells   afs: Overhaul cel...
773
774
775
  		goto again;
  
  	case AFS_CELL_DEACTIVATING:
88c853c3f   David Howells   afs: Fix cell ref...
776
  		if (atomic_read(&cell->active) > 1)
989782dcd   David Howells   afs: Overhaul cel...
777
778
  			goto reverse_deactivation;
  		afs_deactivate_cell(net, cell);
d5c32c89b   David Howells   afs: Fix cell DNS...
779
780
  		smp_store_release(&cell->state, AFS_CELL_INACTIVE);
  		wake_up_var(&cell->state);
989782dcd   David Howells   afs: Overhaul cel...
781
  		goto again;
1d0e850a4   David Howells   afs: Fix cell rem...
782
783
  	case AFS_CELL_REMOVED:
  		goto done;
989782dcd   David Howells   afs: Overhaul cel...
784
785
786
787
788
789
790
791
792
  	default:
  		break;
  	}
  	_debug("bad state %u", cell->state);
  	BUG(); /* Unhandled state */
  
  activation_failed:
  	cell->error = ret;
  	afs_deactivate_cell(net, cell);
d5c32c89b   David Howells   afs: Fix cell DNS...
793
794
  	smp_store_release(&cell->state, AFS_CELL_FAILED); /* vs error */
  	wake_up_var(&cell->state);
989782dcd   David Howells   afs: Overhaul cel...
795
796
797
  	goto again;
  
  reverse_deactivation:
d5c32c89b   David Howells   afs: Fix cell DNS...
798
799
  	smp_store_release(&cell->state, AFS_CELL_ACTIVE);
  	wake_up_var(&cell->state);
989782dcd   David Howells   afs: Overhaul cel...
800
801
802
803
804
805
806
807
  	_leave(" [deact->act]");
  	return;
  
  done:
  	_leave(" [done %u]", cell->state);
  	return;
  
  final_destruction:
88c853c3f   David Howells   afs: Fix cell ref...
808
809
810
  	/* The root volume is pinning the cell */
  	afs_put_volume(cell->net, cell->root_volume, afs_volume_trace_put_cell_root);
  	cell->root_volume = NULL;
dca54a7bb   David Howells   afs: Add tracing ...
811
  	afs_put_cell(cell, afs_cell_trace_put_destroy);
88c853c3f   David Howells   afs: Fix cell ref...
812
813
814
815
816
817
818
  }
  
  static void afs_manage_cell_work(struct work_struct *work)
  {
  	struct afs_cell *cell = container_of(work, struct afs_cell, manager);
  
  	afs_manage_cell(cell);
dca54a7bb   David Howells   afs: Add tracing ...
819
  	afs_put_cell(cell, afs_cell_trace_put_queue_work);
989782dcd   David Howells   afs: Overhaul cel...
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
  }
  
  /*
   * Manage the records of cells known to a network namespace.  This includes
   * updating the DNS records and garbage collecting unused cells that were
   * automatically added.
   *
   * Note that constructed cell records may only be removed from net->cells by
   * this work item, so it is safe for this work item to stash a cursor pointing
   * into the tree and then return to caller (provided it skips cells that are
   * still under construction).
   *
   * Note also that we were given an increment on net->cells_outstanding by
   * whoever queued us that we need to deal with before returning.
   */
  void afs_manage_cells(struct work_struct *work)
  {
  	struct afs_net *net = container_of(work, struct afs_net, cells_manager);
  	struct rb_node *cursor;
  	time64_t now = ktime_get_real_seconds(), next_manage = TIME64_MAX;
  	bool purging = !net->live;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
841
842
  
  	_enter("");
989782dcd   David Howells   afs: Overhaul cel...
843
844
845
846
  	/* Trawl the cell database looking for cells that have expired from
  	 * lack of use and cells whose DNS results have expired and dispatch
  	 * their managers.
  	 */
92e3cc91d   David Howells   afs: Fix rapid ce...
847
  	down_read(&net->cells_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
848

989782dcd   David Howells   afs: Overhaul cel...
849
850
851
  	for (cursor = rb_first(&net->cells); cursor; cursor = rb_next(cursor)) {
  		struct afs_cell *cell =
  			rb_entry(cursor, struct afs_cell, net_node);
88c853c3f   David Howells   afs: Fix cell ref...
852
  		unsigned active;
989782dcd   David Howells   afs: Overhaul cel...
853
  		bool sched_cell = false;
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
854

88c853c3f   David Howells   afs: Fix cell ref...
855
  		active = atomic_read(&cell->active);
dca54a7bb   David Howells   afs: Add tracing ...
856
857
  		trace_afs_cell(cell->debug_id, atomic_read(&cell->ref),
  			       active, afs_cell_trace_manage);
989782dcd   David Howells   afs: Overhaul cel...
858

88c853c3f   David Howells   afs: Fix cell ref...
859
  		ASSERTCMP(active, >=, 1);
989782dcd   David Howells   afs: Overhaul cel...
860
861
  
  		if (purging) {
dca54a7bb   David Howells   afs: Add tracing ...
862
863
864
865
866
  			if (test_and_clear_bit(AFS_CELL_FL_NO_GC, &cell->flags)) {
  				active = atomic_dec_return(&cell->active);
  				trace_afs_cell(cell->debug_id, atomic_read(&cell->ref),
  					       active, afs_cell_trace_unuse_pin);
  			}
989782dcd   David Howells   afs: Overhaul cel...
867
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
868

88c853c3f   David Howells   afs: Fix cell ref...
869
  		if (active == 1) {
d5c32c89b   David Howells   afs: Fix cell DNS...
870
  			struct afs_vlserver_list *vllist;
989782dcd   David Howells   afs: Overhaul cel...
871
  			time64_t expire_at = cell->last_inactive;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
872

d5c32c89b   David Howells   afs: Fix cell DNS...
873
874
875
876
877
  			read_lock(&cell->vl_servers_lock);
  			vllist = rcu_dereference_protected(
  				cell->vl_servers,
  				lockdep_is_held(&cell->vl_servers_lock));
  			if (vllist->nr_servers > 0)
989782dcd   David Howells   afs: Overhaul cel...
878
  				expire_at += afs_cell_gc_delay;
d5c32c89b   David Howells   afs: Fix cell DNS...
879
  			read_unlock(&cell->vl_servers_lock);
989782dcd   David Howells   afs: Overhaul cel...
880
881
882
883
  			if (purging || expire_at <= now)
  				sched_cell = true;
  			else if (expire_at < next_manage)
  				next_manage = expire_at;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
884
  		}
989782dcd   David Howells   afs: Overhaul cel...
885
  		if (!purging) {
d5c32c89b   David Howells   afs: Fix cell DNS...
886
  			if (test_bit(AFS_CELL_FL_DO_LOOKUP, &cell->flags))
989782dcd   David Howells   afs: Overhaul cel...
887
  				sched_cell = true;
989782dcd   David Howells   afs: Overhaul cel...
888
889
890
  		}
  
  		if (sched_cell)
dca54a7bb   David Howells   afs: Add tracing ...
891
  			afs_queue_cell(cell, afs_cell_trace_get_queue_manage);
989782dcd   David Howells   afs: Overhaul cel...
892
  	}
92e3cc91d   David Howells   afs: Fix rapid ce...
893
  	up_read(&net->cells_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
894

989782dcd   David Howells   afs: Overhaul cel...
895
896
897
898
899
900
  	/* Update the timer on the way out.  We have to pass an increment on
  	 * cells_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();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
901

989782dcd   David Howells   afs: Overhaul cel...
902
903
904
905
906
  		if (next_manage - now <= 0) {
  			if (queue_work(afs_wq, &net->cells_manager))
  				atomic_inc(&net->cells_outstanding);
  		} else {
  			afs_set_cell_timer(net, next_manage - now);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
907
908
  		}
  	}
989782dcd   David Howells   afs: Overhaul cel...
909
910
911
912
913
914
915
916
917
918
919
920
  	afs_dec_cells_outstanding(net);
  	_leave(" [%d]", atomic_read(&net->cells_outstanding));
  }
  
  /*
   * Purge in-memory cell database.
   */
  void afs_cell_purge(struct afs_net *net)
  {
  	struct afs_cell *ws;
  
  	_enter("");
92e3cc91d   David Howells   afs: Fix rapid ce...
921
922
923
924
  	down_write(&net->cells_lock);
  	ws = net->ws_cell;
  	net->ws_cell = NULL;
  	up_write(&net->cells_lock);
dca54a7bb   David Howells   afs: Add tracing ...
925
  	afs_unuse_cell(net, ws, afs_cell_trace_unuse_ws);
989782dcd   David Howells   afs: Overhaul cel...
926
927
928
929
930
931
932
933
934
  
  	_debug("del timer");
  	if (del_timer_sync(&net->cells_timer))
  		atomic_dec(&net->cells_outstanding);
  
  	_debug("kick mgr");
  	afs_queue_cell_manager(net);
  
  	_debug("wait");
ab1fbe324   Peter Zijlstra   sched/wait, fs/af...
935
936
  	wait_var_event(&net->cells_outstanding,
  		       !atomic_read(&net->cells_outstanding));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
937
  	_leave("");
ec26815ad   David Howells   [AFS]: Clean up t...
938
  }