Commit 8113a85f authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag 'ceph-for-5.1-rc7' of git://github.com/ceph/ceph-client

Pull ceph fixes from Ilya Dryomov:
 "dentry name handling fixes from Jeff and a memory leak fix from Zheng.

  Both are old issues, marked for stable"

* tag 'ceph-for-5.1-rc7' of git://github.com/ceph/ceph-client:
  ceph: fix ci->i_head_snapc leak
  ceph: handle the case where a dentry has been renamed on outstanding req
  ceph: ensure d_name stability in ceph_dentry_hash()
  ceph: only use d_name directly when parent is locked
parents f6f3e747 37659182
Loading
Loading
Loading
Loading
+5 −1
Original line number Diff line number Diff line
@@ -1766,6 +1766,7 @@ static ssize_t ceph_read_dir(struct file *file, char __user *buf, size_t size,
unsigned ceph_dentry_hash(struct inode *dir, struct dentry *dn)
{
	struct ceph_inode_info *dci = ceph_inode(dir);
	unsigned hash;

	switch (dci->i_dir_layout.dl_dir_hash) {
	case 0:	/* for backward compat */
@@ -1773,8 +1774,11 @@ unsigned ceph_dentry_hash(struct inode *dir, struct dentry *dn)
		return dn->d_name.hash;

	default:
		return ceph_str_hash(dci->i_dir_layout.dl_dir_hash,
		spin_lock(&dn->d_lock);
		hash = ceph_str_hash(dci->i_dir_layout.dl_dir_hash,
				     dn->d_name.name, dn->d_name.len);
		spin_unlock(&dn->d_lock);
		return hash;
	}
}

+15 −1
Original line number Diff line number Diff line
@@ -1163,6 +1163,19 @@ static int splice_dentry(struct dentry **pdn, struct inode *in)
	return 0;
}

static int d_name_cmp(struct dentry *dentry, const char *name, size_t len)
{
	int ret;

	/* take d_lock to ensure dentry->d_name stability */
	spin_lock(&dentry->d_lock);
	ret = dentry->d_name.len - len;
	if (!ret)
		ret = memcmp(dentry->d_name.name, name, len);
	spin_unlock(&dentry->d_lock);
	return ret;
}

/*
 * Incorporate results into the local cache.  This is either just
 * one inode, or a directory, dentry, and possibly linked-to inode (e.g.,
@@ -1412,7 +1425,8 @@ retry_lookup:
		err = splice_dentry(&req->r_dentry, in);
		if (err < 0)
			goto done;
	} else if (rinfo->head->is_dentry) {
	} else if (rinfo->head->is_dentry &&
		   !d_name_cmp(req->r_dentry, rinfo->dname, rinfo->dname_len)) {
		struct ceph_vino *ptvino = NULL;

		if ((le32_to_cpu(rinfo->diri.in->cap.caps) & CEPH_CAP_FILE_SHARED) ||
+59 −11
Original line number Diff line number Diff line
@@ -1414,6 +1414,15 @@ static int remove_session_caps_cb(struct inode *inode, struct ceph_cap *cap,
			list_add(&ci->i_prealloc_cap_flush->i_list, &to_remove);
			ci->i_prealloc_cap_flush = NULL;
		}

               if (drop &&
                  ci->i_wrbuffer_ref_head == 0 &&
                  ci->i_wr_ref == 0 &&
                  ci->i_dirty_caps == 0 &&
                  ci->i_flushing_caps == 0) {
                      ceph_put_snap_context(ci->i_head_snapc);
                      ci->i_head_snapc = NULL;
               }
	}
	spin_unlock(&ci->i_ceph_lock);
	while (!list_empty(&to_remove)) {
@@ -2161,10 +2170,39 @@ retry:
	return path;
}

/* Duplicate the dentry->d_name.name safely */
static int clone_dentry_name(struct dentry *dentry, const char **ppath,
			     int *ppathlen)
{
	u32 len;
	char *name;

retry:
	len = READ_ONCE(dentry->d_name.len);
	name = kmalloc(len + 1, GFP_NOFS);
	if (!name)
		return -ENOMEM;

	spin_lock(&dentry->d_lock);
	if (dentry->d_name.len != len) {
		spin_unlock(&dentry->d_lock);
		kfree(name);
		goto retry;
	}
	memcpy(name, dentry->d_name.name, len);
	spin_unlock(&dentry->d_lock);

	name[len] = '\0';
	*ppath = name;
	*ppathlen = len;
	return 0;
}

static int build_dentry_path(struct dentry *dentry, struct inode *dir,
			     const char **ppath, int *ppathlen, u64 *pino,
			     int *pfreepath)
			     bool *pfreepath, bool parent_locked)
{
	int ret;
	char *path;

	rcu_read_lock();
@@ -2173,8 +2211,15 @@ static int build_dentry_path(struct dentry *dentry, struct inode *dir,
	if (dir && ceph_snap(dir) == CEPH_NOSNAP) {
		*pino = ceph_ino(dir);
		rcu_read_unlock();
		if (parent_locked) {
			*ppath = dentry->d_name.name;
			*ppathlen = dentry->d_name.len;
		} else {
			ret = clone_dentry_name(dentry, ppath, ppathlen);
			if (ret)
				return ret;
			*pfreepath = true;
		}
		return 0;
	}
	rcu_read_unlock();
@@ -2182,13 +2227,13 @@ static int build_dentry_path(struct dentry *dentry, struct inode *dir,
	if (IS_ERR(path))
		return PTR_ERR(path);
	*ppath = path;
	*pfreepath = 1;
	*pfreepath = true;
	return 0;
}

static int build_inode_path(struct inode *inode,
			    const char **ppath, int *ppathlen, u64 *pino,
			    int *pfreepath)
			    bool *pfreepath)
{
	struct dentry *dentry;
	char *path;
@@ -2204,7 +2249,7 @@ static int build_inode_path(struct inode *inode,
	if (IS_ERR(path))
		return PTR_ERR(path);
	*ppath = path;
	*pfreepath = 1;
	*pfreepath = true;
	return 0;
}

@@ -2215,7 +2260,7 @@ static int build_inode_path(struct inode *inode,
static int set_request_path_attr(struct inode *rinode, struct dentry *rdentry,
				  struct inode *rdiri, const char *rpath,
				  u64 rino, const char **ppath, int *pathlen,
				  u64 *ino, int *freepath)
				  u64 *ino, bool *freepath, bool parent_locked)
{
	int r = 0;

@@ -2225,7 +2270,7 @@ static int set_request_path_attr(struct inode *rinode, struct dentry *rdentry,
		     ceph_snap(rinode));
	} else if (rdentry) {
		r = build_dentry_path(rdentry, rdiri, ppath, pathlen, ino,
					freepath);
					freepath, parent_locked);
		dout(" dentry %p %llx/%.*s\n", rdentry, *ino, *pathlen,
		     *ppath);
	} else if (rpath || rino) {
@@ -2251,7 +2296,7 @@ static struct ceph_msg *create_request_message(struct ceph_mds_client *mdsc,
	const char *path2 = NULL;
	u64 ino1 = 0, ino2 = 0;
	int pathlen1 = 0, pathlen2 = 0;
	int freepath1 = 0, freepath2 = 0;
	bool freepath1 = false, freepath2 = false;
	int len;
	u16 releases;
	void *p, *end;
@@ -2259,16 +2304,19 @@ static struct ceph_msg *create_request_message(struct ceph_mds_client *mdsc,

	ret = set_request_path_attr(req->r_inode, req->r_dentry,
			      req->r_parent, req->r_path1, req->r_ino1.ino,
			      &path1, &pathlen1, &ino1, &freepath1);
			      &path1, &pathlen1, &ino1, &freepath1,
			      test_bit(CEPH_MDS_R_PARENT_LOCKED,
					&req->r_req_flags));
	if (ret < 0) {
		msg = ERR_PTR(ret);
		goto out;
	}

	/* If r_old_dentry is set, then assume that its parent is locked */
	ret = set_request_path_attr(NULL, req->r_old_dentry,
			      req->r_old_dentry_dir,
			      req->r_path2, req->r_ino2.ino,
			      &path2, &pathlen2, &ino2, &freepath2);
			      &path2, &pathlen2, &ino2, &freepath2, true);
	if (ret < 0) {
		msg = ERR_PTR(ret);
		goto out_free1;
+6 −1
Original line number Diff line number Diff line
@@ -572,7 +572,12 @@ void ceph_queue_cap_snap(struct ceph_inode_info *ci)
	old_snapc = NULL;

update_snapc:
	if (ci->i_head_snapc) {
       if (ci->i_wrbuffer_ref_head == 0 &&
           ci->i_wr_ref == 0 &&
           ci->i_dirty_caps == 0 &&
           ci->i_flushing_caps == 0) {
               ci->i_head_snapc = NULL;
       } else {
		ci->i_head_snapc = ceph_get_snap_context(new_snapc);
		dout(" new snapc is %p\n", new_snapc);
	}