Blame view

fs/afs/volume.c 9.74 KB
ec26815ad   David Howells   [AFS]: Clean up t...
1
  /* AFS volume management
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2
   *
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
3
   * Copyright (C) 2002, 2007 Red Hat, Inc. All Rights Reserved.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
4
5
6
7
8
9
10
11
12
13
14
15
16
17
   * Written by David Howells (dhowells@redhat.com)
   *
   * This program is free software; you can redistribute it and/or
   * modify it under the terms of the GNU General Public License
   * as published by the Free Software Foundation; either version
   * 2 of the License, or (at your option) any later version.
   */
  
  #include <linux/kernel.h>
  #include <linux/module.h>
  #include <linux/init.h>
  #include <linux/slab.h>
  #include <linux/fs.h>
  #include <linux/pagemap.h>
e8edc6e03   Alexey Dobriyan   Detach sched.h fr...
18
  #include <linux/sched.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
19
  #include "internal.h"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
20
  static const char *afs_voltypes[] = { "R/W", "R/O", "BAK" };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
21

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
  /*
   * lookup a volume by name
   * - this can be one of the following:
   *	"%[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
   */
00d3b7a45   David Howells   [AFS]: Add securi...
44
  struct afs_volume *afs_volume_lookup(struct afs_mount_params *params)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
