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 | |
| 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>
| -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 | */ |
