Linux-2.6.12-rc2
[linux-2.6.git] / fs / afs / vnode.c
1 /* vnode.c: AFS vnode management
2  *
3  * Copyright (C) 2002 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 <linux/pagemap.h>
18 #include "volume.h"
19 #include "cell.h"
20 #include "cmservice.h"
21 #include "fsclient.h"
22 #include "vlclient.h"
23 #include "vnode.h"
24 #include "internal.h"
25
26 static void afs_vnode_cb_timed_out(struct afs_timer *timer);
27
28 struct afs_timer_ops afs_vnode_cb_timed_out_ops = {
29         .timed_out      = afs_vnode_cb_timed_out,
30 };
31
32 #ifdef AFS_CACHING_SUPPORT
33 static cachefs_match_val_t afs_vnode_cache_match(void *target,
34                                                  const void *entry);
35 static void afs_vnode_cache_update(void *source, void *entry);
36
37 struct cachefs_index_def afs_vnode_cache_index_def = {
38         .name           = "vnode",
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,
43 };
44 #endif
45
46 /*****************************************************************************/
47 /*
48  * handle a callback timing out
49  * TODO: retain a ref to vnode struct for an outstanding callback timeout
50  */
51 static void afs_vnode_cb_timed_out(struct afs_timer *timer)
52 {
53         struct afs_server *oldserver;
54         struct afs_vnode *vnode;
55
56         vnode = list_entry(timer, struct afs_vnode, cb_timeout);
57
58         _enter("%p", vnode);
59
60         /* set the changed flag in the vnode and release the server */
61         spin_lock(&vnode->lock);
62
63         oldserver = xchg(&vnode->cb_server, NULL);
64         if (oldserver) {
65                 vnode->flags |= AFS_VNODE_CHANGED;
66
67                 spin_lock(&afs_cb_hash_lock);
68                 list_del_init(&vnode->cb_hash_link);
69                 spin_unlock(&afs_cb_hash_lock);
70
71                 spin_lock(&oldserver->cb_lock);
72                 list_del_init(&vnode->cb_link);
73                 spin_unlock(&oldserver->cb_lock);
74         }
75
76         spin_unlock(&vnode->lock);
77
78         afs_put_server(oldserver);
79
80         _leave("");
81 } /* end afs_vnode_cb_timed_out() */
82
83 /*****************************************************************************/
84 /*
85  * finish off updating the recorded status of a file
86  * - starts callback expiry timer
87  * - adds to server's callback list
88  */
89 static void afs_vnode_finalise_status_update(struct afs_vnode *vnode,
90                                              struct afs_server *server,
91                                              int ret)
92 {
93         struct afs_server *oldserver = NULL;
94
95         _enter("%p,%p,%d", vnode, server, ret);
96
97         spin_lock(&vnode->lock);
98
99         vnode->flags &= ~AFS_VNODE_CHANGED;
100
101         if (ret == 0) {
102                 /* adjust the callback timeout appropriately */
103                 afs_kafstimod_add_timer(&vnode->cb_timeout,
104                                         vnode->cb_expiry * HZ);
105
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);
111
112                 /* swap ref to old callback server with that for new callback
113                  * server */
114                 oldserver = xchg(&vnode->cb_server, server);
115                 if (oldserver != server) {
116                         if (oldserver) {
117                                 spin_lock(&oldserver->cb_lock);
118                                 list_del_init(&vnode->cb_link);
119                                 spin_unlock(&oldserver->cb_lock);
120                         }
121
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);
126                 }
127                 else {
128                         /* same server */
129                         oldserver = NULL;
130                 }
131         }
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);
136
137                 _debug("got NOENT from server - marking file deleted");
138                 vnode->flags |= AFS_VNODE_DELETED;
139         }
140
141         vnode->update_cnt--;
142
143         spin_unlock(&vnode->lock);
144
145         wake_up_all(&vnode->update_waitq);
146
147         afs_put_server(oldserver);
148
149         _leave("");
150
151 } /* end afs_vnode_finalise_status_update() */
152
153 /*****************************************************************************/
154 /*
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
160  */
161 int afs_vnode_fetch_status(struct afs_vnode *vnode)
162 {
163         struct afs_server *server;
164         int ret;
165
166         DECLARE_WAITQUEUE(myself, current);
167
168         _enter("%s,{%u,%u,%u}",
169                vnode->volume->vlocation->vldb.name,
170                vnode->fid.vid, vnode->fid.vnode, vnode->fid.unique);
171
172         if (!(vnode->flags & AFS_VNODE_CHANGED) && vnode->cb_server) {
173                 _leave(" [unchanged]");
174                 return 0;
175         }
176
177         if (vnode->flags & AFS_VNODE_DELETED) {
178                 _leave(" [deleted]");
179                 return -ENOENT;
180         }
181
182         spin_lock(&vnode->lock);
183
184         if (!(vnode->flags & AFS_VNODE_CHANGED)) {
185                 spin_unlock(&vnode->lock);
186                 _leave(" [unchanged]");
187                 return 0;
188         }
189
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);
194
195                 /* wait for the status to be updated */
196                 for (;;) {
197                         if (!(vnode->flags & AFS_VNODE_CHANGED))
198                                 break;
199                         if (vnode->flags & AFS_VNODE_DELETED)
200                                 break;
201
202                         /* it got updated and invalidated all before we saw
203                          * it */
204                         if (vnode->update_cnt == 0) {
205                                 remove_wait_queue(&vnode->update_waitq,
206                                                   &myself);
207                                 set_current_state(TASK_RUNNING);
208                                 goto get_anyway;
209                         }
210
211                         spin_unlock(&vnode->lock);
212
213                         schedule();
214                         set_current_state(TASK_UNINTERRUPTIBLE);
215
216                         spin_lock(&vnode->lock);
217                 }
218
219                 remove_wait_queue(&vnode->update_waitq, &myself);
220                 spin_unlock(&vnode->lock);
221                 set_current_state(TASK_RUNNING);
222
223                 return vnode->flags & AFS_VNODE_DELETED ? -ENOENT : 0;
224         }
225
226  get_anyway:
227         /* okay... we're going to have to initiate the op */
228         vnode->update_cnt++;
229
230         spin_unlock(&vnode->lock);
231
232         /* merge AFS status fetches and clear outstanding callback on this
233          * vnode */
234         do {
235                 /* pick a server to query */
236                 ret = afs_volume_pick_fileserver(vnode->volume, &server);
237                 if (ret<0)
238                         return ret;
239
240                 _debug("USING SERVER: %08x\n", ntohl(server->addr.s_addr));
241
242                 ret = afs_rxfs_fetch_file_status(server, vnode, NULL);
243
244         } while (!afs_volume_release_fileserver(vnode->volume, server, ret));
245
246         /* adjust the flags */
247         afs_vnode_finalise_status_update(vnode, server, ret);
248
249         _leave(" = %d", ret);
250         return ret;
251 } /* end afs_vnode_fetch_status() */
252
253 /*****************************************************************************/
254 /*
255  * fetch file data from the volume
256  * - TODO implement caching and server failover
257  */
258 int afs_vnode_fetch_data(struct afs_vnode *vnode,
259                          struct afs_rxfs_fetch_descriptor *desc)
260 {
261         struct afs_server *server;
262         int ret;
263
264         _enter("%s,{%u,%u,%u}",
265                vnode->volume->vlocation->vldb.name,
266                vnode->fid.vid,
267                vnode->fid.vnode,
268                vnode->fid.unique);
269
270         /* this op will fetch the status */
271         spin_lock(&vnode->lock);
272         vnode->update_cnt++;
273         spin_unlock(&vnode->lock);
274
275         /* merge in AFS status fetches and clear outstanding callback on this
276          * vnode */
277         do {
278                 /* pick a server to query */
279                 ret = afs_volume_pick_fileserver(vnode->volume, &server);
280                 if (ret < 0)
281                         return ret;
282
283                 _debug("USING SERVER: %08x\n", ntohl(server->addr.s_addr));
284
285                 ret = afs_rxfs_fetch_file_data(server, vnode, desc, NULL);
286
287         } while (!afs_volume_release_fileserver(vnode->volume, server, ret));
288
289         /* adjust the flags */
290         afs_vnode_finalise_status_update(vnode, server, ret);
291
292         _leave(" = %d", ret);
293         return ret;
294
295 } /* end afs_vnode_fetch_data() */
296
297 /*****************************************************************************/
298 /*
299  * break any outstanding callback on a vnode
300  * - only relevent to server that issued it
301  */
302 int afs_vnode_give_up_callback(struct afs_vnode *vnode)
303 {
304         struct afs_server *server;
305         int ret;
306
307         _enter("%s,{%u,%u,%u}",
308                vnode->volume->vlocation->vldb.name,
309                vnode->fid.vid,
310                vnode->fid.vnode,
311                vnode->fid.unique);
312
313         spin_lock(&afs_cb_hash_lock);
314         list_del_init(&vnode->cb_hash_link);
315         spin_unlock(&afs_cb_hash_lock);
316
317         /* set the changed flag in the vnode and release the server */
318         spin_lock(&vnode->lock);
319
320         afs_kafstimod_del_timer(&vnode->cb_timeout);
321
322         server = xchg(&vnode->cb_server, NULL);
323         if (server) {
324                 vnode->flags |= AFS_VNODE_CHANGED;
325
326                 spin_lock(&server->cb_lock);
327                 list_del_init(&vnode->cb_link);
328                 spin_unlock(&server->cb_lock);
329         }
330
331         spin_unlock(&vnode->lock);
332
333         ret = 0;
334         if (server) {
335                 ret = afs_rxfs_give_up_callback(server, vnode);
336                 afs_put_server(server);
337         }
338
339         _leave(" = %d", ret);
340         return ret;
341 } /* end afs_vnode_give_up_callback() */
342
343 /*****************************************************************************/
344 /*
345  * match a vnode record stored in the cache
346  */
347 #ifdef AFS_CACHING_SUPPORT
348 static cachefs_match_val_t afs_vnode_cache_match(void *target,
349                                                  const void *entry)
350 {
351         const struct afs_cache_vnode *cvnode = entry;
352         struct afs_vnode *vnode = target;
353
354         _enter("{%x,%x,%Lx},{%x,%x,%Lx}",
355                vnode->fid.vnode,
356                vnode->fid.unique,
357                vnode->status.version,
358                cvnode->vnode_id,
359                cvnode->vnode_unique,
360                cvnode->data_version);
361
362         if (vnode->fid.vnode != cvnode->vnode_id) {
363                 _leave(" = FAILED");
364                 return CACHEFS_MATCH_FAILED;
365         }
366
367         if (vnode->fid.unique != cvnode->vnode_unique ||
368             vnode->status.version != cvnode->data_version) {
369                 _leave(" = DELETE");
370                 return CACHEFS_MATCH_SUCCESS_DELETE;
371         }
372
373         _leave(" = SUCCESS");
374         return CACHEFS_MATCH_SUCCESS;
375 } /* end afs_vnode_cache_match() */
376 #endif
377
378 /*****************************************************************************/
379 /*
380  * update a vnode record stored in the cache
381  */
382 #ifdef AFS_CACHING_SUPPORT
383 static void afs_vnode_cache_update(void *source, void *entry)
384 {
385         struct afs_cache_vnode *cvnode = entry;
386         struct afs_vnode *vnode = source;
387
388         _enter("");
389
390         cvnode->vnode_id        = vnode->fid.vnode;
391         cvnode->vnode_unique    = vnode->fid.unique;
392         cvnode->data_version    = vnode->status.version;
393
394 } /* end afs_vnode_cache_update() */
395 #endif