1 /* vnode.c: AFS vnode management
3 * Copyright (C) 2002 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>
17 #include <linux/pagemap.h>
20 #include "cmservice.h"
26 static void afs_vnode_cb_timed_out(struct afs_timer
*timer
);
28 struct afs_timer_ops afs_vnode_cb_timed_out_ops
= {
29 .timed_out
= afs_vnode_cb_timed_out
,
32 #ifdef AFS_CACHING_SUPPORT
33 static cachefs_match_val_t
afs_vnode_cache_match(void *target
,
35 static void afs_vnode_cache_update(void *source
, void *entry
);
37 struct cachefs_index_def afs_vnode_cache_index_def
= {
39 .data_size
= sizeof(struct afs_cache_vnode
),
40 .keys
[0] = { CACHEFS_INDEX_KEYS_BIN
, 4 },
41 .match
= afs_vnode_cache_match
,
42 .update
= afs_vnode_cache_update
,
46 /*****************************************************************************/
48 * handle a callback timing out
49 * TODO: retain a ref to vnode struct for an outstanding callback timeout
51 static void afs_vnode_cb_timed_out(struct afs_timer
*timer
)
53 struct afs_server
*oldserver
;
54 struct afs_vnode
*vnode
;
56 vnode
= list_entry(timer
, struct afs_vnode
, cb_timeout
);
60 /* set the changed flag in the vnode and release the server */
61 spin_lock(&vnode
->lock
);
63 oldserver
= xchg(&vnode
->cb_server
, NULL
);
65 vnode
->flags
|= AFS_VNODE_CHANGED
;
67 spin_lock(&afs_cb_hash_lock
);
68 list_del_init(&vnode
->cb_hash_link
);
69 spin_unlock(&afs_cb_hash_lock
);
71 spin_lock(&oldserver
->cb_lock
);
72 list_del_init(&vnode
->cb_link
);
73 spin_unlock(&oldserver
->cb_lock
);
76 spin_unlock(&vnode
->lock
);
78 afs_put_server(oldserver
);
81 } /* end afs_vnode_cb_timed_out() */
83 /*****************************************************************************/
85 * finish off updating the recorded status of a file
86 * - starts callback expiry timer
87 * - adds to server's callback list
89 static void afs_vnode_finalise_status_update(struct afs_vnode
*vnode
,
90 struct afs_server
*server
,
93 struct afs_server
*oldserver
= NULL
;
95 _enter("%p,%p,%d", vnode
, server
, ret
);
97 spin_lock(&vnode
->lock
);
99 vnode
->flags
&= ~AFS_VNODE_CHANGED
;
102 /* adjust the callback timeout appropriately */
103 afs_kafstimod_add_timer(&vnode
->cb_timeout
,
104 vnode
->cb_expiry
* HZ
);
106 spin_lock(&afs_cb_hash_lock
);
107 list_move_tail(&vnode
->cb_hash_link
,
108 &afs_cb_hash(server
, &vnode
->fid
));
109 spin_unlock(&afs_cb_hash_lock
);
111 /* swap ref to old callback server with that for new callback
113 oldserver
= xchg(&vnode
->cb_server
, server
);
114 if (oldserver
!= server
) {
116 spin_lock(&oldserver
->cb_lock
);
117 list_del_init(&vnode
->cb_link
);
118 spin_unlock(&oldserver
->cb_lock
);
121 afs_get_server(server
);
122 spin_lock(&server
->cb_lock
);
123 list_add_tail(&vnode
->cb_link
, &server
->cb_promises
);
124 spin_unlock(&server
->cb_lock
);
131 else if (ret
== -ENOENT
) {
132 /* the file was deleted - clear the callback timeout */
133 oldserver
= xchg(&vnode
->cb_server
, NULL
);
134 afs_kafstimod_del_timer(&vnode
->cb_timeout
);
136 _debug("got NOENT from server - marking file deleted");
137 vnode
->flags
|= AFS_VNODE_DELETED
;
142 spin_unlock(&vnode
->lock
);
144 wake_up_all(&vnode
->update_waitq
);
146 afs_put_server(oldserver
);
150 } /* end afs_vnode_finalise_status_update() */
152 /*****************************************************************************/
154 * fetch file status from the volume
155 * - don't issue a fetch if:
156 * - the changed bit is not set and there's a valid callback
157 * - there are any outstanding ops that will fetch the status
158 * - TODO implement local caching
160 int afs_vnode_fetch_status(struct afs_vnode
*vnode
)
162 struct afs_server
*server
;
165 DECLARE_WAITQUEUE(myself
, current
);
167 _enter("%s,{%u,%u,%u}",
168 vnode
->volume
->vlocation
->vldb
.name
,
169 vnode
->fid
.vid
, vnode
->fid
.vnode
, vnode
->fid
.unique
);
171 if (!(vnode
->flags
& AFS_VNODE_CHANGED
) && vnode
->cb_server
) {
172 _leave(" [unchanged]");
176 if (vnode
->flags
& AFS_VNODE_DELETED
) {
177 _leave(" [deleted]");
181 spin_lock(&vnode
->lock
);
183 if (!(vnode
->flags
& AFS_VNODE_CHANGED
)) {
184 spin_unlock(&vnode
->lock
);
185 _leave(" [unchanged]");
189 if (vnode
->update_cnt
> 0) {
190 /* someone else started a fetch */
191 set_current_state(TASK_UNINTERRUPTIBLE
);
192 add_wait_queue(&vnode
->update_waitq
, &myself
);
194 /* wait for the status to be updated */
196 if (!(vnode
->flags
& AFS_VNODE_CHANGED
))
198 if (vnode
->flags
& AFS_VNODE_DELETED
)
201 /* it got updated and invalidated all before we saw
203 if (vnode
->update_cnt
== 0) {
204 remove_wait_queue(&vnode
->update_waitq
,
206 set_current_state(TASK_RUNNING
);
210 spin_unlock(&vnode
->lock
);
213 set_current_state(TASK_UNINTERRUPTIBLE
);
215 spin_lock(&vnode
->lock
);
218 remove_wait_queue(&vnode
->update_waitq
, &myself
);
219 spin_unlock(&vnode
->lock
);
220 set_current_state(TASK_RUNNING
);
222 return vnode
->flags
& AFS_VNODE_DELETED
? -ENOENT
: 0;
226 /* okay... we're going to have to initiate the op */
229 spin_unlock(&vnode
->lock
);
231 /* merge AFS status fetches and clear outstanding callback on this
234 /* pick a server to query */
235 ret
= afs_volume_pick_fileserver(vnode
->volume
, &server
);
239 _debug("USING SERVER: %08x\n", ntohl(server
->addr
.s_addr
));
241 ret
= afs_rxfs_fetch_file_status(server
, vnode
, NULL
);
243 } while (!afs_volume_release_fileserver(vnode
->volume
, server
, ret
));
245 /* adjust the flags */
246 afs_vnode_finalise_status_update(vnode
, server
, ret
);
248 _leave(" = %d", ret
);
250 } /* end afs_vnode_fetch_status() */
252 /*****************************************************************************/
254 * fetch file data from the volume
255 * - TODO implement caching and server failover
257 int afs_vnode_fetch_data(struct afs_vnode
*vnode
,
258 struct afs_rxfs_fetch_descriptor
*desc
)
260 struct afs_server
*server
;
263 _enter("%s,{%u,%u,%u}",
264 vnode
->volume
->vlocation
->vldb
.name
,
269 /* this op will fetch the status */
270 spin_lock(&vnode
->lock
);
272 spin_unlock(&vnode
->lock
);
274 /* merge in AFS status fetches and clear outstanding callback on this
277 /* pick a server to query */
278 ret
= afs_volume_pick_fileserver(vnode
->volume
, &server
);
282 _debug("USING SERVER: %08x\n", ntohl(server
->addr
.s_addr
));
284 ret
= afs_rxfs_fetch_file_data(server
, vnode
, desc
, NULL
);
286 } while (!afs_volume_release_fileserver(vnode
->volume
, server
, ret
));
288 /* adjust the flags */
289 afs_vnode_finalise_status_update(vnode
, server
, ret
);
291 _leave(" = %d", ret
);
294 } /* end afs_vnode_fetch_data() */
296 /*****************************************************************************/
298 * break any outstanding callback on a vnode
299 * - only relevent to server that issued it
301 int afs_vnode_give_up_callback(struct afs_vnode
*vnode
)
303 struct afs_server
*server
;
306 _enter("%s,{%u,%u,%u}",
307 vnode
->volume
->vlocation
->vldb
.name
,
312 spin_lock(&afs_cb_hash_lock
);
313 list_del_init(&vnode
->cb_hash_link
);
314 spin_unlock(&afs_cb_hash_lock
);
316 /* set the changed flag in the vnode and release the server */
317 spin_lock(&vnode
->lock
);
319 afs_kafstimod_del_timer(&vnode
->cb_timeout
);
321 server
= xchg(&vnode
->cb_server
, NULL
);
323 vnode
->flags
|= AFS_VNODE_CHANGED
;
325 spin_lock(&server
->cb_lock
);
326 list_del_init(&vnode
->cb_link
);
327 spin_unlock(&server
->cb_lock
);
330 spin_unlock(&vnode
->lock
);
334 ret
= afs_rxfs_give_up_callback(server
, vnode
);
335 afs_put_server(server
);
338 _leave(" = %d", ret
);
340 } /* end afs_vnode_give_up_callback() */
342 /*****************************************************************************/
344 * match a vnode record stored in the cache
346 #ifdef AFS_CACHING_SUPPORT
347 static cachefs_match_val_t
afs_vnode_cache_match(void *target
,
350 const struct afs_cache_vnode
*cvnode
= entry
;
351 struct afs_vnode
*vnode
= target
;
353 _enter("{%x,%x,%Lx},{%x,%x,%Lx}",
356 vnode
->status
.version
,
358 cvnode
->vnode_unique
,
359 cvnode
->data_version
);
361 if (vnode
->fid
.vnode
!= cvnode
->vnode_id
) {
363 return CACHEFS_MATCH_FAILED
;
366 if (vnode
->fid
.unique
!= cvnode
->vnode_unique
||
367 vnode
->status
.version
!= cvnode
->data_version
) {
369 return CACHEFS_MATCH_SUCCESS_DELETE
;
372 _leave(" = SUCCESS");
373 return CACHEFS_MATCH_SUCCESS
;
374 } /* end afs_vnode_cache_match() */
377 /*****************************************************************************/
379 * update a vnode record stored in the cache
381 #ifdef AFS_CACHING_SUPPORT
382 static void afs_vnode_cache_update(void *source
, void *entry
)
384 struct afs_cache_vnode
*cvnode
= entry
;
385 struct afs_vnode
*vnode
= source
;
389 cvnode
->vnode_id
= vnode
->fid
.vnode
;
390 cvnode
->vnode_unique
= vnode
->fid
.unique
;
391 cvnode
->data_version
= vnode
->status
.version
;
393 } /* end afs_vnode_cache_update() */