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