[POWERPC] cell: Unify spufs syscall path

At present, a built-in spufs will not use the spufs_calls callbacks, but
directly call sys_spu_create.  This saves us an indirect branch, but
means we have duplicated functions - one for CONFIG_SPU_FS=y and one for
=m.

This change unifies the spufs syscall path, and provides access to the
spufs_calls structure through a get/put pair.  At present, the only user
of the spufs_calls structure is spu_syscalls.c, but this will facilitate
adding the coredump calls later.

Everyone likes numbers, right?  Here's a before/after comparison with
CONFIG_SPU_FS=y, doing spu_create(); close(); 64k times.

Before:
	[jk@cell ~]$ time ./spu_create
	performing 65536 spu_create calls

	real    0m24.075s
	user    0m0.146s
	sys     0m23.925s

After:
	[jk@cell ~]$ time ./spu_create
	performing 65536 spu_create calls

	real    0m24.777s
	user    0m0.141s
	sys     0m24.631s

So, we're adding around 11us per syscall, at the benefit of having
only one syscall path.

Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
Signed-off-by: Paul Mackerras <paulus@samba.org>
This commit is contained in:
Jeremy Kerr 2007-09-19 14:38:12 +10:00 committed by Paul Mackerras
parent 36ddbb1380
commit 98f06978ff
5 changed files with 72 additions and 97 deletions

View File

@ -13,15 +13,13 @@ obj-$(CONFIG_PPC_CELL_NATIVE) += smp.o
endif
# needed only when building loadable spufs.ko
spufs-modular-$(CONFIG_SPU_FS) += spu_syscalls.o
spu-priv1-$(CONFIG_PPC_CELL_NATIVE) += spu_priv1_mmio.o
spu-manage-$(CONFIG_PPC_CELLEB) += spu_manage.o
spu-manage-$(CONFIG_PPC_CELL_NATIVE) += spu_manage.o
obj-$(CONFIG_SPU_BASE) += spu_callbacks.o spu_base.o \
spu_coredump.o \
$(spufs-modular-m) \
spu_coredump.o spu_syscalls.o \
$(spu-priv1-y) \
$(spu-manage-y) \
spufs/

View File

