sst-linux/arch/powerpc/platforms/cell/spufs/gang.c
Al Viro 880e7b3da2 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>
2025-04-10 14:33:40 +02:00

76 lines
1.5 KiB
C

// SPDX-License-Identifier: GPL-2.0-or-later
/*
* SPU file system
*
* (C) Copyright IBM Deutschland Entwicklung GmbH 2005
*
* Author: Arnd Bergmann <arndb@de.ibm.com>
*/
#include <linux/list.h>
#include <linux/slab.h>
#include "spufs.h"
struct spu_gang *alloc_spu_gang(void)
{
struct spu_gang *gang;
gang = kzalloc(sizeof *gang, GFP_KERNEL);
if (!gang)
goto out;
kref_init(&gang->kref);
mutex_init(&gang->mutex);
mutex_init(&gang->aff_mutex);
INIT_LIST_HEAD(&gang->list);
INIT_LIST_HEAD(&gang->aff_list_head);
gang->alive = 1;
out:
return gang;
}
static void destroy_spu_gang(struct kref *kref)
{
struct spu_gang *gang;
gang = container_of(kref, struct spu_gang, kref);
WARN_ON(gang->contexts || !list_empty(&gang->list));
kfree(gang);
}
struct spu_gang *get_spu_gang(struct spu_gang *gang)
{
kref_get(&gang->kref);
return gang;
}
int put_spu_gang(struct spu_gang *gang)
{
return kref_put(&gang->kref, &destroy_spu_gang);
}
void spu_gang_add_ctx(struct spu_gang *gang, struct spu_context *ctx)
{
mutex_lock(&gang->mutex);
ctx->gang = get_spu_gang(gang);
list_add(&ctx->gang_list, &gang->list);
gang->contexts++;
mutex_unlock(&gang->mutex);
}
void spu_gang_remove_ctx(struct spu_gang *gang, struct spu_context *ctx)
{
mutex_lock(&gang->mutex);
WARN_ON(ctx->gang != gang);
if (!list_empty(&ctx->aff_list)) {
list_del_init(&ctx->aff_list);
gang->aff_flags &= ~AFF_OFFSETS_SET;
}
list_del_init(&ctx->gang_list);
gang->contexts--;
mutex_unlock(&gang->mutex);
put_spu_gang(gang);
}