Blame view

fs/afs/volume.c 10.3 KB
2874c5fd2   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-or-later
ec26815ad   David Howells   [AFS]: Clean up t...
2
  /* AFS volume 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
   */
  
  #include <linux/kernel.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
9
  #include <linux/slab.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
10
  #include "internal.h"
d2ddc776a   David Howells   afs: Overhaul vol...
11
12
  unsigned __read_mostly afs_volume_gc_delay = 10;
  unsigned __read_mostly afs_volume_record_life = 60 * 60;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
13
  /*
20325960f   David Howells   afs: Reorganise v...
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
   * Insert a volume into a cell.  If there's an existing volume record, that is
   * returned instead with a ref held.
   */
  static struct afs_volume *afs_insert_volume_into_cell(struct afs_cell *cell,
  						      struct afs_volume *volume)
  {
  	struct afs_volume *p;
  	struct rb_node *parent = NULL, **pp;
  
  	write_seqlock(&cell->volume_lock);
  
  	pp = &cell->volumes.rb_node;
  	while (*pp) {
  		parent = *pp;
  		p = rb_entry(parent, struct afs_volume, cell_node);
  		if (p->vid < volume->vid) {
  			pp = &(*pp)->rb_left;
  		} else if (p->vid > volume->vid) {
  			pp = &(*pp)->rb_right;
  		} else {
  			volume = afs_get_volume(p, afs_volume_trace_get_cell_insert);
  			goto found;
  		}
  	}
  
  	rb_link_node_rcu(&volume->cell_node, parent, pp);
  	rb_insert_color(&volume->cell_node, &cell->volumes);
  	hlist_add_head_rcu(&volume->proc_link, &cell->proc_volumes);
  
  found:
  	write_sequnlock(&cell->volume_lock);
  	return volume;
  
  }
  
  static void afs_remove_volume_from_cell(struct afs_volume *volume)
  {
  	struct afs_cell *cell = volume->cell;
  
  	if (!hlist_unhashed(&volume->proc_link)) {
  		trace_afs_volume(volume->vid, atomic_read(&volume->usage),
  				 afs_volume_trace_remove);
  		write_seqlock(&cell->volume_lock);
  		hlist_del_rcu(&volume->proc_link);
  		rb_erase(&volume->cell_node, &cell->volumes);
  		write_sequnlock(&cell->volume_lock);
  	}
  }
  
  /*
d2ddc776a   David Howells   afs: Overhaul vol...
64
65
   * Allocate a volume record and load it up from a vldb record.
   */
