Skip to content

Commit

Permalink
Merge tag 'ceph-for-5.1-rc7' of git://github.com/ceph/ceph-client
Browse files Browse the repository at this point in the history
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
  • Loading branch information
torvalds committed Apr 25, 2019
2 parents f6f3e74 + 3765918 commit 8113a85
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 14 deletions.
6 changes: 5 additions & 1 deletion fs/ceph/dir.c
Original file line number Diff line number Diff line change
Expand Up @@ -1766,15 +1766,19 @@ 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 */
case CEPH_STR_HASH_LINUX:
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;
}
}

Expand Down
16 changes: 15 additions & 1 deletion fs/ceph/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -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.,
Expand Down Expand Up @@ -1412,7 +1425,8 @@ int ceph_fill_trace(struct super_block *sb, struct ceph_mds_request *req)
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) ||
Expand Down
70 changes: 59 additions & 11 deletions fs/ceph/mds_client.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Expand Down Expand Up @@ -2161,10 +2170,39 @@ char *ceph_mdsc_build_path(struct dentry *dentry, int *plen, u64 *base,
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();
Expand All @@ -2173,22 +2211,29 @@ 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();
*ppath = dentry->d_name.name;
*ppathlen = dentry->d_name.len;
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();
path = ceph_mdsc_build_path(dentry, ppathlen, pino, 1);
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;
Expand All @@ -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;
}

Expand All @@ -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;

Expand All @@ -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) {
Expand All @@ -2251,24 +2296,27 @@ 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;
int ret;

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;
Expand Down
7 changes: 6 additions & 1 deletion fs/ceph/snap.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down

0 comments on commit 8113a85

Please sign in to comment.