Blame view

fs/afs/volume.c 8.02 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
  /*
d2ddc776a   David Howells   afs: Overhaul vol...
14
15
   * Allocate a volume record and load it up from a vldb record.
   */
13fcc6837   David Howells   afs: Add fs_conte...
16
  static struct afs_volume *afs_alloc_volume(struct afs_fs_context *params,
d2ddc776a   David Howells   afs: Overhaul vol...
17
18
19
20
  					   struct afs_vldb_entry *vldb,
  					   unsigned long type_mask)
  {
  	struct afs_server_list *slist;
d2ddc776a   David Howells   afs: Overhaul vol...
21
  	struct afs_volume *volume;
45df84627   David Howells   afs: Fix server l...
22
  	int ret = -ENOMEM, nr_servers = 0, i;
d2ddc776a   David Howells   afs: Overhaul vol...
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
  
  	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;
  	volume->cell		= afs_get_cell(params->cell);
  	volume->type		= params->type;
  	volume->type_force	= params->force;
  	volume->name_len	= vldb->name_len;
  
  	atomic_set(&volume->usage, 1);
  	INIT_LIST_HEAD(&volume->proc_link);
  	rwlock_init(&volume->servers_lock);
90fa9b645   David Howells   afs: Fix uninitia...
42
  	rwlock_init(&volume->cb_v_break_lock);
d2ddc776a   David Howells   afs: Overhaul vol...
43
44
45
46
47
48
49
50
51
52
  	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);
  	volume->servers = slist;
d2ddc776a   David Howells   afs: Overhaul vol...
53
  	return volume;
d2ddc776a   David Howells   afs: Overhaul vol...
54
  error_1:
e44150157   David Howells   afs: Add missing ...
55
  	afs_put_cell(params->net, volume->cell);
d2ddc776a   David Howells   afs: Overhaul vol...
56
57
58
59
60
61
62
63
64
65
66
67
68
  	kfree(volume);
  error_0:
  	return ERR_PTR(ret);
  }
  
  /*
   * 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...
69
70
  	struct afs_vldb_entry *vldb = ERR_PTR(-EDESTADDRREQ);
  	struct afs_vl_cursor vc;
d2ddc776a   David Howells   afs: Overhaul vol...
71
  	int ret;
0a5143f2f   David Howells   afs: Implement VL...
72
73
  	if (!afs_begin_vlserver_operation(&vc, cell, key))
  		return ERR_PTR(-ERESTARTSYS);
d2ddc776a   David Howells   afs: Overhaul vol...
74

0a5143f2f   David Howells   afs: Implement VL...
75
  	while (afs_select_vlserver(&vc)) {
0a5143f2f   David Howells   afs: Implement VL...
76
  		vldb = afs_vl_get_entry_by_name_u(&vc, volname, volnamesz);
d2ddc776a   David Howells   afs: Overhaul vol...
77
  	}
0a5143f2f   David Howells   afs: Implement VL...
78
79
  	ret = afs_end_vlserver_operation(&vc);
  	return ret < 0 ? ERR_PTR(ret) : vldb;
d2ddc776a   David Howells   afs: Overhaul vol...
80
81
82
83
84
85
86
  }
  
  /*
   * 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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
   *	"%[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...
106
  struct afs_volume *afs_create_volume(struct afs_fs_context *params)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
107
  {
d2ddc776a   David Howells   afs: Overhaul vol...
108
109
110
  	struct afs_vldb_entry *vldb;
  	struct afs_volume *volume;
  	unsigned long type_mask = 1UL << params->type;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
111

d2ddc776a   David Howells   afs: Overhaul vol...
112
113
114
115
  	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
116

d2ddc776a   David Howells   afs: Overhaul vol...
117
118
119
120
  	if (test_bit(AFS_VLDB_QUERY_ERROR, &vldb->flags)) {
  		volume = ERR_PTR(vldb->error);
  		goto error;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
121

d2ddc776a   David Howells   afs: Overhaul vol...
122
123
  	/* Make the final decision on the type we want */
  	volume = ERR_PTR(-ENOMEDIUM);