13fcc6837   David Howells   afs: Add fs_conte...
66
  static struct afs_volume *afs_alloc_volume(struct afs_fs_context *params,
d2ddc776a   David Howells   afs: Overhaul vol...
67
68
69
70
  					   struct afs_vldb_entry *vldb,
  					   unsigned long type_mask)
  {
  	struct afs_server_list *slist;
d2ddc776a   David Howells   afs: Overhaul vol...
71
  	struct afs_volume *volume;
45df84627   David Howells   afs: Fix server l...
72
  	int ret = -ENOMEM, nr_servers = 0, i;
d2ddc776a   David Howells   afs: Overhaul vol...
73
74
75
76
77
78
79
80
81
82
83
  
  	for (i = 0; i < vldb->nr_servers; i++)
  		if (vldb->fs_mask[i] & type_mask)
  			nr_servers++;
  
  	volume = kzalloc(sizeof(struct afs_volume), GFP_KERNEL);
  	if (!volume)
  		goto error_0;
  
  	volume->vid		= vldb->vid[params->type];
  	volume->update_at	= ktime_get_real_seconds() + afs_volume_record_life;
dca54a7bb   David Howells   afs: Add tracing ...
84
  	volume->cell		= afs_get_cell(params->cell, afs_cell_trace_get_vol);
d2ddc776a   David Howells   afs: Overhaul vol...
85
86
87
88
89
  	volume->type		= params->type;
  	volume->type_force	= params->force;
  	volume->name_len	= vldb->name_len;
  
  	atomic_set(&volume->usage, 1);
20325960f   David Howells   afs: Reorganise v...
90
  	INIT_HLIST_NODE(&volume->proc_link);
d2ddc776a   David Howells   afs: Overhaul vol...
91
  	rwlock_init(&volume->servers_lock);
90fa9b645   David Howells   afs: Fix uninitia...
92
  	rwlock_init(&volume->cb_v_break_lock);
d2ddc776a   David Howells   afs: Overhaul vol...
93
94
95
96
97
98
99
100
101
  	memcpy(volume->name, vldb->name, vldb->name_len + 1);
  
  	slist = afs_alloc_server_list(params->cell, params->key, vldb, type_mask);
  	if (IS_ERR(slist)) {
  		ret = PTR_ERR(slist);
  		goto error_1;
  	}
  
  	refcount_set(&slist->usage, 1);
8a070a964   David Howells   afs: Detect cell ...
102
  	rcu_assign_pointer(volume->servers, slist);
cca37d45d   David Howells   afs: Add a tracep...
103
  	trace_afs_volume(volume->vid, 1, afs_volume_trace_alloc);
d2ddc776a   David Howells   afs: Overhaul vol...
104
  	return volume;
d2ddc776a   David Howells   afs: Overhaul vol...
105
  error_1:
dca54a7bb   David Howells   afs: Add tracing ...
106
  	afs_put_cell(volume->cell, afs_cell_trace_put_vol);
d2ddc776a   David Howells   afs: Overhaul vol...
107
108
109
110
111
112
  	kfree(volume);
  error_0:
  	return ERR_PTR(ret);
  }
  
  /*
20325960f   David Howells   afs: Reorganise v...
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
   * Look up or allocate a volume record.
   */
  static struct afs_volume *afs_lookup_volume(struct afs_fs_context *params,
  					    struct afs_vldb_entry *vldb,
  					    unsigned long type_mask)
  {
  	struct afs_volume *candidate, *volume;
  
  	candidate = afs_alloc_volume(params, vldb, type_mask);
  	if (IS_ERR(candidate))
  		return candidate;
  
  	volume = afs_insert_volume_into_cell(params->cell, candidate);
  	if (volume != candidate)
  		afs_put_volume(params->net, candidate, afs_volume_trace_put_cell_dup);
  	return volume;
  }
  
  /*
d2ddc776a   David Howells   afs: Overhaul vol...
132
133
134
135
136
137
138
   * Look up a VLDB record for a volume.
   */
  static struct afs_vldb_entry *afs_vl_lookup_vldb(struct afs_cell *cell,
  						 struct key *key,
  						 const char *volname,
  						 size_t volnamesz)
  {
0a5143f2f   David Howells   afs: Implement VL...
139
140
  	struct afs_vldb_entry *vldb = ERR_PTR(-EDESTADDRREQ);
  	struct afs_vl_cursor vc;
d2ddc776a   David Howells   afs: Overhaul vol...
141
  	int ret;
0a5143f2f   David Howells   afs: Implement VL...
142
143
  	if (!afs_begin_vlserver_operation(&vc, cell, key))
  		return ERR_PTR(-ERESTARTSYS);
d2ddc776a   David Howells   afs: Overhaul vol...
144

0a5143f2f   David Howells   afs: Implement VL...
145
  	while (afs_select_vlserver(&vc)) {
0a5143f2f   David Howells   afs: Implement VL...
146
  		vldb = afs_vl_get_entry_by_name_u(&vc, volname, volnamesz);
d2ddc776a   David Howells   afs: Overhaul vol...
147
  	}
0a5143f2f   David Howells   afs: Implement VL...
148
149
  	ret = afs_end_vlserver_operation(&vc);
  	return ret < 0 ? ERR_PTR(ret) : vldb;
d2ddc776a   David Howells   afs: Overhaul vol...
150
151
152
153
154
155
156
  }
  
  /*
   * Look up a volume in the VL server and create a candidate volume record for
   * it.
   *
   * The volume name can be one of the following:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
   *	"%[cell:]volume[.]"		R/W volume
   *	"#[cell:]volume[.]"		R/O or R/W volume (rwparent=0),
   *					 or R/W (rwparent=1) volume
   *	"%[cell:]volume.readonly"	R/O volume
   *	"#[cell:]volume.readonly"	R/O volume
   *	"%[cell:]volume.backup"		Backup volume
   *	"#[cell:]volume.backup"		Backup volume
   *
   * The cell name is optional, and defaults to the current cell.
   *
   * See "The Rules of Mount Point Traversal" in Chapter 5 of the AFS SysAdmin
   * Guide
   * - Rule 1: Explicit type suffix forces access of that type or nothing
   *           (no suffix, then use Rule 2 & 3)
   * - Rule 2: If parent volume is R/O, then mount R/O volume by preference, R/W
   *           if not available
   * - Rule 3: If parent volume is R/W, then only mount R/W volume unless
   *           explicitly told otherwise
   */
13fcc6837   David Howells   afs: Add fs_conte...
176
  struct afs_volume *afs_create_volume(struct afs_fs_context *params)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
