diff options
author | Bryan Schumaker <bjschuma@netapp.com> | 2010-09-24 14:48:42 -0400 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2010-10-23 15:27:30 -0400 |
commit | d1bacf9eb2fd0e7ef870acf84b9e3b157dcfa7dc (patch) | |
tree | 8b6746ade07c71f0d40c29b76dbe65e999c038f7 /fs | |
parent | 8c7597f6ce212bbc8ca05090e21820ffe9792b3d (diff) |
NFS: add readdir cache array
This patch adds the readdir cache array and functions to retreive the array
stored on a cache page, clear the array by freeing allocated memory, add an
entry to the array, and search the array for a given cookie.
It then modifies readdir to make use of the new cache array.
With the new cache array method, we no longer need some of this code.
Finally, nfs_llseek_dir() will set file->f_pos to a value greater than 0 and
desc->dir_cookie to zero. When we see this, readdir needs to find the file
at position file->f_pos from the start of the directory.
Signed-off-by: Bryan Schumaker <bjschuma@netapp.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/nfs/dir.c | 632 |
1 files changed, 309 insertions, 323 deletions
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index a847acf4d3cd..f77243907a08 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c | |||
@@ -55,6 +55,7 @@ static int nfs_rename(struct inode *, struct dentry *, | |||
55 | struct inode *, struct dentry *); | 55 | struct inode *, struct dentry *); |
56 | static int nfs_fsync_dir(struct file *, int); | 56 | static int nfs_fsync_dir(struct file *, int); |
57 | static loff_t nfs_llseek_dir(struct file *, loff_t, int); | 57 | static loff_t nfs_llseek_dir(struct file *, loff_t, int); |
58 | static int nfs_readdir_clear_array(struct page*, gfp_t); | ||
58 | 59 | ||
59 | const struct file_operations nfs_dir_operations = { | 60 | const struct file_operations nfs_dir_operations = { |
60 | .llseek = nfs_llseek_dir, | 61 | .llseek = nfs_llseek_dir, |
@@ -80,6 +81,10 @@ const struct inode_operations nfs_dir_inode_operations = { | |||
80 | .setattr = nfs_setattr, | 81 | .setattr = nfs_setattr, |
81 | }; | 82 | }; |
82 | 83 | ||
84 | const struct address_space_operations nfs_dir_addr_space_ops = { | ||
85 | .releasepage = nfs_readdir_clear_array, | ||
86 | }; | ||
87 | |||
83 | #ifdef CONFIG_NFS_V3 | 88 | #ifdef CONFIG_NFS_V3 |
84 | const struct inode_operations nfs3_dir_inode_operations = { | 89 | const struct inode_operations nfs3_dir_inode_operations = { |
85 | .create = nfs_create, | 90 | .create = nfs_create, |
@@ -151,51 +156,188 @@ nfs_opendir(struct inode *inode, struct file *filp) | |||
151 | return res; | 156 | return res; |
152 | } | 157 | } |
153 | 158 | ||
159 | struct nfs_cache_array_entry { | ||
160 | u64 cookie; | ||
161 | u64 ino; | ||
162 | struct qstr string; | ||
163 | }; | ||
164 | |||
165 | struct nfs_cache_array { | ||
166 | unsigned int size; | ||
167 | int eof_index; | ||
168 | u64 last_cookie; | ||
169 | struct nfs_cache_array_entry array[0]; | ||
170 | }; | ||
171 | |||
172 | #define MAX_READDIR_ARRAY ((PAGE_SIZE - sizeof(struct nfs_cache_array)) / sizeof(struct nfs_cache_array_entry)) | ||
173 | |||
154 | typedef __be32 * (*decode_dirent_t)(__be32 *, struct nfs_entry *, int); | 174 | typedef __be32 * (*decode_dirent_t)(__be32 *, struct nfs_entry *, int); |
155 | typedef struct { | 175 | typedef struct { |
156 | struct file *file; | 176 | struct file *file; |
157 | struct page *page; | 177 | struct page *page; |
158 | unsigned long page_index; | 178 | unsigned long page_index; |
159 | __be32 *ptr; | ||
160 | u64 *dir_cookie; | 179 | u64 *dir_cookie; |
161 | loff_t current_index; | 180 | loff_t current_index; |
162 | struct nfs_entry *entry; | ||
163 | decode_dirent_t decode; | 181 | decode_dirent_t decode; |
164 | int plus; | 182 | |
165 | unsigned long timestamp; | 183 | unsigned long timestamp; |
166 | unsigned long gencount; | 184 | unsigned long gencount; |
167 | int timestamp_valid; | 185 | unsigned int cache_entry_index; |
186 | unsigned int plus:1; | ||
187 | unsigned int eof:1; | ||
168 | } nfs_readdir_descriptor_t; | 188 | } nfs_readdir_descriptor_t; |
169 | 189 | ||
170 | /* Now we cache directories properly, by stuffing the dirent | 190 | /* |
171 | * data directly in the page cache. | 191 | * The caller is responsible for calling nfs_readdir_release_array(page) |
172 | * | ||
173 | * Inode invalidation due to refresh etc. takes care of | ||
174 | * _everything_, no sloppy entry flushing logic, no extraneous | ||
175 | * copying, network direct to page cache, the way it was meant | ||
176 | * to be. | ||
177 | * | ||
178 | * NOTE: Dirent information verification is done always by the | ||
179 | * page-in of the RPC reply, nowhere else, this simplies | ||
180 | * things substantially. | ||
181 | */ | 192 | */ |
182 | static | 193 | static |
183 | int nfs_readdir_filler(nfs_readdir_descriptor_t *desc, struct page *page) | 194 | struct nfs_cache_array *nfs_readdir_get_array(struct page *page) |
195 | { | ||
196 | if (page == NULL) | ||
197 | return ERR_PTR(-EIO); | ||
198 | return (struct nfs_cache_array *)kmap(page); | ||
199 | } | ||
200 | |||
201 | static | ||
202 | void nfs_readdir_release_array(struct page *page) | ||
203 | { | ||
204 | kunmap(page); | ||
205 | } | ||
206 | |||
207 | /* | ||
208 | * we are freeing strings created by nfs_add_to_readdir_array() | ||
209 | */ | ||
210 | static | ||
211 | int nfs_readdir_clear_array(struct page *page, gfp_t mask) | ||
212 | { | ||
213 | struct nfs_cache_array *array = nfs_readdir_get_array(page); | ||
214 | int i; | ||
215 | for (i = 0; i < array->size; i++) | ||
216 | kfree(array->array[i].string.name); | ||
217 | nfs_readdir_release_array(page); | ||
218 | return 0; | ||
219 | } | ||
220 | |||
221 | /* | ||
222 | * the caller is responsible for freeing qstr.name | ||
223 | * when called by nfs_readdir_add_to_array, the strings will be freed in | ||
224 | * nfs_clear_readdir_array() | ||
225 | */ | ||
226 | static | ||
227 | void nfs_readdir_make_qstr(struct qstr *string, const char *name, unsigned int len) | ||
228 | { | ||
229 | string->len = len; | ||
230 | string->name = kmemdup(name, len, GFP_KERNEL); | ||
231 | string->hash = full_name_hash(string->name, string->len); | ||
232 | } | ||
233 | |||
234 | static | ||
235 | int nfs_readdir_add_to_array(struct nfs_entry *entry, struct page *page) | ||
236 | { | ||
237 | struct nfs_cache_array *array = nfs_readdir_get_array(page); | ||
238 | if (IS_ERR(array)) | ||
239 | return PTR_ERR(array); | ||
240 | if (array->size >= MAX_READDIR_ARRAY) { | ||
241 | nfs_readdir_release_array(page); | ||
242 | return -EIO; | ||
243 | } | ||
244 | |||
245 | array->array[array->size].cookie = entry->prev_cookie; | ||
246 | array->last_cookie = entry->cookie; | ||
247 | array->array[array->size].ino = entry->ino; | ||
248 | nfs_readdir_make_qstr(&array->array[array->size].string, entry->name, entry->len); | ||
249 | if (entry->eof == 1) | ||
250 | array->eof_index = array->size; | ||
251 | array->size++; | ||
252 | nfs_readdir_release_array(page); | ||
253 | return 0; | ||
254 | } | ||
255 | |||
256 | static | ||
257 | int nfs_readdir_search_for_pos(struct nfs_cache_array *array, nfs_readdir_descriptor_t *desc) | ||
258 | { | ||
259 | loff_t diff = desc->file->f_pos - desc->current_index; | ||
260 | unsigned int index; | ||
261 | |||
262 | if (diff < 0) | ||
263 | goto out_eof; | ||
264 | if (diff >= array->size) { | ||
265 | if (array->eof_index > 0) | ||
266 | goto out_eof; | ||
267 | desc->current_index += array->size; | ||
268 | return -EAGAIN; | ||
269 | } | ||
270 | |||
271 | index = (unsigned int)diff; | ||
272 | *desc->dir_cookie = array->array[index].cookie; | ||
273 | desc->cache_entry_index = index; | ||
274 | if (index == array->eof_index) | ||
275 | desc->eof = 1; | ||
276 | return 0; | ||
277 | out_eof: | ||
278 | desc->eof = 1; | ||
279 | return -EBADCOOKIE; | ||
280 | } | ||
281 | |||
282 | static | ||
283 | int nfs_readdir_search_for_cookie(struct nfs_cache_array *array, nfs_readdir_descriptor_t *desc) | ||
284 | { | ||
285 | int i; | ||
286 | int status = -EAGAIN; | ||
287 | |||
288 | for (i = 0; i < array->size; i++) { | ||
289 | if (i == array->eof_index) { | ||
290 | desc->eof = 1; | ||
291 | status = -EBADCOOKIE; | ||
292 | } | ||
293 | if (array->array[i].cookie == *desc->dir_cookie) { | ||
294 | desc->cache_entry_index = i; | ||
295 | status = 0; | ||
296 | break; | ||
297 | } | ||
298 | } | ||
299 | |||
300 | return status; | ||
301 | } | ||
302 | |||
303 | static | ||
304 | int nfs_readdir_search_array(nfs_readdir_descriptor_t *desc) | ||
305 | { | ||
306 | struct nfs_cache_array *array; | ||
307 | int status = -EBADCOOKIE; | ||
308 | |||
309 | if (desc->dir_cookie == NULL) | ||
310 | goto out; | ||
311 | |||
312 | array = nfs_readdir_get_array(desc->page); | ||
313 | if (IS_ERR(array)) { | ||
314 | status = PTR_ERR(array); | ||
315 | goto out; | ||
316 | } | ||
317 | |||
318 | if (*desc->dir_cookie == 0) | ||
319 | status = nfs_readdir_search_for_pos(array, desc); | ||
320 | else | ||
321 | status = nfs_readdir_search_for_cookie(array, desc); | ||
322 | |||
323 | nfs_readdir_release_array(desc->page); | ||
324 | out: | ||
325 | return status; | ||
326 | } | ||
327 | |||
328 | /* Fill a page with xdr information before transferring to the cache page */ | ||
329 | static | ||
330 | int nfs_readdir_xdr_filler(struct page *xdr_page, nfs_readdir_descriptor_t *desc, | ||
331 | struct nfs_entry *entry, struct file *file, struct inode *inode) | ||
184 | { | 332 | { |
185 | struct file *file = desc->file; | ||
186 | struct inode *inode = file->f_path.dentry->d_inode; | ||
187 | struct rpc_cred *cred = nfs_file_cred(file); | 333 | struct rpc_cred *cred = nfs_file_cred(file); |
188 | unsigned long timestamp, gencount; | 334 | unsigned long timestamp, gencount; |
189 | int error; | 335 | int error; |
190 | 336 | ||
191 | dfprintk(DIRCACHE, "NFS: %s: reading cookie %Lu into page %lu\n", | ||
192 | __func__, (long long)desc->entry->cookie, | ||
193 | page->index); | ||
194 | |||
195 | again: | 337 | again: |
196 | timestamp = jiffies; | 338 | timestamp = jiffies; |
197 | gencount = nfs_inc_attr_generation_counter(); | 339 | gencount = nfs_inc_attr_generation_counter(); |
198 | error = NFS_PROTO(inode)->readdir(file->f_path.dentry, cred, desc->entry->cookie, page, | 340 | error = NFS_PROTO(inode)->readdir(file->f_path.dentry, cred, entry->cookie, xdr_page, |
199 | NFS_SERVER(inode)->dtsize, desc->plus); | 341 | NFS_SERVER(inode)->dtsize, desc->plus); |
200 | if (error < 0) { | 342 | if (error < 0) { |
201 | /* We requested READDIRPLUS, but the server doesn't grok it */ | 343 | /* We requested READDIRPLUS, but the server doesn't grok it */ |
@@ -209,190 +351,157 @@ int nfs_readdir_filler(nfs_readdir_descriptor_t *desc, struct page *page) | |||
209 | } | 351 | } |
210 | desc->timestamp = timestamp; | 352 | desc->timestamp = timestamp; |
211 | desc->gencount = gencount; | 353 | desc->gencount = gencount; |
212 | desc->timestamp_valid = 1; | 354 | error: |
213 | SetPageUptodate(page); | 355 | return error; |
214 | /* Ensure consistent page alignment of the data. | ||
215 | * Note: assumes we have exclusive access to this mapping either | ||
216 | * through inode->i_mutex or some other mechanism. | ||
217 | */ | ||
218 | if (invalidate_inode_pages2_range(inode->i_mapping, page->index + 1, -1) < 0) { | ||
219 | /* Should never happen */ | ||
220 | nfs_zap_mapping(inode, inode->i_mapping); | ||
221 | } | ||
222 | unlock_page(page); | ||
223 | return 0; | ||
224 | error: | ||
225 | unlock_page(page); | ||
226 | return -EIO; | ||
227 | } | 356 | } |
228 | 357 | ||
229 | static inline | 358 | /* Fill in an entry based on the xdr code stored in desc->page */ |
230 | int dir_decode(nfs_readdir_descriptor_t *desc) | 359 | static |
360 | int xdr_decode(nfs_readdir_descriptor_t *desc, struct nfs_entry *entry, __be32 **ptr) | ||
231 | { | 361 | { |
232 | __be32 *p = desc->ptr; | 362 | __be32 *p = *ptr; |
233 | p = desc->decode(p, desc->entry, desc->plus); | 363 | p = desc->decode(p, entry, desc->plus); |
234 | if (IS_ERR(p)) | 364 | if (IS_ERR(p)) |
235 | return PTR_ERR(p); | 365 | return PTR_ERR(p); |
236 | desc->ptr = p; | 366 | *ptr = p; |
237 | if (desc->timestamp_valid) { | ||
238 | desc->entry->fattr->time_start = desc->timestamp; | ||
239 | desc->entry->fattr->gencount = desc->gencount; | ||
240 | } else | ||
241 | desc->entry->fattr->valid &= ~NFS_ATTR_FATTR; | ||
242 | return 0; | ||
243 | } | ||
244 | 367 | ||
245 | static inline | 368 | entry->fattr->time_start = desc->timestamp; |
246 | void dir_page_release(nfs_readdir_descriptor_t *desc) | 369 | entry->fattr->gencount = desc->gencount; |
247 | { | 370 | return 0; |
248 | kunmap(desc->page); | ||
249 | page_cache_release(desc->page); | ||
250 | desc->page = NULL; | ||
251 | desc->ptr = NULL; | ||
252 | } | 371 | } |
253 | 372 | ||
254 | /* | 373 | /* Perform conversion from xdr to cache array */ |
255 | * Given a pointer to a buffer that has already been filled by a call | 374 | static |
256 | * to readdir, find the next entry with cookie '*desc->dir_cookie'. | 375 | void nfs_readdir_page_filler(nfs_readdir_descriptor_t *desc, struct nfs_entry *entry, |
257 | * | 376 | struct page *xdr_page, struct page *page) |
258 | * If the end of the buffer has been reached, return -EAGAIN, if not, | ||
259 | * return the offset within the buffer of the next entry to be | ||
260 | * read. | ||
261 | */ | ||
262 | static inline | ||
263 | int find_dirent(nfs_readdir_descriptor_t *desc) | ||
264 | { | 377 | { |
265 | struct nfs_entry *entry = desc->entry; | 378 | __be32 *ptr = kmap(xdr_page); |
266 | int loop_count = 0, | 379 | while (xdr_decode(desc, entry, &ptr) == 0) { |
267 | status; | 380 | if (nfs_readdir_add_to_array(entry, page) == -1) |
268 | |||
269 | while((status = dir_decode(desc)) == 0) { | ||
270 | dfprintk(DIRCACHE, "NFS: %s: examining cookie %Lu\n", | ||
271 | __func__, (unsigned long long)entry->cookie); | ||
272 | if (entry->prev_cookie == *desc->dir_cookie) | ||
273 | break; | 381 | break; |
274 | if (loop_count++ > 200) { | ||
275 | loop_count = 0; | ||
276 | schedule(); | ||
277 | } | ||
278 | } | 382 | } |
279 | return status; | 383 | kunmap(xdr_page); |
280 | } | 384 | } |
281 | 385 | ||
282 | /* | 386 | static |
283 | * Given a pointer to a buffer that has already been filled by a call | 387 | int nfs_readdir_xdr_to_array(nfs_readdir_descriptor_t *desc, struct page *page, struct inode *inode) |
284 | * to readdir, find the entry at offset 'desc->file->f_pos'. | ||
285 | * | ||
286 | * If the end of the buffer has been reached, return -EAGAIN, if not, | ||
287 | * return the offset within the buffer of the next entry to be | ||
288 | * read. | ||
289 | */ | ||
290 | static inline | ||
291 | int find_dirent_index(nfs_readdir_descriptor_t *desc) | ||
292 | { | 388 | { |
293 | struct nfs_entry *entry = desc->entry; | 389 | struct page *xdr_page; |
294 | int loop_count = 0, | 390 | struct nfs_entry entry; |
295 | status; | 391 | struct file *file = desc->file; |
296 | 392 | struct nfs_cache_array *array; | |
297 | for(;;) { | 393 | int status = 0; |
298 | status = dir_decode(desc); | 394 | |
299 | if (status) | 395 | entry.prev_cookie = 0; |
300 | break; | 396 | entry.cookie = *desc->dir_cookie; |
397 | entry.eof = 0; | ||
398 | entry.fh = nfs_alloc_fhandle(); | ||
399 | entry.fattr = nfs_alloc_fattr(); | ||
400 | if (entry.fh == NULL || entry.fattr == NULL) | ||
401 | goto out; | ||
301 | 402 | ||
302 | dfprintk(DIRCACHE, "NFS: found cookie %Lu at index %Ld\n", | 403 | array = nfs_readdir_get_array(page); |
303 | (unsigned long long)entry->cookie, desc->current_index); | 404 | memset(array, 0, sizeof(struct nfs_cache_array)); |
405 | array->eof_index = -1; | ||
304 | 406 | ||
305 | if (desc->file->f_pos == desc->current_index) { | 407 | xdr_page = alloc_page(GFP_KERNEL); |
306 | *desc->dir_cookie = entry->cookie; | 408 | if (!xdr_page) |
409 | goto out_release_array; | ||
410 | do { | ||
411 | status = nfs_readdir_xdr_filler(xdr_page, desc, &entry, file, inode); | ||
412 | if (status < 0) | ||
307 | break; | 413 | break; |
308 | } | 414 | nfs_readdir_page_filler(desc, &entry, xdr_page, page); |
309 | desc->current_index++; | 415 | } while (array->eof_index < 0 && array->size < MAX_READDIR_ARRAY); |
310 | if (loop_count++ > 200) { | 416 | |
311 | loop_count = 0; | 417 | put_page(xdr_page); |
312 | schedule(); | 418 | out_release_array: |
313 | } | 419 | nfs_readdir_release_array(page); |
314 | } | 420 | out: |
421 | nfs_free_fattr(entry.fattr); | ||
422 | nfs_free_fhandle(entry.fh); | ||
315 | return status; | 423 | return status; |
316 | } | 424 | } |
317 | 425 | ||
318 | /* | 426 | /* |
319 | * Find the given page, and call find_dirent() or find_dirent_index in | 427 | * Now we cache directories properly, by converting xdr information |
320 | * order to try to return the next entry. | 428 | * to an array that can be used for lookups later. This results in |
429 | * fewer cache pages, since we can store more information on each page. | ||
430 | * We only need to convert from xdr once so future lookups are much simpler | ||
321 | */ | 431 | */ |
322 | static inline | 432 | static |
323 | int find_dirent_page(nfs_readdir_descriptor_t *desc) | 433 | int nfs_readdir_filler(nfs_readdir_descriptor_t *desc, struct page* page) |
324 | { | 434 | { |
325 | struct inode *inode = desc->file->f_path.dentry->d_inode; | 435 | struct inode *inode = desc->file->f_path.dentry->d_inode; |
326 | struct page *page; | ||
327 | int status; | ||
328 | 436 | ||
329 | dfprintk(DIRCACHE, "NFS: %s: searching page %ld for target %Lu\n", | 437 | if (nfs_readdir_xdr_to_array(desc, page, inode) == -1) |
330 | __func__, desc->page_index, | 438 | goto error; |
331 | (long long) *desc->dir_cookie); | 439 | SetPageUptodate(page); |
332 | 440 | ||
333 | /* If we find the page in the page_cache, we cannot be sure | 441 | if (invalidate_inode_pages2_range(inode->i_mapping, page->index + 1, -1) < 0) { |
334 | * how fresh the data is, so we will ignore readdir_plus attributes. | 442 | /* Should never happen */ |
335 | */ | 443 | nfs_zap_mapping(inode, inode->i_mapping); |
336 | desc->timestamp_valid = 0; | ||
337 | page = read_cache_page(inode->i_mapping, desc->page_index, | ||
338 | (filler_t *)nfs_readdir_filler, desc); | ||
339 | if (IS_ERR(page)) { | ||
340 | status = PTR_ERR(page); | ||
341 | goto out; | ||
342 | } | 444 | } |
445 | unlock_page(page); | ||
446 | return 0; | ||
447 | error: | ||
448 | unlock_page(page); | ||
449 | return -EIO; | ||
450 | } | ||
343 | 451 | ||
344 | /* NOTE: Someone else may have changed the READDIRPLUS flag */ | 452 | static |
345 | desc->page = page; | 453 | void cache_page_release(nfs_readdir_descriptor_t *desc) |
346 | desc->ptr = kmap(page); /* matching kunmap in nfs_do_filldir */ | 454 | { |
347 | if (*desc->dir_cookie != 0) | 455 | page_cache_release(desc->page); |
348 | status = find_dirent(desc); | 456 | desc->page = NULL; |
349 | else | 457 | } |
350 | status = find_dirent_index(desc); | 458 | |
351 | if (status < 0) | 459 | static |
352 | dir_page_release(desc); | 460 | struct page *get_cache_page(nfs_readdir_descriptor_t *desc) |
353 | out: | 461 | { |
354 | dfprintk(DIRCACHE, "NFS: %s: returns %d\n", __func__, status); | 462 | struct page *page; |
355 | return status; | 463 | page = read_cache_page(desc->file->f_path.dentry->d_inode->i_mapping, |
464 | desc->page_index, (filler_t *)nfs_readdir_filler, desc); | ||
465 | if (IS_ERR(page)) | ||
466 | desc->eof = 1; | ||
467 | return page; | ||
356 | } | 468 | } |
357 | 469 | ||
358 | /* | 470 | /* |
359 | * Recurse through the page cache pages, and return a | 471 | * Returns 0 if desc->dir_cookie was found on page desc->page_index |
360 | * filled nfs_entry structure of the next directory entry if possible. | ||
361 | * | ||
362 | * The target for the search is '*desc->dir_cookie' if non-0, | ||
363 | * 'desc->file->f_pos' otherwise | ||
364 | */ | 472 | */ |
473 | static | ||
474 | int find_cache_page(nfs_readdir_descriptor_t *desc) | ||
475 | { | ||
476 | int res; | ||
477 | |||
478 | desc->page = get_cache_page(desc); | ||
479 | if (IS_ERR(desc->page)) | ||
480 | return PTR_ERR(desc->page); | ||
481 | |||
482 | res = nfs_readdir_search_array(desc); | ||
483 | if (res == 0) | ||
484 | return 0; | ||
485 | cache_page_release(desc); | ||
486 | return res; | ||
487 | } | ||
488 | |||
489 | /* Search for desc->dir_cookie from the beginning of the page cache */ | ||
365 | static inline | 490 | static inline |
366 | int readdir_search_pagecache(nfs_readdir_descriptor_t *desc) | 491 | int readdir_search_pagecache(nfs_readdir_descriptor_t *desc) |
367 | { | 492 | { |
368 | int loop_count = 0; | 493 | int res = -EAGAIN; |
369 | int res; | 494 | desc->page_index = 0; |
370 | |||
371 | /* Always search-by-index from the beginning of the cache */ | ||
372 | if (*desc->dir_cookie == 0) { | ||
373 | dfprintk(DIRCACHE, "NFS: readdir_search_pagecache() searching for offset %Ld\n", | ||
374 | (long long)desc->file->f_pos); | ||
375 | desc->page_index = 0; | ||
376 | desc->entry->cookie = desc->entry->prev_cookie = 0; | ||
377 | desc->entry->eof = 0; | ||
378 | desc->current_index = 0; | ||
379 | } else | ||
380 | dfprintk(DIRCACHE, "NFS: readdir_search_pagecache() searching for cookie %Lu\n", | ||
381 | (unsigned long long)*desc->dir_cookie); | ||
382 | 495 | ||
383 | for (;;) { | 496 | if (*desc->dir_cookie == 0) |
384 | res = find_dirent_page(desc); | 497 | desc->cache_entry_index = 0; |
498 | |||
499 | while (1) { | ||
500 | res = find_cache_page(desc); | ||
385 | if (res != -EAGAIN) | 501 | if (res != -EAGAIN) |
386 | break; | 502 | break; |
387 | /* Align to beginning of next page */ | 503 | desc->page_index++; |
388 | desc->page_index ++; | ||
389 | if (loop_count++ > 200) { | ||
390 | loop_count = 0; | ||
391 | schedule(); | ||
392 | } | ||
393 | } | 504 | } |
394 | |||
395 | dfprintk(DIRCACHE, "NFS: %s: returns %d\n", __func__, res); | ||
396 | return res; | 505 | return res; |
397 | } | 506 | } |
398 | 507 | ||
@@ -401,8 +510,6 @@ static inline unsigned int dt_type(struct inode *inode) | |||
401 | return (inode->i_mode >> 12) & 15; | 510 | return (inode->i_mode >> 12) & 15; |
402 | } | 511 | } |
403 | 512 | ||
404 | static struct dentry *nfs_readdir_lookup(nfs_readdir_descriptor_t *desc); | ||
405 | |||
406 | /* | 513 | /* |
407 | * Once we've found the start of the dirent within a page: fill 'er up... | 514 | * Once we've found the start of the dirent within a page: fill 'er up... |
408 | */ | 515 | */ |
@@ -411,49 +518,36 @@ int nfs_do_filldir(nfs_readdir_descriptor_t *desc, void *dirent, | |||
411 | filldir_t filldir) | 518 | filldir_t filldir) |
412 | { | 519 | { |
413 | struct file *file = desc->file; | 520 | struct file *file = desc->file; |
414 | struct nfs_entry *entry = desc->entry; | 521 | int i = 0; |
415 | struct dentry *dentry = NULL; | 522 | int res = 0; |
416 | u64 fileid; | 523 | struct nfs_cache_array *array = NULL; |
417 | int loop_count = 0, | 524 | unsigned int d_type = DT_UNKNOWN; |
418 | res; | 525 | struct dentry *dentry = NULL; |
419 | |||
420 | dfprintk(DIRCACHE, "NFS: nfs_do_filldir() filling starting @ cookie %Lu\n", | ||
421 | (unsigned long long)entry->cookie); | ||
422 | |||
423 | for(;;) { | ||
424 | unsigned d_type = DT_UNKNOWN; | ||
425 | /* Note: entry->prev_cookie contains the cookie for | ||
426 | * retrieving the current dirent on the server */ | ||
427 | fileid = entry->ino; | ||
428 | |||
429 | /* Get a dentry if we have one */ | ||
430 | if (dentry != NULL) | ||
431 | dput(dentry); | ||
432 | dentry = nfs_readdir_lookup(desc); | ||
433 | |||
434 | /* Use readdirplus info */ | ||
435 | if (dentry != NULL && dentry->d_inode != NULL) { | ||
436 | d_type = dt_type(dentry->d_inode); | ||
437 | fileid = NFS_FILEID(dentry->d_inode); | ||
438 | } | ||
439 | 526 | ||
440 | res = filldir(dirent, entry->name, entry->len, | 527 | array = nfs_readdir_get_array(desc->page); |
441 | file->f_pos, nfs_compat_user_ino64(fileid), | 528 | |
442 | d_type); | 529 | for (i = desc->cache_entry_index; i < array->size; i++) { |
530 | d_type = DT_UNKNOWN; | ||
531 | |||
532 | res = filldir(dirent, array->array[i].string.name, | ||
533 | array->array[i].string.len, file->f_pos, | ||
534 | nfs_compat_user_ino64(array->array[i].ino), d_type); | ||
443 | if (res < 0) | 535 | if (res < 0) |
444 | break; | 536 | break; |
445 | file->f_pos++; | 537 | file->f_pos++; |
446 | *desc->dir_cookie = entry->cookie; | 538 | desc->cache_entry_index = i; |
447 | if (dir_decode(desc) != 0) { | 539 | if (i < (array->size-1)) |
448 | desc->page_index ++; | 540 | *desc->dir_cookie = array->array[i+1].cookie; |
541 | else | ||
542 | *desc->dir_cookie = array->last_cookie; | ||
543 | if (i == array->eof_index) { | ||
544 | desc->eof = 1; | ||
449 | break; | 545 | break; |
450 | } | 546 | } |
451 | if (loop_count++ > 200) { | ||
452 | loop_count = 0; | ||
453 | schedule(); | ||
454 | } | ||
455 | } | 547 | } |
456 | dir_page_release(desc); | 548 | |
549 | nfs_readdir_release_array(desc->page); | ||
550 | cache_page_release(desc); | ||
457 | if (dentry != NULL) | 551 | if (dentry != NULL) |
458 | dput(dentry); | 552 | dput(dentry); |
459 | dfprintk(DIRCACHE, "NFS: nfs_do_filldir() filling ended @ cookie %Lu; returning = %d\n", | 553 | dfprintk(DIRCACHE, "NFS: nfs_do_filldir() filling ended @ cookie %Lu; returning = %d\n", |
@@ -477,12 +571,9 @@ static inline | |||
477 | int uncached_readdir(nfs_readdir_descriptor_t *desc, void *dirent, | 571 | int uncached_readdir(nfs_readdir_descriptor_t *desc, void *dirent, |
478 | filldir_t filldir) | 572 | filldir_t filldir) |
479 | { | 573 | { |
480 | struct file *file = desc->file; | ||
481 | struct inode *inode = file->f_path.dentry->d_inode; | ||
482 | struct rpc_cred *cred = nfs_file_cred(file); | ||
483 | struct page *page = NULL; | 574 | struct page *page = NULL; |
484 | int status; | 575 | int status; |
485 | unsigned long timestamp, gencount; | 576 | struct inode *inode = desc->file->f_path.dentry->d_inode; |
486 | 577 | ||
487 | dfprintk(DIRCACHE, "NFS: uncached_readdir() searching for cookie %Lu\n", | 578 | dfprintk(DIRCACHE, "NFS: uncached_readdir() searching for cookie %Lu\n", |
488 | (unsigned long long)*desc->dir_cookie); | 579 | (unsigned long long)*desc->dir_cookie); |
@@ -492,38 +583,21 @@ int uncached_readdir(nfs_readdir_descriptor_t *desc, void *dirent, | |||
492 | status = -ENOMEM; | 583 | status = -ENOMEM; |
493 | goto out; | 584 | goto out; |
494 | } | 585 | } |
495 | timestamp = jiffies; | 586 | |
496 | gencount = nfs_inc_attr_generation_counter(); | 587 | if (nfs_readdir_xdr_to_array(desc, page, inode) == -1) { |
497 | status = NFS_PROTO(inode)->readdir(file->f_path.dentry, cred, | ||
498 | *desc->dir_cookie, page, | ||
499 | NFS_SERVER(inode)->dtsize, | ||
500 | desc->plus); | ||
501 | desc->page = page; | ||
502 | desc->ptr = kmap(page); /* matching kunmap in nfs_do_filldir */ | ||
503 | if (status >= 0) { | ||
504 | desc->timestamp = timestamp; | ||
505 | desc->gencount = gencount; | ||
506 | desc->timestamp_valid = 1; | ||
507 | if ((status = dir_decode(desc)) == 0) | ||
508 | desc->entry->prev_cookie = *desc->dir_cookie; | ||
509 | } else | ||
510 | status = -EIO; | 588 | status = -EIO; |
511 | if (status < 0) | ||
512 | goto out_release; | 589 | goto out_release; |
590 | } | ||
513 | 591 | ||
592 | desc->page = page; | ||
514 | status = nfs_do_filldir(desc, dirent, filldir); | 593 | status = nfs_do_filldir(desc, dirent, filldir); |
515 | 594 | ||
516 | /* Reset read descriptor so it searches the page cache from | ||
517 | * the start upon the next call to readdir_search_pagecache() */ | ||
518 | desc->page_index = 0; | ||
519 | desc->entry->cookie = desc->entry->prev_cookie = 0; | ||
520 | desc->entry->eof = 0; | ||
521 | out: | 595 | out: |
522 | dfprintk(DIRCACHE, "NFS: %s: returns %d\n", | 596 | dfprintk(DIRCACHE, "NFS: %s: returns %d\n", |
523 | __func__, status); | 597 | __func__, status); |
524 | return status; | 598 | return status; |
525 | out_release: | 599 | out_release: |
526 | dir_page_release(desc); | 600 | cache_page_release(desc); |
527 | goto out; | 601 | goto out; |
528 | } | 602 | } |
529 | 603 | ||
@@ -537,7 +611,6 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir) | |||
537 | struct inode *inode = dentry->d_inode; | 611 | struct inode *inode = dentry->d_inode; |
538 | nfs_readdir_descriptor_t my_desc, | 612 | nfs_readdir_descriptor_t my_desc, |
539 | *desc = &my_desc; | 613 | *desc = &my_desc; |
540 | struct nfs_entry my_entry; | ||
541 | int res = -ENOMEM; | 614 | int res = -ENOMEM; |
542 | 615 | ||
543 | dfprintk(FILE, "NFS: readdir(%s/%s) starting at cookie %llu\n", | 616 | dfprintk(FILE, "NFS: readdir(%s/%s) starting at cookie %llu\n", |
@@ -558,26 +631,17 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir) | |||
558 | desc->decode = NFS_PROTO(inode)->decode_dirent; | 631 | desc->decode = NFS_PROTO(inode)->decode_dirent; |
559 | desc->plus = NFS_USE_READDIRPLUS(inode); | 632 | desc->plus = NFS_USE_READDIRPLUS(inode); |
560 | 633 | ||
561 | my_entry.cookie = my_entry.prev_cookie = 0; | ||
562 | my_entry.eof = 0; | ||
563 | my_entry.fh = nfs_alloc_fhandle(); | ||
564 | my_entry.fattr = nfs_alloc_fattr(); | ||
565 | if (my_entry.fh == NULL || my_entry.fattr == NULL) | ||
566 | goto out_alloc_failed; | ||
567 | |||
568 | desc->entry = &my_entry; | ||
569 | |||
570 | nfs_block_sillyrename(dentry); | 634 | nfs_block_sillyrename(dentry); |
571 | res = nfs_revalidate_mapping(inode, filp->f_mapping); | 635 | res = nfs_revalidate_mapping(inode, filp->f_mapping); |
572 | if (res < 0) | 636 | if (res < 0) |
573 | goto out; | 637 | goto out; |
574 | 638 | ||
575 | while(!desc->entry->eof) { | 639 | while (desc->eof != 1) { |
576 | res = readdir_search_pagecache(desc); | 640 | res = readdir_search_pagecache(desc); |
577 | 641 | ||
578 | if (res == -EBADCOOKIE) { | 642 | if (res == -EBADCOOKIE) { |
579 | /* This means either end of directory */ | 643 | /* This means either end of directory */ |
580 | if (*desc->dir_cookie && desc->entry->cookie != *desc->dir_cookie) { | 644 | if (*desc->dir_cookie && desc->eof == 0) { |
581 | /* Or that the server has 'lost' a cookie */ | 645 | /* Or that the server has 'lost' a cookie */ |
582 | res = uncached_readdir(desc, dirent, filldir); | 646 | res = uncached_readdir(desc, dirent, filldir); |
583 | if (res >= 0) | 647 | if (res >= 0) |
@@ -590,7 +654,7 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir) | |||
590 | clear_bit(NFS_INO_ADVISE_RDPLUS, &NFS_I(inode)->flags); | 654 | clear_bit(NFS_INO_ADVISE_RDPLUS, &NFS_I(inode)->flags); |
591 | nfs_zap_caches(inode); | 655 | nfs_zap_caches(inode); |
592 | desc->plus = 0; | 656 | desc->plus = 0; |
593 | desc->entry->eof = 0; | 657 | desc->eof = 0; |
594 | continue; | 658 | continue; |
595 | } | 659 | } |
596 | if (res < 0) | 660 | if (res < 0) |
@@ -606,9 +670,6 @@ out: | |||
606 | nfs_unblock_sillyrename(dentry); | 670 | nfs_unblock_sillyrename(dentry); |
607 | if (res > 0) | 671 | if (res > 0) |
608 | res = 0; | 672 | res = 0; |
609 | out_alloc_failed: | ||
610 | nfs_free_fattr(my_entry.fattr); | ||
611 | nfs_free_fhandle(my_entry.fh); | ||
612 | dfprintk(FILE, "NFS: readdir(%s/%s) returns %d\n", | 673 | dfprintk(FILE, "NFS: readdir(%s/%s) returns %d\n", |
613 | dentry->d_parent->d_name.name, dentry->d_name.name, | 674 | dentry->d_parent->d_name.name, dentry->d_name.name, |
614 | res); | 675 | res); |
@@ -1292,81 +1353,6 @@ out_err: | |||
1292 | 1353 | ||
1293 | #endif /* CONFIG_NFSV4 */ | 1354 | #endif /* CONFIG_NFSV4 */ |
1294 | 1355 | ||
1295 | static struct dentry *nfs_readdir_lookup(nfs_readdir_descriptor_t *desc) | ||
1296 | { | ||
1297 | struct dentry *parent = desc->file->f_path.dentry; | ||
1298 | struct inode *dir = parent->d_inode; | ||
1299 | struct nfs_entry *entry = desc->entry; | ||
1300 | struct dentry *dentry, *alias; | ||
1301 | struct qstr name = { | ||
1302 | .name = entry->name, | ||
1303 | .len = entry->len, | ||
1304 | }; | ||
1305 | struct inode *inode; | ||
1306 | unsigned long verf = nfs_save_change_attribute(dir); | ||
1307 | |||
1308 | switch (name.len) { | ||
1309 | case 2: | ||
1310 | if (name.name[0] == '.' && name.name[1] == '.') | ||
1311 | return dget_parent(parent); | ||
1312 | break; | ||
1313 | case 1: | ||
1314 | if (name.name[0] == '.') | ||
1315 | return dget(parent); | ||
1316 | } | ||
1317 | |||
1318 | spin_lock(&dir->i_lock); | ||
1319 | if (NFS_I(dir)->cache_validity & NFS_INO_INVALID_DATA) { | ||
1320 | spin_unlock(&dir->i_lock); | ||
1321 | return NULL; | ||
1322 | } | ||
1323 | spin_unlock(&dir->i_lock); | ||
1324 | |||
1325 | name.hash = full_name_hash(name.name, name.len); | ||
1326 | dentry = d_lookup(parent, &name); | ||
1327 | if (dentry != NULL) { | ||
1328 | /* Is this a positive dentry that matches the readdir info? */ | ||
1329 | if (dentry->d_inode != NULL && | ||
1330 | (NFS_FILEID(dentry->d_inode) == entry->ino || | ||
1331 | d_mountpoint(dentry))) { | ||
1332 | if (!desc->plus || entry->fh->size == 0) | ||
1333 | return dentry; | ||
1334 | if (nfs_compare_fh(NFS_FH(dentry->d_inode), | ||
1335 | entry->fh) == 0) | ||
1336 | goto out_renew; | ||
1337 | } | ||
1338 | /* No, so d_drop to allow one to be created */ | ||
1339 | d_drop(dentry); | ||
1340 | dput(dentry); | ||
1341 | } | ||
1342 | if (!desc->plus || !(entry->fattr->valid & NFS_ATTR_FATTR)) | ||
1343 | return NULL; | ||
1344 | if (name.len > NFS_SERVER(dir)->namelen) | ||
1345 | return NULL; | ||
1346 | /* Note: caller is already holding the dir->i_mutex! */ | ||
1347 | dentry = d_alloc(parent, &name); | ||
1348 | if (dentry == NULL) | ||
1349 | return NULL; | ||
1350 | dentry->d_op = NFS_PROTO(dir)->dentry_ops; | ||
1351 | inode = nfs_fhget(dentry->d_sb, entry->fh, entry->fattr); | ||
1352 | if (IS_ERR(inode)) { | ||
1353 | dput(dentry); | ||
1354 | return NULL; | ||
1355 | } | ||
1356 | |||
1357 | alias = d_materialise_unique(dentry, inode); | ||
1358 | if (alias != NULL) { | ||
1359 | dput(dentry); | ||
1360 | if (IS_ERR(alias)) | ||
1361 | return NULL; | ||
1362 | dentry = alias; | ||
1363 | } | ||
1364 | |||
1365 | out_renew: | ||
1366 | nfs_set_verifier(dentry, verf); | ||
1367 | return dentry; | ||
1368 | } | ||
1369 | |||
1370 | /* | 1356 | /* |
1371 | * Code common to create, mkdir, and mknod. | 1357 | * Code common to create, mkdir, and mknod. |
1372 | */ | 1358 | */ |