fs: dcache scale subdirs
[linux-flexiantxendom0-natty.git] / drivers / staging / smbfs / cache.c
1 /*
2  *  cache.c
3  *
4  * Copyright (C) 1997 by Bill Hawes
5  *
6  * Routines to support directory cacheing using the page cache.
7  * This cache code is almost directly taken from ncpfs.
8  *
9  * Please add a note about your changes to smbfs in the ChangeLog file.
10  */
11
12 #include <linux/time.h>
13 #include <linux/errno.h>
14 #include <linux/kernel.h>
15 #include <linux/mm.h>
16 #include <linux/pagemap.h>
17 #include <linux/net.h>
18
19 #include <asm/page.h>
20
21 #include "smb_fs.h"
22 #include "smb_debug.h"
23 #include "proto.h"
24
25 /*
26  * Force the next attempt to use the cache to be a timeout.
27  * If we can't find the page that's fine, it will cause a refresh.
28  */
29 void
30 smb_invalid_dir_cache(struct inode * dir)
31 {
32         struct smb_sb_info *server = server_from_inode(dir);
33         union  smb_dir_cache *cache = NULL;
34         struct page *page = NULL;
35
36         page = grab_cache_page(&dir->i_data, 0);
37         if (!page)
38                 goto out;
39
40         if (!PageUptodate(page))
41                 goto out_unlock;
42
43         cache = kmap(page);
44         cache->head.time = jiffies - SMB_MAX_AGE(server);
45
46         kunmap(page);
47         SetPageUptodate(page);
48 out_unlock:
49         unlock_page(page);
50         page_cache_release(page);
51 out:
52         return;
53 }
54
55 /*
56  * Mark all dentries for 'parent' as invalid, forcing them to be re-read
57  */
58 void
59 smb_invalidate_dircache_entries(struct dentry *parent)
60 {
61         struct smb_sb_info *server = server_from_dentry(parent);
62         struct list_head *next;
63         struct dentry *dentry;
64
65         spin_lock(&dcache_lock);
66         spin_lock(&parent->d_lock);
67         next = parent->d_subdirs.next;
68         while (next != &parent->d_subdirs) {
69                 dentry = list_entry(next, struct dentry, d_u.d_child);
70                 dentry->d_fsdata = NULL;
71                 smb_age_dentry(server, dentry);
72                 next = next->next;
73         }
74         spin_unlock(&parent->d_lock);
75         spin_unlock(&dcache_lock);
76 }
77
78 /*
79  * dget, but require that fpos and parent matches what the dentry contains.
80  * dentry is not known to be a valid pointer at entry.
81  */
82 struct dentry *
83 smb_dget_fpos(struct dentry *dentry, struct dentry *parent, unsigned long fpos)
84 {
85         struct dentry *dent = dentry;
86         struct list_head *next;
87
88         if (d_validate(dent, parent)) {
89                 if (dent->d_name.len <= SMB_MAXNAMELEN &&
90                     (unsigned long)dent->d_fsdata == fpos) {
91                         if (!dent->d_inode) {
92                                 dput(dent);
93                                 dent = NULL;
94                         }
95                         return dent;
96                 }
97                 dput(dent);
98         }
99
100         /* If a pointer is invalid, we search the dentry. */
101         spin_lock(&dcache_lock);
102         spin_lock(&parent->d_lock);
103         next = parent->d_subdirs.next;
104         while (next != &parent->d_subdirs) {
105                 dent = list_entry(next, struct dentry, d_u.d_child);
106                 if ((unsigned long)dent->d_fsdata == fpos) {
107                         if (dent->d_inode)
108                                 dget_locked(dent);
109                         else
110                                 dent = NULL;
111                         goto out_unlock;
112                 }
113                 next = next->next;
114         }
115         dent = NULL;
116 out_unlock:
117         spin_unlock(&parent->d_lock);
118         spin_unlock(&dcache_lock);
119         return dent;
120 }
121
122
123 /*
124  * Create dentry/inode for this file and add it to the dircache.
125  */
126 int
127 smb_fill_cache(struct file *filp, void *dirent, filldir_t filldir,
128                struct smb_cache_control *ctrl, struct qstr *qname,
129                struct smb_fattr *entry)
130 {
131         struct dentry *newdent, *dentry = filp->f_path.dentry;
132         struct inode *newino, *inode = dentry->d_inode;
133         struct smb_cache_control ctl = *ctrl;
134         int valid = 0;
135         int hashed = 0;
136         ino_t ino = 0;
137
138         qname->hash = full_name_hash(qname->name, qname->len);
139
140         if (dentry->d_op && dentry->d_op->d_hash)
141                 if (dentry->d_op->d_hash(dentry, inode, qname) != 0)
142                         goto end_advance;
143
144         newdent = d_lookup(dentry, qname);
145
146         if (!newdent) {
147                 newdent = d_alloc(dentry, qname);
148                 if (!newdent)
149                         goto end_advance;
150         } else {
151                 hashed = 1;
152                 /* dir i_mutex is locked because we're in readdir */
153                 dentry_update_name_case(newdent, qname);
154         }
155
156         if (!newdent->d_inode) {
157                 smb_renew_times(newdent);
158                 entry->f_ino = iunique(inode->i_sb, 2);
159                 newino = smb_iget(inode->i_sb, entry);
160                 if (newino) {
161                         smb_new_dentry(newdent);
162                         d_instantiate(newdent, newino);
163                         if (!hashed)
164                                 d_rehash(newdent);
165                 }
166         } else
167                 smb_set_inode_attr(newdent->d_inode, entry);
168
169         if (newdent->d_inode) {
170                 ino = newdent->d_inode->i_ino;
171                 newdent->d_fsdata = (void *) ctl.fpos;
172                 smb_new_dentry(newdent);
173         }
174
175         if (ctl.idx >= SMB_DIRCACHE_SIZE) {
176                 if (ctl.page) {
177                         kunmap(ctl.page);
178                         SetPageUptodate(ctl.page);
179                         unlock_page(ctl.page);
180                         page_cache_release(ctl.page);
181                 }
182                 ctl.cache = NULL;
183                 ctl.idx  -= SMB_DIRCACHE_SIZE;
184                 ctl.ofs  += 1;
185                 ctl.page  = grab_cache_page(&inode->i_data, ctl.ofs);
186                 if (ctl.page)
187                         ctl.cache = kmap(ctl.page);
188         }
189         if (ctl.cache) {
190                 ctl.cache->dentry[ctl.idx] = newdent;
191                 valid = 1;
192         }
193         dput(newdent);
194
195 end_advance:
196         if (!valid)
197                 ctl.valid = 0;
198         if (!ctl.filled && (ctl.fpos == filp->f_pos)) {
199                 if (!ino)
200                         ino = find_inode_number(dentry, qname);
201                 if (!ino)
202                         ino = iunique(inode->i_sb, 2);
203                 ctl.filled = filldir(dirent, qname->name, qname->len,
204                                      filp->f_pos, ino, DT_UNKNOWN);
205                 if (!ctl.filled)
206                         filp->f_pos += 1;
207         }
208         ctl.fpos += 1;
209         ctl.idx  += 1;
210         *ctrl = ctl;
211         return (ctl.valid || !ctl.filled);
212 }