diff options
author | Sage Weil <sage@inktank.com> | 2012-07-31 14:27:36 -0400 |
---|---|---|
committer | Sage Weil <sage@inktank.com> | 2012-08-02 12:11:19 -0400 |
commit | 5ef50c3bec20060bc114f62d6503c5d86d70bdd7 (patch) | |
tree | 0777f7c9bef358fe7778e11874a6a750e0d2ceab /fs/ceph | |
parent | 1a9b4993b70fb1884716902774dc9025b457760d (diff) |
ceph: simplify+fix atomic_open
The initial ->atomic_open op was carried over from the old intent code,
which was incomplete and didn't really work. Replace it with a fresh
method. In particular:
* always attempt to do an atomic open+lookup, both for the create case
and for lookups of existing files.
* fix symlink handling by returning 1 to the VFS so that we can follow
the link to its destination. This fixes a longstanding ceph bug (#2392).
Signed-off-by: Sage Weil <sage@inktank.com>
Diffstat (limited to 'fs/ceph')
-rw-r--r-- | fs/ceph/dir.c | 38 | ||||
-rw-r--r-- | fs/ceph/file.c | 62 | ||||
-rw-r--r-- | fs/ceph/super.h | 6 |
3 files changed, 40 insertions, 66 deletions
diff --git a/fs/ceph/dir.c b/fs/ceph/dir.c index f391f1e7541..e5b77319c97 100644 --- a/fs/ceph/dir.c +++ b/fs/ceph/dir.c | |||
@@ -633,44 +633,6 @@ static struct dentry *ceph_lookup(struct inode *dir, struct dentry *dentry, | |||
633 | return dentry; | 633 | return dentry; |
634 | } | 634 | } |
635 | 635 | ||
636 | int ceph_atomic_open(struct inode *dir, struct dentry *dentry, | ||
637 | struct file *file, unsigned flags, umode_t mode, | ||
638 | int *opened) | ||
639 | { | ||
640 | int err; | ||
641 | struct dentry *res = NULL; | ||
642 | |||
643 | if (!(flags & O_CREAT)) { | ||
644 | if (dentry->d_name.len > NAME_MAX) | ||
645 | return -ENAMETOOLONG; | ||
646 | |||
647 | err = ceph_init_dentry(dentry); | ||
648 | if (err < 0) | ||
649 | return err; | ||
650 | |||
651 | return ceph_lookup_open(dir, dentry, file, flags, mode, opened); | ||
652 | } | ||
653 | |||
654 | if (d_unhashed(dentry)) { | ||
655 | res = ceph_lookup(dir, dentry, 0); | ||
656 | if (IS_ERR(res)) | ||
657 | return PTR_ERR(res); | ||
658 | |||
659 | if (res) | ||
660 | dentry = res; | ||
661 | } | ||
662 | |||
663 | /* We don't deal with positive dentries here */ | ||
664 | if (dentry->d_inode) | ||
665 | return finish_no_open(file, res); | ||
666 | |||
667 | *opened |= FILE_CREATED; | ||
668 | err = ceph_lookup_open(dir, dentry, file, flags, mode, opened); | ||
669 | dput(res); | ||
670 | |||
671 | return err; | ||
672 | } | ||
673 | |||
674 | /* | 636 | /* |
675 | * If we do a create but get no trace back from the MDS, follow up with | 637 | * If we do a create but get no trace back from the MDS, follow up with |
676 | * a lookup (the VFS expects us to link up the provided dentry). | 638 | * a lookup (the VFS expects us to link up the provided dentry). |
diff --git a/fs/ceph/file.c b/fs/ceph/file.c index 1b81d6c3187..ecebbc09bfc 100644 --- a/fs/ceph/file.c +++ b/fs/ceph/file.c | |||
@@ -4,6 +4,7 @@ | |||
4 | #include <linux/sched.h> | 4 | #include <linux/sched.h> |
5 | #include <linux/slab.h> | 5 | #include <linux/slab.h> |
6 | #include <linux/file.h> | 6 | #include <linux/file.h> |
7 | #include <linux/mount.h> | ||
7 | #include <linux/namei.h> | 8 | #include <linux/namei.h> |
8 | #include <linux/writeback.h> | 9 | #include <linux/writeback.h> |
9 | 10 | ||
@@ -106,9 +107,6 @@ static int ceph_init_file(struct inode *inode, struct file *file, int fmode) | |||
106 | } | 107 | } |
107 | 108 | ||
108 | /* | 109 | /* |
109 | * If the filp already has private_data, that means the file was | ||
110 | * already opened by intent during lookup, and we do nothing. | ||
111 | * | ||
112 | * If we already have the requisite capabilities, we can satisfy | 110 | * If we already have the requisite capabilities, we can satisfy |
113 | * the open request locally (no need to request new caps from the | 111 | * the open request locally (no need to request new caps from the |
114 | * MDS). We do, however, need to inform the MDS (asynchronously) | 112 | * MDS). We do, however, need to inform the MDS (asynchronously) |
@@ -207,24 +205,29 @@ out: | |||
207 | 205 | ||
208 | 206 | ||
209 | /* | 207 | /* |
210 | * Do a lookup + open with a single request. | 208 | * Do a lookup + open with a single request. If we get a non-existent |
211 | * | 209 | * file or symlink, return 1 so the VFS can retry. |
212 | * If this succeeds, but some subsequent check in the vfs | ||
213 | * may_open() fails, the struct *file gets cleaned up (i.e. | ||
214 | * ceph_release gets called). So fear not! | ||
215 | */ | 210 | */ |
216 | int ceph_lookup_open(struct inode *dir, struct dentry *dentry, | 211 | int ceph_atomic_open(struct inode *dir, struct dentry *dentry, |
217 | struct file *file, unsigned flags, umode_t mode, | 212 | struct file *file, unsigned flags, umode_t mode, |
218 | int *opened) | 213 | int *opened) |
219 | { | 214 | { |
220 | struct ceph_fs_client *fsc = ceph_sb_to_client(dir->i_sb); | 215 | struct ceph_fs_client *fsc = ceph_sb_to_client(dir->i_sb); |
221 | struct ceph_mds_client *mdsc = fsc->mdsc; | 216 | struct ceph_mds_client *mdsc = fsc->mdsc; |
222 | struct ceph_mds_request *req; | 217 | struct ceph_mds_request *req; |
223 | struct dentry *ret; | 218 | struct dentry *dn; |
224 | int err; | 219 | int err; |
225 | 220 | ||
226 | dout("ceph_lookup_open dentry %p '%.*s' flags %d mode 0%o\n", | 221 | dout("atomic_open %p dentry %p '%.*s' %s flags %d mode 0%o\n", |
227 | dentry, dentry->d_name.len, dentry->d_name.name, flags, mode); | 222 | dir, dentry, dentry->d_name.len, dentry->d_name.name, |
223 | d_unhashed(dentry) ? "unhashed" : "hashed", flags, mode); | ||
224 | |||
225 | if (dentry->d_name.len > NAME_MAX) | ||
226 | return -ENAMETOOLONG; | ||
227 | |||
228 | err = ceph_init_dentry(dentry); | ||
229 | if (err < 0) | ||
230 | return err; | ||
228 | 231 | ||
229 | /* do the open */ | 232 | /* do the open */ |
230 | req = prepare_open_request(dir->i_sb, flags, mode); | 233 | req = prepare_open_request(dir->i_sb, flags, mode); |
@@ -241,22 +244,31 @@ int ceph_lookup_open(struct inode *dir, struct dentry *dentry, | |||
241 | (flags & (O_CREAT|O_TRUNC)) ? dir : NULL, | 244 | (flags & (O_CREAT|O_TRUNC)) ? dir : NULL, |
242 | req); | 245 | req); |
243 | err = ceph_handle_snapdir(req, dentry, err); | 246 | err = ceph_handle_snapdir(req, dentry, err); |
244 | if (err) | 247 | if (err == 0 && (flags & O_CREAT) && !req->r_reply_info.head->is_dentry) |
245 | goto out; | ||
246 | if ((flags & O_CREAT) && !req->r_reply_info.head->is_dentry) | ||
247 | err = ceph_handle_notrace_create(dir, dentry); | 248 | err = ceph_handle_notrace_create(dir, dentry); |
248 | if (err) | ||
249 | goto out; | ||
250 | err = finish_open(file, req->r_dentry, ceph_open, opened); | ||
251 | out: | ||
252 | ret = ceph_finish_lookup(req, dentry, err); | ||
253 | ceph_mdsc_put_request(req); | ||
254 | dout("ceph_lookup_open result=%p\n", ret); | ||
255 | 249 | ||
256 | if (IS_ERR(ret)) | 250 | if (d_unhashed(dentry)) { |
257 | return PTR_ERR(ret); | 251 | dn = ceph_finish_lookup(req, dentry, err); |
252 | if (IS_ERR(dn)) | ||
253 | err = PTR_ERR(dn); | ||
254 | } else { | ||
255 | /* we were given a hashed negative dentry */ | ||
256 | dn = NULL; | ||
257 | } | ||
258 | if (err) | ||
259 | goto out_err; | ||
260 | if (dn || dentry->d_inode == NULL || S_ISLNK(dentry->d_inode->i_mode)) { | ||
261 | /* make vfs retry on splice, ENOENT, or symlink */ | ||
262 | dout("atomic_open finish_no_open on dn %p\n", dn); | ||
263 | err = finish_no_open(file, dn); | ||
264 | } else { | ||
265 | dout("atomic_open finish_open on dn %p\n", dn); | ||
266 | err = finish_open(file, dentry, ceph_open, opened); | ||
267 | } | ||
258 | 268 | ||
259 | dput(ret); | 269 | out_err: |
270 | ceph_mdsc_put_request(req); | ||
271 | dout("atomic_open result=%d\n", err); | ||
260 | return err; | 272 | return err; |
261 | } | 273 | } |
262 | 274 | ||
diff --git a/fs/ceph/super.h b/fs/ceph/super.h index ebc95cc652b..66ebe720e40 100644 --- a/fs/ceph/super.h +++ b/fs/ceph/super.h | |||
@@ -806,9 +806,9 @@ extern int ceph_copy_from_page_vector(struct page **pages, | |||
806 | loff_t off, size_t len); | 806 | loff_t off, size_t len); |
807 | extern struct page **ceph_alloc_page_vector(int num_pages, gfp_t flags); | 807 | extern struct page **ceph_alloc_page_vector(int num_pages, gfp_t flags); |
808 | extern int ceph_open(struct inode *inode, struct file *file); | 808 | extern int ceph_open(struct inode *inode, struct file *file); |
809 | extern int ceph_lookup_open(struct inode *dir, struct dentry *dentry, | 809 | extern int ceph_atomic_open(struct inode *dir, struct dentry *dentry, |
810 | struct file *od, unsigned flags, | 810 | struct file *file, unsigned flags, umode_t mode, |
811 | umode_t mode, int *opened); | 811 | int *opened); |
812 | extern int ceph_release(struct inode *inode, struct file *filp); | 812 | extern int ceph_release(struct inode *inode, struct file *filp); |
813 | 813 | ||
814 | /* dir.c */ | 814 | /* dir.c */ |