@ -22,42 +22,70 @@
#include <linux/file.h>
#include <linux/module.h>
#include <linux/syscalls.h>
#include <linux/rcupdate.h>
#include <asm/spu.h>
struct spufs_calls spufs_calls = {
.owner = NULL,
};
/* protected by rcu */
static struct spufs_calls *spufs_calls;
/* These stub syscalls are needed to have the actual implementation
* within a loadable module. When spufs is built into the kernel,
* this file is not used and the syscalls directly enter the fs code */
#ifdef CONFIG_SPU_FS_MODULE
static inline struct spufs_calls *spufs_calls_get(void)
{
struct spufs_calls *calls = NULL;
rcu_read_lock();
calls = rcu_dereference(spufs_calls);
if (calls && !try_module_get(calls->owner))
calls = NULL;
rcu_read_unlock();
return calls;
}
static inline void spufs_calls_put(struct spufs_calls *calls)
{
BUG_ON(calls != spufs_calls);
/* we don't need to rcu this, as we hold a reference to the module */
module_put(spufs_calls->owner);
}
#else /* !defined CONFIG_SPU_FS_MODULE */
static inline struct spufs_calls *spufs_calls_get(void)
{
return spufs_calls;
}
static inline void spufs_calls_put(struct spufs_calls *calls) { }
#endif /* CONFIG_SPU_FS_MODULE */
asmlinkage long sys_spu_create(const char __user *name,
unsigned int flags, mode_t mode, int neighbor_fd)
{
long ret;
struct module *owner = spufs_calls.owner;
struct file *neighbor;
int fput_needed;
struct spufs_calls *calls;
ret = -ENOSYS;
if (owner && try_module_get(owner)) {
if (flags & SPU_CREATE_AFFINITY_SPU) {
neighbor = fget_light(neighbor_fd, &fput_needed);
ret = -EBADF;
if (neighbor) {
ret = spufs_calls.create_thread(name, flags,
mode, neighbor);
fput_light(neighbor, fput_needed);
}
calls = spufs_calls_get();
if (!calls)
return -ENOSYS;
if (flags & SPU_CREATE_AFFINITY_SPU) {
ret = -EBADF;
neighbor = fget_light(neighbor_fd, &fput_needed);
if (neighbor) {
ret = calls->create_thread(name, flags, mode, neighbor);
fput_light(neighbor, fput_needed);
}
else {
ret = spufs_calls.create_thread(name, flags,
mode, NULL);
}
module_put(owner);
}
} else
ret = calls->create_thread(name, flags, mode, NULL);
spufs_calls_put(calls);
return ret;
}
@ -66,37 +94,37 @@ asmlinkage long sys_spu_run(int fd, __u32 __user *unpc, __u32 __user *ustatus)
long ret;
struct file *filp;
int fput_needed;
struct module *owner = spufs_calls.owner;
struct spufs_calls *calls;
ret = -ENOSYS;
if (owner && try_module_get(owner)) {
ret = -EBADF;
filp = fget_light(fd, &fput_needed);
if (filp) {
ret = spufs_calls.spu_run(filp, unpc, ustatus);
fput_light(filp, fput_needed);
}
module_put(owner);
calls = spufs_calls_get();
if (!calls)
return -ENOSYS;
ret = -EBADF;
filp = fget_light(fd, &fput_needed);
if (filp) {
ret = calls->spu_run(filp, unpc, ustatus);
fput_light(filp, fput_needed);
}
spufs_calls_put(calls);
return ret;
}
int register_spu_syscalls(struct spufs_calls *calls)
{
if (spufs_calls.owner)
if (spufs_calls)
return -EBUSY;
spufs_calls.create_thread = calls->create_thread;
spufs_calls.spu_run = calls->spu_run;
smp_mb();
spufs_calls.owner = calls->owner;
rcu_assign_pointer(spufs_calls, calls);
return 0;
}
EXPORT_SYMBOL_GPL(register_spu_syscalls);
void unregister_spu_syscalls(struct spufs_calls *calls)
{
BUG_ON(spufs_calls.owner != calls->owner);
spufs_calls.owner = NULL;
BUG_ON(spufs_calls->owner != calls->owner);
rcu_assign_pointer(spufs_calls, NULL);
synchronize_rcu();
}
EXPORT_SYMBOL_GPL(unregister_spu_syscalls);

View File

@ -200,6 +200,7 @@ extern struct tree_descr spufs_dir_contents[];
extern struct tree_descr spufs_dir_nosched_contents[];
/* system call implementation */
extern struct spufs_calls spufs_calls;
long spufs_run_spu(struct spu_context *ctx, u32 *npc, u32 *status);
long spufs_create(struct nameidata *nd, unsigned int flags,
mode_t mode, struct file *filp);

View File

@ -58,24 +58,6 @@ out:
return ret;
}
#ifndef MODULE
asmlinkage long sys_spu_run(int fd, __u32 __user *unpc, __u32 __user *ustatus)
{
int fput_needed;
struct file *filp;
long ret;
ret = -EBADF;
filp = fget_light(fd, &fput_needed);
if (filp) {
ret = do_spu_run(filp, unpc, ustatus);
fput_light(filp, fput_needed);
}
return ret;
}
#endif
static long do_spu_create(const char __user *pathname, unsigned int flags,
mode_t mode, struct file *neighbor)
{
@ -99,30 +81,6 @@ static long do_spu_create(const char __user *pathname, unsigned int flags,
return ret;
}
#ifndef MODULE
asmlinkage long sys_spu_create(const char __user *pathname, unsigned int flags,
mode_t mode, int neighbor_fd)
{
int fput_needed;
struct file *neighbor;
long ret;
if (flags & SPU_CREATE_AFFINITY_SPU) {
ret = -EBADF;
neighbor = fget_light(neighbor_fd, &fput_needed);
if (neighbor) {
ret = do_spu_create(pathname, flags, mode, neighbor);
fput_light(neighbor, fput_needed);
}
}
else {
ret = do_spu_create(pathname, flags, mode, NULL);
}
return ret;
}
#endif
struct spufs_calls spufs_calls = {
.create_thread = do_spu_create,
.spu_run = do_spu_run,

View File

@ -238,14 +238,14 @@ extern long spu_sys_callback(struct spu_syscall_block *s);
/* syscalls implemented in spufs */
struct file;
extern struct spufs_calls {
struct spufs_calls {
asmlinkage long (*create_thread)(const char __user *name,
unsigned int flags, mode_t mode,
struct file *neighbor);
asmlinkage long (*spu_run)(struct file *filp, __u32 __user *unpc,
__u32 __user *ustatus);
struct module *owner;
} spufs_calls;
};
/* coredump calls implemented in spufs */
struct spu_coredump_calls {
@ -274,18 +274,8 @@ struct spu_coredump_calls {
#define SPU_CREATE_FLAG_ALL 0x003f /* mask of all valid flags */
#ifdef CONFIG_SPU_FS_MODULE
int register_spu_syscalls(struct spufs_calls *calls);
void unregister_spu_syscalls(struct spufs_calls *calls);
#else
static inline int register_spu_syscalls(struct spufs_calls *calls)
{
return 0;
}
static inline void unregister_spu_syscalls(struct spufs_calls *calls)
{
}
#endif /* MODULE */
int register_arch_coredump_calls(struct spu_coredump_calls *calls);
void unregister_arch_coredump_calls(struct spu_coredump_calls *calls);