diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2011-07-26 16:38:50 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-07-26 16:38:50 -0400 |
commit | ba5b56cb3e3d2cab73d4fee9a022bb69462a8cd9 (patch) | |
tree | eda7ea059a41ae5d68e2ad5a36a87069187ef22a /fs/ceph/dir.c | |
parent | 243dd2809a5edd2e0e3e62781083aa44049af37d (diff) | |
parent | d79698da32b317e96216236f265a9b72b78ae568 (diff) |
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/sage/ceph-client
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/sage/ceph-client: (23 commits)
ceph: document unlocked d_parent accesses
ceph: explicitly reference rename old_dentry parent dir in request
ceph: document locking for ceph_set_dentry_offset
ceph: avoid d_parent in ceph_dentry_hash; fix ceph_encode_fh() hashing bug
ceph: protect d_parent access in ceph_d_revalidate
ceph: protect access to d_parent
ceph: handle racing calls to ceph_init_dentry
ceph: set dir complete frag after adding capability
rbd: set blk_queue request sizes to object size
ceph: set up readahead size when rsize is not passed
rbd: cancel watch request when releasing the device
ceph: ignore lease mask
ceph: fix ceph_lookup_open intent usage
ceph: only link open operations to directory unsafe list if O_CREAT|O_TRUNC
ceph: fix bad parent_inode calc in ceph_lookup_open
ceph: avoid carrying Fw cap during write into page cache
libceph: don't time out osd requests that haven't been received
ceph: report f_bfree based on kb_avail rather than diffing.
ceph: only queue capsnap if caps are dirty
ceph: fix snap writeback when racing with writes
...
Diffstat (limited to 'fs/ceph/dir.c')
-rw-r--r-- | fs/ceph/dir.c | 116 |
1 files changed, 73 insertions, 43 deletions
diff --git a/fs/ceph/dir.c b/fs/ceph/dir.c index 1065ac779840..382abc9a6a54 100644 --- a/fs/ceph/dir.c +++ b/fs/ceph/dir.c | |||
@@ -40,14 +40,6 @@ int ceph_init_dentry(struct dentry *dentry) | |||
40 | if (dentry->d_fsdata) | 40 | if (dentry->d_fsdata) |
41 | return 0; | 41 | return 0; |
42 | 42 | ||
43 | if (dentry->d_parent == NULL || /* nfs fh_to_dentry */ | ||
44 | ceph_snap(dentry->d_parent->d_inode) == CEPH_NOSNAP) | ||
45 | d_set_d_op(dentry, &ceph_dentry_ops); | ||
46 | else if (ceph_snap(dentry->d_parent->d_inode) == CEPH_SNAPDIR) | ||
47 | d_set_d_op(dentry, &ceph_snapdir_dentry_ops); | ||
48 | else | ||
49 | d_set_d_op(dentry, &ceph_snap_dentry_ops); | ||
50 | |||
51 | di = kmem_cache_alloc(ceph_dentry_cachep, GFP_NOFS | __GFP_ZERO); | 43 | di = kmem_cache_alloc(ceph_dentry_cachep, GFP_NOFS | __GFP_ZERO); |
52 | if (!di) | 44 | if (!di) |
53 | return -ENOMEM; /* oh well */ | 45 | return -ENOMEM; /* oh well */ |
@@ -58,16 +50,42 @@ int ceph_init_dentry(struct dentry *dentry) | |||
58 | kmem_cache_free(ceph_dentry_cachep, di); | 50 | kmem_cache_free(ceph_dentry_cachep, di); |
59 | goto out_unlock; | 51 | goto out_unlock; |
60 | } | 52 | } |
53 | |||
54 | if (dentry->d_parent == NULL || /* nfs fh_to_dentry */ | ||
55 | ceph_snap(dentry->d_parent->d_inode) == CEPH_NOSNAP) | ||
56 | d_set_d_op(dentry, &ceph_dentry_ops); | ||
57 | else if (ceph_snap(dentry->d_parent->d_inode) == CEPH_SNAPDIR) | ||
58 | d_set_d_op(dentry, &ceph_snapdir_dentry_ops); | ||
59 | else | ||
60 | d_set_d_op(dentry, &ceph_snap_dentry_ops); | ||
61 | |||
61 | di->dentry = dentry; | 62 | di->dentry = dentry; |
62 | di->lease_session = NULL; | 63 | di->lease_session = NULL; |
63 | dentry->d_fsdata = di; | ||
64 | dentry->d_time = jiffies; | 64 | dentry->d_time = jiffies; |
65 | /* avoid reordering d_fsdata setup so that the check above is safe */ | ||
66 | smp_mb(); | ||
67 | dentry->d_fsdata = di; | ||
65 | ceph_dentry_lru_add(dentry); | 68 | ceph_dentry_lru_add(dentry); |
66 | out_unlock: | 69 | out_unlock: |
67 | spin_unlock(&dentry->d_lock); | 70 | spin_unlock(&dentry->d_lock); |
68 | return 0; | 71 | return 0; |
69 | } | 72 | } |
70 | 73 | ||
74 | struct inode *ceph_get_dentry_parent_inode(struct dentry *dentry) | ||
75 | { | ||
76 | struct inode *inode = NULL; | ||
77 | |||
78 | if (!dentry) | ||
79 | return NULL; | ||
80 | |||
81 | spin_lock(&dentry->d_lock); | ||
82 | if (dentry->d_parent) { | ||
83 | inode = dentry->d_parent->d_inode; | ||
84 | ihold(inode); | ||
85 | } | ||
86 | spin_unlock(&dentry->d_lock); | ||
87 | return inode; | ||
88 | } | ||
71 | 89 | ||
72 | 90 | ||
73 | /* | 91 | /* |
@@ -133,7 +151,7 @@ more: | |||
133 | d_unhashed(dentry) ? "!hashed" : "hashed", | 151 | d_unhashed(dentry) ? "!hashed" : "hashed", |
134 | parent->d_subdirs.prev, parent->d_subdirs.next); | 152 | parent->d_subdirs.prev, parent->d_subdirs.next); |
135 | if (p == &parent->d_subdirs) { | 153 | if (p == &parent->d_subdirs) { |
136 | fi->at_end = 1; | 154 | fi->flags |= CEPH_F_ATEND; |
137 | goto out_unlock; | 155 | goto out_unlock; |
138 | } | 156 | } |
139 | spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED); | 157 | spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED); |
@@ -234,7 +252,7 @@ static int ceph_readdir(struct file *filp, void *dirent, filldir_t filldir) | |||
234 | const int max_bytes = fsc->mount_options->max_readdir_bytes; | 252 | const int max_bytes = fsc->mount_options->max_readdir_bytes; |
235 | 253 | ||
236 | dout("readdir %p filp %p frag %u off %u\n", inode, filp, frag, off); | 254 | dout("readdir %p filp %p frag %u off %u\n", inode, filp, frag, off); |
237 | if (fi->at_end) | 255 | if (fi->flags & CEPH_F_ATEND) |
238 | return 0; | 256 | return 0; |
239 | 257 | ||
240 | /* always start with . and .. */ | 258 | /* always start with . and .. */ |
@@ -403,7 +421,7 @@ more: | |||
403 | dout("readdir next frag is %x\n", frag); | 421 | dout("readdir next frag is %x\n", frag); |
404 | goto more; | 422 | goto more; |
405 | } | 423 | } |
406 | fi->at_end = 1; | 424 | fi->flags |= CEPH_F_ATEND; |
407 | 425 | ||
408 | /* | 426 | /* |
409 | * if dir_release_count still matches the dir, no dentries | 427 | * if dir_release_count still matches the dir, no dentries |
@@ -435,7 +453,7 @@ static void reset_readdir(struct ceph_file_info *fi) | |||
435 | dput(fi->dentry); | 453 | dput(fi->dentry); |
436 | fi->dentry = NULL; | 454 | fi->dentry = NULL; |
437 | } | 455 | } |
438 | fi->at_end = 0; | 456 | fi->flags &= ~CEPH_F_ATEND; |
439 | } | 457 | } |
440 | 458 | ||
441 | static loff_t ceph_dir_llseek(struct file *file, loff_t offset, int origin) | 459 | static loff_t ceph_dir_llseek(struct file *file, loff_t offset, int origin) |
@@ -463,7 +481,7 @@ static loff_t ceph_dir_llseek(struct file *file, loff_t offset, int origin) | |||
463 | if (offset != file->f_pos) { | 481 | if (offset != file->f_pos) { |
464 | file->f_pos = offset; | 482 | file->f_pos = offset; |
465 | file->f_version = 0; | 483 | file->f_version = 0; |
466 | fi->at_end = 0; | 484 | fi->flags &= ~CEPH_F_ATEND; |
467 | } | 485 | } |
468 | retval = offset; | 486 | retval = offset; |
469 | 487 | ||
@@ -488,21 +506,13 @@ out: | |||
488 | } | 506 | } |
489 | 507 | ||
490 | /* | 508 | /* |
491 | * Process result of a lookup/open request. | 509 | * Handle lookups for the hidden .snap directory. |
492 | * | ||
493 | * Mainly, make sure we return the final req->r_dentry (if it already | ||
494 | * existed) in place of the original VFS-provided dentry when they | ||
495 | * differ. | ||
496 | * | ||
497 | * Gracefully handle the case where the MDS replies with -ENOENT and | ||
498 | * no trace (which it may do, at its discretion, e.g., if it doesn't | ||
499 | * care to issue a lease on the negative dentry). | ||
500 | */ | 510 | */ |
501 | struct dentry *ceph_finish_lookup(struct ceph_mds_request *req, | 511 | int ceph_handle_snapdir(struct ceph_mds_request *req, |
502 | struct dentry *dentry, int err) | 512 | struct dentry *dentry, int err) |
503 | { | 513 | { |
504 | struct ceph_fs_client *fsc = ceph_sb_to_client(dentry->d_sb); | 514 | struct ceph_fs_client *fsc = ceph_sb_to_client(dentry->d_sb); |
505 | struct inode *parent = dentry->d_parent->d_inode; | 515 | struct inode *parent = dentry->d_parent->d_inode; /* we hold i_mutex */ |
506 | 516 | ||
507 | /* .snap dir? */ | 517 | /* .snap dir? */ |
508 | if (err == -ENOENT && | 518 | if (err == -ENOENT && |
@@ -516,7 +526,23 @@ struct dentry *ceph_finish_lookup(struct ceph_mds_request *req, | |||
516 | d_add(dentry, inode); | 526 | d_add(dentry, inode); |
517 | err = 0; | 527 | err = 0; |
518 | } | 528 | } |
529 | return err; | ||
530 | } | ||
519 | 531 | ||
532 | /* | ||
533 | * Figure out final result of a lookup/open request. | ||
534 | * | ||
535 | * Mainly, make sure we return the final req->r_dentry (if it already | ||
536 | * existed) in place of the original VFS-provided dentry when they | ||
537 | * differ. | ||
538 | * | ||
539 | * Gracefully handle the case where the MDS replies with -ENOENT and | ||
540 | * no trace (which it may do, at its discretion, e.g., if it doesn't | ||
541 | * care to issue a lease on the negative dentry). | ||
542 | */ | ||
543 | struct dentry *ceph_finish_lookup(struct ceph_mds_request *req, | ||
544 | struct dentry *dentry, int err) | ||
545 | { | ||
520 | if (err == -ENOENT) { | 546 | if (err == -ENOENT) { |
521 | /* no trace? */ | 547 | /* no trace? */ |
522 | err = 0; | 548 | err = 0; |
@@ -610,6 +636,7 @@ static struct dentry *ceph_lookup(struct inode *dir, struct dentry *dentry, | |||
610 | req->r_args.getattr.mask = cpu_to_le32(CEPH_STAT_CAP_INODE); | 636 | req->r_args.getattr.mask = cpu_to_le32(CEPH_STAT_CAP_INODE); |
611 | req->r_locked_dir = dir; | 637 | req->r_locked_dir = dir; |
612 | err = ceph_mdsc_do_request(mdsc, NULL, req); | 638 | err = ceph_mdsc_do_request(mdsc, NULL, req); |
639 | err = ceph_handle_snapdir(req, dentry, err); | ||
613 | dentry = ceph_finish_lookup(req, dentry, err); | 640 | dentry = ceph_finish_lookup(req, dentry, err); |
614 | ceph_mdsc_put_request(req); /* will dput(dentry) */ | 641 | ceph_mdsc_put_request(req); /* will dput(dentry) */ |
615 | dout("lookup result=%p\n", dentry); | 642 | dout("lookup result=%p\n", dentry); |
@@ -789,6 +816,7 @@ static int ceph_link(struct dentry *old_dentry, struct inode *dir, | |||
789 | req->r_dentry = dget(dentry); | 816 | req->r_dentry = dget(dentry); |
790 | req->r_num_caps = 2; | 817 | req->r_num_caps = 2; |
791 | req->r_old_dentry = dget(old_dentry); /* or inode? hrm. */ | 818 | req->r_old_dentry = dget(old_dentry); /* or inode? hrm. */ |
819 | req->r_old_dentry_dir = ceph_get_dentry_parent_inode(old_dentry); | ||
792 | req->r_locked_dir = dir; | 820 | req->r_locked_dir = dir; |
793 | req->r_dentry_drop = CEPH_CAP_FILE_SHARED; | 821 | req->r_dentry_drop = CEPH_CAP_FILE_SHARED; |
794 | req->r_dentry_unless = CEPH_CAP_FILE_EXCL; | 822 | req->r_dentry_unless = CEPH_CAP_FILE_EXCL; |
@@ -887,6 +915,7 @@ static int ceph_rename(struct inode *old_dir, struct dentry *old_dentry, | |||
887 | req->r_dentry = dget(new_dentry); | 915 | req->r_dentry = dget(new_dentry); |
888 | req->r_num_caps = 2; | 916 | req->r_num_caps = 2; |
889 | req->r_old_dentry = dget(old_dentry); | 917 | req->r_old_dentry = dget(old_dentry); |
918 | req->r_old_dentry_dir = ceph_get_dentry_parent_inode(old_dentry); | ||
890 | req->r_locked_dir = new_dir; | 919 | req->r_locked_dir = new_dir; |
891 | req->r_old_dentry_drop = CEPH_CAP_FILE_SHARED; | 920 | req->r_old_dentry_drop = CEPH_CAP_FILE_SHARED; |
892 | req->r_old_dentry_unless = CEPH_CAP_FILE_EXCL; | 921 | req->r_old_dentry_unless = CEPH_CAP_FILE_EXCL; |
@@ -1002,36 +1031,38 @@ static int dir_lease_is_valid(struct inode *dir, struct dentry *dentry) | |||
1002 | */ | 1031 | */ |
1003 | static int ceph_d_revalidate(struct dentry *dentry, struct nameidata *nd) | 1032 | static int ceph_d_revalidate(struct dentry *dentry, struct nameidata *nd) |
1004 | { | 1033 | { |
1034 | int valid = 0; | ||
1005 | struct inode *dir; | 1035 | struct inode *dir; |
1006 | 1036 | ||
1007 | if (nd && nd->flags & LOOKUP_RCU) | 1037 | if (nd && nd->flags & LOOKUP_RCU) |
1008 | return -ECHILD; | 1038 | return -ECHILD; |
1009 | 1039 | ||
1010 | dir = dentry->d_parent->d_inode; | ||
1011 | |||
1012 | dout("d_revalidate %p '%.*s' inode %p offset %lld\n", dentry, | 1040 | dout("d_revalidate %p '%.*s' inode %p offset %lld\n", dentry, |
1013 | dentry->d_name.len, dentry->d_name.name, dentry->d_inode, | 1041 | dentry->d_name.len, dentry->d_name.name, dentry->d_inode, |
1014 | ceph_dentry(dentry)->offset); | 1042 | ceph_dentry(dentry)->offset); |
1015 | 1043 | ||
1044 | dir = ceph_get_dentry_parent_inode(dentry); | ||
1045 | |||
1016 | /* always trust cached snapped dentries, snapdir dentry */ | 1046 | /* always trust cached snapped dentries, snapdir dentry */ |
1017 | if (ceph_snap(dir) != CEPH_NOSNAP) { | 1047 | if (ceph_snap(dir) != CEPH_NOSNAP) { |
1018 | dout("d_revalidate %p '%.*s' inode %p is SNAPPED\n", dentry, | 1048 | dout("d_revalidate %p '%.*s' inode %p is SNAPPED\n", dentry, |
1019 | dentry->d_name.len, dentry->d_name.name, dentry->d_inode); | 1049 | dentry->d_name.len, dentry->d_name.name, dentry->d_inode); |
1020 | goto out_touch; | 1050 | valid = 1; |
1051 | } else if (dentry->d_inode && | ||
1052 | ceph_snap(dentry->d_inode) == CEPH_SNAPDIR) { | ||
1053 | valid = 1; | ||
1054 | } else if (dentry_lease_is_valid(dentry) || | ||
1055 | dir_lease_is_valid(dir, dentry)) { | ||
1056 | valid = 1; | ||
1021 | } | 1057 | } |
1022 | if (dentry->d_inode && ceph_snap(dentry->d_inode) == CEPH_SNAPDIR) | ||
1023 | goto out_touch; | ||
1024 | |||
1025 | if (dentry_lease_is_valid(dentry) || | ||
1026 | dir_lease_is_valid(dir, dentry)) | ||
1027 | goto out_touch; | ||
1028 | 1058 | ||
1029 | dout("d_revalidate %p invalid\n", dentry); | 1059 | dout("d_revalidate %p %s\n", dentry, valid ? "valid" : "invalid"); |
1030 | d_drop(dentry); | 1060 | if (valid) |
1031 | return 0; | 1061 | ceph_dentry_lru_touch(dentry); |
1032 | out_touch: | 1062 | else |
1033 | ceph_dentry_lru_touch(dentry); | 1063 | d_drop(dentry); |
1034 | return 1; | 1064 | iput(dir); |
1065 | return valid; | ||
1035 | } | 1066 | } |
1036 | 1067 | ||
1037 | /* | 1068 | /* |
@@ -1228,9 +1259,8 @@ void ceph_dentry_lru_del(struct dentry *dn) | |||
1228 | * Return name hash for a given dentry. This is dependent on | 1259 | * Return name hash for a given dentry. This is dependent on |
1229 | * the parent directory's hash function. | 1260 | * the parent directory's hash function. |
1230 | */ | 1261 | */ |
1231 | unsigned ceph_dentry_hash(struct dentry *dn) | 1262 | unsigned ceph_dentry_hash(struct inode *dir, struct dentry *dn) |
1232 | { | 1263 | { |
1233 | struct inode *dir = dn->d_parent->d_inode; | ||
1234 | struct ceph_inode_info *dci = ceph_inode(dir); | 1264 | struct ceph_inode_info *dci = ceph_inode(dir); |
1235 | 1265 | ||
1236 | switch (dci->i_dir_layout.dl_dir_hash) { | 1266 | switch (dci->i_dir_layout.dl_dir_hash) { |