177
  {
d2ddc776a   David Howells   afs: Overhaul vol...
178
179
180
  	struct afs_vldb_entry *vldb;
  	struct afs_volume *volume;
  	unsigned long type_mask = 1UL << params->type;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
181

d2ddc776a   David Howells   afs: Overhaul vol...
182
183
184
185
  	vldb = afs_vl_lookup_vldb(params->cell, params->key,
  				  params->volname, params->volnamesz);
  	if (IS_ERR(vldb))
  		return ERR_CAST(vldb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
186

d2ddc776a   David Howells   afs: Overhaul vol...
187
188
189
190
  	if (test_bit(AFS_VLDB_QUERY_ERROR, &vldb->flags)) {
  		volume = ERR_PTR(vldb->error);
  		goto error;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
191

d2ddc776a   David Howells   afs: Overhaul vol...
192
193
  	/* Make the final decision on the type we want */
  	volume = ERR_PTR(-ENOMEDIUM);
00d3b7a45   David Howells   [AFS]: Add securi...
194
  	if (params->force) {
d2ddc776a   David Howells   afs: Overhaul vol...
195
  		if (!(vldb->flags & type_mask))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
196
  			goto error;
d2ddc776a   David Howells   afs: Overhaul vol...
197
  	} else if (test_bit(AFS_VLDB_HAS_RO, &vldb->flags)) {
00d3b7a45   David Howells   [AFS]: Add securi...
198
  		params->type = AFSVL_ROVOL;
d2ddc776a   David Howells   afs: Overhaul vol...
199
  	} else if (test_bit(AFS_VLDB_HAS_RW, &vldb->flags)) {
00d3b7a45   David Howells   [AFS]: Add securi...
200
  		params->type = AFSVL_RWVOL;
ec26815ad   David Howells   [AFS]: Clean up t...
201
  	} else {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
202
203
  		goto error;
  	}
d2ddc776a   David Howells   afs: Overhaul vol...
204
  	type_mask = 1UL << params->type;
20325960f   David Howells   afs: Reorganise v...
205
  	volume = afs_lookup_volume(params, vldb, type_mask);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
206

d2ddc776a   David Howells   afs: Overhaul vol...
207
208
209
210
  error:
  	kfree(vldb);
  	return volume;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
211

d2ddc776a   David Howells   afs: Overhaul vol...
212
213
214
215
216
217
  /*
   * Destroy a volume record
   */
  static void afs_destroy_volume(struct afs_net *net, struct afs_volume *volume)
  {
  	_enter("%p", volume);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
218

d2ddc776a   David Howells   afs: Overhaul vol...
219
220
221
  #ifdef CONFIG_AFS_FSCACHE
  	ASSERTCMP(volume->cache, ==, NULL);
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
222

20325960f   David Howells   afs: Reorganise v...
223
  	afs_remove_volume_from_cell(volume);
8a070a964   David Howells   afs: Detect cell ...
224
  	afs_put_serverlist(net, rcu_access_pointer(volume->servers));
dca54a7bb   David Howells   afs: Add tracing ...
225
  	afs_put_cell(volume->cell, afs_cell_trace_put_vol);
cca37d45d   David Howells   afs: Add a tracep...
226
227
  	trace_afs_volume(volume->vid, atomic_read(&volume->usage),
  			 afs_volume_trace_free);
20325960f   David Howells   afs: Reorganise v...
228
  	kfree_rcu(volume, rcu);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
229

d2ddc776a   David Howells   afs: Overhaul vol...
230
231
232
233
  	_leave(" [destroyed]");
  }
  
  /*
cca37d45d   David Howells   afs: Add a tracep...
234
   * Get a reference on a volume record.
d2ddc776a   David Howells   afs: Overhaul vol...
235
   */
cca37d45d   David Howells   afs: Add a tracep...
236
237
  struct afs_volume *afs_get_volume(struct afs_volume *volume,
  				  enum afs_volume_trace reason)
d2ddc776a   David Howells   afs: Overhaul vol...
238
239
  {
  	if (volume) {
cca37d45d   David Howells   afs: Add a tracep...
240
241
242
243
244
  		int u = atomic_inc_return(&volume->usage);
  		trace_afs_volume(volume->vid, u, reason);
  	}
  	return volume;
  }
d2ddc776a   David Howells   afs: Overhaul vol...
245

cca37d45d   David Howells   afs: Add a tracep...
246
247
248
249
250
251
252
253
254
255
256
  /*
   * Drop a reference on a volume record.
   */
  void afs_put_volume(struct afs_net *net, struct afs_volume *volume,
  		    enum afs_volume_trace reason)
  {
  	if (volume) {
  		afs_volid_t vid = volume->vid;
  		int u = atomic_dec_return(&volume->usage);
  		trace_afs_volume(vid, u, reason);
  		if (u == 0)
e49c7b2f6   David Howells   afs: Build an abs...
257
  			afs_destroy_volume(net, volume);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
258
  	}
d2ddc776a   David Howells   afs: Overhaul vol...
259
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
260

d2ddc776a   David Howells   afs: Overhaul vol...
261
262
263
264
265
  /*
   * Activate a volume.
   */
  void afs_activate_volume(struct afs_volume *volume)
  {
9b3f26c91   David Howells   FS-Cache: Make kA...
266
  #ifdef CONFIG_AFS_FSCACHE
ad6a942a9   David Howells   afs: Update the c...
267
  	volume->cache = fscache_acquire_cookie(volume->cell->cache,
9b3f26c91   David Howells   FS-Cache: Make kA...
268
  					       &afs_volume_cache_index_def,
402cb8dda   David Howells   fscache: Attach t...
269
270
  					       &volume->vid, sizeof(volume->vid),
  					       NULL, 0,
ee1235a9a   David Howells   fscache: Pass obj...
271
  					       volume, 0, true);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
272
  #endif
d2ddc776a   David Howells   afs: Overhaul vol...
273
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
274

d2ddc776a   David Howells   afs: Overhaul vol...
275
276
277
278
279
280
  /*
   * Deactivate a volume.
   */
  void afs_deactivate_volume(struct afs_volume *volume)
  {
  	_enter("%s", volume->name);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
281

d2ddc776a   David Howells   afs: Overhaul vol...
282
  #ifdef CONFIG_AFS_FSCACHE
402cb8dda   David Howells   fscache: Attach t...
283
  	fscache_relinquish_cookie(volume->cache, NULL,
d2ddc776a   David Howells   afs: Overhaul vol...
284
285
286
  				  test_bit(AFS_VOLUME_DELETED, &volume->flags));
  	volume->cache = NULL;
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
287

d2ddc776a   David Howells   afs: Overhaul vol...
288
  	_leave("");
ec26815ad   David Howells   [AFS]: Clean up t...
289
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
290

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
291
  /*
d2ddc776a   David Howells   afs: Overhaul vol...
292
   * Query the VL service to update the volume status.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
293
   */
d2ddc776a   David Howells   afs: Overhaul vol...
294
  static int afs_update_volume_status(struct afs_volume *volume, struct key *key)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
295
  {
d2ddc776a   David Howells   afs: Overhaul vol...
296
297
298
299
  	struct afs_server_list *new, *old, *discard;
  	struct afs_vldb_entry *vldb;
  	char idbuf[16];
  	int ret, idsz;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
300

d2ddc776a   David Howells   afs: Overhaul vol...
301
  	_enter("");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
302

d2ddc776a   David Howells   afs: Overhaul vol...
303
304
305
  	/* We look up an ID by passing it as a decimal string in the
  	 * operation's name parameter.
  	 */
3b6492df4   David Howells   afs: Increase to ...
306
  	idsz = sprintf(idbuf, "%llu", volume->vid);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
307

d2ddc776a   David Howells   afs: Overhaul vol...
308
309
310
311
312
  	vldb = afs_vl_lookup_vldb(volume->cell, key, idbuf, idsz);
  	if (IS_ERR(vldb)) {
  		ret = PTR_ERR(vldb);
  		goto error;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
313

d2ddc776a   David Howells   afs: Overhaul vol...
314
315
316
317
318
319
320
321
322
323
  	/* See if the volume got renamed. */
  	if (vldb->name_len != volume->name_len ||
  	    memcmp(vldb->name, volume->name, vldb->name_len) != 0) {
  		/* TODO: Use RCU'd string. */
  		memcpy(volume->name, vldb->name, AFS_MAXVOLNAME);
  		volume->name_len = vldb->name_len;
  	}
  
  	/* See if the volume's server list got updated. */
  	new = afs_alloc_server_list(volume->cell, key,
45df84627   David Howells   afs: Fix server l...
324
  				    vldb, (1 << volume->type));
d2ddc776a   David Howells   afs: Overhaul vol...
325
326
327
328
  	if (IS_ERR(new)) {
  		ret = PTR_ERR(new);
  		goto error_vldb;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
329

d2ddc776a   David Howells   afs: Overhaul vol...
330
  	write_lock(&volume->servers_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
331

d2ddc776a   David Howells   afs: Overhaul vol...
332
  	discard = new;
8a070a964   David Howells   afs: Detect cell ...
333
334
  	old = rcu_dereference_protected(volume->servers,
  					lockdep_is_held(&volume->servers_lock));
d2ddc776a   David Howells   afs: Overhaul vol...
335
336
  	if (afs_annotate_server_list(new, old)) {
  		new->seq = volume->servers_seq + 1;
8a070a964   David Howells   afs: Detect cell ...
337
  		rcu_assign_pointer(volume->servers, new);
d2ddc776a   David Howells   afs: Overhaul vol...
338
339
340
  		smp_wmb();
  		volume->servers_seq++;
  		discard = old;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
341
  	}
d2ddc776a   David Howells   afs: Overhaul vol...
342
  	volume->update_at = ktime_get_real_seconds() + afs_volume_record_life;
d2ddc776a   David Howells   afs: Overhaul vol...
343
344
  	write_unlock(&volume->servers_lock);
  	ret = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
345

d2ddc776a   David Howells   afs: Overhaul vol...
346
347
348
349
350
351
352
  	afs_put_serverlist(volume->cell->net, discard);
  error_vldb:
  	kfree(vldb);
  error:
  	_leave(" = %d", ret);
  	return ret;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
353

d2ddc776a   David Howells   afs: Overhaul vol...
354
355
356
  /*
   * Make sure the volume record is up to date.
   */
e49c7b2f6   David Howells   afs: Build an abs...
357
  int afs_check_volume_status(struct afs_volume *volume, struct afs_operation *op)
d2ddc776a   David Howells   afs: Overhaul vol...
358
  {
d2ddc776a   David Howells   afs: Overhaul vol...
359
  	int ret, retries = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
360

d2ddc776a   David Howells   afs: Overhaul vol...
361
  	_enter("");
d2ddc776a   David Howells   afs: Overhaul vol...
362
  retry:
f6cbb368b   David Howells   afs: Actively pol...
363
364
365
366
367
368
369
370
371
  	if (test_bit(AFS_VOLUME_WAIT, &volume->flags))
  		goto wait;
  	if (volume->update_at <= ktime_get_real_seconds() ||
  	    test_bit(AFS_VOLUME_NEEDS_UPDATE, &volume->flags))
  		goto update;
  	_leave(" = 0");
  	return 0;
  
  update:
d2ddc776a   David Howells   afs: Overhaul vol...
372
  	if (!test_and_set_bit_lock(AFS_VOLUME_UPDATING, &volume->flags)) {
f6cbb368b   David Howells   afs: Actively pol...
373
  		clear_bit(AFS_VOLUME_NEEDS_UPDATE, &volume->flags);
e49c7b2f6   David Howells   afs: Build an abs...
374
  		ret = afs_update_volume_status(volume, op->key);
f6cbb368b   David Howells   afs: Actively pol...
375
376
  		if (ret < 0)
  			set_bit(AFS_VOLUME_NEEDS_UPDATE, &volume->flags);
d2ddc776a   David Howells   afs: Overhaul vol...
377
378
379
380
381
382
  		clear_bit_unlock(AFS_VOLUME_WAIT, &volume->flags);
  		clear_bit_unlock(AFS_VOLUME_UPDATING, &volume->flags);
  		wake_up_bit(&volume->flags, AFS_VOLUME_WAIT);
  		_leave(" = %d", ret);
  		return ret;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
383

f6cbb368b   David Howells   afs: Actively pol...
384
  wait:
d2ddc776a   David Howells   afs: Overhaul vol...
385
386
387
388
  	if (!test_bit(AFS_VOLUME_WAIT, &volume->flags)) {
  		_leave(" = 0 [no wait]");
  		return 0;
  	}
c4bfda16d   David Howells   afs: Make record ...
389
  	ret = wait_on_bit(&volume->flags, AFS_VOLUME_WAIT,
e49c7b2f6   David Howells   afs: Build an abs...
390
391
  			  (op->flags & AFS_OPERATION_UNINTR) ?
  			  TASK_UNINTERRUPTIBLE : TASK_INTERRUPTIBLE);
d2ddc776a   David Howells   afs: Overhaul vol...
392
393
394
395
396
397
398
399
400
401
402
  	if (ret == -ERESTARTSYS) {
  		_leave(" = %d", ret);
  		return ret;
  	}
  
  	retries++;
  	if (retries == 4) {
  		_leave(" = -ESTALE");
  		return -ESTALE;
  	}
  	goto retry;
ec26815ad   David Howells   [AFS]: Clean up t...
403
  }