1376 lines · 36.9 KB · C
RawDownload
#include <linux/ceph/ceph_debug.h> #include <linux/spinlock.h>#include <linux/fs_struct.h>#include <linux/namei.h>#include <linux/slab.h>#include <linux/sched.h> #include "super.h"#include "mds_client.h" /* * Directory operations: readdir, lookup, create, link, unlink, * rename, etc. */ /* * Ceph MDS operations are specified in terms of a base ino and * relative path.  Thus, the client can specify an operation on a * specific inode (e.g., a getattr due to fstat(2)), or as a path * relative to, say, the root directory. * * Normally, we limit ourselves to strict inode ops (no path component) * or dentry operations (a single path component relative to an ino).  The * exception to this is open_root_dentry(), which will open the mount * point by name. */ const struct inode_operations ceph_dir_iops;const struct file_operations ceph_dir_fops;const struct dentry_operations ceph_dentry_ops; /* * Initialize ceph dentry state. */int ceph_init_dentry(struct dentry *dentry){	struct ceph_dentry_info *di; 	if (dentry->d_fsdata)		return 0; 	di = kmem_cache_alloc(ceph_dentry_cachep, GFP_NOFS | __GFP_ZERO);	if (!di)		return -ENOMEM;          /* oh well */ 	spin_lock(&dentry->d_lock);	if (dentry->d_fsdata) {		/* lost a race */		kmem_cache_free(ceph_dentry_cachep, di);		goto out_unlock;	} 	if (dentry->d_parent == NULL ||   /* nfs fh_to_dentry */	    ceph_snap(dentry->d_parent->d_inode) == CEPH_NOSNAP)		d_set_d_op(dentry, &ceph_dentry_ops);	else if (ceph_snap(dentry->d_parent->d_inode) == CEPH_SNAPDIR)		d_set_d_op(dentry, &ceph_snapdir_dentry_ops);	else		d_set_d_op(dentry, &ceph_snap_dentry_ops); 	di->dentry = dentry;	di->lease_session = NULL;	dentry->d_time = jiffies;	/* avoid reordering d_fsdata setup so that the check above is safe */	smp_mb();	dentry->d_fsdata = di;	ceph_dentry_lru_add(dentry);out_unlock:	spin_unlock(&dentry->d_lock);	return 0;} struct inode *ceph_get_dentry_parent_inode(struct dentry *dentry){	struct inode *inode = NULL; 	if (!dentry)		return NULL; 	spin_lock(&dentry->d_lock);	if (dentry->d_parent) {		inode = dentry->d_parent->d_inode;		ihold(inode);	}	spin_unlock(&dentry->d_lock);	return inode;}  /* * for readdir, we encode the directory frag and offset within that * frag into f_pos. */static unsigned fpos_frag(loff_t p){	return p >> 32;}static unsigned fpos_off(loff_t p){	return p & 0xffffffff;} /* * When possible, we try to satisfy a readdir by peeking at the * dcache.  We make this work by carefully ordering dentries on * d_u.d_child when we initially get results back from the MDS, and * falling back to a "normal" sync readdir if any dentries in the dir * are dropped. * * D_COMPLETE tells indicates we have all dentries in the dir.  It is * defined IFF we hold CEPH_CAP_FILE_SHARED (which will be revoked by * the MDS if/when the directory is modified). */static int __dcache_readdir(struct file *filp,			    void *dirent, filldir_t filldir){	struct ceph_file_info *fi = filp->private_data;	struct dentry *parent = filp->f_dentry;	struct inode *dir = parent->d_inode;	struct list_head *p;	struct dentry *dentry, *last;	struct ceph_dentry_info *di;	int err = 0; 	/* claim ref on last dentry we returned */	last = fi->dentry;	fi->dentry = NULL; 	dout("__dcache_readdir %p at %llu (last %p)\n", dir, filp->f_pos,	     last); 	spin_lock(&parent->d_lock); 	/* start at beginning? */	if (filp->f_pos == 2 || last == NULL ||	    filp->f_pos < ceph_dentry(last)->offset) {		if (list_empty(&parent->d_subdirs))			goto out_unlock;		p = parent->d_subdirs.prev;		dout(" initial p %p/%p\n", p->prev, p->next);	} else {		p = last->d_u.d_child.prev;	} more:	dentry = list_entry(p, struct dentry, d_u.d_child);	di = ceph_dentry(dentry);	while (1) {		dout(" p %p/%p %s d_subdirs %p/%p\n", p->prev, p->next,		     d_unhashed(dentry) ? "!hashed" : "hashed",		     parent->d_subdirs.prev, parent->d_subdirs.next);		if (p == &parent->d_subdirs) {			fi->flags |= CEPH_F_ATEND;			goto out_unlock;		}		spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED);		if (!d_unhashed(dentry) && dentry->d_inode &&		    ceph_snap(dentry->d_inode) != CEPH_SNAPDIR &&		    ceph_ino(dentry->d_inode) != CEPH_INO_CEPH &&		    filp->f_pos <= di->offset)			break;		dout(" skipping %p %.*s at %llu (%llu)%s%s\n", dentry,		     dentry->d_name.len, dentry->d_name.name, di->offset,		     filp->f_pos, d_unhashed(dentry) ? " unhashed" : "",		     !dentry->d_inode ? " null" : "");		spin_unlock(&dentry->d_lock);		p = p->prev;		dentry = list_entry(p, struct dentry, d_u.d_child);		di = ceph_dentry(dentry);	} 	dget_dlock(dentry);	spin_unlock(&dentry->d_lock);	spin_unlock(&parent->d_lock); 	dout(" %llu (%llu) dentry %p %.*s %p\n", di->offset, filp->f_pos,	     dentry, dentry->d_name.len, dentry->d_name.name, dentry->d_inode);	filp->f_pos = di->offset;	err = filldir(dirent, dentry->d_name.name,		      dentry->d_name.len, di->offset,		      ceph_translate_ino(dentry->d_sb, dentry->d_inode->i_ino),		      dentry->d_inode->i_mode >> 12); 	if (last) {		if (err < 0) {			/* remember our position */			fi->dentry = last;			fi->next_offset = di->offset;		} else {			dput(last);		}	}	last = dentry; 	if (err < 0)		goto out; 	filp->f_pos++; 	/* make sure a dentry wasn't dropped while we didn't have parent lock */	if (!ceph_dir_test_complete(dir)) {		dout(" lost D_COMPLETE on %p; falling back to mds\n", dir);		err = -EAGAIN;		goto out;	} 	spin_lock(&parent->d_lock);	p = p->prev;	/* advance to next dentry */	goto more; out_unlock:	spin_unlock(&parent->d_lock);out:	if (last)		dput(last);	return err;} /* * make note of the last dentry we read, so we can * continue at the same lexicographical point, * regardless of what dir changes take place on the * server. */static int note_last_dentry(struct ceph_file_info *fi, const char *name,			    int len){	kfree(fi->last_name);	fi->last_name = kmalloc(len+1, GFP_NOFS);	if (!fi->last_name)		return -ENOMEM;	memcpy(fi->last_name, name, len);	fi->last_name[len] = 0;	dout("note_last_dentry '%s'\n", fi->last_name);	return 0;} static int ceph_readdir(struct file *filp, void *dirent, filldir_t filldir){	struct ceph_file_info *fi = filp->private_data;	struct inode *inode = filp->f_dentry->d_inode;	struct ceph_inode_info *ci = ceph_inode(inode);	struct ceph_fs_client *fsc = ceph_inode_to_client(inode);	struct ceph_mds_client *mdsc = fsc->mdsc;	unsigned frag = fpos_frag(filp->f_pos);	int off = fpos_off(filp->f_pos);	int err;	u32 ftype;	struct ceph_mds_reply_info_parsed *rinfo;	const int max_entries = fsc->mount_options->max_readdir;	const int max_bytes = fsc->mount_options->max_readdir_bytes; 	dout("readdir %p filp %p frag %u off %u\n", inode, filp, frag, off);	if (fi->flags & CEPH_F_ATEND)		return 0; 	/* always start with . and .. */	if (filp->f_pos == 0) {		/* note dir version at start of readdir so we can tell		 * if any dentries get dropped */		fi->dir_release_count = ci->i_release_count; 		dout("readdir off 0 -> '.'\n");		if (filldir(dirent, ".", 1, ceph_make_fpos(0, 0),			    ceph_translate_ino(inode->i_sb, inode->i_ino),			    inode->i_mode >> 12) < 0)			return 0;		filp->f_pos = 1;		off = 1;	}	if (filp->f_pos == 1) {		ino_t ino = parent_ino(filp->f_dentry);		dout("readdir off 1 -> '..'\n");		if (filldir(dirent, "..", 2, ceph_make_fpos(0, 1),			    ceph_translate_ino(inode->i_sb, ino),			    inode->i_mode >> 12) < 0)			return 0;		filp->f_pos = 2;		off = 2;	} 	/* can we use the dcache? */	spin_lock(&ci->i_ceph_lock);	if ((filp->f_pos == 2 || fi->dentry) &&	    !ceph_test_mount_opt(fsc, NOASYNCREADDIR) &&	    ceph_snap(inode) != CEPH_SNAPDIR &&	    ceph_dir_test_complete(inode) &&	    __ceph_caps_issued_mask(ci, CEPH_CAP_FILE_SHARED, 1)) {		spin_unlock(&ci->i_ceph_lock);		err = __dcache_readdir(filp, dirent, filldir);		if (err != -EAGAIN)			return err;	} else {		spin_unlock(&ci->i_ceph_lock);	}	if (fi->dentry) {		err = note_last_dentry(fi, fi->dentry->d_name.name,				       fi->dentry->d_name.len);		if (err)			return err;		dput(fi->dentry);		fi->dentry = NULL;	} 	/* proceed with a normal readdir */ more:	/* do we have the correct frag content buffered? */	if (fi->frag != frag || fi->last_readdir == NULL) {		struct ceph_mds_request *req;		int op = ceph_snap(inode) == CEPH_SNAPDIR ?			CEPH_MDS_OP_LSSNAP : CEPH_MDS_OP_READDIR; 		/* discard old result, if any */		if (fi->last_readdir) {			ceph_mdsc_put_request(fi->last_readdir);			fi->last_readdir = NULL;		} 		/* requery frag tree, as the frag topology may have changed */		frag = ceph_choose_frag(ceph_inode(inode), frag, NULL, NULL); 		dout("readdir fetching %llx.%llx frag %x offset '%s'\n",		     ceph_vinop(inode), frag, fi->last_name);		req = ceph_mdsc_create_request(mdsc, op, USE_AUTH_MDS);		if (IS_ERR(req))			return PTR_ERR(req);		req->r_inode = inode;		ihold(inode);		req->r_dentry = dget(filp->f_dentry);		/* hints to request -> mds selection code */		req->r_direct_mode = USE_AUTH_MDS;		req->r_direct_hash = ceph_frag_value(frag);		req->r_direct_is_hash = true;		req->r_path2 = kstrdup(fi->last_name, GFP_NOFS);		req->r_readdir_offset = fi->next_offset;		req->r_args.readdir.frag = cpu_to_le32(frag);		req->r_args.readdir.max_entries = cpu_to_le32(max_entries);		req->r_args.readdir.max_bytes = cpu_to_le32(max_bytes);		req->r_num_caps = max_entries + 1;		err = ceph_mdsc_do_request(mdsc, NULL, req);		if (err < 0) {			ceph_mdsc_put_request(req);			return err;		}		dout("readdir got and parsed readdir result=%d"		     " on frag %x, end=%d, complete=%d\n", err, frag,		     (int)req->r_reply_info.dir_end,		     (int)req->r_reply_info.dir_complete); 		if (!req->r_did_prepopulate) {			dout("readdir !did_prepopulate");			fi->dir_release_count--;    /* preclude D_COMPLETE */		} 		/* note next offset and last dentry name */		fi->offset = fi->next_offset;		fi->last_readdir = req; 		if (req->r_reply_info.dir_end) {			kfree(fi->last_name);			fi->last_name = NULL;			if (ceph_frag_is_rightmost(frag))				fi->next_offset = 2;			else				fi->next_offset = 0;		} else {			rinfo = &req->r_reply_info;			err = note_last_dentry(fi,				       rinfo->dir_dname[rinfo->dir_nr-1],				       rinfo->dir_dname_len[rinfo->dir_nr-1]);			if (err)				return err;			fi->next_offset += rinfo->dir_nr;		}	} 	rinfo = &fi->last_readdir->r_reply_info;	dout("readdir frag %x num %d off %d chunkoff %d\n", frag,	     rinfo->dir_nr, off, fi->offset);	while (off >= fi->offset && off - fi->offset < rinfo->dir_nr) {		u64 pos = ceph_make_fpos(frag, off);		struct ceph_mds_reply_inode *in =			rinfo->dir_in[off - fi->offset].in;		struct ceph_vino vino;		ino_t ino; 		dout("readdir off %d (%d/%d) -> %lld '%.*s' %p\n",		     off, off - fi->offset, rinfo->dir_nr, pos,		     rinfo->dir_dname_len[off - fi->offset],		     rinfo->dir_dname[off - fi->offset], in);		BUG_ON(!in);		ftype = le32_to_cpu(in->mode) >> 12;		vino.ino = le64_to_cpu(in->ino);		vino.snap = le64_to_cpu(in->snapid);		ino = ceph_vino_to_ino(vino);		if (filldir(dirent,			    rinfo->dir_dname[off - fi->offset],			    rinfo->dir_dname_len[off - fi->offset],			    pos,			    ceph_translate_ino(inode->i_sb, ino), ftype) < 0) {			dout("filldir stopping us...\n");			return 0;		}		off++;		filp->f_pos = pos + 1;	} 	if (fi->last_name) {		ceph_mdsc_put_request(fi->last_readdir);		fi->last_readdir = NULL;		goto more;	} 	/* more frags? */	if (!ceph_frag_is_rightmost(frag)) {		frag = ceph_frag_next(frag);		off = 0;		filp->f_pos = ceph_make_fpos(frag, off);		dout("readdir next frag is %x\n", frag);		goto more;	}	fi->flags |= CEPH_F_ATEND; 	/*	 * if dir_release_count still matches the dir, no dentries	 * were released during the whole readdir, and we should have	 * the complete dir contents in our cache.	 */	spin_lock(&ci->i_ceph_lock);	if (ci->i_release_count == fi->dir_release_count) {		ceph_dir_set_complete(inode);		ci->i_max_offset = filp->f_pos;	}	spin_unlock(&ci->i_ceph_lock); 	dout("readdir %p filp %p done.\n", inode, filp);	return 0;} static void reset_readdir(struct ceph_file_info *fi){	if (fi->last_readdir) {		ceph_mdsc_put_request(fi->last_readdir);		fi->last_readdir = NULL;	}	kfree(fi->last_name);	fi->last_name = NULL;	fi->next_offset = 2;  /* compensate for . and .. */	if (fi->dentry) {		dput(fi->dentry);		fi->dentry = NULL;	}	fi->flags &= ~CEPH_F_ATEND;} static loff_t ceph_dir_llseek(struct file *file, loff_t offset, int origin){	struct ceph_file_info *fi = file->private_data;	struct inode *inode = file->f_mapping->host;	loff_t old_offset = offset;	loff_t retval; 	mutex_lock(&inode->i_mutex);	retval = -EINVAL;	switch (origin) {	case SEEK_END:		offset += inode->i_size + 2;   /* FIXME */		break;	case SEEK_CUR:		offset += file->f_pos;	case SEEK_SET:		break;	default:		goto out;	} 	if (offset >= 0 && offset <= inode->i_sb->s_maxbytes) {		if (offset != file->f_pos) {			file->f_pos = offset;			file->f_version = 0;			fi->flags &= ~CEPH_F_ATEND;		}		retval = offset; 		/*		 * discard buffered readdir content on seekdir(0), or		 * seek to new frag, or seek prior to current chunk.		 */		if (offset == 0 ||		    fpos_frag(offset) != fpos_frag(old_offset) ||		    fpos_off(offset) < fi->offset) {			dout("dir_llseek dropping %p content\n", file);			reset_readdir(fi);		} 		/* bump dir_release_count if we did a forward seek */		if (offset > old_offset)			fi->dir_release_count--;

Lines 1–500 of 1376.