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_del(&vnode
->cb_hash_link
);
108 list_add_tail(&vnode
->cb_hash_link
,
109 &afs_cb_hash(server
, &vnode
->fid
));
110 spin_unlock(&afs_cb_hash_lock
);
112 /* swap ref to old callback server with that for new callback
114 oldserver
= xchg(&vnode
->cb_server
, server
);
115 if (oldserver
!= server
) {
117 spin_lock(&oldserver
->cb_lock
);
118 list_del_init(&vnode
->cb_link
);
119 spin_unlock(&oldserver
->cb_lock
);
122 afs_get_server(server
);
123 spin_lock(&server
->cb_lock
);
124 list_add_tail(&vnode
->cb_link
, &server
->cb_promises
);
125 spin_unlock(&server
->cb_lock
);
132 else if (ret
== -ENOENT
) {
133 /* the file was deleted - clear the callback timeout */
134 oldserver
= xchg(&vnode
->cb_server
, NULL
);
135 afs_kafstimod_del_timer(&vnode
->cb_timeout
);
137 _debug("got NOENT from server - marking file deleted");
138 vnode
->flags
|= AFS_VNODE_DELETED
;
143 spin_unlock(&vnode
->lock
);
145 wake_up_all(&vnode
->update_waitq
);
147 afs_put_server(oldserver
);
151 } /* end afs_vnode_finalise_status_update() */
153 /*****************************************************************************/
155 * fetch file status from the volume
156 * - don't issue a fetch if:
157 * - the changed bit is not set and there's a valid callback
158 * - there are any outstanding ops that will fetch the status
159 * - TODO implement local caching
161 int afs_vnode_fetch_status(struct afs_vnode
*vnode
)
163 struct afs_server
*server
;
166 DECLARE_WAITQUEUE(myself
, current
);
168 _enter("%s,{%u,%u,%u}",
169 vnode
->volume
->vlocation
->vldb
.name
,
170 vnode
->fid
.vid
, vnode
->fid
.vnode
, vnode
->fid
.unique
);
172 if (!(vnode
->flags
& AFS_VNODE_CHANGED
) && vnode
->cb_server
) {
173 _leave(" [unchanged]");
177 if (vnode
->flags
& AFS_VNODE_DELETED
) {
178 _leave(" [deleted]");
182 spin_lock(&vnode
->lock
);
184 if (!(vnode
->flags
& AFS_VNODE_CHANGED
)) {
185 spin_unlock(&vnode
->lock
);
186 _leave(" [unchanged]");
190 if (vnode
->update_cnt
> 0) {
191 /* someone else started a fetch */
192 set_current_state(TASK_UNINTERRUPTIBLE
);
193 add_wait_queue(&vnode
->update_waitq
, &myself
);
195 /* wait for the status to be updated */
197 if (!(vnode
->flags
& AFS_VNODE_CHANGED
))
199 if (vnode
->flags
& AFS_VNODE_DELETED
)
202 /* it got updated and invalidated all before we saw
204 if (vnode
->update_cnt
== 0) {
205 remove_wait_queue(&vnode
->update_waitq
,
207 set_current_state(TASK_RUNNING
);
211 spin_unlock(&vnode
->lock
);
214 set_current_state(TASK_UNINTERRUPTIBLE
);
216 spin_lock(&vnode
->lock
);
219 remove_wait_queue(&vnode
->update_waitq
, &myself
);
220 spin_unlock(&vnode
->lock
);
221 set_current_state(TASK_RUNNING
);
223 return vnode
->flags
& AFS_VNODE_DELETED
? -ENOENT
: 0;
227 /* okay... we're going to have to initiate the op */
230 spin_unlock(&vnode
->lock
);
232 /* merge AFS status fetches and clear outstanding callback on this
235 /* pick a server to query */
236 ret
= afs_volume_pick_fileserver(vnode
->volume
, &server
);
240 _debug("USING SERVER: %08x\n", ntohl(server
->addr
.s_addr
));
242 ret
= afs_rxfs_fetch_file_status(server
, vnode
, NULL
);
244 } while (!afs_volume_release_fileserver(vnode
->volume
, server
, ret
));
246 /* adjust the flags */
247 afs_vnode_finalise_status_update(vnode
, server
, ret
);
249 _leave(" = %d", ret
);
251 } /* end afs_vnode_fetch_status() */
253 /*****************************************************************************/
255 * fetch file data from the volume
256 * - TODO implement caching and server failover
258 int afs_vnode_fetch_data(struct afs_vnode
*vnode
,
259 struct afs_rxfs_fetch_descriptor
*desc
)
261 struct afs_server
*server
;
264 _enter("%s,{%u,%u,%u}",
265 vnode
->volume
->vlocation
->vldb
.name
,
270 /* this op will fetch the status */
271 spin_lock(&vnode
->lock
);
273 spin_unlock(&vnode
->lock
);
275 /* merge in AFS status fetches and clear outstanding callback on this
278 /* pick a server to query */
279 ret
= afs_volume_pick_fileserver(vnode
->volume
, &server
);
283 _debug("USING SERVER: %08x\n", ntohl(server
->addr
.s_addr
));
285 ret
= afs_rxfs_fetch_file_data(server
, vnode
, desc
, NULL
);
287 } while (!afs_volume_release_fileserver(vnode
->volume
, server
, ret
));
289 /* adjust the flags */
290 afs_vnode_finalise_status_update(vnode
, server
, ret
);
292 _leave(" = %d", ret
);
295 } /* end afs_vnode_fetch_data() */
297 /*****************************************************************************/
299 * break any outstanding callback on a vnode
300 * - only relevent to server that issued it
302 int afs_vnode_give_up_callback(struct afs_vnode
*vnode
)
304 struct afs_server
*server
;
307 _enter("%s,{%u,%u,%u}",
308 vnode
->volume
->vlocation
->vldb
.name
,
313 spin_lock(&afs_cb_hash_lock
);
314 list_del_init(&vnode
->cb_hash_link
);
315 spin_unlock(&afs_cb_hash_lock
);
317 /* set the changed flag in the vnode and release the server */
318 spin_lock(&vnode
->lock
);
320 afs_kafstimod_del_timer(&vnode
->cb_timeout
);
322 server
= xchg(&vnode
->cb_server
, NULL
);
324 vnode
->flags
|= AFS_VNODE_CHANGED
;
326 spin_lock(&server
->cb_lock
);
327 list_del_init(&vnode
->cb_link
);
328 spin_unlock(&server
->cb_lock
);
331 spin_unlock(&vnode
->lock
);
335 ret
= afs_rxfs_give_up_callback(server
, vnode
);
336 afs_put_server(server
);
339 _leave(" = %d", ret
);
341 } /* end afs_vnode_give_up_callback() */
343 /*****************************************************************************/
345 * match a vnode record stored in the cache
347 #ifdef AFS_CACHING_SUPPORT
348 static cachefs_match_val_t
afs_vnode_cache_match(void *target
,
351 const struct afs_cache_vnode
*cvnode
= entry
;
352 struct afs_vnode
*vnode
= target
;
354 _enter("{%x,%x,%Lx},{%x,%x,%Lx}",
357 vnode
->status
.version
,
359 cvnode
->vnode_unique
,
360 cvnode
->data_version
);
362 if (vnode
->fid
.vnode
!= cvnode
->vnode_id
) {
364 return CACHEFS_MATCH_FAILED
;
367 if (vnode
->fid
.unique
!= cvnode
->vnode_unique
||
368 vnode
->status
.version
!= cvnode
->data_version
) {
370 return CACHEFS_MATCH_SUCCESS_DELETE
;
373 _leave(" = SUCCESS");
374 return CACHEFS_MATCH_SUCCESS
;
375 } /* end afs_vnode_cache_match() */
378 /*****************************************************************************/
380 * update a vnode record stored in the cache
382 #ifdef AFS_CACHING_SUPPORT
383 static void afs_vnode_cache_update(void *source
, void *entry
)
385 struct afs_cache_vnode
*cvnode
= entry
;
386 struct afs_vnode
*vnode
= source
;
390 cvnode
->vnode_id
= vnode
->fid
.vnode
;
391 cvnode
->vnode_unique
= vnode
->fid
.unique
;
392 cvnode
->data_version
= vnode
->status
.version
;
394 } /* end afs_vnode_cache_update() */