1/* AFS vnode management
2 *
3 * Copyright (C) 2002, 2007 Red Hat, Inc. All Rights Reserved.
4 * Written by David Howells (dhowells@redhat.com)
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
10 */
11
12#include <linux/kernel.h>
13#include <linux/module.h>
14#include <linux/init.h>
15#include <linux/slab.h>
16#include <linux/fs.h>
17#include <linux/sched.h>
18#include "internal.h"
19
20
21/*
22 * insert a vnode into the backing server's vnode tree
23 */
24static void afs_install_vnode(struct afs_vnode *vnode,
25			      struct afs_server *server)
26{
27	struct afs_server *old_server = vnode->server;
28	struct afs_vnode *xvnode;
29	struct rb_node *parent, **p;
30
31	_enter("%p,%p", vnode, server);
32
33	if (old_server) {
34		spin_lock(&old_server->fs_lock);
35		rb_erase(&vnode->server_rb, &old_server->fs_vnodes);
36		spin_unlock(&old_server->fs_lock);
37	}
38
39	afs_get_server(server);
40	vnode->server = server;
41	afs_put_server(old_server);
42
43	/* insert into the server's vnode tree in FID order */
44	spin_lock(&server->fs_lock);
45
46	parent = NULL;
47	p = &server->fs_vnodes.rb_node;
48	while (*p) {
49		parent = *p;
50		xvnode = rb_entry(parent, struct afs_vnode, server_rb);
51		if (vnode->fid.vid < xvnode->fid.vid)
52			p = &(*p)->rb_left;
53		else if (vnode->fid.vid > xvnode->fid.vid)
54			p = &(*p)->rb_right;
55		else if (vnode->fid.vnode < xvnode->fid.vnode)
56			p = &(*p)->rb_left;
57		else if (vnode->fid.vnode > xvnode->fid.vnode)
58			p = &(*p)->rb_right;
59		else if (vnode->fid.unique < xvnode->fid.unique)
60			p = &(*p)->rb_left;
61		else if (vnode->fid.unique > xvnode->fid.unique)
62			p = &(*p)->rb_right;
63		else
64			BUG(); /* can't happen unless afs_iget() malfunctions */
65	}
66
67	rb_link_node(&vnode->server_rb, parent, p);
68	rb_insert_color(&vnode->server_rb, &server->fs_vnodes);
69
70	spin_unlock(&server->fs_lock);
71	_leave("");
72}
73
74/*
75 * insert a vnode into the promising server's update/expiration tree
76 * - caller must hold vnode->lock
77 */
78static void afs_vnode_note_promise(struct afs_vnode *vnode,
79				   struct afs_server *server)
80{
81	struct afs_server *old_server;
82	struct afs_vnode *xvnode;
83	struct rb_node *parent, **p;
84
85	_enter("%p,%p", vnode, server);
86
87	ASSERT(server != NULL);
88
89	old_server = vnode->server;
90	if (vnode->cb_promised) {
91		if (server == old_server &&
92		    vnode->cb_expires == vnode->cb_expires_at) {
93			_leave(" [no change]");
94			return;
95		}
96
97		spin_lock(&old_server->cb_lock);
98		if (vnode->cb_promised) {
99			_debug("delete");
100			rb_erase(&vnode->cb_promise, &old_server->cb_promises);
101			vnode->cb_promised = false;
102		}
103		spin_unlock(&old_server->cb_lock);
104	}
105
106	if (vnode->server != server)
107		afs_install_vnode(vnode, server);
108
109	vnode->cb_expires_at = vnode->cb_expires;
110	_debug("PROMISE on %p {%lu}",
111	       vnode, (unsigned long) vnode->cb_expires_at);
112
113	/* abuse an RB-tree to hold the expiration order (we may have multiple
114	 * items with the same expiration time) */
115	spin_lock(&server->cb_lock);
116
117	parent = NULL;
118	p = &server->cb_promises.rb_node;
119	while (*p) {
120		parent = *p;
121		xvnode = rb_entry(parent, struct afs_vnode, cb_promise);
122		if (vnode->cb_expires_at < xvnode->cb_expires_at)
123			p = &(*p)->rb_left;
124		else
125			p = &(*p)->rb_right;
126	}
127
128	rb_link_node(&vnode->cb_promise, parent, p);
129	rb_insert_color(&vnode->cb_promise, &server->cb_promises);
130	vnode->cb_promised = true;
131
132	spin_unlock(&server->cb_lock);
133	_leave("");
134}
135
136/*
137 * handle remote file deletion by discarding the callback promise
138 */
139static void afs_vnode_deleted_remotely(struct afs_vnode *vnode)
140{
141	struct afs_server *server;
142
143	_enter("{%p}", vnode->server);
144
145	set_bit(AFS_VNODE_DELETED, &vnode->flags);
146
147	server = vnode->server;
148	if (server) {
149		if (vnode->cb_promised) {
150			spin_lock(&server->cb_lock);
151			if (vnode->cb_promised) {
152				rb_erase(&vnode->cb_promise,
153					 &server->cb_promises);
154				vnode->cb_promised = false;
155			}
156			spin_unlock(&server->cb_lock);
157		}
158
159		spin_lock(&server->fs_lock);
160		rb_erase(&vnode->server_rb, &server->fs_vnodes);
161		spin_unlock(&server->fs_lock);
162
163		vnode->server = NULL;
164		afs_put_server(server);
165	} else {
166		ASSERT(!vnode->cb_promised);
167	}
168
169	_leave("");
170}
171
172/*
173 * finish off updating the recorded status of a file after a successful
174 * operation completion
175 * - starts callback expiry timer
176 * - adds to server's callback list
177 */
178void afs_vnode_finalise_status_update(struct afs_vnode *vnode,
179				      struct afs_server *server)
180{
181	struct afs_server *oldserver = NULL;
182
183	_enter("%p,%p", vnode, server);
184
185	spin_lock(&vnode->lock);
186	clear_bit(AFS_VNODE_CB_BROKEN, &vnode->flags);
187	afs_vnode_note_promise(vnode, server);
188	vnode->update_cnt--;
189	ASSERTCMP(vnode->update_cnt, >=, 0);
190	spin_unlock(&vnode->lock);
191
192	wake_up_all(&vnode->update_waitq);
193	afs_put_server(oldserver);
194	_leave("");
195}
196
197/*
198 * finish off updating the recorded status of a file after an operation failed
199 */
200static void afs_vnode_status_update_failed(struct afs_vnode *vnode, int ret)
201{
202	_enter("{%x:%u},%d", vnode->fid.vid, vnode->fid.vnode, ret);
203
204	spin_lock(&vnode->lock);
205
206	clear_bit(AFS_VNODE_CB_BROKEN, &vnode->flags);
207
208	if (ret == -ENOENT) {
209		/* the file was deleted on the server */
210		_debug("got NOENT from server - marking file deleted");
211		afs_vnode_deleted_remotely(vnode);
212	}
213
214	vnode->update_cnt--;
215	ASSERTCMP(vnode->update_cnt, >=, 0);
216	spin_unlock(&vnode->lock);
217
218	wake_up_all(&vnode->update_waitq);
219	_leave("");
220}
221
222/*
223 * fetch file status from the volume
224 * - don't issue a fetch if:
225 *   - the changed bit is not set and there's a valid callback
226 *   - there are any outstanding ops that will fetch the status
227 * - TODO implement local caching
228 */
229int afs_vnode_fetch_status(struct afs_vnode *vnode,
230			   struct afs_vnode *auth_vnode, struct key *key)
231{
232	struct afs_server *server;
233	unsigned long acl_order;
234	int ret;
235
236	DECLARE_WAITQUEUE(myself, current);
237
238	_enter("%s,{%x:%u.%u}",
239	       vnode->volume->vlocation->vldb.name,
240	       vnode->fid.vid, vnode->fid.vnode, vnode->fid.unique);
241
242	if (!test_bit(AFS_VNODE_CB_BROKEN, &vnode->flags) &&
243	    vnode->cb_promised) {
244		_leave(" [unchanged]");
245		return 0;
246	}
247
248	if (test_bit(AFS_VNODE_DELETED, &vnode->flags)) {
249		_leave(" [deleted]");
250		return -ENOENT;
251	}
252
253	acl_order = 0;
254	if (auth_vnode)
255		acl_order = auth_vnode->acl_order;
256
257	spin_lock(&vnode->lock);
258
259	if (!test_bit(AFS_VNODE_CB_BROKEN, &vnode->flags) &&
260	    vnode->cb_promised) {
261		spin_unlock(&vnode->lock);
262		_leave(" [unchanged]");
263		return 0;
264	}
265
266	ASSERTCMP(vnode->update_cnt, >=, 0);
267
268	if (vnode->update_cnt > 0) {
269		/* someone else started a fetch */
270		_debug("wait on fetch %d", vnode->update_cnt);
271
272		set_current_state(TASK_UNINTERRUPTIBLE);
273		ASSERT(myself.func != NULL);
274		add_wait_queue(&vnode->update_waitq, &myself);
275
276		/* wait for the status to be updated */
277		for (;;) {
278			if (!test_bit(AFS_VNODE_CB_BROKEN, &vnode->flags))
279				break;
280			if (test_bit(AFS_VNODE_DELETED, &vnode->flags))
281				break;
282
283			/* check to see if it got updated and invalidated all
284			 * before we saw it */
285			if (vnode->update_cnt == 0) {
286				remove_wait_queue(&vnode->update_waitq,
287						  &myself);
288				set_current_state(TASK_RUNNING);
289				goto get_anyway;
290			}
291
292			spin_unlock(&vnode->lock);
293
294			schedule();
295			set_current_state(TASK_UNINTERRUPTIBLE);
296
297			spin_lock(&vnode->lock);
298		}
299
300		remove_wait_queue(&vnode->update_waitq, &myself);
301		spin_unlock(&vnode->lock);
302		set_current_state(TASK_RUNNING);
303
304		return test_bit(AFS_VNODE_DELETED, &vnode->flags) ?
305			-ENOENT : 0;
306	}
307
308get_anyway:
309	/* okay... we're going to have to initiate the op */
310	vnode->update_cnt++;
311
312	spin_unlock(&vnode->lock);
313
314	/* merge AFS status fetches and clear outstanding callback on this
315	 * vnode */
316	do {
317		/* pick a server to query */
318		server = afs_volume_pick_fileserver(vnode);
319		if (IS_ERR(server))
320			goto no_server;
321
322		_debug("USING SERVER: %p{%08x}",
323		       server, ntohl(server->addr.s_addr));
324
325		ret = afs_fs_fetch_file_status(server, key, vnode, NULL,
326					       &afs_sync_call);
327
328	} while (!afs_volume_release_fileserver(vnode, server, ret));
329
330	/* adjust the flags */
331	if (ret == 0) {
332		_debug("adjust");
333		if (auth_vnode)
334			afs_cache_permit(vnode, key, acl_order);
335		afs_vnode_finalise_status_update(vnode, server);
336		afs_put_server(server);
337	} else {
338		_debug("failed [%d]", ret);
339		afs_vnode_status_update_failed(vnode, ret);
340	}
341
342	ASSERTCMP(vnode->update_cnt, >=, 0);
343
344	_leave(" = %d [cnt %d]", ret, vnode->update_cnt);
345	return ret;
346
347no_server:
348	spin_lock(&vnode->lock);
349	vnode->update_cnt--;
350	ASSERTCMP(vnode->update_cnt, >=, 0);
351	spin_unlock(&vnode->lock);
352	_leave(" = %ld [cnt %d]", PTR_ERR(server), vnode->update_cnt);
353	return PTR_ERR(server);
354}
355
356/*
357 * fetch file data from the volume
358 * - TODO implement caching
359 */
360int afs_vnode_fetch_data(struct afs_vnode *vnode, struct key *key,
361			 off_t offset, size_t length, struct page *page)
362{
363	struct afs_server *server;
364	int ret;
365
366	_enter("%s{%x:%u.%u},%x,,,",
367	       vnode->volume->vlocation->vldb.name,
368	       vnode->fid.vid,
369	       vnode->fid.vnode,
370	       vnode->fid.unique,
371	       key_serial(key));
372
373	/* this op will fetch the status */
374	spin_lock(&vnode->lock);
375	vnode->update_cnt++;
376	spin_unlock(&vnode->lock);
377
378	/* merge in AFS status fetches and clear outstanding callback on this
379	 * vnode */
380	do {
381		/* pick a server to query */
382		server = afs_volume_pick_fileserver(vnode);
383		if (IS_ERR(server))
384			goto no_server;
385
386		_debug("USING SERVER: %08x\n", ntohl(server->addr.s_addr));
387
388		ret = afs_fs_fetch_data(server, key, vnode, offset, length,
389					page, &afs_sync_call);
390
391	} while (!afs_volume_release_fileserver(vnode, server, ret));
392
393	/* adjust the flags */
394	if (ret == 0) {
395		afs_vnode_finalise_status_update(vnode, server);
396		afs_put_server(server);
397	} else {
398		afs_vnode_status_update_failed(vnode, ret);
399	}
400
401	_leave(" = %d", ret);
402	return ret;
403
404no_server:
405	spin_lock(&vnode->lock);
406	vnode->update_cnt--;
407	ASSERTCMP(vnode->update_cnt, >=, 0);
408	spin_unlock(&vnode->lock);
409	return PTR_ERR(server);
410}
411
412/*
413 * make a file or a directory
414 */
415int afs_vnode_create(struct afs_vnode *vnode, struct key *key,
416		     const char *name, umode_t mode, struct afs_fid *newfid,
417		     struct afs_file_status *newstatus,
418		     struct afs_callback *newcb, struct afs_server **_server)
419{
420	struct afs_server *server;
421	int ret;
422
423	_enter("%s{%x:%u.%u},%x,%s,,",
424	       vnode->volume->vlocation->vldb.name,
425	       vnode->fid.vid,
426	       vnode->fid.vnode,
427	       vnode->fid.unique,
428	       key_serial(key),
429	       name);
430
431	/* this op will fetch the status on the directory we're creating in */
432	spin_lock(&vnode->lock);
433	vnode->update_cnt++;
434	spin_unlock(&vnode->lock);
435
436	do {
437		/* pick a server to query */
438		server = afs_volume_pick_fileserver(vnode);
439		if (IS_ERR(server))
440			goto no_server;
441
442		_debug("USING SERVER: %08x\n", ntohl(server->addr.s_addr));
443
444		ret = afs_fs_create(server, key, vnode, name, mode, newfid,
445				    newstatus, newcb, &afs_sync_call);
446
447	} while (!afs_volume_release_fileserver(vnode, server, ret));
448
449	/* adjust the flags */
450	if (ret == 0) {
451		afs_vnode_finalise_status_update(vnode, server);
452		*_server = server;
453	} else {
454		afs_vnode_status_update_failed(vnode, ret);
455		*_server = NULL;
456	}
457
458	_leave(" = %d [cnt %d]", ret, vnode->update_cnt);
459	return ret;
460
461no_server:
462	spin_lock(&vnode->lock);
463	vnode->update_cnt--;
464	ASSERTCMP(vnode->update_cnt, >=, 0);
465	spin_unlock(&vnode->lock);
466	_leave(" = %ld [cnt %d]", PTR_ERR(server), vnode->update_cnt);
467	return PTR_ERR(server);
468}
469
470/*
471 * remove a file or directory
472 */
473int afs_vnode_remove(struct afs_vnode *vnode, struct key *key, const char *name,
474		     bool isdir)
475{
476	struct afs_server *server;
477	int ret;
478
479	_enter("%s{%x:%u.%u},%x,%s",
480	       vnode->volume->vlocation->vldb.name,
481	       vnode->fid.vid,
482	       vnode->fid.vnode,
483	       vnode->fid.unique,
484	       key_serial(key),
485	       name);
486
487	/* this op will fetch the status on the directory we're removing from */
488	spin_lock(&vnode->lock);
489	vnode->update_cnt++;
490	spin_unlock(&vnode->lock);
491
492	do {
493		/* pick a server to query */
494		server = afs_volume_pick_fileserver(vnode);
495		if (IS_ERR(server))
496			goto no_server;
497
498		_debug("USING SERVER: %08x\n", ntohl(server->addr.s_addr));
499
500		ret = afs_fs_remove(server, key, vnode, name, isdir,
501				    &afs_sync_call);
502
503	} while (!afs_volume_release_fileserver(vnode, server, ret));
504
505	/* adjust the flags */
506	if (ret == 0) {
507		afs_vnode_finalise_status_update(vnode, server);
508		afs_put_server(server);
509	} else {
510		afs_vnode_status_update_failed(vnode, ret);
511	}
512
513	_leave(" = %d [cnt %d]", ret, vnode->update_cnt);
514	return ret;
515
516no_server:
517	spin_lock(&vnode->lock);
518	vnode->update_cnt--;
519	ASSERTCMP(vnode->update_cnt, >=, 0);
520	spin_unlock(&vnode->lock);
521	_leave(" = %ld [cnt %d]", PTR_ERR(server), vnode->update_cnt);
522	return PTR_ERR(server);
523}
524
525/*
526 * create a hard link
527 */
528extern int afs_vnode_link(struct afs_vnode *dvnode, struct afs_vnode *vnode,
529			  struct key *key, const char *name)
530{
531	struct afs_server *server;
532	int ret;
533
534	_enter("%s{%x:%u.%u},%s{%x:%u.%u},%x,%s",
535	       dvnode->volume->vlocation->vldb.name,
536	       dvnode->fid.vid,
537	       dvnode->fid.vnode,
538	       dvnode->fid.unique,
539	       vnode->volume->vlocation->vldb.name,
540	       vnode->fid.vid,
541	       vnode->fid.vnode,
542	       vnode->fid.unique,
543	       key_serial(key),
544	       name);
545
546	/* this op will fetch the status on the directory we're removing from */
547	spin_lock(&vnode->lock);
548	vnode->update_cnt++;
549	spin_unlock(&vnode->lock);
550	spin_lock(&dvnode->lock);
551	dvnode->update_cnt++;
552	spin_unlock(&dvnode->lock);
553
554	do {
555		/* pick a server to query */
556		server = afs_volume_pick_fileserver(dvnode);
557		if (IS_ERR(server))
558			goto no_server;
559
560		_debug("USING SERVER: %08x\n", ntohl(server->addr.s_addr));
561
562		ret = afs_fs_link(server, key, dvnode, vnode, name,
563				  &afs_sync_call);
564
565	} while (!afs_volume_release_fileserver(dvnode, server, ret));
566
567	/* adjust the flags */
568	if (ret == 0) {
569		afs_vnode_finalise_status_update(vnode, server);
570		afs_vnode_finalise_status_update(dvnode, server);
571		afs_put_server(server);
572	} else {
573		afs_vnode_status_update_failed(vnode, ret);
574		afs_vnode_status_update_failed(dvnode, ret);
575	}
576
577	_leave(" = %d [cnt %d]", ret, vnode->update_cnt);
578	return ret;
579
580no_server:
581	spin_lock(&vnode->lock);
582	vnode->update_cnt--;
583	ASSERTCMP(vnode->update_cnt, >=, 0);
584	spin_unlock(&vnode->lock);
585	spin_lock(&dvnode->lock);
586	dvnode->update_cnt--;
587	ASSERTCMP(dvnode->update_cnt, >=, 0);
588	spin_unlock(&dvnode->lock);
589	_leave(" = %ld [cnt %d]", PTR_ERR(server), vnode->update_cnt);
590	return PTR_ERR(server);
591}
592
593/*
594 * create a symbolic link
595 */
596int afs_vnode_symlink(struct afs_vnode *vnode, struct key *key,
597		      const char *name, const char *content,
598		      struct afs_fid *newfid,
599		      struct afs_file_status *newstatus,
600		      struct afs_server **_server)
601{
602	struct afs_server *server;
603	int ret;
604
605	_enter("%s{%x:%u.%u},%x,%s,%s,,,",
606	       vnode->volume->vlocation->vldb.name,
607	       vnode->fid.vid,
608	       vnode->fid.vnode,
609	       vnode->fid.unique,
610	       key_serial(key),
611	       name, content);
612
613	/* this op will fetch the status on the directory we're creating in */
614	spin_lock(&vnode->lock);
615	vnode->update_cnt++;
616	spin_unlock(&vnode->lock);
617
618	do {
619		/* pick a server to query */
620		server = afs_volume_pick_fileserver(vnode);
621		if (IS_ERR(server))
622			goto no_server;
623
624		_debug("USING SERVER: %08x\n", ntohl(server->addr.s_addr));
625
626		ret = afs_fs_symlink(server, key, vnode, name, content,
627				     newfid, newstatus, &afs_sync_call);
628
629	} while (!afs_volume_release_fileserver(vnode, server, ret));
630
631	/* adjust the flags */
632	if (ret == 0) {
633		afs_vnode_finalise_status_update(vnode, server);
634		*_server = server;
635	} else {
636		afs_vnode_status_update_failed(vnode, ret);
637		*_server = NULL;
638	}
639
640	_leave(" = %d [cnt %d]", ret, vnode->update_cnt);
641	return ret;
642
643no_server:
644	spin_lock(&vnode->lock);
645	vnode->update_cnt--;
646	ASSERTCMP(vnode->update_cnt, >=, 0);
647	spin_unlock(&vnode->lock);
648	_leave(" = %ld [cnt %d]", PTR_ERR(server), vnode->update_cnt);
649	return PTR_ERR(server);
650}
651
652/*
653 * rename a file
654 */
655int afs_vnode_rename(struct afs_vnode *orig_dvnode,
656		     struct afs_vnode *new_dvnode,
657		     struct key *key,
658		     const char *orig_name,
659		     const char *new_name)
660{
661	struct afs_server *server;
662	int ret;
663
664	_enter("%s{%x:%u.%u},%s{%u,%u,%u},%x,%s,%s",
665	       orig_dvnode->volume->vlocation->vldb.name,
666	       orig_dvnode->fid.vid,
667	       orig_dvnode->fid.vnode,
668	       orig_dvnode->fid.unique,
669	       new_dvnode->volume->vlocation->vldb.name,
670	       new_dvnode->fid.vid,
671	       new_dvnode->fid.vnode,
672	       new_dvnode->fid.unique,
673	       key_serial(key),
674	       orig_name,
675	       new_name);
676
677	/* this op will fetch the status on both the directories we're dealing
678	 * with */
679	spin_lock(&orig_dvnode->lock);
680	orig_dvnode->update_cnt++;
681	spin_unlock(&orig_dvnode->lock);
682	if (new_dvnode != orig_dvnode) {
683		spin_lock(&new_dvnode->lock);
684		new_dvnode->update_cnt++;
685		spin_unlock(&new_dvnode->lock);
686	}
687
688	do {
689		/* pick a server to query */
690		server = afs_volume_pick_fileserver(orig_dvnode);
691		if (IS_ERR(server))
692			goto no_server;
693
694		_debug("USING SERVER: %08x\n", ntohl(server->addr.s_addr));
695
696		ret = afs_fs_rename(server, key, orig_dvnode, orig_name,
697				    new_dvnode, new_name, &afs_sync_call);
698
699	} while (!afs_volume_release_fileserver(orig_dvnode, server, ret));
700
701	/* adjust the flags */
702	if (ret == 0) {
703		afs_vnode_finalise_status_update(orig_dvnode, server);
704		if (new_dvnode != orig_dvnode)
705			afs_vnode_finalise_status_update(new_dvnode, server);
706		afs_put_server(server);
707	} else {
708		afs_vnode_status_update_failed(orig_dvnode, ret);
709		if (new_dvnode != orig_dvnode)
710			afs_vnode_status_update_failed(new_dvnode, ret);
711	}
712
713	_leave(" = %d [cnt %d]", ret, orig_dvnode->update_cnt);
714	return ret;
715
716no_server:
717	spin_lock(&orig_dvnode->lock);
718	orig_dvnode->update_cnt--;
719	ASSERTCMP(orig_dvnode->update_cnt, >=, 0);
720	spin_unlock(&orig_dvnode->lock);
721	if (new_dvnode != orig_dvnode) {
722		spin_lock(&new_dvnode->lock);
723		new_dvnode->update_cnt--;
724		ASSERTCMP(new_dvnode->update_cnt, >=, 0);
725		spin_unlock(&new_dvnode->lock);
726	}
727	_leave(" = %ld [cnt %d]", PTR_ERR(server), orig_dvnode->update_cnt);
728	return PTR_ERR(server);
729}
730
731/*
732 * write to a file
733 */
734int afs_vnode_store_data(struct afs_writeback *wb, pgoff_t first, pgoff_t last,
735			 unsigned offset, unsigned to)
736{
737	struct afs_server *server;
738	struct afs_vnode *vnode = wb->vnode;
739	int ret;
740
741	_enter("%s{%x:%u.%u},%x,%lx,%lx,%x,%x",
742	       vnode->volume->vlocation->vldb.name,
743	       vnode->fid.vid,
744	       vnode->fid.vnode,
745	       vnode->fid.unique,
746	       key_serial(wb->key),
747	       first, last, offset, to);
748
749	/* this op will fetch the status */
750	spin_lock(&vnode->lock);
751	vnode->update_cnt++;
752	spin_unlock(&vnode->lock);
753
754	do {
755		/* pick a server to query */
756		server = afs_volume_pick_fileserver(vnode);
757		if (IS_ERR(server))
758			goto no_server;
759
760		_debug("USING SERVER: %08x\n", ntohl(server->addr.s_addr));
761
762		ret = afs_fs_store_data(server, wb, first, last, offset, to,
763					&afs_sync_call);
764
765	} while (!afs_volume_release_fileserver(vnode, server, ret));
766
767	/* adjust the flags */
768	if (ret == 0) {
769		afs_vnode_finalise_status_update(vnode, server);
770		afs_put_server(server);
771	} else {
772		afs_vnode_status_update_failed(vnode, ret);
773	}
774
775	_leave(" = %d", ret);
776	return ret;
777
778no_server:
779	spin_lock(&vnode->lock);
780	vnode->update_cnt--;
781	ASSERTCMP(vnode->update_cnt, >=, 0);
782	spin_unlock(&vnode->lock);
783	return PTR_ERR(server);
784}
785
786/*
787 * set the attributes on a file
788 */
789int afs_vnode_setattr(struct afs_vnode *vnode, struct key *key,
790		      struct iattr *attr)
791{
792	struct afs_server *server;
793	int ret;
794
795	_enter("%s{%x:%u.%u},%x",
796	       vnode->volume->vlocation->vldb.name,
797	       vnode->fid.vid,
798	       vnode->fid.vnode,
799	       vnode->fid.unique,
800	       key_serial(key));
801
802	/* this op will fetch the status */
803	spin_lock(&vnode->lock);
804	vnode->update_cnt++;
805	spin_unlock(&vnode->lock);
806
807	do {
808		/* pick a server to query */
809		server = afs_volume_pick_fileserver(vnode);
810		if (IS_ERR(server))
811			goto no_server;
812
813		_debug("USING SERVER: %08x\n", ntohl(server->addr.s_addr));
814
815		ret = afs_fs_setattr(server, key, vnode, attr, &afs_sync_call);
816
817	} while (!afs_volume_release_fileserver(vnode, server, ret));
818
819	/* adjust the flags */
820	if (ret == 0) {
821		afs_vnode_finalise_status_update(vnode, server);
822		afs_put_server(server);
823	} else {
824		afs_vnode_status_update_failed(vnode, ret);
825	}
826
827	_leave(" = %d", ret);
828	return ret;
829
830no_server:
831	spin_lock(&vnode->lock);
832	vnode->update_cnt--;
833	ASSERTCMP(vnode->update_cnt, >=, 0);
834	spin_unlock(&vnode->lock);
835	return PTR_ERR(server);
836}
837
838/*
839 * get the status of a volume
840 */
841int afs_vnode_get_volume_status(struct afs_vnode *vnode, struct key *key,
842				struct afs_volume_status *vs)
843{
844	struct afs_server *server;
845	int ret;
846
847	_enter("%s{%x:%u.%u},%x,",
848	       vnode->volume->vlocation->vldb.name,
849	       vnode->fid.vid,
850	       vnode->fid.vnode,
851	       vnode->fid.unique,
852	       key_serial(key));
853
854	/* this op will fetch the status */
855	spin_lock(&vnode->lock);
856	vnode->update_cnt++;
857	spin_unlock(&vnode->lock);
858
859	do {
860		/* pick a server to query */
861		server = afs_volume_pick_fileserver(vnode);
862		if (IS_ERR(server))
863			goto no_server;
864
865		_debug("USING SERVER: %08x\n", ntohl(server->addr.s_addr));
866
867		ret = afs_fs_get_volume_status(server, key, vnode, vs, &afs_sync_call);
868
869	} while (!afs_volume_release_fileserver(vnode, server, ret));
870
871	/* adjust the flags */
872	if (ret == 0) {
873		afs_vnode_finalise_status_update(vnode, server);
874		afs_put_server(server);
875	} else {
876		afs_vnode_status_update_failed(vnode, ret);
877	}
878
879	_leave(" = %d", ret);
880	return ret;
881
882no_server:
883	spin_lock(&vnode->lock);
884	vnode->update_cnt--;
885	ASSERTCMP(vnode->update_cnt, >=, 0);
886	spin_unlock(&vnode->lock);
887	return PTR_ERR(server);
888}
889