smaps: add clear_refs file to clear reference
[linux-2.6/verdex.git] / fs / afs / vnode.c
bloba1904ab8426adb12fdecfcc3a353da6b909bdee1
1 /* AFS vnode management
3 * Copyright (C) 2002, 2007 Red Hat, Inc. All Rights Reserved.
4 * Written by David Howells (dhowells@redhat.com)
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.
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 "internal.h"
19 #if 0
20 static noinline bool dump_tree_aux(struct rb_node *node, struct rb_node *parent,
21 int depth, char lr)
23 struct afs_vnode *vnode;
24 bool bad = false;
26 if (!node)
27 return false;
29 if (node->rb_left)
30 bad = dump_tree_aux(node->rb_left, node, depth + 2, '/');
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;
42 if (node->rb_right)
43 bad |= dump_tree_aux(node->rb_right, node, depth + 2, '\\');
45 return bad;
48 static noinline void dump_tree(const char *name, struct afs_server *server)
50 _enter("%s", name);
51 if (dump_tree_aux(server->cb_promises.rb_node, NULL, 0, '-'))
52 BUG();
54 #endif
57 * insert a vnode into the backing server's vnode tree
59 static void afs_install_vnode(struct afs_vnode *vnode,
60 struct afs_server *server)
62 struct afs_server *old_server = vnode->server;
63 struct afs_vnode *xvnode;
64 struct rb_node *parent, **p;
66 _enter("%p,%p", vnode, server);
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);
74 afs_get_server(server);
75 vnode->server = server;
76 afs_put_server(old_server);
78 /* insert into the server's vnode tree in FID order */
79 spin_lock(&server->fs_lock);
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 */
102 rb_link_node(&vnode->server_rb, parent, p);
103 rb_insert_color(&vnode->server_rb, &server->fs_vnodes);
105 spin_unlock(&server->fs_lock);
106 _leave("");
110 * insert a vnode into the promising server's update/expiration tree
111 * - caller must hold vnode->lock
113 static void afs_vnode_note_promise(struct afs_vnode *vnode,
114 struct afs_server *server)
116 struct afs_server *old_server;
117 struct afs_vnode *xvnode;
118 struct rb_node *parent, **p;
120 _enter("%p,%p", vnode, server);
122 ASSERT(server != NULL);
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;
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;
138 spin_unlock(&old_server->cb_lock);
141 if (vnode->server != server)
142 afs_install_vnode(vnode, server);
144 vnode->cb_expires_at = vnode->cb_expires;
145 _debug("PROMISE on %p {%lu}",
146 vnode, (unsigned long) vnode->cb_expires_at);
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);
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;
163 rb_link_node(&vnode->cb_promise, parent, p);
164 rb_insert_color(&vnode->cb_promise, &server->cb_promises);
165 vnode->cb_promised = true;
167 spin_unlock(&server->cb_lock);
168 _leave("");
172 * handle remote file deletion by discarding the callback promise
174 static void afs_vnode_deleted_remotely(struct afs_vnode *vnode)
176 struct afs_server *server;
178 set_bit(AFS_VNODE_DELETED, &vnode->flags);
180 server = vnode->server;
181 if (vnode->cb_promised) {
182 spin_lock(&server->cb_lock);
183 if (vnode->cb_promised) {
184 rb_erase(&vnode->cb_promise, &server->cb_promises);
185 vnode->cb_promised = false;
187 spin_unlock(&server->cb_lock);
190 spin_lock(&vnode->server->fs_lock);
191 rb_erase(&vnode->server_rb, &vnode->server->fs_vnodes);
192 spin_unlock(&vnode->server->fs_lock);
194 vnode->server = NULL;
195 afs_put_server(server);
199 * finish off updating the recorded status of a file after a successful
200 * operation completion
201 * - starts callback expiry timer
202 * - adds to server's callback list
204 void afs_vnode_finalise_status_update(struct afs_vnode *vnode,
205 struct afs_server *server)
207 struct afs_server *oldserver = NULL;
209 _enter("%p,%p", vnode, server);
211 spin_lock(&vnode->lock);
212 clear_bit(AFS_VNODE_CB_BROKEN, &vnode->flags);
213 afs_vnode_note_promise(vnode, server);
214 vnode->update_cnt--;
215 ASSERTCMP(vnode->update_cnt, >=, 0);
216 spin_unlock(&vnode->lock);
218 wake_up_all(&vnode->update_waitq);
219 afs_put_server(oldserver);
220 _leave("");
224 * finish off updating the recorded status of a file after an operation failed
226 static void afs_vnode_status_update_failed(struct afs_vnode *vnode, int ret)
228 _enter("%p,%d", vnode, ret);
230 spin_lock(&vnode->lock);
232 clear_bit(AFS_VNODE_CB_BROKEN, &vnode->flags);
234 if (ret == -ENOENT) {
235 /* the file was deleted on the server */
236 _debug("got NOENT from server - marking file deleted");
237 afs_vnode_deleted_remotely(vnode);
240 vnode->update_cnt--;
241 ASSERTCMP(vnode->update_cnt, >=, 0);
242 spin_unlock(&vnode->lock);
244 wake_up_all(&vnode->update_waitq);
245 _leave("");
249 * fetch file status from the volume
250 * - don't issue a fetch if:
251 * - the changed bit is not set and there's a valid callback
252 * - there are any outstanding ops that will fetch the status
253 * - TODO implement local caching
255 int afs_vnode_fetch_status(struct afs_vnode *vnode,
256 struct afs_vnode *auth_vnode, struct key *key)
258 struct afs_server *server;
259 unsigned long acl_order;
260 int ret;
262 DECLARE_WAITQUEUE(myself, current);
264 _enter("%s,{%u,%u,%u}",
265 vnode->volume->vlocation->vldb.name,
266 vnode->fid.vid, vnode->fid.vnode, vnode->fid.unique);
268 if (!test_bit(AFS_VNODE_CB_BROKEN, &vnode->flags) &&
269 vnode->cb_promised) {
270 _leave(" [unchanged]");
271 return 0;
274 if (test_bit(AFS_VNODE_DELETED, &vnode->flags)) {
275 _leave(" [deleted]");
276 return -ENOENT;
279 acl_order = 0;
280 if (auth_vnode)
281 acl_order = auth_vnode->acl_order;
283 spin_lock(&vnode->lock);
285 if (!test_bit(AFS_VNODE_CB_BROKEN, &vnode->flags) &&
286 vnode->cb_promised) {
287 spin_unlock(&vnode->lock);
288 _leave(" [unchanged]");
289 return 0;
292 ASSERTCMP(vnode->update_cnt, >=, 0);
294 if (vnode->update_cnt > 0) {
295 /* someone else started a fetch */
296 _debug("wait on fetch %d", vnode->update_cnt);
298 set_current_state(TASK_UNINTERRUPTIBLE);
299 ASSERT(myself.func != NULL);
300 add_wait_queue(&vnode->update_waitq, &myself);
302 /* wait for the status to be updated */
303 for (;;) {
304 if (!test_bit(AFS_VNODE_CB_BROKEN, &vnode->flags))
305 break;
306 if (test_bit(AFS_VNODE_DELETED, &vnode->flags))
307 break;
309 /* check to see if it got updated and invalidated all
310 * before we saw it */
311 if (vnode->update_cnt == 0) {
312 remove_wait_queue(&vnode->update_waitq,
313 &myself);
314 set_current_state(TASK_RUNNING);
315 goto get_anyway;
318 spin_unlock(&vnode->lock);
320 schedule();
321 set_current_state(TASK_UNINTERRUPTIBLE);
323 spin_lock(&vnode->lock);
326 remove_wait_queue(&vnode->update_waitq, &myself);
327 spin_unlock(&vnode->lock);
328 set_current_state(TASK_RUNNING);
330 return test_bit(AFS_VNODE_DELETED, &vnode->flags) ?
331 -ENOENT : 0;
334 get_anyway:
335 /* okay... we're going to have to initiate the op */
336 vnode->update_cnt++;
338 spin_unlock(&vnode->lock);
340 /* merge AFS status fetches and clear outstanding callback on this
341 * vnode */
342 do {
343 /* pick a server to query */
344 server = afs_volume_pick_fileserver(vnode);
345 if (IS_ERR(server))
346 goto no_server;
348 _debug("USING SERVER: %p{%08x}",
349 server, ntohl(server->addr.s_addr));
351 ret = afs_fs_fetch_file_status(server, key, vnode, NULL,
352 &afs_sync_call);
354 } while (!afs_volume_release_fileserver(vnode, server, ret));
356 /* adjust the flags */
357 if (ret == 0) {
358 _debug("adjust");
359 if (auth_vnode)
360 afs_cache_permit(vnode, key, acl_order);
361 afs_vnode_finalise_status_update(vnode, server);
362 afs_put_server(server);
363 } else {
364 _debug("failed [%d]", ret);
365 afs_vnode_status_update_failed(vnode, ret);
368 ASSERTCMP(vnode->update_cnt, >=, 0);
370 _leave(" = %d [cnt %d]", ret, vnode->update_cnt);
371 return ret;
373 no_server:
374 spin_lock(&vnode->lock);
375 vnode->update_cnt--;
376 ASSERTCMP(vnode->update_cnt, >=, 0);
377 spin_unlock(&vnode->lock);
378 _leave(" = %ld [cnt %d]", PTR_ERR(server), vnode->update_cnt);
379 return PTR_ERR(server);
383 * fetch file data from the volume
384 * - TODO implement caching
386 int afs_vnode_fetch_data(struct afs_vnode *vnode, struct key *key,
387 off_t offset, size_t length, struct page *page)
389 struct afs_server *server;
390 int ret;
392 _enter("%s{%u,%u,%u},%x,,,",
393 vnode->volume->vlocation->vldb.name,
394 vnode->fid.vid,
395 vnode->fid.vnode,
396 vnode->fid.unique,
397 key_serial(key));
399 /* this op will fetch the status */
400 spin_lock(&vnode->lock);
401 vnode->update_cnt++;
402 spin_unlock(&vnode->lock);
404 /* merge in AFS status fetches and clear outstanding callback on this
405 * vnode */
406 do {
407 /* pick a server to query */
408 server = afs_volume_pick_fileserver(vnode);
409 if (IS_ERR(server))
410 goto no_server;
412 _debug("USING SERVER: %08x\n", ntohl(server->addr.s_addr));
414 ret = afs_fs_fetch_data(server, key, vnode, offset, length,
415 page, &afs_sync_call);
417 } while (!afs_volume_release_fileserver(vnode, server, ret));
419 /* adjust the flags */
420 if (ret == 0) {
421 afs_vnode_finalise_status_update(vnode, server);
422 afs_put_server(server);
423 } else {
424 afs_vnode_status_update_failed(vnode, ret);
427 _leave(" = %d", ret);
428 return ret;
430 no_server:
431 spin_lock(&vnode->lock);
432 vnode->update_cnt--;
433 ASSERTCMP(vnode->update_cnt, >=, 0);
434 spin_unlock(&vnode->lock);
435 return PTR_ERR(server);
439 * make a file or a directory
441 int afs_vnode_create(struct afs_vnode *vnode, struct key *key,
442 const char *name, umode_t mode, struct afs_fid *newfid,
443 struct afs_file_status *newstatus,
444 struct afs_callback *newcb, struct afs_server **_server)
446 struct afs_server *server;
447 int ret;
449 _enter("%s{%u,%u,%u},%x,%s,,",
450 vnode->volume->vlocation->vldb.name,
451 vnode->fid.vid,
452 vnode->fid.vnode,
453 vnode->fid.unique,
454 key_serial(key),
455 name);
457 /* this op will fetch the status on the directory we're creating in */
458 spin_lock(&vnode->lock);
459 vnode->update_cnt++;
460 spin_unlock(&vnode->lock);
462 do {
463 /* pick a server to query */
464 server = afs_volume_pick_fileserver(vnode);
465 if (IS_ERR(server))
466 goto no_server;
468 _debug("USING SERVER: %08x\n", ntohl(server->addr.s_addr));
470 ret = afs_fs_create(server, key, vnode, name, mode, newfid,
471 newstatus, newcb, &afs_sync_call);
473 } while (!afs_volume_release_fileserver(vnode, server, ret));
475 /* adjust the flags */
476 if (ret == 0) {
477 afs_vnode_finalise_status_update(vnode, server);
478 *_server = server;
479 } else {
480 afs_vnode_status_update_failed(vnode, ret);
481 *_server = NULL;
484 _leave(" = %d [cnt %d]", ret, vnode->update_cnt);
485 return ret;
487 no_server:
488 spin_lock(&vnode->lock);
489 vnode->update_cnt--;
490 ASSERTCMP(vnode->update_cnt, >=, 0);
491 spin_unlock(&vnode->lock);
492 _leave(" = %ld [cnt %d]", PTR_ERR(server), vnode->update_cnt);
493 return PTR_ERR(server);
497 * remove a file or directory
499 int afs_vnode_remove(struct afs_vnode *vnode, struct key *key, const char *name,
500 bool isdir)
502 struct afs_server *server;
503 int ret;
505 _enter("%s{%u,%u,%u},%x,%s",
506 vnode->volume->vlocation->vldb.name,
507 vnode->fid.vid,
508 vnode->fid.vnode,
509 vnode->fid.unique,
510 key_serial(key),
511 name);
513 /* this op will fetch the status on the directory we're removing from */
514 spin_lock(&vnode->lock);
515 vnode->update_cnt++;
516 spin_unlock(&vnode->lock);
518 do {
519 /* pick a server to query */
520 server = afs_volume_pick_fileserver(vnode);
521 if (IS_ERR(server))
522 goto no_server;
524 _debug("USING SERVER: %08x\n", ntohl(server->addr.s_addr));
526 ret = afs_fs_remove(server, key, vnode, name, isdir,
527 &afs_sync_call);
529 } while (!afs_volume_release_fileserver(vnode, server, ret));
531 /* adjust the flags */
532 if (ret == 0) {
533 afs_vnode_finalise_status_update(vnode, server);
534 afs_put_server(server);
535 } else {
536 afs_vnode_status_update_failed(vnode, ret);
539 _leave(" = %d [cnt %d]", ret, vnode->update_cnt);
540 return ret;
542 no_server:
543 spin_lock(&vnode->lock);
544 vnode->update_cnt--;
545 ASSERTCMP(vnode->update_cnt, >=, 0);
546 spin_unlock(&vnode->lock);
547 _leave(" = %ld [cnt %d]", PTR_ERR(server), vnode->update_cnt);
548 return PTR_ERR(server);
552 * create a hard link
554 extern int afs_vnode_link(struct afs_vnode *dvnode, struct afs_vnode *vnode,
555 struct key *key, const char *name)
557 struct afs_server *server;
558 int ret;
560 _enter("%s{%u,%u,%u},%s{%u,%u,%u},%x,%s",
561 dvnode->volume->vlocation->vldb.name,
562 dvnode->fid.vid,
563 dvnode->fid.vnode,
564 dvnode->fid.unique,
565 vnode->volume->vlocation->vldb.name,
566 vnode->fid.vid,
567 vnode->fid.vnode,
568 vnode->fid.unique,
569 key_serial(key),
570 name);
572 /* this op will fetch the status on the directory we're removing from */
573 spin_lock(&vnode->lock);
574 vnode->update_cnt++;
575 spin_unlock(&vnode->lock);
576 spin_lock(&dvnode->lock);
577 dvnode->update_cnt++;
578 spin_unlock(&dvnode->lock);
580 do {
581 /* pick a server to query */
582 server = afs_volume_pick_fileserver(dvnode);
583 if (IS_ERR(server))
584 goto no_server;
586 _debug("USING SERVER: %08x\n", ntohl(server->addr.s_addr));
588 ret = afs_fs_link(server, key, dvnode, vnode, name,
589 &afs_sync_call);
591 } while (!afs_volume_release_fileserver(dvnode, server, ret));
593 /* adjust the flags */
594 if (ret == 0) {
595 afs_vnode_finalise_status_update(vnode, server);
596 afs_vnode_finalise_status_update(dvnode, server);
597 afs_put_server(server);
598 } else {
599 afs_vnode_status_update_failed(vnode, ret);
600 afs_vnode_status_update_failed(dvnode, ret);
603 _leave(" = %d [cnt %d]", ret, vnode->update_cnt);
604 return ret;
606 no_server:
607 spin_lock(&vnode->lock);
608 vnode->update_cnt--;
609 ASSERTCMP(vnode->update_cnt, >=, 0);
610 spin_unlock(&vnode->lock);
611 spin_lock(&dvnode->lock);
612 dvnode->update_cnt--;
613 ASSERTCMP(dvnode->update_cnt, >=, 0);
614 spin_unlock(&dvnode->lock);
615 _leave(" = %ld [cnt %d]", PTR_ERR(server), vnode->update_cnt);
616 return PTR_ERR(server);
620 * create a symbolic link
622 int afs_vnode_symlink(struct afs_vnode *vnode, struct key *key,
623 const char *name, const char *content,
624 struct afs_fid *newfid,
625 struct afs_file_status *newstatus,
626 struct afs_server **_server)
628 struct afs_server *server;
629 int ret;
631 _enter("%s{%u,%u,%u},%x,%s,%s,,,",
632 vnode->volume->vlocation->vldb.name,
633 vnode->fid.vid,
634 vnode->fid.vnode,
635 vnode->fid.unique,
636 key_serial(key),
637 name, content);
639 /* this op will fetch the status on the directory we're creating in */
640 spin_lock(&vnode->lock);
641 vnode->update_cnt++;
642 spin_unlock(&vnode->lock);
644 do {
645 /* pick a server to query */
646 server = afs_volume_pick_fileserver(vnode);
647 if (IS_ERR(server))
648 goto no_server;
650 _debug("USING SERVER: %08x\n", ntohl(server->addr.s_addr));
652 ret = afs_fs_symlink(server, key, vnode, name, content,
653 newfid, newstatus, &afs_sync_call);
655 } while (!afs_volume_release_fileserver(vnode, server, ret));
657 /* adjust the flags */
658 if (ret == 0) {
659 afs_vnode_finalise_status_update(vnode, server);
660 *_server = server;
661 } else {
662 afs_vnode_status_update_failed(vnode, ret);
663 *_server = NULL;
666 _leave(" = %d [cnt %d]", ret, vnode->update_cnt);
667 return ret;
669 no_server:
670 spin_lock(&vnode->lock);
671 vnode->update_cnt--;
672 ASSERTCMP(vnode->update_cnt, >=, 0);
673 spin_unlock(&vnode->lock);
674 _leave(" = %ld [cnt %d]", PTR_ERR(server), vnode->update_cnt);
675 return PTR_ERR(server);
679 * rename a file
681 int afs_vnode_rename(struct afs_vnode *orig_dvnode,
682 struct afs_vnode *new_dvnode,
683 struct key *key,
684 const char *orig_name,
685 const char *new_name)
687 struct afs_server *server;
688 int ret;
690 _enter("%s{%u,%u,%u},%s{%u,%u,%u},%x,%s,%s",
691 orig_dvnode->volume->vlocation->vldb.name,
692 orig_dvnode->fid.vid,
693 orig_dvnode->fid.vnode,
694 orig_dvnode->fid.unique,
695 new_dvnode->volume->vlocation->vldb.name,
696 new_dvnode->fid.vid,
697 new_dvnode->fid.vnode,
698 new_dvnode->fid.unique,
699 key_serial(key),
700 orig_name,
701 new_name);
703 /* this op will fetch the status on both the directories we're dealing
704 * with */
705 spin_lock(&orig_dvnode->lock);
706 orig_dvnode->update_cnt++;
707 spin_unlock(&orig_dvnode->lock);
708 if (new_dvnode != orig_dvnode) {
709 spin_lock(&new_dvnode->lock);
710 new_dvnode->update_cnt++;
711 spin_unlock(&new_dvnode->lock);
714 do {
715 /* pick a server to query */
716 server = afs_volume_pick_fileserver(orig_dvnode);
717 if (IS_ERR(server))
718 goto no_server;
720 _debug("USING SERVER: %08x\n", ntohl(server->addr.s_addr));
722 ret = afs_fs_rename(server, key, orig_dvnode, orig_name,
723 new_dvnode, new_name, &afs_sync_call);
725 } while (!afs_volume_release_fileserver(orig_dvnode, server, ret));
727 /* adjust the flags */
728 if (ret == 0) {
729 afs_vnode_finalise_status_update(orig_dvnode, server);
730 if (new_dvnode != orig_dvnode)
731 afs_vnode_finalise_status_update(new_dvnode, server);
732 afs_put_server(server);
733 } else {
734 afs_vnode_status_update_failed(orig_dvnode, ret);
735 if (new_dvnode != orig_dvnode)
736 afs_vnode_status_update_failed(new_dvnode, ret);
739 _leave(" = %d [cnt %d]", ret, orig_dvnode->update_cnt);
740 return ret;
742 no_server:
743 spin_lock(&orig_dvnode->lock);
744 orig_dvnode->update_cnt--;
745 ASSERTCMP(orig_dvnode->update_cnt, >=, 0);
746 spin_unlock(&orig_dvnode->lock);
747 if (new_dvnode != orig_dvnode) {
748 spin_lock(&new_dvnode->lock);
749 new_dvnode->update_cnt--;
750 ASSERTCMP(new_dvnode->update_cnt, >=, 0);
751 spin_unlock(&new_dvnode->lock);
753 _leave(" = %ld [cnt %d]", PTR_ERR(server), orig_dvnode->update_cnt);
754 return PTR_ERR(server);