This commit was manufactured by cvs2svn to create branch 'vserver'.
[linux-2.6.git] / fs / intermezzo / dcache.c
1 /* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
2  * vim:expandtab:shiftwidth=8:tabstop=8:
3  *
4  *  Original version: Copyright (C) 1996 P. Braam and M. Callahan
5  *  Rewritten for Linux 2.1. Copyright (C) 1997 Carnegie Mellon University
6  *  d_fsdata and NFS compatiblity fixes Copyright (C) 2001 Tacit Networks, Inc.
7  *
8  *   This file is part of InterMezzo, http://www.inter-mezzo.org.
9  *
10  *   InterMezzo is free software; you can redistribute it and/or
11  *   modify it under the terms of version 2 of the GNU General Public
12  *   License as published by the Free Software Foundation.
13  *
14  *   InterMezzo is distributed in the hope that it will be useful,
15  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  *   GNU General Public License for more details.
18  *
19  *   You should have received a copy of the GNU General Public License
20  *   along with InterMezzo; if not, write to the Free Software
21  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22  *
23  * Directory operations for InterMezzo filesystem
24  */
25
26 /* inode dentry alias list walking code adapted from linux/fs/dcache.c
27  *
28  * fs/dcache.c
29  *
30  * (C) 1997 Thomas Schoebel-Theuer,
31  * with heavy changes by Linus Torvalds
32  */
33
34 #include <linux/types.h>
35 #include <linux/kernel.h>
36 #include <linux/sched.h>
37 #include <linux/fs.h>
38 #include <linux/stat.h>
39 #include <linux/errno.h>
40 #include <linux/slab.h>
41 #include <asm/segment.h>
42 #include <asm/uaccess.h>
43 #include <linux/string.h>
44 #include <linux/vmalloc.h>
45
46 #include "intermezzo_fs.h"
47
48 kmem_cache_t * presto_dentry_slab;
49
50 /* called when a cache lookup succeeds */
51 static int presto_d_revalidate(struct dentry *de, struct nameidata *nd)
52 {
53         struct inode *inode = de->d_inode;
54         struct presto_file_set * root_fset;
55
56         ENTRY;
57         if (!inode) {
58                 EXIT;
59                 return 0;
60         }
61
62         if (is_bad_inode(inode)) {
63                 EXIT;
64                 return 0;
65         }
66
67         if (!presto_d2d(de)) {
68                 presto_set_dd(de);
69         }
70
71         if (!presto_d2d(de)) {
72                 EXIT;
73                 return 0;
74         }
75
76         root_fset = presto_d2d(de->d_inode->i_sb->s_root)->dd_fset;
77         if (root_fset->fset_flags & FSET_FLAT_BRANCH && 
78             (presto_d2d(de)->dd_fset != root_fset )) {
79                 presto_d2d(de)->dd_fset = root_fset;
80         }
81
82         EXIT;
83         return 1;
84
85 #if 0
86         /* The following is needed for metadata on demand. */
87         if ( S_ISDIR(inode->i_mode) ) {
88                 EXIT;
89                 return (presto_chk(de, PRESTO_DATA) &&
90                         (presto_chk(de, PRESTO_ATTR)));
91         } else {
92                 EXIT;
93                 return presto_chk(de, PRESTO_ATTR);
94         }
95 #endif
96 }
97
98 static void presto_d_release(struct dentry *dentry)
99 {
100         if (!presto_d2d(dentry)) {
101                 /* This can happen for dentries from NFSd */
102                 return;
103         }
104         presto_d2d(dentry)->dd_count--;
105
106         if (!presto_d2d(dentry)->dd_count) {
107                 kmem_cache_free(presto_dentry_slab, presto_d2d(dentry));
108                 dentry->d_fsdata = NULL;
109         }
110 }
111
112 struct dentry_operations presto_dentry_ops = 
113 {
114         .d_revalidate =  presto_d_revalidate,
115         .d_release = presto_d_release
116 };
117
118 static inline int presto_is_dentry_ROOT (struct dentry *dentry)
119 {
120         return(dentry_name_cmp(dentry,"ROOT") &&
121                !dentry_name_cmp(dentry->d_parent,".intermezzo"));
122 }
123
124 static struct presto_file_set* presto_try_find_fset(struct dentry* dentry,
125                 int *is_under_d_intermezzo)
126 {
127         struct dentry* temp_dentry;
128         struct presto_dentry_data *d_data;
129         int found_root=0;
130
131         ENTRY;
132         CDEBUG(D_FSDATA, "finding fileset for %p:%s\n", dentry, 
133                         dentry->d_name.name);
134
135         *is_under_d_intermezzo = 0;
136
137         /* walk up through the branch to get the fileset */
138         /* The dentry we are passed presumably does not have the correct
139          * fset information. However, we still want to start walking up
140          * the branch from this dentry to get our found_root and 
141          * is_under_d_intermezzo decisions correct
142          */
143         for (temp_dentry = dentry ; ; temp_dentry = temp_dentry->d_parent) {
144                 CDEBUG(D_FSDATA, "--->dentry %p:%*s\n", temp_dentry, 
145                         temp_dentry->d_name.len,temp_dentry->d_name.name);
146                 if (presto_is_dentry_ROOT(temp_dentry))
147                         found_root = 1;
148                 if (!found_root &&
149                     dentry_name_cmp(temp_dentry, ".intermezzo")) {
150                         *is_under_d_intermezzo = 1;
151                 }
152                 d_data = presto_d2d(temp_dentry);
153                 if (d_data) {
154                         /* If we found a "ROOT" dentry while walking up the
155                          * branch, we will journal regardless of whether
156                          * we are under .intermezzo or not.
157                          * If we are already under d_intermezzo don't reverse
158                          * the decision here...even if we found a "ROOT"
159                          * dentry above .intermezzo (if we were ever to
160                          * modify the directory structure).
161                          */
162                         if (!*is_under_d_intermezzo)  
163                                 *is_under_d_intermezzo = !found_root &&
164                                   (d_data->dd_flags & PRESTO_DONT_JOURNAL);
165                         EXIT;
166                         return d_data->dd_fset;
167                 }
168                 if (temp_dentry->d_parent == temp_dentry) {
169                         break;
170                 }
171         }
172         EXIT;
173         return NULL;
174 }
175
176 /* Only call this function on positive dentries */
177 static struct presto_dentry_data* presto_try_find_alias_with_dd (
178                   struct dentry* dentry)
179 {
180         struct inode *inode=dentry->d_inode;
181         struct list_head *head, *next, *tmp;
182         struct dentry *tmp_dentry;
183
184         /* Search through the alias list for dentries with d_fsdata */
185         spin_lock(&dcache_lock);
186         head = &inode->i_dentry;
187         next = inode->i_dentry.next;
188         while (next != head) {
189                 tmp = next;
190                 next = tmp->next;
191                 tmp_dentry = list_entry(tmp, struct dentry, d_alias);
192                 if (!presto_d2d(tmp_dentry)) {
193                         spin_unlock(&dcache_lock);
194                         return presto_d2d(tmp_dentry);
195                 }
196         }
197         spin_unlock(&dcache_lock);
198         return NULL;
199 }
200
201 /* Only call this function on positive dentries */
202 static void presto_set_alias_dd (struct dentry *dentry, 
203                 struct presto_dentry_data* dd)
204 {
205         struct inode *inode=dentry->d_inode;
206         struct list_head *head, *next, *tmp;
207         struct dentry *tmp_dentry;
208
209         /* Set d_fsdata for this dentry */
210         dd->dd_count++;
211         dentry->d_fsdata = dd;
212
213         /* Now set d_fsdata for all dentries in the alias list. */
214         spin_lock(&dcache_lock);
215         head = &inode->i_dentry;
216         next = inode->i_dentry.next;
217         while (next != head) {
218                 tmp = next;
219                 next = tmp->next;
220                 tmp_dentry = list_entry(tmp, struct dentry, d_alias);
221                 if (!presto_d2d(tmp_dentry)) {
222                         dd->dd_count++;
223                         tmp_dentry->d_fsdata = dd;
224                 }
225         }
226         spin_unlock(&dcache_lock);
227         return;
228 }
229
230 inline struct presto_dentry_data *izo_alloc_ddata(void)
231 {
232         struct presto_dentry_data *dd;
233
234         dd = kmem_cache_alloc(presto_dentry_slab, SLAB_KERNEL);
235         if (dd == NULL) {
236                 CERROR("IZO: out of memory trying to allocate presto_dentry_data\n");
237                 return NULL;
238         }
239         memset(dd, 0, sizeof(*dd));
240         dd->dd_count = 1;
241
242         return dd;
243 }
244
245 /* This uses the BKL! */
246 int presto_set_dd(struct dentry * dentry)
247 {
248         struct presto_file_set *fset;
249         struct presto_dentry_data *dd;
250         int is_under_d_izo;
251         int error=0;
252
253         ENTRY;
254
255         if (!dentry)
256                 BUG();
257
258         lock_kernel();
259
260         /* Did we lose a race? */
261         if (dentry->d_fsdata) {
262                 CERROR("dentry %p already has d_fsdata set\n", dentry);
263                 if (dentry->d_inode)
264                         CERROR("    inode: %ld\n", dentry->d_inode->i_ino);
265                 EXIT;
266                 goto out_unlock;
267         }
268
269         if (dentry->d_inode != NULL) {
270                 /* NFSd runs find_fh_dentry which instantiates disconnected
271                  * dentries which are then connected without a lookup(). 
272                  * So it is possible to have connected dentries that do not 
273                  * have d_fsdata set. So we walk the list trying to find 
274                  * an alias which has its d_fsdata set and then use that 
275                  * for all the other dentries  as well. 
276                  * - SHP,Vinny. 
277                  */
278
279                 /* If there is an alias with d_fsdata use it. */
280                 if ((dd = presto_try_find_alias_with_dd (dentry))) {
281                         presto_set_alias_dd (dentry, dd);
282                         EXIT;
283                         goto out_unlock;
284                 }
285         } else {
286                 /* Negative dentry */
287                 CDEBUG(D_FSDATA,"negative dentry %p: %*s\n", dentry, 
288                                 dentry->d_name.len, dentry->d_name.name);
289         }
290
291         /* No pre-existing d_fsdata, we need to construct one.
292          * First, we must walk up the tree to find the fileset 
293          * If a fileset can't be found, we leave a null fsdata
294          * and return EROFS to indicate that we can't journal
295          * updates. 
296          */
297         fset = presto_try_find_fset (dentry, &is_under_d_izo);
298         if (!fset) { 
299 #ifdef PRESTO_NO_NFS
300                 CERROR("No fileset for dentry %p: %*s\n", dentry,
301                                 dentry->d_name.len, dentry->d_name.name);
302 #endif
303                 error = -EROFS;
304                 EXIT;
305                 goto out_unlock;
306         }
307
308         dentry->d_fsdata = izo_alloc_ddata();
309         if (!presto_d2d(dentry)) {
310                 CERROR ("InterMezzo: out of memory allocating d_fsdata\n");
311                 error = -ENOMEM;
312                 goto out_unlock;
313         }
314         presto_d2d(dentry)->dd_fset = fset;
315         if (is_under_d_izo)
316                 presto_d2d(dentry)->dd_flags |= PRESTO_DONT_JOURNAL;
317         EXIT;
318
319 out_unlock:    
320         CDEBUG(D_FSDATA,"presto_set_dd dentry %p: %*s, d_fsdata %p\n", 
321                         dentry, dentry->d_name.len, dentry->d_name.name, 
322                         dentry->d_fsdata);
323         unlock_kernel();
324         return error; 
325 }
326
327 int presto_init_ddata_cache(void)
328 {
329         ENTRY;
330         presto_dentry_slab =
331                 kmem_cache_create("presto_cache",
332                                   sizeof(struct presto_dentry_data), 0,
333                                   SLAB_HWCACHE_ALIGN, NULL,
334                                   NULL);
335         EXIT;
336         return (presto_dentry_slab != NULL);
337 }
338
339 void presto_cleanup_ddata_cache(void)
340 {
341         kmem_cache_destroy(presto_dentry_slab);
342 }