45
46
47
  {
  	struct afs_vlocation *vlocation = NULL;
  	struct afs_volume *volume = NULL;
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
48
  	struct afs_server *server = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
49
  	char srvtmask;
00d3b7a45   David Howells   [AFS]: Add securi...
50
  	int ret, loop;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
51

00d3b7a45   David Howells   [AFS]: Add securi...
52
53
  	_enter("{%*.*s,%d}",
  	       params->volnamesz, params->volnamesz, params->volname, params->rwpath);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
54
55
  
  	/* lookup the volume location record */
00d3b7a45   David Howells   [AFS]: Add securi...
56
57
  	vlocation = afs_vlocation_lookup(params->cell, params->key,
  					 params->volname, params->volnamesz);
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
58
59
60
  	if (IS_ERR(vlocation)) {
  		ret = PTR_ERR(vlocation);
  		vlocation = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
61
  		goto error;
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
62
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
63
64
65
  
  	/* make the final decision on the type we want */
  	ret = -ENOMEDIUM;
00d3b7a45   David Howells   [AFS]: Add securi...
66
  	if (params->force && !(vlocation->vldb.vidmask & (1 << params->type)))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
67
68
69
70
71
  		goto error;
  
  	srvtmask = 0;
  	for (loop = 0; loop < vlocation->vldb.nservers; loop++)
  		srvtmask |= vlocation->vldb.srvtmask[loop];
00d3b7a45   David Howells   [AFS]: Add securi...
72
73
  	if (params->force) {
  		if (!(srvtmask & (1 << params->type)))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
74
  			goto error;
ec26815ad   David Howells   [AFS]: Clean up t...
75
  	} else if (srvtmask & AFS_VOL_VTM_RO) {
00d3b7a45   David Howells   [AFS]: Add securi...
76
  		params->type = AFSVL_ROVOL;
ec26815ad   David Howells   [AFS]: Clean up t...
77
  	} else if (srvtmask & AFS_VOL_VTM_RW) {
00d3b7a45   David Howells   [AFS]: Add securi...
78
  		params->type = AFSVL_RWVOL;
ec26815ad   David Howells   [AFS]: Clean up t...
79
  	} else {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
80
81
  		goto error;
  	}
00d3b7a45   David Howells   [AFS]: Add securi...
82
  	down_write(&params->cell->vl_sem);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
83
84
  
  	/* is the volume already active? */
00d3b7a45   David Howells   [AFS]: Add securi...
85
  	if (vlocation->vols[params->type]) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
86
  		/* yes - re-use it */
00d3b7a45   David Howells   [AFS]: Add securi...
87
  		volume = vlocation->vols[params->type];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
88
89
90
91
92
93
94
95
  		afs_get_volume(volume);
  		goto success;
  	}
  
  	/* create a new volume record */
  	_debug("creating new volume record");
  
  	ret = -ENOMEM;
f8314dc60   Panagiotis Issaris   [PATCH] fs: Conve...
96
  	volume = kzalloc(sizeof(struct afs_volume), GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
97
98
  	if (!volume)
  		goto error_up;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
99
  	atomic_set(&volume->usage, 1);
00d3b7a45   David Howells   [AFS]: Add securi...
100
101
102
103
  	volume->type		= params->type;
  	volume->type_force	= params->force;
  	volume->cell		= params->cell;
  	volume->vid		= vlocation->vldb.vid[params->type];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
104
105
106
107
108
109
  
  	init_rwsem(&volume->server_sem);
  
  	/* look up all the applicable server records */
  	for (loop = 0; loop < 8; loop++) {
  		if (vlocation->vldb.srvtmask[loop] & (1 << volume->type)) {
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
110
111
112
113
  			server = afs_lookup_server(
  			       volume->cell, &vlocation->vldb.servers[loop]);
  			if (IS_ERR(server)) {
  				ret = PTR_ERR(server);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
114
  				goto error_discard;
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
115
  			}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
116

08e0e7c82   David Howells   [AF_RXRPC]: Make ...
117
  			volume->servers[volume->nservers] = server;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
118
119
120
121
122
  			volume->nservers++;
  		}
  	}
  
  	/* attach the cache and volume location */
9b3f26c91   David Howells   FS-Cache: Make kA...
123
124
125
  #ifdef CONFIG_AFS_FSCACHE
  	volume->cache = fscache_acquire_cookie(vlocation->cache,
  					       &afs_volume_cache_index_def,
94d30ae90   David Howells   FS-Cache: Provide...
126
  					       volume, true);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
127
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
128
129
  	afs_get_vlocation(vlocation);
  	volume->vlocation = vlocation;
00d3b7a45   David Howells   [AFS]: Add securi...
130
  	vlocation->vols[volume->type] = volume;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
131

ec26815ad   David Howells   [AFS]: Clean up t...
132
  success:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
133
134
  	_debug("kAFS selected %s volume %08x",
  	       afs_voltypes[volume->type], volume->vid);
00d3b7a45   David Howells   [AFS]: Add securi...
135
  	up_write(&params->cell->vl_sem);
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
136
  	afs_put_vlocation(vlocation);
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
137
138
  	_leave(" = %p", volume);
  	return volume;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
139
140
  
  	/* clean up */
ec26815ad   David Howells   [AFS]: Clean up t...
141
  error_up:
00d3b7a45   David Howells   [AFS]: Add securi...
142
  	up_write(&params->cell->vl_sem);
ec26815ad   David Howells   [AFS]: Clean up t...
143
  error:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
144
  	afs_put_vlocation(vlocation);
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
145
146
  	_leave(" = %d", ret);
  	return ERR_PTR(ret);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
147

ec26815ad   David Howells   [AFS]: Clean up t...
148
  error_discard:
00d3b7a45   David Howells   [AFS]: Add securi...
149
  	up_write(&params->cell->vl_sem);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
150
151
152
153
154
155
  
  	for (loop = volume->nservers - 1; loop >= 0; loop--)
  		afs_put_server(volume->servers[loop]);
  
  	kfree(volume);
  	goto error;
ec26815ad   David Howells   [AFS]: Clean up t...
156
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
157

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
158
159
160
161
162
163
164
165
166
167
168
169
  /*
   * destroy a volume record
   */
  void afs_put_volume(struct afs_volume *volume)
  {
  	struct afs_vlocation *vlocation;
  	int loop;
  
  	if (!volume)
  		return;
  
  	_enter("%p", volume);
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
170
  	ASSERTCMP(atomic_read(&volume->usage), >, 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
171

08e0e7c82   David Howells   [AF_RXRPC]: Make ...
172
  	vlocation = volume->vlocation;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
  
  	/* to prevent a race, the decrement and the dequeue must be effectively
  	 * atomic */
  	down_write(&vlocation->cell->vl_sem);
  
  	if (likely(!atomic_dec_and_test(&volume->usage))) {
  		up_write(&vlocation->cell->vl_sem);
  		_leave("");
  		return;
  	}
  
  	vlocation->vols[volume->type] = NULL;
  
  	up_write(&vlocation->cell->vl_sem);
  
  	/* finish cleaning up the volume */
9b3f26c91   David Howells   FS-Cache: Make kA...
189
190
  #ifdef CONFIG_AFS_FSCACHE
  	fscache_relinquish_cookie(volume->cache, 0);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
191
192
193
194
195
196
197
198
199
  #endif
  	afs_put_vlocation(vlocation);
  
  	for (loop = volume->nservers - 1; loop >= 0; loop--)
  		afs_put_server(volume->servers[loop]);
  
  	kfree(volume);
  
  	_leave(" [destroyed]");
ec26815ad   David Howells   [AFS]: Clean up t...
200
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
201

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
202
203
204
205
  /*
   * pick a server to use to try accessing this volume
   * - returns with an elevated usage count on the server chosen
   */
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
206
  struct afs_server *afs_volume_pick_fileserver(struct afs_vnode *vnode)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
207
  {
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
208
  	struct afs_volume *volume = vnode->volume;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
209
210
211
212
  	struct afs_server *server;
  	int ret, state, loop;
  
  	_enter("%s", volume->vlocation->vldb.name);
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
213
214
215
216
217
218
  	/* stick with the server we're already using if we can */
  	if (vnode->server && vnode->server->fs_state == 0) {
  		afs_get_server(vnode->server);
  		_leave(" = %p [current]", vnode->server);
  		return vnode->server;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
219
220
221
222
223
224
225
  	down_read(&volume->server_sem);
  
  	/* handle the no-server case */
  	if (volume->nservers == 0) {
  		ret = volume->rjservers ? -ENOMEDIUM : -ESTALE;
  		up_read(&volume->server_sem);
  		_leave(" = %d [no servers]", ret);
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
226
  		return ERR_PTR(ret);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
227
228
229
230
231
232
233
234
  	}
  
  	/* basically, just search the list for the first live server and use
  	 * that */
  	ret = 0;
  	for (loop = 0; loop < volume->nservers; loop++) {
  		server = volume->servers[loop];
  		state = server->fs_state;
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
235
  		_debug("consider %d [%d]", loop, state);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
236
237
238
239
240
  		switch (state) {
  			/* found an apparently healthy server */
  		case 0:
  			afs_get_server(server);
  			up_read(&volume->server_sem);
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
241
242
243
  			_leave(" = %p (picked %08x)",
  			       server, ntohl(server->addr.s_addr));
  			return server;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
  
  		case -ENETUNREACH:
  			if (ret == 0)
  				ret = state;
  			break;
  
  		case -EHOSTUNREACH:
  			if (ret == 0 ||
  			    ret == -ENETUNREACH)
  				ret = state;
  			break;
  
  		case -ECONNREFUSED:
  			if (ret == 0 ||
  			    ret == -ENETUNREACH ||
  			    ret == -EHOSTUNREACH)
  				ret = state;
  			break;
  
  		default:
  		case -EREMOTEIO:
  			if (ret == 0 ||
  			    ret == -ENETUNREACH ||
  			    ret == -EHOSTUNREACH ||
  			    ret == -ECONNREFUSED)
  				ret = state;
  			break;
  		}
  	}
  
  	/* no available servers
  	 * - TODO: handle the no active servers case better
  	 */
  	up_read(&volume->server_sem);
  	_leave(" = %d", ret);
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
279
  	return ERR_PTR(ret);
ec26815ad   David Howells   [AFS]: Clean up t...
280
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
281

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
282
283
284
285
286
  /*
   * release a server after use
   * - releases the ref on the server struct that was acquired by picking
   * - records result of using a particular server to access a volume
   * - return 0 to try again, 1 if okay or to issue error
260a98031   David Howells   [AFS]: Add "direc...
287
   * - the caller must release the server struct if result was 0
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
288
   */
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
289
  int afs_volume_release_fileserver(struct afs_vnode *vnode,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
290
291
292
  				  struct afs_server *server,
  				  int result)
  {
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
293
  	struct afs_volume *volume = vnode->volume;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
294
295
296
297
298
299
300
301
302
303
  	unsigned loop;
  
  	_enter("%s,%08x,%d",
  	       volume->vlocation->vldb.name, ntohl(server->addr.s_addr),
  	       result);
  
  	switch (result) {
  		/* success */
  	case 0:
  		server->fs_act_jif = jiffies;
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
304
  		server->fs_state = 0;
260a98031   David Howells   [AFS]: Add "direc...
305
306
  		_leave("");
  		return 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
307
308
309
310
311
  
  		/* the fileserver denied all knowledge of the volume */
  	case -ENOMEDIUM:
  		server->fs_act_jif = jiffies;
  		down_write(&volume->server_sem);
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
312
  		/* firstly, find where the server is in the active list (if it
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
  		 * is) */
  		for (loop = 0; loop < volume->nservers; loop++)
  			if (volume->servers[loop] == server)
  				goto present;
  
  		/* no longer there - may have been discarded by another op */
  		goto try_next_server_upw;
  
  	present:
  		volume->nservers--;
  		memmove(&volume->servers[loop],
  			&volume->servers[loop + 1],
  			sizeof(volume->servers[loop]) *
  			(volume->nservers - loop));
  		volume->servers[volume->nservers] = NULL;
  		afs_put_server(server);
  		volume->rjservers++;
  
  		if (volume->nservers > 0)
  			/* another server might acknowledge its existence */
  			goto try_next_server_upw;
  
  		/* handle the case where all the fileservers have rejected the
  		 * volume
  		 * - TODO: try asking the fileservers for volume information
  		 * - TODO: contact the VL server again to see if the volume is
  		 *         no longer registered
  		 */
  		up_write(&volume->server_sem);
  		afs_put_server(server);
  		_leave(" [completely rejected]");
  		return 1;
  
  		/* problem reaching the server */
  	case -ENETUNREACH:
  	case -EHOSTUNREACH:
  	case -ECONNREFUSED:
08e0e7c82   David Howells   [AF_RXRPC]: Make ...
350
  	case -ETIME:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
  	case -ETIMEDOUT:
  	case -EREMOTEIO:
  		/* mark the server as dead
  		 * TODO: vary dead timeout depending on error
  		 */
  		spin_lock(&server->fs_lock);
  		if (!server->fs_state) {
  			server->fs_dead_jif = jiffies + HZ * 10;
  			server->fs_state = result;
  			printk("kAFS: SERVER DEAD state=%d
  ", result);
  		}
  		spin_unlock(&server->fs_lock);
  		goto try_next_server;
  
  		/* miscellaneous error */
  	default:
  		server->fs_act_jif = jiffies;
  	case -ENOMEM:
  	case -ENONET:
260a98031   David Howells   [AFS]: Add "direc...
371
372
373
374
  		/* tell the caller to accept the result */
  		afs_put_server(server);
  		_leave(" [local failure]");
  		return 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
375
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
376
  	/* tell the caller to loop around and try the next server */
ec26815ad   David Howells   [AFS]: Clean up t...
377
  try_next_server_upw:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
378
  	up_write(&volume->server_sem);
ec26815ad   David Howells   [AFS]: Clean up t...
379
  try_next_server:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
380
381
382
  	afs_put_server(server);
  	_leave(" [try next server]");
  	return 0;
ec26815ad   David Howells   [AFS]: Clean up t...
383
  }