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>
20 static noinline
bool dump_tree_aux(struct rb_node
*node
, struct rb_node
*parent
,
23 struct afs_vnode
*vnode
;
30 bad
= dump_tree_aux(node
->rb_left
, node
, depth
+ 2, '/');
32 vnode
= rb_entry(node
, struct afs_vnode
, cb_promise
);
33 kdebug("%c %*.*s%c%p {%d}",
34 rb_is_red(node
) ? 'R' : 'B',
36 vnode
, vnode
->cb_expires_at
);
37 if (rb_parent(node
) != parent
) {
38 printk("BAD: %p != %p\n", rb_parent(node
), parent
);
43 bad
|= dump_tree_aux(node
->rb_right
, node
, depth
+ 2, '\\');
48 static noinline
void dump_tree(const char *name
, struct afs_server
*server
)
51 if (dump_tree_aux(server
->cb_promises
.rb_node
, NULL
, 0, '-'))
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
);
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
);
82 p
= &server
->fs_vnodes
.rb_node
;
85 xvnode
= rb_entry(parent
, struct afs_vnode
, server_rb
);
86 if (vnode
->fid
.vid
< xvnode
->fid
.vid
)
88 else if (vnode
->fid
.vid
> xvnode
->fid
.vid
)
90 else if (vnode
->fid
.vnode
< xvnode
->fid
.vnode
)
92 else if (vnode
->fid
.vnode
> xvnode
->fid
.vnode
)
94 else if (vnode
->fid
.unique
< xvnode
->fid
.unique
)
96 else if (vnode
->fid
.unique
> xvnode
->fid
.unique
)
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
);
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]");
132 spin_lock(&old_server
->cb_lock
);
133 if (vnode
->cb_promised
) {
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
);
153 p
= &server
->cb_promises
.rb_node
;
156 xvnode
= rb_entry(parent
, struct afs_vnode
, cb_promise
);
157 if (vnode
->cb_expires_at
< xvnode
->cb_expires_at
)
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
);
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 afs_put_server(server
);
194 * finish off updating the recorded status of a file
195 * - starts callback expiry timer
196 * - adds to server's callback list
198 static void afs_vnode_finalise_status_update(struct afs_vnode
*vnode
,
199 struct afs_server
*server
,
202 struct afs_server
*oldserver
= NULL
;
204 _enter("%p,%p,%d", vnode
, server
, ret
);
206 spin_lock(&vnode
->lock
);
208 clear_bit(AFS_VNODE_CB_BROKEN
, &vnode
->flags
);
212 afs_vnode_note_promise(vnode
, server
);
215 /* the file was deleted on the server */
216 _debug("got NOENT from server - marking file deleted");
217 afs_vnode_deleted_remotely(vnode
);
225 spin_unlock(&vnode
->lock
);
227 wake_up_all(&vnode
->update_waitq
);
229 afs_put_server(oldserver
);
235 * fetch file status from the volume
236 * - don't issue a fetch if:
237 * - the changed bit is not set and there's a valid callback
238 * - there are any outstanding ops that will fetch the status
239 * - TODO implement local caching
241 int afs_vnode_fetch_status(struct afs_vnode
*vnode
,
242 struct afs_vnode
*auth_vnode
, struct key
*key
)
244 struct afs_server
*server
;
245 unsigned long acl_order
;
248 DECLARE_WAITQUEUE(myself
, current
);
250 _enter("%s,{%u,%u,%u}",
251 vnode
->volume
->vlocation
->vldb
.name
,
252 vnode
->fid
.vid
, vnode
->fid
.vnode
, vnode
->fid
.unique
);
254 if (!test_bit(AFS_VNODE_CB_BROKEN
, &vnode
->flags
) &&
255 vnode
->cb_promised
) {
256 _leave(" [unchanged]");
260 if (test_bit(AFS_VNODE_DELETED
, &vnode
->flags
)) {
261 _leave(" [deleted]");
267 acl_order
= auth_vnode
->acl_order
;
269 spin_lock(&vnode
->lock
);
271 if (!test_bit(AFS_VNODE_CB_BROKEN
, &vnode
->flags
) &&
272 vnode
->cb_promised
) {
273 spin_unlock(&vnode
->lock
);
274 _leave(" [unchanged]");
278 if (vnode
->update_cnt
> 0) {
279 /* someone else started a fetch */
280 set_current_state(TASK_UNINTERRUPTIBLE
);
281 ASSERT(myself
.func
!= NULL
);
282 add_wait_queue(&vnode
->update_waitq
, &myself
);
284 /* wait for the status to be updated */
286 if (!test_bit(AFS_VNODE_CB_BROKEN
, &vnode
->flags
))
288 if (test_bit(AFS_VNODE_DELETED
, &vnode
->flags
))
291 /* check to see if it got updated and invalidated all
292 * before we saw it */
293 if (vnode
->update_cnt
== 0) {
294 remove_wait_queue(&vnode
->update_waitq
,
296 set_current_state(TASK_RUNNING
);
300 spin_unlock(&vnode
->lock
);
303 set_current_state(TASK_UNINTERRUPTIBLE
);
305 spin_lock(&vnode
->lock
);
308 remove_wait_queue(&vnode
->update_waitq
, &myself
);
309 spin_unlock(&vnode
->lock
);
310 set_current_state(TASK_RUNNING
);
312 return test_bit(AFS_VNODE_DELETED
, &vnode
->flags
) ?
317 /* okay... we're going to have to initiate the op */
320 spin_unlock(&vnode
->lock
);
322 /* merge AFS status fetches and clear outstanding callback on this
325 /* pick a server to query */
326 server
= afs_volume_pick_fileserver(vnode
);
328 return PTR_ERR(server
);
330 _debug("USING SERVER: %p{%08x}",
331 server
, ntohl(server
->addr
.s_addr
));
333 ret
= afs_fs_fetch_file_status(server
, key
, vnode
, NULL
,
336 } while (!afs_volume_release_fileserver(vnode
, server
, ret
));
338 /* adjust the flags */
339 if (ret
== 0 && auth_vnode
)
340 afs_cache_permit(vnode
, key
, acl_order
);
341 afs_vnode_finalise_status_update(vnode
, server
, ret
);
343 _leave(" = %d", ret
);
348 * fetch file data from the volume
349 * - TODO implement caching and server failover
351 int afs_vnode_fetch_data(struct afs_vnode
*vnode
, struct key
*key
,
352 off_t offset
, size_t length
, struct page
*page
)
354 struct afs_server
*server
;
357 _enter("%s{%u,%u,%u},%x,,,",
358 vnode
->volume
->vlocation
->vldb
.name
,
364 /* this op will fetch the status */
365 spin_lock(&vnode
->lock
);
367 spin_unlock(&vnode
->lock
);
369 /* merge in AFS status fetches and clear outstanding callback on this
372 /* pick a server to query */
373 server
= afs_volume_pick_fileserver(vnode
);
375 return PTR_ERR(server
);
377 _debug("USING SERVER: %08x\n", ntohl(server
->addr
.s_addr
));
379 ret
= afs_fs_fetch_data(server
, key
, vnode
, offset
, length
,
380 page
, NULL
, &afs_sync_call
);
382 } while (!afs_volume_release_fileserver(vnode
, server
, ret
));
384 /* adjust the flags */
385 afs_vnode_finalise_status_update(vnode
, server
, ret
);
387 _leave(" = %d", ret
);
This page took 0.055596 seconds and 5 git commands to generate.