00d3b7a45   David Howells   [AFS]: Add securi...
124
  	if (params->force) {
d2ddc776a   David Howells   afs: Overhaul vol...
125
  		if (!(vldb->flags & type_mask))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
126
  			goto error;
d2ddc776a   David Howells   afs: Overhaul vol...
127
  	} else if (test_bit(AFS_VLDB_HAS_RO, &vldb->flags)) {
00d3b7a45   David Howells   [AFS]: Add securi...
128
  		params->type = AFSVL_ROVOL;
d2ddc776a   David Howells   afs: Overhaul vol...
129
  	} else if (test_bit(AFS_VLDB_HAS_RW, &vldb->flags)) {
00d3b7a45   David Howells   [AFS]: Add securi...
130
  		params->type = AFSVL_RWVOL;
ec26815ad   David Howells   [AFS]: Clean up t...
131
  	} else {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
132
133
  		goto error;
  	}
d2ddc776a   David Howells   afs: Overhaul vol...
134
135
  	type_mask = 1UL << params->type;
  	volume = afs_alloc_volume(params, vldb, type_mask);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
136

d2ddc776a   David Howells   afs: Overhaul vol...
137
138
139
140
  error:
  	kfree(vldb);
  	return volume;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
141

d2ddc776a   David Howells   afs: Overhaul vol...
142
143
144
145
146
147
  /*
   * 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
148

d2ddc776a   David Howells   afs: Overhaul vol...
149
150
151
  #ifdef CONFIG_AFS_FSCACHE
  	ASSERTCMP(volume->cache, ==, NULL);
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
152

d2ddc776a   David Howells   afs: Overhaul vol...
153
154
155
  	afs_put_serverlist(net, volume->servers);
  	afs_put_cell(net, volume->cell);
  	kfree(volume);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
156

d2ddc776a   David Howells   afs: Overhaul vol...
157
158
159
160
161
162
163
164
165
166
167
168
169
  	_leave(" [destroyed]");
  }
  
  /*
   * Drop a reference on a volume record.
   */
  void afs_put_volume(struct afs_cell *cell, struct afs_volume *volume)
  {
  	if (volume) {
  		_enter("%s", volume->name);
  
  		if (atomic_dec_and_test(&volume->usage))
  			afs_destroy_volume(cell->net, volume);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
170
  	}
d2ddc776a   David Howells   afs: Overhaul vol...
171
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
172

d2ddc776a   David Howells   afs: Overhaul vol...
173
174
175
176
177
  /*
   * Activate a volume.
   */
  void afs_activate_volume(struct afs_volume *volume)
  {
9b3f26c91   David Howells   FS-Cache: Make kA...
178
  #ifdef CONFIG_AFS_FSCACHE
ad6a942a9   David Howells   afs: Update the c...
179
  	volume->cache = fscache_acquire_cookie(volume->cell->cache,
9b3f26c91   David Howells   FS-Cache: Make kA...
180
  					       &afs_volume_cache_index_def,
402cb8dda   David Howells   fscache: Attach t...
181
182
  					       &volume->vid, sizeof(volume->vid),
  					       NULL, 0,
ee1235a9a   David Howells   fscache: Pass obj...
183
  					       volume, 0, true);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
184
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
185

d2ddc776a   David Howells   afs: Overhaul vol...
186
187
188
189
  	write_lock(&volume->cell->proc_lock);
  	list_add_tail(&volume->proc_link, &volume->cell->proc_volumes);
  	write_unlock(&volume->cell->proc_lock);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
190

d2ddc776a   David Howells   afs: Overhaul vol...
191
192
193
194
195
196
  /*
   * Deactivate a volume.
   */
  void afs_deactivate_volume(struct afs_volume *volume)
  {
  	_enter("%s", volume->name);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
197

d2ddc776a   David Howells   afs: Overhaul vol...
198
199
200
  	write_lock(&volume->cell->proc_lock);
  	list_del_init(&volume->proc_link);
  	write_unlock(&volume->cell->proc_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
201

d2ddc776a   David Howells   afs: Overhaul vol...
202
  #ifdef CONFIG_AFS_FSCACHE
402cb8dda   David Howells   fscache: Attach t...
203
  	fscache_relinquish_cookie(volume->cache, NULL,
d2ddc776a   David Howells   afs: Overhaul vol...
204
205
206
  				  test_bit(AFS_VOLUME_DELETED, &volume->flags));
  	volume->cache = NULL;
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
207

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

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
211
  /*
d2ddc776a   David Howells   afs: Overhaul vol...
212
   * Query the VL service to update the volume status.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
213
   */
d2ddc776a   David Howells   afs: Overhaul vol...
214
  static int afs_update_volume_status(struct afs_volume *volume, struct key *key)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
215
  {
d2ddc776a   David Howells   afs: Overhaul vol...
216
217
218
219
  	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
220

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

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

d2ddc776a   David Howells   afs: Overhaul vol...
228
229
230
231
232
  	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
233

d2ddc776a   David Howells   afs: Overhaul vol...
234
235
236
237
238
239
240
241
242
243
  	/* 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...
244
  				    vldb, (1 << volume->type));
d2ddc776a   David Howells   afs: Overhaul vol...
245
246
247
248
  	if (IS_ERR(new)) {
  		ret = PTR_ERR(new);
  		goto error_vldb;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
249

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

d2ddc776a   David Howells   afs: Overhaul vol...
252
253
254
255
256
257
258
259
  	discard = new;
  	old = volume->servers;
  	if (afs_annotate_server_list(new, old)) {
  		new->seq = volume->servers_seq + 1;
  		volume->servers = new;
  		smp_wmb();
  		volume->servers_seq++;
  		discard = old;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
260
  	}
d2ddc776a   David Howells   afs: Overhaul vol...
261
262
263
264
  	volume->update_at = ktime_get_real_seconds() + afs_volume_record_life;
  	clear_bit(AFS_VOLUME_NEEDS_UPDATE, &volume->flags);
  	write_unlock(&volume->servers_lock);
  	ret = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
265

d2ddc776a   David Howells   afs: Overhaul vol...
266
267
268
269
270
271
272
  	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
273

d2ddc776a   David Howells   afs: Overhaul vol...
274
275
276
  /*
   * Make sure the volume record is up to date.
   */
c2bdc86ec   David Howells   afs: Make record ...
277
  int afs_check_volume_status(struct afs_volume *volume, struct afs_fs_cursor *fc)
d2ddc776a   David Howells   afs: Overhaul vol...
278
279
280
  {
  	time64_t now = ktime_get_real_seconds();
  	int ret, retries = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
281

d2ddc776a   David Howells   afs: Overhaul vol...
282
283
284
285
286
287
288
289
290
291
  	_enter("");
  
  	if (volume->update_at <= now)
  		set_bit(AFS_VOLUME_NEEDS_UPDATE, &volume->flags);
  
  retry:
  	if (!test_bit(AFS_VOLUME_NEEDS_UPDATE, &volume->flags) &&
  	    !test_bit(AFS_VOLUME_WAIT, &volume->flags)) {
  		_leave(" = 0");
  		return 0;
c435ee345   David Howells   afs: Overhaul the...
292
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
293

d2ddc776a   David Howells   afs: Overhaul vol...
294
  	if (!test_and_set_bit_lock(AFS_VOLUME_UPDATING, &volume->flags)) {
c2bdc86ec   David Howells   afs: Make record ...
295
  		ret = afs_update_volume_status(volume, fc->key);
d2ddc776a   David Howells   afs: Overhaul vol...
296
297
298
299
300
301
  		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
302

d2ddc776a   David Howells   afs: Overhaul vol...
303
304
305
306
  	if (!test_bit(AFS_VOLUME_WAIT, &volume->flags)) {
  		_leave(" = 0 [no wait]");
  		return 0;
  	}
c2bdc86ec   David Howells   afs: Make record ...
307
308
309
  	ret = wait_on_bit(&volume->flags, AFS_VOLUME_WAIT,
  			  (fc->flags & AFS_FS_CURSOR_INTR) ?
  			  TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE);
d2ddc776a   David Howells   afs: Overhaul vol...
310
311
312
313
314
315
316
317
318
319
320
  	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...
321
  }