Merge to Fedora kernel-2.6.18-1.2224_FC5 patched with stable patch-2.6.18.1-vs2.0...
[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 CONFIG_AFS_FSCACHE
33 static uint16_t afs_vnode_cache_get_key(const void *cookie_netfs_data,
34                                         void *buffer, uint16_t buflen);
35 static void afs_vnode_cache_get_attr(const void *cookie_netfs_data,
36                                      uint64_t *size);
37 static uint16_t afs_vnode_cache_get_aux(const void *cookie_netfs_data,
38                                         void *buffer, uint16_t buflen);
39 static fscache_checkaux_t afs_vnode_cache_check_aux(void *cookie_netfs_data,
40                                                     const void *buffer,
41                                                     uint16_t buflen);
42 static void afs_vnode_cache_mark_pages_cached(void *cookie_netfs_data,
43                                               struct address_space *mapping,
44                                               struct pagevec *cached_pvec);
45 static void afs_vnode_cache_now_uncached(void *cookie_netfs_data);
46
47 struct fscache_cookie_def afs_vnode_cache_index_def = {
48         .name                   = "AFS.vnode",
49         .type                   = FSCACHE_COOKIE_TYPE_DATAFILE,
50         .get_key                = afs_vnode_cache_get_key,
51         .get_attr               = afs_vnode_cache_get_attr,
52         .get_aux                = afs_vnode_cache_get_aux,
53         .check_aux              = afs_vnode_cache_check_aux,
54         .mark_pages_cached      = afs_vnode_cache_mark_pages_cached,
55         .now_uncached           = afs_vnode_cache_now_uncached,
56 };
57 #endif
58
59 /*****************************************************************************/
60 /*
61  * handle a callback timing out
62  * TODO: retain a ref to vnode struct for an outstanding callback timeout
63  */
64 static void afs_vnode_cb_timed_out(struct afs_timer *timer)
65 {
66         struct afs_server *oldserver;
67         struct afs_vnode *vnode;
68
69         vnode = list_entry(timer, struct afs_vnode, cb_timeout);
70
71         _enter("%p", vnode);
72
73         /* set the changed flag in the vnode and release the server */
74         spin_lock(&vnode->lock);
75
76         oldserver = xchg(&vnode->cb_server, NULL);
77         if (oldserver) {
78                 vnode->flags |= AFS_VNODE_CHANGED;
79
80                 spin_lock(&afs_cb_hash_lock);
81                 list_del_init(&vnode->cb_hash_link);
82                 spin_unlock(&afs_cb_hash_lock);
83
84                 spin_lock(&oldserver->cb_lock);
85                 list_del_init(&vnode->cb_link);
86                 spin_unlock(&oldserver->cb_lock);
87         }
88
89         spin_unlock(&vnode->lock);
90
91         afs_put_server(oldserver);
92
93         _leave("");
94 } /* end afs_vnode_cb_timed_out() */
95
96 /*****************************************************************************/
97 /*
98  * finish off updating the recorded status of a file
99  * - starts callback expiry timer
100  * - adds to server's callback list
101  */
102 static void afs_vnode_finalise_status_update(struct afs_vnode *vnode,
103                                              struct afs_server *server,
104                                              int ret)
105 {
106         struct afs_server *oldserver = NULL;
107
108         _enter("%p,%p,%d", vnode, server, ret);
109
110         spin_lock(&vnode->lock);
111
112         vnode->flags &= ~AFS_VNODE_CHANGED;
113
114         if (ret == 0) {
115                 /* adjust the callback timeout appropriately */
116                 afs_kafstimod_add_timer(&vnode->cb_timeout,
117                                         vnode->cb_expiry * HZ);
118
119                 spin_lock(&afs_cb_hash_lock);
120                 list_move_tail(&vnode->cb_hash_link,
121                               &afs_cb_hash(server, &vnode->fid));
122                 spin_unlock(&afs_cb_hash_lock);
123
124                 /* swap ref to old callback server with that for new callback
125                  * server */
126                 oldserver = xchg(&vnode->cb_server, server);
127                 if (oldserver != server) {
128                         if (oldserver) {
129                                 spin_lock(&oldserver->cb_lock);
130                                 list_del_init(&vnode->cb_link);
131                                 spin_unlock(&oldserver->cb_lock);
132                         }
133
134                         afs_get_server(server);
135                         spin_lock(&server->cb_lock);
136                         list_add_tail(&vnode->cb_link, &server->cb_promises);
137                         spin_unlock(&server->cb_lock);
138                 }
139                 else {
140                         /* same server */
141                         oldserver = NULL;
142                 }
143         }
144         else if (ret == -ENOENT) {
145                 /* the file was deleted - clear the callback timeout */
146                 oldserver = xchg(&vnode->cb_server, NULL);
147                 afs_kafstimod_del_timer(&vnode->cb_timeout);
148
149                 _debug("got NOENT from server - marking file deleted");
150                 vnode->flags |= AFS_VNODE_DELETED;
151         }
152
153         vnode->update_cnt--;
154
155         spin_unlock(&vnode->lock);
156
157         wake_up_all(&vnode->update_waitq);
158
159         afs_put_server(oldserver);
160
161         _leave("");
162
163 } /* end afs_vnode_finalise_status_update() */
164
165 /*****************************************************************************/
166 /*
167  * fetch file status from the volume
168  * - don't issue a fetch if:
169  *   - the changed bit is not set and there's a valid callback
170  *   - there are any outstanding ops that will fetch the status
171  * - TODO implement local caching
172  */
173 int afs_vnode_fetch_status(struct afs_vnode *vnode)
174 {
175         struct afs_server *server;
176         int ret;
177
178         DECLARE_WAITQUEUE(myself, current);
179
180         _enter("%s,{%u,%u,%u}",
181                vnode->volume->vlocation->vldb.name,
182                vnode->fid.vid, vnode->fid.vnode, vnode->fid.unique);
183
184         if (!(vnode->flags & AFS_VNODE_CHANGED) && vnode->cb_server) {
185                 _leave(" [unchanged]");
186                 return 0;
187         }
188
189         if (vnode->flags & AFS_VNODE_DELETED) {
190                 _leave(" [deleted]");
191                 return -ENOENT;
192         }
193
194         spin_lock(&vnode->lock);
195
196         if (!(vnode->flags & AFS_VNODE_CHANGED)) {
197                 spin_unlock(&vnode->lock);
198                 _leave(" [unchanged]");
199                 return 0;
200         }
201
202         if (vnode->update_cnt > 0) {
203                 /* someone else started a fetch */
204                 _debug("conflict");
205
206                 set_current_state(TASK_UNINTERRUPTIBLE);
207                 add_wait_queue(&vnode->update_waitq, &myself);
208
209                 /* wait for the status to be updated */
210                 for (;;) {
211                         if (!(vnode->flags & AFS_VNODE_CHANGED))
212                                 break;
213                         if (vnode->flags & AFS_VNODE_DELETED)
214                                 break;
215
216                         /* it got updated and invalidated all before we saw
217                          * it */
218                         if (vnode->update_cnt == 0) {
219                                 remove_wait_queue(&vnode->update_waitq,
220                                                   &myself);
221                                 set_current_state(TASK_RUNNING);
222                                 goto get_anyway;
223                         }
224
225                         spin_unlock(&vnode->lock);
226
227                         schedule();
228                         set_current_state(TASK_UNINTERRUPTIBLE);
229
230                         spin_lock(&vnode->lock);
231                 }
232
233                 remove_wait_queue(&vnode->update_waitq, &myself);
234                 spin_unlock(&vnode->lock);
235                 set_current_state(TASK_RUNNING);
236
237                 _leave(" [conflicted, %d", !!(vnode->flags & AFS_VNODE_DELETED));
238                 return vnode->flags & AFS_VNODE_DELETED ? -ENOENT : 0;
239         }
240
241  get_anyway:
242         /* okay... we're going to have to initiate the op */
243         vnode->update_cnt++;
244
245         spin_unlock(&vnode->lock);
246
247         /* merge AFS status fetches and clear outstanding callback on this
248          * vnode */
249         do {
250                 /* pick a server to query */
251                 ret = afs_volume_pick_fileserver(vnode->volume, &server);
252                 if (ret<0)
253                         return ret;
254
255                 _debug("USING SERVER: %08x\n", ntohl(server->addr.s_addr));
256
257                 ret = afs_rxfs_fetch_file_status(server, vnode, NULL);
258
259         } while (!afs_volume_release_fileserver(vnode->volume, server, ret));
260
261         /* adjust the flags */
262         afs_vnode_finalise_status_update(vnode, server, ret);
263
264         _leave(" = %d", ret);
265         return ret;
266 } /* end afs_vnode_fetch_status() */
267
268 /*****************************************************************************/
269 /*
270  * fetch file data from the volume
271  * - TODO implement caching and server failover
272  */
273 int afs_vnode_fetch_data(struct afs_vnode *vnode,
274                          struct afs_rxfs_fetch_descriptor *desc)
275 {
276         struct afs_server *server;
277         int ret;
278
279         _enter("%s,{%u,%u,%u}",
280                vnode->volume->vlocation->vldb.name,
281                vnode->fid.vid,
282                vnode->fid.vnode,
283                vnode->fid.unique);
284
285         /* this op will fetch the status */
286         spin_lock(&vnode->lock);
287         vnode->update_cnt++;
288         spin_unlock(&vnode->lock);
289
290         /* merge in AFS status fetches and clear outstanding callback on this
291          * vnode */
292         do {
293                 /* pick a server to query */
294                 ret = afs_volume_pick_fileserver(vnode->volume, &server);
295                 if (ret < 0)
296                         return ret;
297
298                 _debug("USING SERVER: %08x\n", ntohl(server->addr.s_addr));
299
300                 ret = afs_rxfs_fetch_file_data(server, vnode, desc, NULL);
301
302         } while (!afs_volume_release_fileserver(vnode->volume, server, ret));
303
304         /* adjust the flags */
305         afs_vnode_finalise_status_update(vnode, server, ret);
306
307         _leave(" = %d", ret);
308         return ret;
309
310 } /* end afs_vnode_fetch_data() */
311
312 /*****************************************************************************/
313 /*
314  * break any outstanding callback on a vnode
315  * - only relevent to server that issued it
316  */
317 int afs_vnode_give_up_callback(struct afs_vnode *vnode)
318 {
319         struct afs_server *server;
320         int ret;
321
322         _enter("%s,{%u,%u,%u}",
323                vnode->volume->vlocation->vldb.name,
324                vnode->fid.vid,
325                vnode->fid.vnode,
326                vnode->fid.unique);
327
328         spin_lock(&afs_cb_hash_lock);
329         list_del_init(&vnode->cb_hash_link);
330         spin_unlock(&afs_cb_hash_lock);
331
332         /* set the changed flag in the vnode and release the server */
333         spin_lock(&vnode->lock);
334
335         afs_kafstimod_del_timer(&vnode->cb_timeout);
336
337         server = xchg(&vnode->cb_server, NULL);
338         if (server) {
339                 vnode->flags |= AFS_VNODE_CHANGED;
340
341                 spin_lock(&server->cb_lock);
342                 list_del_init(&vnode->cb_link);
343                 spin_unlock(&server->cb_lock);
344         }
345
346         spin_unlock(&vnode->lock);
347
348         ret = 0;
349         if (server) {
350                 ret = afs_rxfs_give_up_callback(server, vnode);
351                 afs_put_server(server);
352         }
353
354         _leave(" = %d", ret);
355         return ret;
356 } /* end afs_vnode_give_up_callback() */
357
358 /*****************************************************************************/
359 /*
360  * set the key for the index entry
361  */
362 #ifdef CONFIG_AFS_FSCACHE
363 static uint16_t afs_vnode_cache_get_key(const void *cookie_netfs_data,
364                                         void *buffer, uint16_t bufmax)
365 {
366         const struct afs_vnode *vnode = cookie_netfs_data;
367         uint16_t klen;
368
369         _enter("{%x,%x,%Lx},%p,%u",
370                vnode->fid.vnode, vnode->fid.unique, vnode->status.version,
371                buffer, bufmax);
372
373         klen = sizeof(vnode->fid.vnode);
374         if (klen > bufmax)
375                 return 0;
376
377         memcpy(buffer, &vnode->fid.vnode, sizeof(vnode->fid.vnode));
378
379         _leave(" = %u", klen);
380         return klen;
381
382 } /* end afs_vnode_cache_get_key() */
383 #endif
384
385 /*****************************************************************************/
386 /*
387  * provide an updated file attributes
388  */
389 #ifdef CONFIG_AFS_FSCACHE
390 static void afs_vnode_cache_get_attr(const void *cookie_netfs_data,
391                                      uint64_t *size)
392 {
393         const struct afs_vnode *vnode = cookie_netfs_data;
394
395         _enter("{%x,%x,%Lx},",
396                vnode->fid.vnode, vnode->fid.unique, vnode->status.version);
397
398         *size = i_size_read((struct inode *) &vnode->vfs_inode);
399
400 } /* end afs_vnode_cache_get_attr() */
401 #endif
402
403 /*****************************************************************************/
404 /*
405  * provide new auxilliary cache data
406  */
407 #ifdef CONFIG_AFS_FSCACHE
408 static uint16_t afs_vnode_cache_get_aux(const void *cookie_netfs_data,
409                                         void *buffer, uint16_t bufmax)
410 {
411         const struct afs_vnode *vnode = cookie_netfs_data;
412         uint16_t dlen;
413
414         _enter("{%x,%x,%Lx},%p,%u",
415                vnode->fid.vnode, vnode->fid.unique, vnode->status.version,
416                buffer, bufmax);
417
418         dlen = sizeof(vnode->fid.unique) + sizeof(vnode->status.version);
419         if (dlen > bufmax)
420                 return 0;
421
422         memcpy(buffer, &vnode->fid.unique, sizeof(vnode->fid.unique));
423         buffer += sizeof(vnode->fid.unique);
424         memcpy(buffer, &vnode->status.version, sizeof(vnode->status.version));
425
426         _leave(" = %u", dlen);
427         return dlen;
428
429 } /* end afs_vnode_cache_get_aux() */
430 #endif
431
432 /*****************************************************************************/
433 /*
434  * check that the auxilliary data indicates that the entry is still valid
435  */
436 #ifdef CONFIG_AFS_FSCACHE
437 static fscache_checkaux_t afs_vnode_cache_check_aux(void *cookie_netfs_data,
438                                                     const void *buffer,
439                                                     uint16_t buflen)
440 {
441         struct afs_vnode *vnode = cookie_netfs_data;
442         uint16_t dlen;
443
444         _enter("{%x,%x,%Lx},%p,%u",
445                vnode->fid.vnode, vnode->fid.unique, vnode->status.version,
446                buffer, buflen);
447
448         /* check the size of the data is what we're expecting */
449         dlen = sizeof(vnode->fid.unique) + sizeof(vnode->status.version);
450         if (dlen != buflen) {
451                 _leave(" = OBSOLETE [len %hx != %hx]", dlen, buflen);
452                 return FSCACHE_CHECKAUX_OBSOLETE;
453         }
454
455         if (memcmp(buffer,
456                    &vnode->fid.unique,
457                    sizeof(vnode->fid.unique)
458                    ) != 0
459             ) {
460                 unsigned unique;
461
462                 memcpy(&unique, buffer, sizeof(unique));
463
464                 _leave(" = OBSOLETE [uniq %x != %x]",
465                        unique, vnode->fid.unique);
466                 return FSCACHE_CHECKAUX_OBSOLETE;
467         }
468
469         if (memcmp(buffer + sizeof(vnode->fid.unique),
470                    &vnode->status.version,
471                    sizeof(vnode->status.version)
472                    ) != 0
473             ) {
474                 afs_dataversion_t version;
475
476                 memcpy(&version, buffer + sizeof(vnode->fid.unique),
477                        sizeof(version));
478
479                 _leave(" = OBSOLETE [vers %llx != %llx]",
480                        version, vnode->status.version);
481                 return FSCACHE_CHECKAUX_OBSOLETE;
482         }
483
484         _leave(" = SUCCESS");
485         return FSCACHE_CHECKAUX_OKAY;
486
487 } /* end afs_vnode_cache_check_aux() */
488 #endif
489
490 /*****************************************************************************/
491 /*
492  * indication of pages that now have cache metadata retained
493  * - this function should mark the specified pages as now being cached
494  */
495 #ifdef CONFIG_AFS_FSCACHE
496 static void afs_vnode_cache_mark_pages_cached(void *cookie_netfs_data,
497                                               struct address_space *mapping,
498                                               struct pagevec *cached_pvec)
499 {
500         unsigned long loop;
501
502         for (loop = 0; loop < cached_pvec->nr; loop++) {
503                 struct page *page = cached_pvec->pages[loop];
504
505                 _debug("- mark %p{%lx}", page, page->index);
506
507                 SetPagePrivate(page);
508         }
509
510 } /* end afs_vnode_cache_mark_pages_cached() */
511 #endif
512
513 /*****************************************************************************/
514 /*
515  * indication the cookie is no longer uncached
516  * - this function is called when the backing store currently caching a cookie
517  *   is removed
518  * - the netfs should use this to clean up any markers indicating cached pages
519  * - this is mandatory for any object that may have data
520  */
521 static void afs_vnode_cache_now_uncached(void *cookie_netfs_data)
522 {
523         struct afs_vnode *vnode = cookie_netfs_data;
524         struct pagevec pvec;
525         pgoff_t first;
526         int loop, nr_pages;
527
528         _enter("{%x,%x,%Lx}",
529                vnode->fid.vnode, vnode->fid.unique, vnode->status.version);
530
531         pagevec_init(&pvec, 0);
532         first = 0;
533
534         for (;;) {
535                 /* grab a bunch of pages to clean */
536                 nr_pages = pagevec_lookup(&pvec, vnode->vfs_inode.i_mapping,
537                                           first,
538                                           PAGEVEC_SIZE - pagevec_count(&pvec));
539                 if (!nr_pages)
540                         break;
541
542                 for (loop = 0; loop < nr_pages; loop++)
543                         ClearPagePrivate(pvec.pages[loop]);
544
545                 first = pvec.pages[nr_pages - 1]->index + 1;
546
547                 pvec.nr = nr_pages;
548                 pagevec_release(&pvec);
549                 cond_resched();
550         }
551
552         _leave("");
553
554 } /* end afs_vnode_cache_now_uncached() */