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