AFS: AFS fixups
[safe/jmp/linux-2.6] / fs / afs / vnode.c
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 "internal.h"
18
19 #if 0
20 static 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
48 static 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  */
59 static 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  */
113 static 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  */
174 static void afs_vnode_deleted_remotely(struct afs_vnode *vnode)
175 {
176         struct afs_server *server;
177
178         set_bit(AFS_VNODE_DELETED, &vnode->flags);
179
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;
186                 }
187                 spin_unlock(&server->cb_lock);
188         }
189
190         spin_lock(&vnode->server->fs_lock);
191         rb_erase(&vnode->server_rb, &vnode->server->fs_vnodes);
192         spin_unlock(&vnode->server->fs_lock);
193
194         vnode->server = NULL;
195         afs_put_server(server);
196 }
197
198 /*
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
203  */
204 void afs_vnode_finalise_status_update(struct afs_vnode *vnode,
205                                       struct afs_server *server)
206 {
207         struct afs_server *oldserver = NULL;
208
209         _enter("%p,%p", vnode, server);
210
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);
217
218         wake_up_all(&vnode->update_waitq);
219         afs_put_server(oldserver);
220         _leave("");
221 }
222
223 /*
224  * finish off updating the recorded status of a file after an operation failed
225  */
226 static void afs_vnode_status_update_failed(struct afs_vnode *vnode, int ret)
227 {
228         _enter("%p,%d", vnode, ret);
229
230         spin_lock(&vnode->lock);
231
232         clear_bit(AFS_VNODE_CB_BROKEN, &vnode->flags);
233
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);
238         }
239
240         vnode->update_cnt--;
241         ASSERTCMP(vnode->update_cnt, >=, 0);
242         spin_unlock(&vnode->lock);
243
244         wake_up_all(&vnode->update_waitq);
245         _leave("");
246 }
247
248 /*
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
254  */
255 int afs_vnode_fetch_status(struct afs_vnode *vnode,
256                            struct afs_vnode *auth_vnode, struct key *key)
257 {
258         struct afs_server *server;
259         unsigned long acl_order;
260         int ret;
261
262         DECLARE_WAITQUEUE(myself, current);
263
264         _enter("%s,{%x:%u.%u}",
265                vnode->volume->vlocation->vldb.name,
266                vnode->fid.vid, vnode->fid.vnode, vnode->fid.unique);
267
268         if (!test_bit(AFS_VNODE_CB_BROKEN, &vnode->flags) &&
269             vnode->cb_promised) {
270                 _leave(" [unchanged]");
271                 return 0;
272         }
273
274         if (test_bit(AFS_VNODE_DELETED, &vnode->flags)) {
275                 _leave(" [deleted]");
276                 return -ENOENT;
277         }
278
279         acl_order = 0;
280         if (auth_vnode)
281                 acl_order = auth_vnode->acl_order;
282
283         spin_lock(&vnode->lock);
284
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;
290         }
291
292         ASSERTCMP(vnode->update_cnt, >=, 0);
293
294         if (vnode->update_cnt > 0) {
295                 /* someone else started a fetch */
296                 _debug("wait on fetch %d", vnode->update_cnt);
297
298                 set_current_state(TASK_UNINTERRUPTIBLE);
299                 ASSERT(myself.func != NULL);
300                 add_wait_queue(&vnode->update_waitq, &myself);
301
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;
308
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;
316                         }
317
318                         spin_unlock(&vnode->lock);
319
320                         schedule();
321                         set_current_state(TASK_UNINTERRUPTIBLE);
322
323                         spin_lock(&vnode->lock);
324                 }
325
326                 remove_wait_queue(&vnode->update_waitq, &myself);
327                 spin_unlock(&vnode->lock);
328                 set_current_state(TASK_RUNNING);
329
330                 return test_bit(AFS_VNODE_DELETED, &vnode->flags) ?
331                         -ENOENT : 0;
332         }
333
334 get_anyway:
335         /* okay... we're going to have to initiate the op */
336         vnode->update_cnt++;
337
338         spin_unlock(&vnode->lock);
339
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;
347
348                 _debug("USING SERVER: %p{%08x}",
349                        server, ntohl(server->addr.s_addr));
350
351                 ret = afs_fs_fetch_file_status(server, key, vnode, NULL,
352                                                &afs_sync_call);
353
354         } while (!afs_volume_release_fileserver(vnode, server, ret));
355
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);
366         }
367
368         ASSERTCMP(vnode->update_cnt, >=, 0);
369
370         _leave(" = %d [cnt %d]", ret, vnode->update_cnt);
371         return ret;
372
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);
380 }
381
382 /*
383  * fetch file data from the volume
384  * - TODO implement caching
385  */
386 int afs_vnode_fetch_data(struct afs_vnode *vnode, struct key *key,
387                          off_t offset, size_t length, struct page *page)
388 {
389         struct afs_server *server;
390         int ret;
391
392         _enter("%s{%x:%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));
398
399         /* this op will fetch the status */
400         spin_lock(&vnode->lock);
401         vnode->update_cnt++;
402         spin_unlock(&vnode->lock);
403
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;
411
412                 _debug("USING SERVER: %08x\n", ntohl(server->addr.s_addr));
413
414                 ret = afs_fs_fetch_data(server, key, vnode, offset, length,
415                                         page, &afs_sync_call);
416
417         } while (!afs_volume_release_fileserver(vnode, server, ret));
418
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);
425         }
426
427         _leave(" = %d", ret);
428         return ret;
429
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);
436 }
437
438 /*
439  * make a file or a directory
440  */
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)
445 {
446         struct afs_server *server;
447         int ret;
448
449         _enter("%s{%x:%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);
456
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);
461
462         do {
463                 /* pick a server to query */
464                 server = afs_volume_pick_fileserver(vnode);
465                 if (IS_ERR(server))
466                         goto no_server;
467
468                 _debug("USING SERVER: %08x\n", ntohl(server->addr.s_addr));
469
470                 ret = afs_fs_create(server, key, vnode, name, mode, newfid,
471                                     newstatus, newcb, &afs_sync_call);
472
473         } while (!afs_volume_release_fileserver(vnode, server, ret));
474
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;
482         }
483
484         _leave(" = %d [cnt %d]", ret, vnode->update_cnt);
485         return ret;
486
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);
494 }
495
496 /*
497  * remove a file or directory
498  */
499 int afs_vnode_remove(struct afs_vnode *vnode, struct key *key, const char *name,
500                      bool isdir)
501 {
502         struct afs_server *server;
503         int ret;
504
505         _enter("%s{%x:%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);
512
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);
517
518         do {
519                 /* pick a server to query */
520                 server = afs_volume_pick_fileserver(vnode);
521                 if (IS_ERR(server))
522                         goto no_server;
523
524                 _debug("USING SERVER: %08x\n", ntohl(server->addr.s_addr));
525
526                 ret = afs_fs_remove(server, key, vnode, name, isdir,
527                                     &afs_sync_call);
528
529         } while (!afs_volume_release_fileserver(vnode, server, ret));
530
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);
537         }
538
539         _leave(" = %d [cnt %d]", ret, vnode->update_cnt);
540         return ret;
541
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);
549 }
550
551 /*
552  * create a hard link
553  */
554 extern int afs_vnode_link(struct afs_vnode *dvnode, struct afs_vnode *vnode,
555                           struct key *key, const char *name)
556 {
557         struct afs_server *server;
558         int ret;
559
560         _enter("%s{%x:%u.%u},%s{%x:%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);
571
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);
579
580         do {
581                 /* pick a server to query */
582                 server = afs_volume_pick_fileserver(dvnode);
583                 if (IS_ERR(server))
584                         goto no_server;
585
586                 _debug("USING SERVER: %08x\n", ntohl(server->addr.s_addr));
587
588                 ret = afs_fs_link(server, key, dvnode, vnode, name,
589                                   &afs_sync_call);
590
591         } while (!afs_volume_release_fileserver(dvnode, server, ret));
592
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);
601         }
602
603         _leave(" = %d [cnt %d]", ret, vnode->update_cnt);
604         return ret;
605
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);
617 }
618
619 /*
620  * create a symbolic link
621  */
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)
627 {
628         struct afs_server *server;
629         int ret;
630
631         _enter("%s{%x:%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);
638
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);
643
644         do {
645                 /* pick a server to query */
646                 server = afs_volume_pick_fileserver(vnode);
647                 if (IS_ERR(server))
648                         goto no_server;
649
650                 _debug("USING SERVER: %08x\n", ntohl(server->addr.s_addr));
651
652                 ret = afs_fs_symlink(server, key, vnode, name, content,
653                                      newfid, newstatus, &afs_sync_call);
654
655         } while (!afs_volume_release_fileserver(vnode, server, ret));
656
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;
664         }
665
666         _leave(" = %d [cnt %d]", ret, vnode->update_cnt);
667         return ret;
668
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);
676 }
677
678 /*
679  * rename a file
680  */
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)
686 {
687         struct afs_server *server;
688         int ret;
689
690         _enter("%s{%x:%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);
702
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);
712         }
713
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;
719
720                 _debug("USING SERVER: %08x\n", ntohl(server->addr.s_addr));
721
722                 ret = afs_fs_rename(server, key, orig_dvnode, orig_name,
723                                     new_dvnode, new_name, &afs_sync_call);
724
725         } while (!afs_volume_release_fileserver(orig_dvnode, server, ret));
726
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);
737         }
738
739         _leave(" = %d [cnt %d]", ret, orig_dvnode->update_cnt);
740         return ret;
741
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);
752         }
753         _leave(" = %ld [cnt %d]", PTR_ERR(server), orig_dvnode->update_cnt);
754         return PTR_ERR(server);
755 }