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

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
...@@ -1766,6 +1766,7 @@ static ssize_t ceph_read_dir(struct file *file, char __user *buf, size_t size, ...@@ -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) unsigned ceph_dentry_hash(struct inode *dir, struct dentry *dn)
{ {
struct ceph_inode_info *dci = ceph_inode(dir); struct ceph_inode_info *dci = ceph_inode(dir);
unsigned hash;
switch (dci->i_dir_layout.dl_dir_hash) { switch (dci->i_dir_layout.dl_dir_hash) {
case 0: /* for backward compat */ case 0: /* for backward compat */
...@@ -1773,8 +1774,11 @@ unsigned ceph_dentry_hash(struct inode *dir, struct dentry *dn) ...@@ -1773,8 +1774,11 @@ unsigned ceph_dentry_hash(struct inode *dir, struct dentry *dn)
return dn->d_name.hash; return dn->d_name.hash;
default: 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); dn->d_name.name, dn->d_name.len);
spin_unlock(&dn->d_lock);
return hash;
} }
} }
......
...@@ -1163,6 +1163,19 @@ static int splice_dentry(struct dentry **pdn, struct inode *in) ...@@ -1163,6 +1163,19 @@ static int splice_dentry(struct dentry **pdn, struct inode *in)
return 0; 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 * Incorporate results into the local cache. This is either just
* one inode, or a directory, dentry, and possibly linked-to inode (e.g., * one inode, or a directory, dentry, and possibly linked-to inode (e.g.,
...@@ -1412,7 +1425,8 @@ int ceph_fill_trace(struct super_block *sb, struct ceph_mds_request *req) ...@@ -1412,7 +1425,8 @@ int ceph_fill_trace(struct super_block *sb, struct ceph_mds_request *req)
err = splice_dentry(&req->r_dentry, in); err = splice_dentry(&req->r_dentry, in);
if (err < 0) if (err < 0)
goto done; 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; struct ceph_vino *ptvino = NULL;
if ((le32_to_cpu(rinfo->diri.in->cap.caps) & CEPH_CAP_FILE_SHARED) || if ((le32_to_cpu(rinfo->diri.in->cap.caps) & CEPH_CAP_FILE_SHARED) ||
......
...@@ -1414,6 +1414,15 @@ static int remove_session_caps_cb(struct inode *inode, struct ceph_cap *cap, ...@@ -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); list_add(&ci->i_prealloc_cap_flush->i_list, &to_remove);
ci->i_prealloc_cap_flush = NULL; 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); spin_unlock(&ci->i_ceph_lock);
while (!list_empty(&to_remove)) { while (!list_empty(&to_remove)) {
...@@ -2161,10 +2170,39 @@ char *ceph_mdsc_build_path(struct dentry *dentry, int *plen, u64 *base, ...@@ -2161,10 +2170,39 @@ char *ceph_mdsc_build_path(struct dentry *dentry, int *plen, u64 *base,
return path; 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, static int build_dentry_path(struct dentry *dentry, struct inode *dir,
const char **ppath, int *ppathlen, u64 *pino, const char **ppath, int *ppathlen, u64 *pino,
int *pfreepath) bool *pfreepath, bool parent_locked)
{ {
int ret;
char *path; char *path;
rcu_read_lock(); rcu_read_lock();
...@@ -2173,8 +2211,15 @@ static int build_dentry_path(struct dentry *dentry, struct inode *dir, ...@@ -2173,8 +2211,15 @@ static int build_dentry_path(struct dentry *dentry, struct inode *dir,
if (dir && ceph_snap(dir) == CEPH_NOSNAP) { if (dir && ceph_snap(dir) == CEPH_NOSNAP) {
*pino = ceph_ino(dir); *pino = ceph_ino(dir);
rcu_read_unlock(); rcu_read_unlock();
*ppath = dentry->d_name.name; if (parent_locked) {
*ppathlen = dentry->d_name.len; *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; return 0;
} }
rcu_read_unlock(); rcu_read_unlock();
...@@ -2182,13 +2227,13 @@ static int build_dentry_path(struct dentry *dentry, struct inode *dir, ...@@ -2182,13 +2227,13 @@ static int build_dentry_path(struct dentry *dentry, struct inode *dir,
if (IS_ERR(path)) if (IS_ERR(path))
return PTR_ERR(path); return PTR_ERR(path);
*ppath = path; *ppath = path;
*pfreepath = 1; *pfreepath = true;
return 0; return 0;
} }
static int build_inode_path(struct inode *inode, static int build_inode_path(struct inode *inode,
const char **ppath, int *ppathlen, u64 *pino, const char **ppath, int *ppathlen, u64 *pino,
int *pfreepath) bool *pfreepath)
{ {
struct dentry *dentry; struct dentry *dentry;
char *path; char *path;
...@@ -2204,7 +2249,7 @@ static int build_inode_path(struct inode *inode, ...@@ -2204,7 +2249,7 @@ static int build_inode_path(struct inode *inode,
if (IS_ERR(path)) if (IS_ERR(path))
return PTR_ERR(path); return PTR_ERR(path);
*ppath = path; *ppath = path;
*pfreepath = 1; *pfreepath = true;
return 0; return 0;
} }
...@@ -2215,7 +2260,7 @@ static int build_inode_path(struct inode *inode, ...@@ -2215,7 +2260,7 @@ static int build_inode_path(struct inode *inode,
static int set_request_path_attr(struct inode *rinode, struct dentry *rdentry, static int set_request_path_attr(struct inode *rinode, struct dentry *rdentry,
struct inode *rdiri, const char *rpath, struct inode *rdiri, const char *rpath,
u64 rino, const char **ppath, int *pathlen, u64 rino, const char **ppath, int *pathlen,
u64 *ino, int *freepath) u64 *ino, bool *freepath, bool parent_locked)
{ {
int r = 0; int r = 0;
...@@ -2225,7 +2270,7 @@ static int set_request_path_attr(struct inode *rinode, struct dentry *rdentry, ...@@ -2225,7 +2270,7 @@ static int set_request_path_attr(struct inode *rinode, struct dentry *rdentry,
ceph_snap(rinode)); ceph_snap(rinode));
} else if (rdentry) { } else if (rdentry) {
r = build_dentry_path(rdentry, rdiri, ppath, pathlen, ino, r = build_dentry_path(rdentry, rdiri, ppath, pathlen, ino,
freepath); freepath, parent_locked);
dout(" dentry %p %llx/%.*s\n", rdentry, *ino, *pathlen, dout(" dentry %p %llx/%.*s\n", rdentry, *ino, *pathlen,
*ppath); *ppath);
} else if (rpath || rino) { } else if (rpath || rino) {
...@@ -2251,7 +2296,7 @@ static struct ceph_msg *create_request_message(struct ceph_mds_client *mdsc, ...@@ -2251,7 +2296,7 @@ static struct ceph_msg *create_request_message(struct ceph_mds_client *mdsc,
const char *path2 = NULL; const char *path2 = NULL;
u64 ino1 = 0, ino2 = 0; u64 ino1 = 0, ino2 = 0;
int pathlen1 = 0, pathlen2 = 0; int pathlen1 = 0, pathlen2 = 0;
int freepath1 = 0, freepath2 = 0; bool freepath1 = false, freepath2 = false;
int len; int len;
u16 releases; u16 releases;
void *p, *end; void *p, *end;
...@@ -2259,16 +2304,19 @@ static struct ceph_msg *create_request_message(struct ceph_mds_client *mdsc, ...@@ -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, ret = set_request_path_attr(req->r_inode, req->r_dentry,
req->r_parent, req->r_path1, req->r_ino1.ino, 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) { if (ret < 0) {
msg = ERR_PTR(ret); msg = ERR_PTR(ret);
goto out; 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, ret = set_request_path_attr(NULL, req->r_old_dentry,
req->r_old_dentry_dir, req->r_old_dentry_dir,
req->r_path2, req->r_ino2.ino, req->r_path2, req->r_ino2.ino,
&path2, &pathlen2, &ino2, &freepath2); &path2, &pathlen2, &ino2, &freepath2, true);
if (ret < 0) { if (ret < 0) {
msg = ERR_PTR(ret); msg = ERR_PTR(ret);
goto out_free1; goto out_free1;
......
...@@ -572,7 +572,12 @@ void ceph_queue_cap_snap(struct ceph_inode_info *ci) ...@@ -572,7 +572,12 @@ void ceph_queue_cap_snap(struct ceph_inode_info *ci)
old_snapc = NULL; old_snapc = NULL;
update_snapc: 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); ci->i_head_snapc = ceph_get_snap_context(new_snapc);
dout(" new snapc is %p\n", new_snapc); dout(" new snapc is %p\n", new_snapc);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment