aboutsummaryrefslogtreecommitdiffstats
path: root/fs/ceph
diff options
context:
space:
mode:
authorSage Weil <sage@inktank.com>2012-07-31 14:27:36 -0400
committerSage Weil <sage@inktank.com>2012-08-02 12:11:19 -0400
commit5ef50c3bec20060bc114f62d6503c5d86d70bdd7 (patch)
tree0777f7c9bef358fe7778e11874a6a750e0d2ceab /fs/ceph
parent1a9b4993b70fb1884716902774dc9025b457760d (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.c38
-rw-r--r--fs/ceph/file.c62
-rw-r--r--fs/ceph/super.h6
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
636int 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 */
216int ceph_lookup_open(struct inode *dir, struct dentry *dentry, 211int 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);
251out:
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); 269out_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);
807extern struct page **ceph_alloc_page_vector(int num_pages, gfp_t flags); 807extern struct page **ceph_alloc_page_vector(int num_pages, gfp_t flags);
808extern int ceph_open(struct inode *inode, struct file *file); 808extern int ceph_open(struct inode *inode, struct file *file);
809extern int ceph_lookup_open(struct inode *dir, struct dentry *dentry, 809extern 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);
812extern int ceph_release(struct inode *inode, struct file *filp); 812extern int ceph_release(struct inode *inode, struct file *filp);
813 813
814/* dir.c */ 814/* dir.c */