Skip to content

Commit c134dea

Browse files
author
Al Viro
committed
spufs: fix gang directory lifetimes
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: 877907d "[POWERPC] spufs: Fix gang destroy leaks" Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
1 parent d1ca869 commit c134dea

File tree

3 files changed

+49
-8
lines changed

3 files changed

+49
-8
lines changed

arch/powerpc/platforms/cell/spufs/gang.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ struct spu_gang *alloc_spu_gang(void)
2525
mutex_init(&gang->aff_mutex);
2626
INIT_LIST_HEAD(&gang->list);
2727
INIT_LIST_HEAD(&gang->aff_list_head);
28+
gang->alive = 1;
2829

2930
out:
3031
return gang;

arch/powerpc/platforms/cell/spufs/inode.c

Lines changed: 46 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,23 @@ static int spufs_fill_dir(struct dentry *dir,
201201
return 0;
202202
}
203203

204+
static void unuse_gang(struct dentry *dir)
205+
{
206+
struct inode *inode = dir->d_inode;
207+
struct spu_gang *gang = SPUFS_I(inode)->i_gang;
208+
209+
if (gang) {
210+
bool dead;
211+
212+
inode_lock(inode); // exclusion with spufs_create_context()
213+
dead = !--gang->alive;
214+
inode_unlock(inode);
215+
216+
if (dead)
217+
simple_recursive_removal(dir, NULL);
218+
}
219+
}
220+
204221
static int spufs_dir_close(struct inode *inode, struct file *file)
205222
{
206223
struct inode *parent;
@@ -215,6 +232,7 @@ static int spufs_dir_close(struct inode *inode, struct file *file)
215232
inode_unlock(parent);
216233
WARN_ON(ret);
217234

235+
unuse_gang(dir->d_parent);
218236
return dcache_dir_close(inode, file);
219237
}
220238

@@ -407,7 +425,7 @@ spufs_create_context(struct inode *inode, struct dentry *dentry,
407425
{
408426
int ret;
409427
int affinity;
410-
struct spu_gang *gang;
428+
struct spu_gang *gang = SPUFS_I(inode)->i_gang;
411429
struct spu_context *neighbor;
412430
struct path path = {.mnt = mnt, .dentry = dentry};
413431

@@ -422,11 +440,15 @@ spufs_create_context(struct inode *inode, struct dentry *dentry,
422440
if ((flags & SPU_CREATE_ISOLATE) && !isolated_loader)
423441
return -ENODEV;
424442

425-
gang = NULL;
443+
if (gang) {
444+
if (!gang->alive)
445+
return -ENOENT;
446+
gang->alive++;
447+
}
448+
426449
neighbor = NULL;
427450
affinity = flags & (SPU_CREATE_AFFINITY_MEM | SPU_CREATE_AFFINITY_SPU);
428451
if (affinity) {
429-
gang = SPUFS_I(inode)->i_gang;
430452
if (!gang)
431453
return -EINVAL;
432454
mutex_lock(&gang->aff_mutex);
@@ -455,6 +477,8 @@ spufs_create_context(struct inode *inode, struct dentry *dentry,
455477
out_aff_unlock:
456478
if (affinity)
457479
mutex_unlock(&gang->aff_mutex);
480+
if (ret && gang)
481+
gang->alive--; // can't reach 0
458482
return ret;
459483
}
460484

@@ -484,6 +508,7 @@ spufs_mkgang(struct inode *dir, struct dentry *dentry, umode_t mode)
484508
inode->i_fop = &simple_dir_operations;
485509

486510
d_instantiate(dentry, inode);
511+
dget(dentry);
487512
inc_nlink(dir);
488513
inc_nlink(d_inode(dentry));
489514
return ret;
@@ -494,6 +519,21 @@ spufs_mkgang(struct inode *dir, struct dentry *dentry, umode_t mode)
494519
return ret;
495520
}
496521

522+
static int spufs_gang_close(struct inode *inode, struct file *file)
523+
{
524+
unuse_gang(file->f_path.dentry);
525+
return dcache_dir_close(inode, file);
526+
}
527+
528+
static const struct file_operations spufs_gang_fops = {
529+
.open = dcache_dir_open,
530+
.release = spufs_gang_close,
531+
.llseek = dcache_dir_lseek,
532+
.read = generic_read_dir,
533+
.iterate_shared = dcache_readdir,
534+
.fsync = noop_fsync,
535+
};
536+
497537
static int spufs_gang_open(const struct path *path)
498538
{
499539
int ret;
@@ -513,7 +553,7 @@ static int spufs_gang_open(const struct path *path)
513553
return PTR_ERR(filp);
514554
}
515555

516-
filp->f_op = &simple_dir_operations;
556+
filp->f_op = &spufs_gang_fops;
517557
fd_install(ret, filp);
518558
return ret;
519559
}
@@ -528,10 +568,8 @@ static int spufs_create_gang(struct inode *inode,
528568
ret = spufs_mkgang(inode, dentry, mode & 0777);
529569
if (!ret) {
530570
ret = spufs_gang_open(&path);
531-
if (ret < 0) {
532-
int err = simple_rmdir(inode, dentry);
533-
WARN_ON(err);
534-
}
571+
if (ret < 0)
572+
unuse_gang(dentry);
535573
}
536574
return ret;
537575
}

arch/powerpc/platforms/cell/spufs/spufs.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,8 @@ struct spu_gang {
151151
int aff_flags;
152152
struct spu *aff_ref_spu;
153153
atomic_t aff_sched_count;
154+
155+
int alive;
154156
};
155157

156158
/* Flag bits for spu_gang aff_flags */

0 commit comments

Comments
 (0)