spufs: fix gang directory lifetimes
[ Upstream commit c134deabf4784e155d360744d4a6a835b9de4dd4 ]
prior to "[POWERPC] spufs: Fix gang destroy leaks" we used to have
a problem with gang lifetimes - creation of a gang returns opened
gang directory, which normally gets removed when that gets closed,
but if somebody has created a context belonging to that gang and
kept it alive until the gang got closed, removal failed and we
ended up with a leak.
Unfortunately, it had been fixed the wrong way. Dentry of gang
directory was no longer pinned, and rmdir on close was gone.
One problem was that failure of open kept calling simple_rmdir()
as cleanup, which meant an unbalanced dput(). Another bug was
in the success case - gang creation incremented link count on
root directory, but that was no longer undone when gang got
destroyed.
Fix consists of
* reverting the commit in question
* adding a counter to gang, protected by ->i_rwsem
of gang directory inode.
* having it set to 1 at creation time, dropped
in both spufs_dir_close() and spufs_gang_close() and bumped
in spufs_create_context(), provided that it's not 0.
* using simple_recursive_removal() to take the gang
directory out when counter reaches zero.
Fixes: 877907d37d
"[POWERPC] spufs: Fix gang destroy leaks"
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
parent
96de7fbdc2
commit
880e7b3da2
@ -25,6 +25,7 @@ struct spu_gang *alloc_spu_gang(void)
|
|||||||
mutex_init(&gang->aff_mutex);
|
mutex_init(&gang->aff_mutex);
|
||||||
INIT_LIST_HEAD(&gang->list);
|
INIT_LIST_HEAD(&gang->list);
|
||||||
INIT_LIST_HEAD(&gang->aff_list_head);
|
INIT_LIST_HEAD(&gang->aff_list_head);
|
||||||
|
gang->alive = 1;
|
||||||
|
|
||||||
out:
|
out:
|
||||||
return gang;
|
return gang;
|
||||||
|
@ -200,6 +200,23 @@ static int spufs_fill_dir(struct dentry *dir,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void unuse_gang(struct dentry *dir)
|
||||||
|
{
|
||||||
|
struct inode *inode = dir->d_inode;
|
||||||
|
struct spu_gang *gang = SPUFS_I(inode)->i_gang;
|
||||||
|
|
||||||
|
if (gang) {
|
||||||
|
bool dead;
|
||||||
|
|
||||||
|
inode_lock(inode); // exclusion with spufs_create_context()
|
||||||
|
dead = !--gang->alive;
|
||||||
|
inode_unlock(inode);
|
||||||
|
|
||||||
|
if (dead)
|
||||||
|
simple_recursive_removal(dir, NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static int spufs_dir_close(struct inode *inode, struct file *file)
|
static int spufs_dir_close(struct inode *inode, struct file *file)
|
||||||
{
|
{
|
||||||
struct inode *parent;
|
struct inode *parent;
|
||||||
@ -214,6 +231,7 @@ static int spufs_dir_close(struct inode *inode, struct file *file)
|
|||||||
inode_unlock(parent);
|
inode_unlock(parent);
|
||||||
WARN_ON(ret);
|
WARN_ON(ret);
|
||||||
|
|
||||||
|
unuse_gang(dir->d_parent);
|
||||||
return dcache_dir_close(inode, file);
|
return dcache_dir_close(inode, file);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -406,7 +424,7 @@ spufs_create_context(struct inode *inode, struct dentry *dentry,
|
|||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
int affinity;
|
int affinity;
|
||||||
struct spu_gang *gang;
|
struct spu_gang *gang = SPUFS_I(inode)->i_gang;
|
||||||
struct spu_context *neighbor;
|
struct spu_context *neighbor;
|
||||||
struct path path = {.mnt = mnt, .dentry = dentry};
|
struct path path = {.mnt = mnt, .dentry = dentry};
|
||||||
|
|
||||||
@ -421,11 +439,15 @@ spufs_create_context(struct inode *inode, struct dentry *dentry,
|
|||||||
if ((flags & SPU_CREATE_ISOLATE) && !isolated_loader)
|
if ((flags & SPU_CREATE_ISOLATE) && !isolated_loader)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
gang = NULL;
|
if (gang) {
|
||||||
|
if (!gang->alive)
|
||||||
|
return -ENOENT;
|
||||||
|
gang->alive++;
|
||||||
|
}
|
||||||
|
|
||||||
neighbor = NULL;
|
neighbor = NULL;
|
||||||
affinity = flags & (SPU_CREATE_AFFINITY_MEM | SPU_CREATE_AFFINITY_SPU);
|
affinity = flags & (SPU_CREATE_AFFINITY_MEM | SPU_CREATE_AFFINITY_SPU);
|
||||||
if (affinity) {
|
if (affinity) {
|
||||||
gang = SPUFS_I(inode)->i_gang;
|
|
||||||
if (!gang)
|
if (!gang)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
mutex_lock(&gang->aff_mutex);
|
mutex_lock(&gang->aff_mutex);
|
||||||
@ -454,6 +476,8 @@ spufs_create_context(struct inode *inode, struct dentry *dentry,
|
|||||||
out_aff_unlock:
|
out_aff_unlock:
|
||||||
if (affinity)
|
if (affinity)
|
||||||
mutex_unlock(&gang->aff_mutex);
|
mutex_unlock(&gang->aff_mutex);
|
||||||
|
if (ret && gang)
|
||||||
|
gang->alive--; // can't reach 0
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -483,6 +507,7 @@ spufs_mkgang(struct inode *dir, struct dentry *dentry, umode_t mode)
|
|||||||
inode->i_fop = &simple_dir_operations;
|
inode->i_fop = &simple_dir_operations;
|
||||||
|
|
||||||
d_instantiate(dentry, inode);
|
d_instantiate(dentry, inode);
|
||||||
|
dget(dentry);
|
||||||
inc_nlink(dir);
|
inc_nlink(dir);
|
||||||
inc_nlink(d_inode(dentry));
|
inc_nlink(d_inode(dentry));
|
||||||
return ret;
|
return ret;
|
||||||
@ -493,6 +518,21 @@ out:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int spufs_gang_close(struct inode *inode, struct file *file)
|
||||||
|
{
|
||||||
|
unuse_gang(file->f_path.dentry);
|
||||||
|
return dcache_dir_close(inode, file);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct file_operations spufs_gang_fops = {
|
||||||
|
.open = dcache_dir_open,
|
||||||
|
.release = spufs_gang_close,
|
||||||
|
.llseek = dcache_dir_lseek,
|
||||||
|
.read = generic_read_dir,
|
||||||
|
.iterate_shared = dcache_readdir,
|
||||||
|
.fsync = noop_fsync,
|
||||||
|
};
|
||||||
|
|
||||||
static int spufs_gang_open(const struct path *path)
|
static int spufs_gang_open(const struct path *path)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
@ -512,7 +552,7 @@ static int spufs_gang_open(const struct path *path)
|
|||||||
return PTR_ERR(filp);
|
return PTR_ERR(filp);
|
||||||
}
|
}
|
||||||
|
|
||||||
filp->f_op = &simple_dir_operations;
|
filp->f_op = &spufs_gang_fops;
|
||||||
fd_install(ret, filp);
|
fd_install(ret, filp);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -527,10 +567,8 @@ static int spufs_create_gang(struct inode *inode,
|
|||||||
ret = spufs_mkgang(inode, dentry, mode & 0777);
|
ret = spufs_mkgang(inode, dentry, mode & 0777);
|
||||||
if (!ret) {
|
if (!ret) {
|
||||||
ret = spufs_gang_open(&path);
|
ret = spufs_gang_open(&path);
|
||||||
if (ret < 0) {
|
if (ret < 0)
|
||||||
int err = simple_rmdir(inode, dentry);
|
unuse_gang(dentry);
|
||||||
WARN_ON(err);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -151,6 +151,8 @@ struct spu_gang {
|
|||||||
int aff_flags;
|
int aff_flags;
|
||||||
struct spu *aff_ref_spu;
|
struct spu *aff_ref_spu;
|
||||||
atomic_t aff_sched_count;
|
atomic_t aff_sched_count;
|
||||||
|
|
||||||
|
int alive;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Flag bits for spu_gang aff_flags */
|
/* Flag bits for spu_gang aff_flags */
|
||||||
|
Loading…
Reference in New Issue
Block a user