9p: add refcount to p9_fid struct
Fix race issue in fid contention.
Eric's and Greg's patch offer a mechanism to fix open-unlink-f*syscall
bug in 9p. But there is race issue in fid parallel accesses.
As Greg's patch stores all of fids from opened files into according inode,
so all the lookup fid ops can retrieve fid from inode preferentially. But
there is no mechanism to handle the fid contention issue. For example,
there are two threads get the same fid in the same time and one of them
clunk the fid before the other thread ready to discard the fid. In this
scenario, it will lead to some fatal problems, even kernel core dump.
I introduce a mechanism to fix this race issue. A counter field introduced
into p9_fid struct to store the reference counter to the fid. When a fid
is allocated from the inode or dentry, the counter will increase, and
will decrease at the end of its occupation. It is guaranteed that the
fid won't be clunked before the reference counter go down to 0, then
we can avoid the clunked fid to be used.
tests:
race issue test from the old test case:
for file in {01..50}; do touch f.${file}; done
seq 1 1000 | xargs -n 1 -P 50 -I{} cat f.* > /dev/null
open-unlink-f*syscall test:
I have tested for f*syscall include: ftruncate fstat fchown fchmod faccessat.
Link: http://lkml.kernel.org/r/20200923141146.90046-5-jianyong.wu@arm.com
Fixes: 478ba09edc
("fs/9p: search open fids first")
Signed-off-by: Jianyong Wu <jianyong.wu@arm.com>
Signed-off-by: Dominique Martinet <asmadeus@codewreck.org>
This commit is contained in:
parent
478ba09edc
commit
6636b6dcc3
22
fs/9p/fid.c
22
fs/9p/fid.c
|
@ -28,6 +28,7 @@
|
||||||
|
|
||||||
static inline void __add_fid(struct dentry *dentry, struct p9_fid *fid)
|
static inline void __add_fid(struct dentry *dentry, struct p9_fid *fid)
|
||||||
{
|
{
|
||||||
|
atomic_set(&fid->count, 1);
|
||||||
hlist_add_head(&fid->dlist, (struct hlist_head *)&dentry->d_fsdata);
|
hlist_add_head(&fid->dlist, (struct hlist_head *)&dentry->d_fsdata);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,6 +61,8 @@ static struct p9_fid *v9fs_fid_find_inode(struct inode *inode, kuid_t uid)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (ret && !IS_ERR(ret))
|
||||||
|
atomic_inc(&ret->count);
|
||||||
spin_unlock(&inode->i_lock);
|
spin_unlock(&inode->i_lock);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -74,6 +77,7 @@ static struct p9_fid *v9fs_fid_find_inode(struct inode *inode, kuid_t uid)
|
||||||
void v9fs_open_fid_add(struct inode *inode, struct p9_fid *fid)
|
void v9fs_open_fid_add(struct inode *inode, struct p9_fid *fid)
|
||||||
{
|
{
|
||||||
spin_lock(&inode->i_lock);
|
spin_lock(&inode->i_lock);
|
||||||
|
atomic_set(&fid->count, 1);
|
||||||
hlist_add_head(&fid->ilist, (struct hlist_head *)&inode->i_private);
|
hlist_add_head(&fid->ilist, (struct hlist_head *)&inode->i_private);
|
||||||
spin_unlock(&inode->i_lock);
|
spin_unlock(&inode->i_lock);
|
||||||
}
|
}
|
||||||
|
@ -106,6 +110,7 @@ static struct p9_fid *v9fs_fid_find(struct dentry *dentry, kuid_t uid, int any)
|
||||||
hlist_for_each_entry(fid, h, dlist) {
|
hlist_for_each_entry(fid, h, dlist) {
|
||||||
if (any || uid_eq(fid->uid, uid)) {
|
if (any || uid_eq(fid->uid, uid)) {
|
||||||
ret = fid;
|
ret = fid;
|
||||||
|
atomic_inc(&ret->count);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -167,7 +172,10 @@ static struct p9_fid *v9fs_fid_lookup_with_uid(struct dentry *dentry,
|
||||||
fid = v9fs_fid_find(ds, uid, any);
|
fid = v9fs_fid_find(ds, uid, any);
|
||||||
if (fid) {
|
if (fid) {
|
||||||
/* Found the parent fid do a lookup with that */
|
/* Found the parent fid do a lookup with that */
|
||||||
fid = p9_client_walk(fid, 1, &dentry->d_name.name, 1);
|
struct p9_fid *ofid = fid;
|
||||||
|
|
||||||
|
fid = p9_client_walk(ofid, 1, &dentry->d_name.name, 1);
|
||||||
|
p9_client_clunk(ofid);
|
||||||
goto fid_out;
|
goto fid_out;
|
||||||
}
|
}
|
||||||
up_read(&v9ses->rename_sem);
|
up_read(&v9ses->rename_sem);
|
||||||
|
@ -192,8 +200,10 @@ static struct p9_fid *v9fs_fid_lookup_with_uid(struct dentry *dentry,
|
||||||
v9fs_fid_add(dentry->d_sb->s_root, fid);
|
v9fs_fid_add(dentry->d_sb->s_root, fid);
|
||||||
}
|
}
|
||||||
/* If we are root ourself just return that */
|
/* If we are root ourself just return that */
|
||||||
if (dentry->d_sb->s_root == dentry)
|
if (dentry->d_sb->s_root == dentry) {
|
||||||
|
atomic_inc(&fid->count);
|
||||||
return fid;
|
return fid;
|
||||||
|
}
|
||||||
/*
|
/*
|
||||||
* Do a multipath walk with attached root.
|
* Do a multipath walk with attached root.
|
||||||
* When walking parent we need to make sure we
|
* When walking parent we need to make sure we
|
||||||
|
@ -240,6 +250,7 @@ static struct p9_fid *v9fs_fid_lookup_with_uid(struct dentry *dentry,
|
||||||
fid = ERR_PTR(-ENOENT);
|
fid = ERR_PTR(-ENOENT);
|
||||||
} else {
|
} else {
|
||||||
__add_fid(dentry, fid);
|
__add_fid(dentry, fid);
|
||||||
|
atomic_inc(&fid->count);
|
||||||
spin_unlock(&dentry->d_lock);
|
spin_unlock(&dentry->d_lock);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -290,11 +301,14 @@ struct p9_fid *v9fs_fid_lookup(struct dentry *dentry)
|
||||||
struct p9_fid *v9fs_writeback_fid(struct dentry *dentry)
|
struct p9_fid *v9fs_writeback_fid(struct dentry *dentry)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
struct p9_fid *fid;
|
struct p9_fid *fid, *ofid;
|
||||||
|
|
||||||
fid = clone_fid(v9fs_fid_lookup_with_uid(dentry, GLOBAL_ROOT_UID, 0));
|
ofid = v9fs_fid_lookup_with_uid(dentry, GLOBAL_ROOT_UID, 0);
|
||||||
|
if (ofid && !IS_ERR(ofid))
|
||||||
|
fid = clone_fid(ofid);
|
||||||
if (IS_ERR(fid))
|
if (IS_ERR(fid))
|
||||||
goto error_out;
|
goto error_out;
|
||||||
|
p9_client_clunk(ofid);
|
||||||
/*
|
/*
|
||||||
* writeback fid will only be used to write back the
|
* writeback fid will only be used to write back the
|
||||||
* dirty pages. We always request for the open fid in read-write
|
* dirty pages. We always request for the open fid in read-write
|
||||||
|
|
10
fs/9p/fid.h
10
fs/9p/fid.h
|
@ -22,6 +22,14 @@ static inline struct p9_fid *clone_fid(struct p9_fid *fid)
|
||||||
}
|
}
|
||||||
static inline struct p9_fid *v9fs_fid_clone(struct dentry *dentry)
|
static inline struct p9_fid *v9fs_fid_clone(struct dentry *dentry)
|
||||||
{
|
{
|
||||||
return clone_fid(v9fs_fid_lookup(dentry));
|
struct p9_fid *fid, *nfid;
|
||||||
|
|
||||||
|
fid = v9fs_fid_lookup(dentry);
|
||||||
|
if (!fid || IS_ERR(fid))
|
||||||
|
return fid;
|
||||||
|
|
||||||
|
nfid = p9_client_walk(fid, 0, NULL, 1);
|
||||||
|
p9_client_clunk(fid);
|
||||||
|
return nfid;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -85,6 +85,8 @@ static int v9fs_lookup_revalidate(struct dentry *dentry, unsigned int flags)
|
||||||
retval = v9fs_refresh_inode_dotl(fid, inode);
|
retval = v9fs_refresh_inode_dotl(fid, inode);
|
||||||
else
|
else
|
||||||
retval = v9fs_refresh_inode(fid, inode);
|
retval = v9fs_refresh_inode(fid, inode);
|
||||||
|
p9_client_clunk(fid);
|
||||||
|
|
||||||
if (retval == -ENOENT)
|
if (retval == -ENOENT)
|
||||||
return 0;
|
return 0;
|
||||||
if (retval < 0)
|
if (retval < 0)
|
||||||
|
|
|
@ -210,11 +210,12 @@ int v9fs_dir_release(struct inode *inode, struct file *filp)
|
||||||
fid = filp->private_data;
|
fid = filp->private_data;
|
||||||
p9_debug(P9_DEBUG_VFS, "inode: %p filp: %p fid: %d\n",
|
p9_debug(P9_DEBUG_VFS, "inode: %p filp: %p fid: %d\n",
|
||||||
inode, filp, fid ? fid->fid : -1);
|
inode, filp, fid ? fid->fid : -1);
|
||||||
spin_lock(&inode->i_lock);
|
if (fid) {
|
||||||
hlist_del(&fid->ilist);
|
spin_lock(&inode->i_lock);
|
||||||
spin_unlock(&inode->i_lock);
|
hlist_del(&fid->ilist);
|
||||||
if (fid)
|
spin_unlock(&inode->i_lock);
|
||||||
p9_client_clunk(fid);
|
p9_client_clunk(fid);
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -551,6 +551,7 @@ static int v9fs_remove(struct inode *dir, struct dentry *dentry, int flags)
|
||||||
if (v9fs_proto_dotl(v9ses))
|
if (v9fs_proto_dotl(v9ses))
|
||||||
retval = p9_client_unlinkat(dfid, dentry->d_name.name,
|
retval = p9_client_unlinkat(dfid, dentry->d_name.name,
|
||||||
v9fs_at_to_dotl_flags(flags));
|
v9fs_at_to_dotl_flags(flags));
|
||||||
|
p9_client_clunk(dfid);
|
||||||
if (retval == -EOPNOTSUPP) {
|
if (retval == -EOPNOTSUPP) {
|
||||||
/* Try the one based on path */
|
/* Try the one based on path */
|
||||||
v9fid = v9fs_fid_clone(dentry);
|
v9fid = v9fs_fid_clone(dentry);
|
||||||
|
@ -595,14 +596,12 @@ v9fs_create(struct v9fs_session_info *v9ses, struct inode *dir,
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
const unsigned char *name;
|
const unsigned char *name;
|
||||||
struct p9_fid *dfid, *ofid, *fid;
|
struct p9_fid *dfid, *ofid = NULL, *fid = NULL;
|
||||||
struct inode *inode;
|
struct inode *inode;
|
||||||
|
|
||||||
p9_debug(P9_DEBUG_VFS, "name %pd\n", dentry);
|
p9_debug(P9_DEBUG_VFS, "name %pd\n", dentry);
|
||||||
|
|
||||||
err = 0;
|
err = 0;
|
||||||
ofid = NULL;
|
|
||||||
fid = NULL;
|
|
||||||
name = dentry->d_name.name;
|
name = dentry->d_name.name;
|
||||||
dfid = v9fs_parent_fid(dentry);
|
dfid = v9fs_parent_fid(dentry);
|
||||||
if (IS_ERR(dfid)) {
|
if (IS_ERR(dfid)) {
|
||||||
|
@ -616,12 +615,14 @@ v9fs_create(struct v9fs_session_info *v9ses, struct inode *dir,
|
||||||
if (IS_ERR(ofid)) {
|
if (IS_ERR(ofid)) {
|
||||||
err = PTR_ERR(ofid);
|
err = PTR_ERR(ofid);
|
||||||
p9_debug(P9_DEBUG_VFS, "p9_client_walk failed %d\n", err);
|
p9_debug(P9_DEBUG_VFS, "p9_client_walk failed %d\n", err);
|
||||||
|
p9_client_clunk(dfid);
|
||||||
return ERR_PTR(err);
|
return ERR_PTR(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
err = p9_client_fcreate(ofid, name, perm, mode, extension);
|
err = p9_client_fcreate(ofid, name, perm, mode, extension);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
p9_debug(P9_DEBUG_VFS, "p9_client_fcreate failed %d\n", err);
|
p9_debug(P9_DEBUG_VFS, "p9_client_fcreate failed %d\n", err);
|
||||||
|
p9_client_clunk(dfid);
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -633,6 +634,7 @@ v9fs_create(struct v9fs_session_info *v9ses, struct inode *dir,
|
||||||
p9_debug(P9_DEBUG_VFS,
|
p9_debug(P9_DEBUG_VFS,
|
||||||
"p9_client_walk failed %d\n", err);
|
"p9_client_walk failed %d\n", err);
|
||||||
fid = NULL;
|
fid = NULL;
|
||||||
|
p9_client_clunk(dfid);
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
|
@ -643,11 +645,13 @@ v9fs_create(struct v9fs_session_info *v9ses, struct inode *dir,
|
||||||
err = PTR_ERR(inode);
|
err = PTR_ERR(inode);
|
||||||
p9_debug(P9_DEBUG_VFS,
|
p9_debug(P9_DEBUG_VFS,
|
||||||
"inode creation failed %d\n", err);
|
"inode creation failed %d\n", err);
|
||||||
|
p9_client_clunk(dfid);
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
v9fs_fid_add(dentry, fid);
|
v9fs_fid_add(dentry, fid);
|
||||||
d_instantiate(dentry, inode);
|
d_instantiate(dentry, inode);
|
||||||
}
|
}
|
||||||
|
p9_client_clunk(dfid);
|
||||||
return ofid;
|
return ofid;
|
||||||
error:
|
error:
|
||||||
if (ofid)
|
if (ofid)
|
||||||
|
@ -760,6 +764,7 @@ struct dentry *v9fs_vfs_lookup(struct inode *dir, struct dentry *dentry,
|
||||||
*/
|
*/
|
||||||
name = dentry->d_name.name;
|
name = dentry->d_name.name;
|
||||||
fid = p9_client_walk(dfid, 1, &name, 1);
|
fid = p9_client_walk(dfid, 1, &name, 1);
|
||||||
|
p9_client_clunk(dfid);
|
||||||
if (fid == ERR_PTR(-ENOENT))
|
if (fid == ERR_PTR(-ENOENT))
|
||||||
inode = NULL;
|
inode = NULL;
|
||||||
else if (IS_ERR(fid))
|
else if (IS_ERR(fid))
|
||||||
|
@ -910,7 +915,7 @@ v9fs_vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||||
struct inode *old_inode;
|
struct inode *old_inode;
|
||||||
struct inode *new_inode;
|
struct inode *new_inode;
|
||||||
struct v9fs_session_info *v9ses;
|
struct v9fs_session_info *v9ses;
|
||||||
struct p9_fid *oldfid;
|
struct p9_fid *oldfid, *dfid;
|
||||||
struct p9_fid *olddirfid;
|
struct p9_fid *olddirfid;
|
||||||
struct p9_fid *newdirfid;
|
struct p9_fid *newdirfid;
|
||||||
struct p9_wstat wstat;
|
struct p9_wstat wstat;
|
||||||
|
@ -927,13 +932,20 @@ v9fs_vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||||
if (IS_ERR(oldfid))
|
if (IS_ERR(oldfid))
|
||||||
return PTR_ERR(oldfid);
|
return PTR_ERR(oldfid);
|
||||||
|
|
||||||
olddirfid = clone_fid(v9fs_parent_fid(old_dentry));
|
dfid = v9fs_parent_fid(old_dentry);
|
||||||
|
olddirfid = clone_fid(dfid);
|
||||||
|
if (dfid && !IS_ERR(dfid))
|
||||||
|
p9_client_clunk(dfid);
|
||||||
|
|
||||||
if (IS_ERR(olddirfid)) {
|
if (IS_ERR(olddirfid)) {
|
||||||
retval = PTR_ERR(olddirfid);
|
retval = PTR_ERR(olddirfid);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
newdirfid = clone_fid(v9fs_parent_fid(new_dentry));
|
dfid = v9fs_parent_fid(new_dentry);
|
||||||
|
newdirfid = clone_fid(dfid);
|
||||||
|
p9_client_clunk(dfid);
|
||||||
|
|
||||||
if (IS_ERR(newdirfid)) {
|
if (IS_ERR(newdirfid)) {
|
||||||
retval = PTR_ERR(newdirfid);
|
retval = PTR_ERR(newdirfid);
|
||||||
goto clunk_olddir;
|
goto clunk_olddir;
|
||||||
|
@ -990,6 +1002,7 @@ v9fs_vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||||
p9_client_clunk(olddirfid);
|
p9_client_clunk(olddirfid);
|
||||||
|
|
||||||
done:
|
done:
|
||||||
|
p9_client_clunk(oldfid);
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1022,6 +1035,7 @@ v9fs_vfs_getattr(const struct path *path, struct kstat *stat,
|
||||||
return PTR_ERR(fid);
|
return PTR_ERR(fid);
|
||||||
|
|
||||||
st = p9_client_stat(fid);
|
st = p9_client_stat(fid);
|
||||||
|
p9_client_clunk(fid);
|
||||||
if (IS_ERR(st))
|
if (IS_ERR(st))
|
||||||
return PTR_ERR(st);
|
return PTR_ERR(st);
|
||||||
|
|
||||||
|
@ -1042,7 +1056,7 @@ v9fs_vfs_getattr(const struct path *path, struct kstat *stat,
|
||||||
|
|
||||||
static int v9fs_vfs_setattr(struct dentry *dentry, struct iattr *iattr)
|
static int v9fs_vfs_setattr(struct dentry *dentry, struct iattr *iattr)
|
||||||
{
|
{
|
||||||
int retval;
|
int retval, use_dentry = 0;
|
||||||
struct v9fs_session_info *v9ses;
|
struct v9fs_session_info *v9ses;
|
||||||
struct p9_fid *fid = NULL;
|
struct p9_fid *fid = NULL;
|
||||||
struct p9_wstat wstat;
|
struct p9_wstat wstat;
|
||||||
|
@ -1058,8 +1072,10 @@ static int v9fs_vfs_setattr(struct dentry *dentry, struct iattr *iattr)
|
||||||
fid = iattr->ia_file->private_data;
|
fid = iattr->ia_file->private_data;
|
||||||
WARN_ON(!fid);
|
WARN_ON(!fid);
|
||||||
}
|
}
|
||||||
if (!fid)
|
if (!fid) {
|
||||||
fid = v9fs_fid_lookup(dentry);
|
fid = v9fs_fid_lookup(dentry);
|
||||||
|
use_dentry = 1;
|
||||||
|
}
|
||||||
if(IS_ERR(fid))
|
if(IS_ERR(fid))
|
||||||
return PTR_ERR(fid);
|
return PTR_ERR(fid);
|
||||||
|
|
||||||
|
@ -1089,6 +1105,10 @@ static int v9fs_vfs_setattr(struct dentry *dentry, struct iattr *iattr)
|
||||||
filemap_write_and_wait(d_inode(dentry)->i_mapping);
|
filemap_write_and_wait(d_inode(dentry)->i_mapping);
|
||||||
|
|
||||||
retval = p9_client_wstat(fid, &wstat);
|
retval = p9_client_wstat(fid, &wstat);
|
||||||
|
|
||||||
|
if (use_dentry)
|
||||||
|
p9_client_clunk(fid);
|
||||||
|
|
||||||
if (retval < 0)
|
if (retval < 0)
|
||||||
return retval;
|
return retval;
|
||||||
|
|
||||||
|
@ -1213,6 +1233,7 @@ static const char *v9fs_vfs_get_link(struct dentry *dentry,
|
||||||
return ERR_PTR(-EBADF);
|
return ERR_PTR(-EBADF);
|
||||||
|
|
||||||
st = p9_client_stat(fid);
|
st = p9_client_stat(fid);
|
||||||
|
p9_client_clunk(fid);
|
||||||
if (IS_ERR(st))
|
if (IS_ERR(st))
|
||||||
return ERR_CAST(st);
|
return ERR_CAST(st);
|
||||||
|
|
||||||
|
|
|
@ -296,6 +296,7 @@ v9fs_vfs_atomic_open_dotl(struct inode *dir, struct dentry *dentry,
|
||||||
|
|
||||||
/* instantiate inode and assign the unopened fid to the dentry */
|
/* instantiate inode and assign the unopened fid to the dentry */
|
||||||
fid = p9_client_walk(dfid, 1, &name, 1);
|
fid = p9_client_walk(dfid, 1, &name, 1);
|
||||||
|
p9_client_clunk(dfid);
|
||||||
if (IS_ERR(fid)) {
|
if (IS_ERR(fid)) {
|
||||||
err = PTR_ERR(fid);
|
err = PTR_ERR(fid);
|
||||||
p9_debug(P9_DEBUG_VFS, "p9_client_walk failed %d\n", err);
|
p9_debug(P9_DEBUG_VFS, "p9_client_walk failed %d\n", err);
|
||||||
|
@ -408,7 +409,6 @@ static int v9fs_vfs_mkdir_dotl(struct inode *dir,
|
||||||
err = p9_client_mkdir_dotl(dfid, name, mode, gid, &qid);
|
err = p9_client_mkdir_dotl(dfid, name, mode, gid, &qid);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
fid = p9_client_walk(dfid, 1, &name, 1);
|
fid = p9_client_walk(dfid, 1, &name, 1);
|
||||||
if (IS_ERR(fid)) {
|
if (IS_ERR(fid)) {
|
||||||
err = PTR_ERR(fid);
|
err = PTR_ERR(fid);
|
||||||
|
@ -452,6 +452,7 @@ static int v9fs_vfs_mkdir_dotl(struct inode *dir,
|
||||||
if (fid)
|
if (fid)
|
||||||
p9_client_clunk(fid);
|
p9_client_clunk(fid);
|
||||||
v9fs_put_acl(dacl, pacl);
|
v9fs_put_acl(dacl, pacl);
|
||||||
|
p9_client_clunk(dfid);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -479,6 +480,7 @@ v9fs_vfs_getattr_dotl(const struct path *path, struct kstat *stat,
|
||||||
*/
|
*/
|
||||||
|
|
||||||
st = p9_client_getattr_dotl(fid, P9_STATS_ALL);
|
st = p9_client_getattr_dotl(fid, P9_STATS_ALL);
|
||||||
|
p9_client_clunk(fid);
|
||||||
if (IS_ERR(st))
|
if (IS_ERR(st))
|
||||||
return PTR_ERR(st);
|
return PTR_ERR(st);
|
||||||
|
|
||||||
|
@ -540,7 +542,7 @@ static int v9fs_mapped_iattr_valid(int iattr_valid)
|
||||||
|
|
||||||
int v9fs_vfs_setattr_dotl(struct dentry *dentry, struct iattr *iattr)
|
int v9fs_vfs_setattr_dotl(struct dentry *dentry, struct iattr *iattr)
|
||||||
{
|
{
|
||||||
int retval;
|
int retval, use_dentry = 0;
|
||||||
struct p9_fid *fid = NULL;
|
struct p9_fid *fid = NULL;
|
||||||
struct p9_iattr_dotl p9attr;
|
struct p9_iattr_dotl p9attr;
|
||||||
struct inode *inode = d_inode(dentry);
|
struct inode *inode = d_inode(dentry);
|
||||||
|
@ -565,8 +567,10 @@ int v9fs_vfs_setattr_dotl(struct dentry *dentry, struct iattr *iattr)
|
||||||
fid = iattr->ia_file->private_data;
|
fid = iattr->ia_file->private_data;
|
||||||
WARN_ON(!fid);
|
WARN_ON(!fid);
|
||||||
}
|
}
|
||||||
if (!fid)
|
if (!fid) {
|
||||||
fid = v9fs_fid_lookup(dentry);
|
fid = v9fs_fid_lookup(dentry);
|
||||||
|
use_dentry = 1;
|
||||||
|
}
|
||||||
if (IS_ERR(fid))
|
if (IS_ERR(fid))
|
||||||
return PTR_ERR(fid);
|
return PTR_ERR(fid);
|
||||||
|
|
||||||
|
@ -575,8 +579,11 @@ int v9fs_vfs_setattr_dotl(struct dentry *dentry, struct iattr *iattr)
|
||||||
filemap_write_and_wait(inode->i_mapping);
|
filemap_write_and_wait(inode->i_mapping);
|
||||||
|
|
||||||
retval = p9_client_setattr(fid, &p9attr);
|
retval = p9_client_setattr(fid, &p9attr);
|
||||||
if (retval < 0)
|
if (retval < 0) {
|
||||||
|
if (use_dentry)
|
||||||
|
p9_client_clunk(fid);
|
||||||
return retval;
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
if ((iattr->ia_valid & ATTR_SIZE) &&
|
if ((iattr->ia_valid & ATTR_SIZE) &&
|
||||||
iattr->ia_size != i_size_read(inode))
|
iattr->ia_size != i_size_read(inode))
|
||||||
|
@ -588,9 +595,15 @@ int v9fs_vfs_setattr_dotl(struct dentry *dentry, struct iattr *iattr)
|
||||||
if (iattr->ia_valid & ATTR_MODE) {
|
if (iattr->ia_valid & ATTR_MODE) {
|
||||||
/* We also want to update ACL when we update mode bits */
|
/* We also want to update ACL when we update mode bits */
|
||||||
retval = v9fs_acl_chmod(inode, fid);
|
retval = v9fs_acl_chmod(inode, fid);
|
||||||
if (retval < 0)
|
if (retval < 0) {
|
||||||
|
if (use_dentry)
|
||||||
|
p9_client_clunk(fid);
|
||||||
return retval;
|
return retval;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
if (use_dentry)
|
||||||
|
p9_client_clunk(fid);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -742,6 +755,7 @@ v9fs_vfs_symlink_dotl(struct inode *dir, struct dentry *dentry,
|
||||||
if (fid)
|
if (fid)
|
||||||
p9_client_clunk(fid);
|
p9_client_clunk(fid);
|
||||||
|
|
||||||
|
p9_client_clunk(dfid);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -770,11 +784,15 @@ v9fs_vfs_link_dotl(struct dentry *old_dentry, struct inode *dir,
|
||||||
return PTR_ERR(dfid);
|
return PTR_ERR(dfid);
|
||||||
|
|
||||||
oldfid = v9fs_fid_lookup(old_dentry);
|
oldfid = v9fs_fid_lookup(old_dentry);
|
||||||
if (IS_ERR(oldfid))
|
if (IS_ERR(oldfid)) {
|
||||||
|
p9_client_clunk(dfid);
|
||||||
return PTR_ERR(oldfid);
|
return PTR_ERR(oldfid);
|
||||||
|
}
|
||||||
|
|
||||||
err = p9_client_link(dfid, oldfid, dentry->d_name.name);
|
err = p9_client_link(dfid, oldfid, dentry->d_name.name);
|
||||||
|
|
||||||
|
p9_client_clunk(dfid);
|
||||||
|
p9_client_clunk(oldfid);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
p9_debug(P9_DEBUG_VFS, "p9_client_link failed %d\n", err);
|
p9_debug(P9_DEBUG_VFS, "p9_client_link failed %d\n", err);
|
||||||
return err;
|
return err;
|
||||||
|
@ -789,6 +807,7 @@ v9fs_vfs_link_dotl(struct dentry *old_dentry, struct inode *dir,
|
||||||
return PTR_ERR(fid);
|
return PTR_ERR(fid);
|
||||||
|
|
||||||
v9fs_refresh_inode_dotl(fid, d_inode(old_dentry));
|
v9fs_refresh_inode_dotl(fid, d_inode(old_dentry));
|
||||||
|
p9_client_clunk(fid);
|
||||||
}
|
}
|
||||||
ihold(d_inode(old_dentry));
|
ihold(d_inode(old_dentry));
|
||||||
d_instantiate(dentry, d_inode(old_dentry));
|
d_instantiate(dentry, d_inode(old_dentry));
|
||||||
|
@ -887,6 +906,8 @@ v9fs_vfs_mknod_dotl(struct inode *dir, struct dentry *dentry, umode_t omode,
|
||||||
if (fid)
|
if (fid)
|
||||||
p9_client_clunk(fid);
|
p9_client_clunk(fid);
|
||||||
v9fs_put_acl(dacl, pacl);
|
v9fs_put_acl(dacl, pacl);
|
||||||
|
p9_client_clunk(dfid);
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -915,6 +936,7 @@ v9fs_vfs_get_link_dotl(struct dentry *dentry,
|
||||||
if (IS_ERR(fid))
|
if (IS_ERR(fid))
|
||||||
return ERR_CAST(fid);
|
return ERR_CAST(fid);
|
||||||
retval = p9_client_readlink(fid, &target);
|
retval = p9_client_readlink(fid, &target);
|
||||||
|
p9_client_clunk(fid);
|
||||||
if (retval)
|
if (retval)
|
||||||
return ERR_PTR(retval);
|
return ERR_PTR(retval);
|
||||||
set_delayed_call(done, kfree_link, target);
|
set_delayed_call(done, kfree_link, target);
|
||||||
|
|
|
@ -268,6 +268,7 @@ static int v9fs_statfs(struct dentry *dentry, struct kstatfs *buf)
|
||||||
}
|
}
|
||||||
res = simple_statfs(dentry, buf);
|
res = simple_statfs(dentry, buf);
|
||||||
done:
|
done:
|
||||||
|
p9_client_clunk(fid);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -71,14 +71,17 @@ ssize_t v9fs_xattr_get(struct dentry *dentry, const char *name,
|
||||||
void *buffer, size_t buffer_size)
|
void *buffer, size_t buffer_size)
|
||||||
{
|
{
|
||||||
struct p9_fid *fid;
|
struct p9_fid *fid;
|
||||||
|
int ret;
|
||||||
|
|
||||||
p9_debug(P9_DEBUG_VFS, "name = %s value_len = %zu\n",
|
p9_debug(P9_DEBUG_VFS, "name = %s value_len = %zu\n",
|
||||||
name, buffer_size);
|
name, buffer_size);
|
||||||
fid = v9fs_fid_lookup(dentry);
|
fid = v9fs_fid_lookup(dentry);
|
||||||
if (IS_ERR(fid))
|
if (IS_ERR(fid))
|
||||||
return PTR_ERR(fid);
|
return PTR_ERR(fid);
|
||||||
|
ret = v9fs_fid_xattr_get(fid, name, buffer, buffer_size);
|
||||||
|
p9_client_clunk(fid);
|
||||||
|
|
||||||
return v9fs_fid_xattr_get(fid, name, buffer, buffer_size);
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -96,8 +99,15 @@ ssize_t v9fs_xattr_get(struct dentry *dentry, const char *name,
|
||||||
int v9fs_xattr_set(struct dentry *dentry, const char *name,
|
int v9fs_xattr_set(struct dentry *dentry, const char *name,
|
||||||
const void *value, size_t value_len, int flags)
|
const void *value, size_t value_len, int flags)
|
||||||
{
|
{
|
||||||
struct p9_fid *fid = v9fs_fid_lookup(dentry);
|
int ret;
|
||||||
return v9fs_fid_xattr_set(fid, name, value, value_len, flags);
|
struct p9_fid *fid;
|
||||||
|
|
||||||
|
fid = v9fs_fid_lookup(dentry);
|
||||||
|
if (IS_ERR(fid))
|
||||||
|
return PTR_ERR(fid);
|
||||||
|
ret = v9fs_fid_xattr_set(fid, name, value, value_len, flags);
|
||||||
|
p9_client_clunk(fid);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int v9fs_fid_xattr_set(struct p9_fid *fid, const char *name,
|
int v9fs_fid_xattr_set(struct p9_fid *fid, const char *name,
|
||||||
|
|
|
@ -140,10 +140,16 @@ struct p9_client {
|
||||||
*
|
*
|
||||||
* TODO: This needs lots of explanation.
|
* TODO: This needs lots of explanation.
|
||||||
*/
|
*/
|
||||||
|
enum fid_source {
|
||||||
|
FID_FROM_OTHER,
|
||||||
|
FID_FROM_INODE,
|
||||||
|
FID_FROM_DENTRY,
|
||||||
|
};
|
||||||
|
|
||||||
struct p9_fid {
|
struct p9_fid {
|
||||||
struct p9_client *clnt;
|
struct p9_client *clnt;
|
||||||
u32 fid;
|
u32 fid;
|
||||||
|
atomic_t count;
|
||||||
int mode;
|
int mode;
|
||||||
struct p9_qid qid;
|
struct p9_qid qid;
|
||||||
u32 iounit;
|
u32 iounit;
|
||||||
|
|
|
@ -901,6 +901,7 @@ static struct p9_fid *p9_fid_create(struct p9_client *clnt)
|
||||||
fid->clnt = clnt;
|
fid->clnt = clnt;
|
||||||
fid->rdir = NULL;
|
fid->rdir = NULL;
|
||||||
fid->fid = 0;
|
fid->fid = 0;
|
||||||
|
atomic_set(&fid->count, 1);
|
||||||
|
|
||||||
idr_preload(GFP_KERNEL);
|
idr_preload(GFP_KERNEL);
|
||||||
spin_lock_irq(&clnt->lock);
|
spin_lock_irq(&clnt->lock);
|
||||||
|
@ -908,7 +909,6 @@ static struct p9_fid *p9_fid_create(struct p9_client *clnt)
|
||||||
GFP_NOWAIT);
|
GFP_NOWAIT);
|
||||||
spin_unlock_irq(&clnt->lock);
|
spin_unlock_irq(&clnt->lock);
|
||||||
idr_preload_end();
|
idr_preload_end();
|
||||||
|
|
||||||
if (!ret)
|
if (!ret)
|
||||||
return fid;
|
return fid;
|
||||||
|
|
||||||
|
@ -1187,7 +1187,6 @@ struct p9_fid *p9_client_walk(struct p9_fid *oldfid, uint16_t nwname,
|
||||||
|
|
||||||
p9_debug(P9_DEBUG_9P, ">>> TWALK fids %d,%d nwname %ud wname[0] %s\n",
|
p9_debug(P9_DEBUG_9P, ">>> TWALK fids %d,%d nwname %ud wname[0] %s\n",
|
||||||
oldfid->fid, fid->fid, nwname, wnames ? wnames[0] : NULL);
|
oldfid->fid, fid->fid, nwname, wnames ? wnames[0] : NULL);
|
||||||
|
|
||||||
req = p9_client_rpc(clnt, P9_TWALK, "ddT", oldfid->fid, fid->fid,
|
req = p9_client_rpc(clnt, P9_TWALK, "ddT", oldfid->fid, fid->fid,
|
||||||
nwname, wnames);
|
nwname, wnames);
|
||||||
if (IS_ERR(req)) {
|
if (IS_ERR(req)) {
|
||||||
|
@ -1461,12 +1460,14 @@ int p9_client_clunk(struct p9_fid *fid)
|
||||||
struct p9_req_t *req;
|
struct p9_req_t *req;
|
||||||
int retries = 0;
|
int retries = 0;
|
||||||
|
|
||||||
if (!fid) {
|
if (!fid || IS_ERR(fid)) {
|
||||||
pr_warn("%s (%d): Trying to clunk with NULL fid\n",
|
pr_warn("%s (%d): Trying to clunk with invalid fid\n",
|
||||||
__func__, task_pid_nr(current));
|
__func__, task_pid_nr(current));
|
||||||
dump_stack();
|
dump_stack();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
if (!atomic_dec_and_test(&fid->count))
|
||||||
|
return 0;
|
||||||
|
|
||||||
again:
|
again:
|
||||||
p9_debug(P9_DEBUG_9P, ">>> TCLUNK fid %d (try %d)\n", fid->fid,
|
p9_debug(P9_DEBUG_9P, ">>> TCLUNK fid %d (try %d)\n", fid->fid,
|
||||||
|
|
Loading…
Reference in New Issue