aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorArnd Bergmann <arnd@arndb.de>2006-10-04 11:26:15 -0400
committerPaul Mackerras <paulus@samba.org>2006-10-04 19:21:01 -0400
commit6263203ed6e9ff107129a1ebe613290b342a4465 (patch)
treedc7d68b783fed2b5ffcb8905c62086ebe078368b /arch
parent9add11daeee2f6d69f6b86237f197824332a4a3b (diff)
[POWERPC] spufs: Add infrastructure needed for gang scheduling
Add the concept of a gang to spufs as a new type of object. So far, this has no impact whatsover on scheduling, but makes it possible to add that later. A new type of object in spufs is now a spu_gang. It is created with the spu_create system call with the flags argument set to SPU_CREATE_GANG (0x2). Inside of a spu_gang, it is then possible to create spu_context objects, which until now was only possible at the root of spufs. There is a new member in struct spu_context pointing to the spu_gang it belongs to, if any. The spu_gang maintains a list of spu_context structures that are its children. This information can then be used in the scheduler in the future. There is still a bug that needs to be resolved in this basic infrastructure regarding the order in which objects are removed. When the spu_gang file descriptor is closed before the spu_context descriptors, we leak the dentry and inode for the gang. Any ideas how to cleanly solve this are appreciated. Signed-off-by: Arnd Bergmann <arnd.bergmann@de.ibm.com> Signed-off-by: Paul Mackerras <paulus@samba.org>
Diffstat (limited to 'arch')
-rw-r--r--arch/powerpc/platforms/cell/spufs/Makefile2
-rw-r--r--arch/powerpc/platforms/cell/spufs/context.c6
-rw-r--r--arch/powerpc/platforms/cell/spufs/gang.c81
-rw-r--r--arch/powerpc/platforms/cell/spufs/inode.c225
-rw-r--r--arch/powerpc/platforms/cell/spufs/spufs.h24
-rw-r--r--arch/powerpc/platforms/cell/spufs/syscalls.c2
6 files changed, 299 insertions, 41 deletions
diff --git a/arch/powerpc/platforms/cell/spufs/Makefile b/arch/powerpc/platforms/cell/spufs/Makefile
index bb5dc634272c..ecdfbb35f82e 100644
--- a/arch/powerpc/platforms/cell/spufs/Makefile
+++ b/arch/powerpc/platforms/cell/spufs/Makefile
@@ -2,7 +2,7 @@ obj-y += switch.o
2 2
3obj-$(CONFIG_SPU_FS) += spufs.o 3obj-$(CONFIG_SPU_FS) += spufs.o
4spufs-y += inode.o file.o context.o syscalls.o 4spufs-y += inode.o file.o context.o syscalls.o
5spufs-y += sched.o backing_ops.o hw_ops.o run.o 5spufs-y += sched.o backing_ops.o hw_ops.o run.o gang.o
6 6
7# Rules to build switch.o with the help of SPU tool chain 7# Rules to build switch.o with the help of SPU tool chain
8SPU_CROSS := spu- 8SPU_CROSS := spu-
diff --git a/arch/powerpc/platforms/cell/spufs/context.c b/arch/powerpc/platforms/cell/spufs/context.c
index 36439c5e9f2d..034cf6af53a2 100644
--- a/arch/powerpc/platforms/cell/spufs/context.c
+++ b/arch/powerpc/platforms/cell/spufs/context.c
@@ -27,7 +27,7 @@
27#include <asm/spu_csa.h> 27#include <asm/spu_csa.h>
28#include "spufs.h" 28#include "spufs.h"
29 29
30struct spu_context *alloc_spu_context(void) 30struct spu_context *alloc_spu_context(struct spu_gang *gang)
31{ 31{
32 struct spu_context *ctx; 32 struct spu_context *ctx;
33 ctx = kzalloc(sizeof *ctx, GFP_KERNEL); 33 ctx = kzalloc(sizeof *ctx, GFP_KERNEL);
@@ -51,6 +51,8 @@ struct spu_context *alloc_spu_context(void)
51 ctx->state = SPU_STATE_SAVED; 51 ctx->state = SPU_STATE_SAVED;
52 ctx->ops = &spu_backing_ops; 52 ctx->ops = &spu_backing_ops;
53 ctx->owner = get_task_mm(current); 53 ctx->owner = get_task_mm(current);
54 if (gang)
55 spu_gang_add_ctx(gang, ctx);
54 goto out; 56 goto out;
55out_free: 57out_free:
56 kfree(ctx); 58 kfree(ctx);
@@ -67,6 +69,8 @@ void destroy_spu_context(struct kref *kref)
67 spu_deactivate(ctx); 69 spu_deactivate(ctx);
68 up_write(&ctx->state_sema); 70 up_write(&ctx->state_sema);
69 spu_fini_csa(&ctx->csa); 71 spu_fini_csa(&ctx->csa);
72 if (ctx->gang)
73 spu_gang_remove_ctx(ctx->gang, ctx);
70 kfree(ctx); 74 kfree(ctx);
71} 75}
72 76
diff --git a/arch/powerpc/platforms/cell/spufs/gang.c b/arch/powerpc/platforms/cell/spufs/gang.c
new file mode 100644
index 000000000000..212ea78f9051
--- /dev/null
+++ b/arch/powerpc/platforms/cell/spufs/gang.c
@@ -0,0 +1,81 @@
1/*
2 * SPU file system
3 *
4 * (C) Copyright IBM Deutschland Entwicklung GmbH 2005
5 *
6 * Author: Arnd Bergmann <arndb@de.ibm.com>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2, or (at your option)
11 * any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22
23#include <linux/list.h>
24#include <linux/slab.h>
25
26#include "spufs.h"
27
28struct spu_gang *alloc_spu_gang(void)
29{
30 struct spu_gang *gang;
31
32 gang = kzalloc(sizeof *gang, GFP_KERNEL);
33 if (!gang)
34 goto out;
35
36 kref_init(&gang->kref);
37 mutex_init(&gang->mutex);
38 INIT_LIST_HEAD(&gang->list);
39
40out:
41 return gang;
42}
43
44static void destroy_spu_gang(struct kref *kref)
45{
46 struct spu_gang *gang;
47 gang = container_of(kref, struct spu_gang, kref);
48 WARN_ON(gang->contexts || !list_empty(&gang->list));
49 kfree(gang);
50}
51
52struct spu_gang *get_spu_gang(struct spu_gang *gang)
53{
54 kref_get(&gang->kref);
55 return gang;
56}
57
58int put_spu_gang(struct spu_gang *gang)
59{
60 return kref_put(&gang->kref, &destroy_spu_gang);
61}
62
63void spu_gang_add_ctx(struct spu_gang *gang, struct spu_context *ctx)
64{
65 mutex_lock(&gang->mutex);
66 ctx->gang = get_spu_gang(gang);
67 list_add(&ctx->gang_list, &gang->list);
68 gang->contexts++;
69 mutex_unlock(&gang->mutex);
70}
71
72void spu_gang_remove_ctx(struct spu_gang *gang, struct spu_context *ctx)
73{
74 mutex_lock(&gang->mutex);
75 WARN_ON(ctx->gang != gang);
76 list_del_init(&ctx->gang_list);
77 gang->contexts--;
78 mutex_unlock(&gang->mutex);
79
80 put_spu_gang(gang);
81}
diff --git a/arch/powerpc/platforms/cell/spufs/inode.c b/arch/powerpc/platforms/cell/spufs/inode.c
index 8cc615ff3637..427d00a4f6a0 100644
--- a/arch/powerpc/platforms/cell/spufs/inode.c
+++ b/arch/powerpc/platforms/cell/spufs/inode.c
@@ -50,6 +50,10 @@ spufs_alloc_inode(struct super_block *sb)
50 ei = kmem_cache_alloc(spufs_inode_cache, SLAB_KERNEL); 50 ei = kmem_cache_alloc(spufs_inode_cache, SLAB_KERNEL);
51 if (!ei) 51 if (!ei)
52 return NULL; 52 return NULL;
53
54 ei->i_gang = NULL;
55 ei->i_ctx = NULL;
56
53 return &ei->vfs_inode; 57 return &ei->vfs_inode;
54} 58}
55 59
@@ -128,14 +132,19 @@ out:
128static void 132static void
129spufs_delete_inode(struct inode *inode) 133spufs_delete_inode(struct inode *inode)
130{ 134{
131 if (SPUFS_I(inode)->i_ctx) 135 struct spufs_inode_info *ei = SPUFS_I(inode);
132 put_spu_context(SPUFS_I(inode)->i_ctx); 136
137 if (ei->i_ctx)
138 put_spu_context(ei->i_ctx);
139 if (ei->i_gang)
140 put_spu_gang(ei->i_gang);
133 clear_inode(inode); 141 clear_inode(inode);
134} 142}
135 143
136static void spufs_prune_dir(struct dentry *dir) 144static void spufs_prune_dir(struct dentry *dir)
137{ 145{
138 struct dentry *dentry, *tmp; 146 struct dentry *dentry, *tmp;
147
139 mutex_lock(&dir->d_inode->i_mutex); 148 mutex_lock(&dir->d_inode->i_mutex);
140 list_for_each_entry_safe(dentry, tmp, &dir->d_subdirs, d_u.d_child) { 149 list_for_each_entry_safe(dentry, tmp, &dir->d_subdirs, d_u.d_child) {
141 spin_lock(&dcache_lock); 150 spin_lock(&dcache_lock);
@@ -156,13 +165,13 @@ static void spufs_prune_dir(struct dentry *dir)
156 mutex_unlock(&dir->d_inode->i_mutex); 165 mutex_unlock(&dir->d_inode->i_mutex);
157} 166}
158 167
159/* Caller must hold root->i_mutex */ 168/* Caller must hold parent->i_mutex */
160static int spufs_rmdir(struct inode *root, struct dentry *dir_dentry) 169static int spufs_rmdir(struct inode *parent, struct dentry *dir)
161{ 170{
162 /* remove all entries */ 171 /* remove all entries */
163 spufs_prune_dir(dir_dentry); 172 spufs_prune_dir(dir);
164 173
165 return simple_rmdir(root, dir_dentry); 174 return simple_rmdir(parent, dir);
166} 175}
167 176
168static int spufs_fill_dir(struct dentry *dir, struct tree_descr *files, 177static int spufs_fill_dir(struct dentry *dir, struct tree_descr *files,
@@ -191,17 +200,17 @@ out:
191static int spufs_dir_close(struct inode *inode, struct file *file) 200static int spufs_dir_close(struct inode *inode, struct file *file)
192{ 201{
193 struct spu_context *ctx; 202 struct spu_context *ctx;
194 struct inode *dir; 203 struct inode *parent;
195 struct dentry *dentry; 204 struct dentry *dir;
196 int ret; 205 int ret;
197 206
198 dentry = file->f_dentry; 207 dir = file->f_dentry;
199 dir = dentry->d_parent->d_inode; 208 parent = dir->d_parent->d_inode;
200 ctx = SPUFS_I(dentry->d_inode)->i_ctx; 209 ctx = SPUFS_I(dir->d_inode)->i_ctx;
201 210
202 mutex_lock(&dir->i_mutex); 211 mutex_lock(&parent->i_mutex);
203 ret = spufs_rmdir(dir, dentry); 212 ret = spufs_rmdir(parent, dir);
204 mutex_unlock(&dir->i_mutex); 213 mutex_unlock(&parent->i_mutex);
205 WARN_ON(ret); 214 WARN_ON(ret);
206 215
207 /* We have to give up the mm_struct */ 216 /* We have to give up the mm_struct */
@@ -240,7 +249,7 @@ spufs_mkdir(struct inode *dir, struct dentry *dentry, unsigned int flags,
240 inode->i_gid = dir->i_gid; 249 inode->i_gid = dir->i_gid;
241 inode->i_mode &= S_ISGID; 250 inode->i_mode &= S_ISGID;
242 } 251 }
243 ctx = alloc_spu_context(); 252 ctx = alloc_spu_context(SPUFS_I(dir)->i_gang); /* XXX gang */
244 SPUFS_I(inode)->i_ctx = ctx; 253 SPUFS_I(inode)->i_ctx = ctx;
245 if (!ctx) 254 if (!ctx)
246 goto out_iput; 255 goto out_iput;
@@ -292,24 +301,177 @@ out:
292 return ret; 301 return ret;
293} 302}
294 303
304static int spufs_create_context(struct inode *inode,
305 struct dentry *dentry,
306 struct vfsmount *mnt, int flags, int mode)
307{
308 int ret;
309
310 ret = spufs_mkdir(inode, dentry, flags, mode & S_IRWXUGO);
311 if (ret)
312 goto out_unlock;
313
314 /*
315 * get references for dget and mntget, will be released
316 * in error path of *_open().
317 */
318 ret = spufs_context_open(dget(dentry), mntget(mnt));
319 if (ret < 0) {
320 WARN_ON(spufs_rmdir(inode, dentry));
321 mutex_unlock(&inode->i_mutex);
322 spu_forget(SPUFS_I(dentry->d_inode)->i_ctx);
323 goto out;
324 }
325
326out_unlock:
327 mutex_unlock(&inode->i_mutex);
328out:
329 dput(dentry);
330 return ret;
331}
332
333static int spufs_rmgang(struct inode *root, struct dentry *dir)
334{
335 /* FIXME: this fails if the dir is not empty,
336 which causes a leak of gangs. */
337 return simple_rmdir(root, dir);
338}
339
340static int spufs_gang_close(struct inode *inode, struct file *file)
341{
342 struct inode *parent;
343 struct dentry *dir;
344 int ret;
345
346 dir = file->f_dentry;
347 parent = dir->d_parent->d_inode;
348
349 ret = spufs_rmgang(parent, dir);
350 WARN_ON(ret);
351
352 return dcache_dir_close(inode, file);
353}
354
355struct file_operations spufs_gang_fops = {
356 .open = dcache_dir_open,
357 .release = spufs_gang_close,
358 .llseek = dcache_dir_lseek,
359 .read = generic_read_dir,
360 .readdir = dcache_readdir,
361 .fsync = simple_sync_file,
362};
363
364static int
365spufs_mkgang(struct inode *dir, struct dentry *dentry, int mode)
366{
367 int ret;
368 struct inode *inode;
369 struct spu_gang *gang;
370
371 ret = -ENOSPC;
372 inode = spufs_new_inode(dir->i_sb, mode | S_IFDIR);
373 if (!inode)
374 goto out;
375
376 ret = 0;
377 if (dir->i_mode & S_ISGID) {
378 inode->i_gid = dir->i_gid;
379 inode->i_mode &= S_ISGID;
380 }
381 gang = alloc_spu_gang();
382 SPUFS_I(inode)->i_ctx = NULL;
383 SPUFS_I(inode)->i_gang = gang;
384 if (!gang)
385 goto out_iput;
386
387 inode->i_op = &spufs_dir_inode_operations;
388 inode->i_fop = &simple_dir_operations;
389
390 d_instantiate(dentry, inode);
391 dget(dentry);
392 dir->i_nlink++;
393 dentry->d_inode->i_nlink++;
394 return ret;
395
396out_iput:
397 iput(inode);
398out:
399 return ret;
400}
401
402static int spufs_gang_open(struct dentry *dentry, struct vfsmount *mnt)
403{
404 int ret;
405 struct file *filp;
406
407 ret = get_unused_fd();
408 if (ret < 0) {
409 dput(dentry);
410 mntput(mnt);
411 goto out;
412 }
413
414 filp = dentry_open(dentry, mnt, O_RDONLY);
415 if (IS_ERR(filp)) {
416 put_unused_fd(ret);
417 ret = PTR_ERR(filp);
418 goto out;
419 }
420
421 filp->f_op = &spufs_gang_fops;
422 fd_install(ret, filp);
423out:
424 return ret;
425}
426
427static int spufs_create_gang(struct inode *inode,
428 struct dentry *dentry,
429 struct vfsmount *mnt, int mode)
430{
431 int ret;
432
433 ret = spufs_mkgang(inode, dentry, mode & S_IRWXUGO);
434 if (ret)
435 goto out;
436
437 /*
438 * get references for dget and mntget, will be released
439 * in error path of *_open().
440 */
441 ret = spufs_gang_open(dget(dentry), mntget(mnt));
442 if (ret < 0)
443 WARN_ON(spufs_rmgang(inode, dentry));
444
445out:
446 mutex_unlock(&inode->i_mutex);
447 dput(dentry);
448 return ret;
449}
450
451
295static struct file_system_type spufs_type; 452static struct file_system_type spufs_type;
296 453
297long spufs_create_thread(struct nameidata *nd, 454long spufs_create(struct nameidata *nd, unsigned int flags, mode_t mode)
298 unsigned int flags, mode_t mode)
299{ 455{
300 struct dentry *dentry; 456 struct dentry *dentry;
301 int ret; 457 int ret;
302 458
303 /* need to be at the root of spufs */
304 ret = -EINVAL; 459 ret = -EINVAL;
305 if (nd->dentry->d_sb->s_type != &spufs_type || 460 /* check if we are on spufs */
306 nd->dentry != nd->dentry->d_sb->s_root) 461 if (nd->dentry->d_sb->s_type != &spufs_type)
307 goto out; 462 goto out;
308 463
309 /* all flags are reserved */ 464 /* don't accept undefined flags */
310 if (flags & (~SPU_CREATE_FLAG_ALL)) 465 if (flags & (~SPU_CREATE_FLAG_ALL))
311 goto out; 466 goto out;
312 467
468 /* only threads can be underneath a gang */
469 if (nd->dentry != nd->dentry->d_sb->s_root) {
470 if ((flags & SPU_CREATE_GANG) ||
471 !SPUFS_I(nd->dentry->d_inode)->i_gang)
472 goto out;
473 }
474
313 dentry = lookup_create(nd, 1); 475 dentry = lookup_create(nd, 1);
314 ret = PTR_ERR(dentry); 476 ret = PTR_ERR(dentry);
315 if (IS_ERR(dentry)) 477 if (IS_ERR(dentry))
@@ -320,22 +482,13 @@ long spufs_create_thread(struct nameidata *nd,
320 goto out_dput; 482 goto out_dput;
321 483
322 mode &= ~current->fs->umask; 484 mode &= ~current->fs->umask;
323 ret = spufs_mkdir(nd->dentry->d_inode, dentry, flags, mode & S_IRWXUGO);
324 if (ret)
325 goto out_dput;
326 485
327 /* 486 if (flags & SPU_CREATE_GANG)
328 * get references for dget and mntget, will be released 487 return spufs_create_gang(nd->dentry->d_inode,
329 * in error path of *_open(). 488 dentry, nd->mnt, mode);
330 */ 489 else
331 ret = spufs_context_open(dget(dentry), mntget(nd->mnt)); 490 return spufs_create_context(nd->dentry->d_inode,
332 if (ret < 0) { 491 dentry, nd->mnt, flags, mode);
333 WARN_ON(spufs_rmdir(nd->dentry->d_inode, dentry));
334 mutex_unlock(&nd->dentry->d_inode->i_mutex);
335 spu_forget(SPUFS_I(dentry->d_inode)->i_ctx);
336 dput(dentry);
337 goto out;
338 }
339 492
340out_dput: 493out_dput:
341 dput(dentry); 494 dput(dentry);
diff --git a/arch/powerpc/platforms/cell/spufs/spufs.h b/arch/powerpc/platforms/cell/spufs/spufs.h
index 2fb6a0099112..f6624ceedf70 100644
--- a/arch/powerpc/platforms/cell/spufs/spufs.h
+++ b/arch/powerpc/platforms/cell/spufs/spufs.h
@@ -39,6 +39,8 @@ struct spu_context_ops;
39 39
40#define SPU_CONTEXT_PREEMPT 0UL 40#define SPU_CONTEXT_PREEMPT 0UL
41 41
42struct spu_gang;
43
42struct spu_context { 44struct spu_context {
43 struct spu *spu; /* pointer to a physical SPU */ 45 struct spu *spu; /* pointer to a physical SPU */
44 struct spu_state csa; /* SPU context save area. */ 46 struct spu_state csa; /* SPU context save area. */
@@ -68,6 +70,16 @@ struct spu_context {
68 struct work_struct reap_work; 70 struct work_struct reap_work;
69 unsigned long flags; 71 unsigned long flags;
70 unsigned long event_return; 72 unsigned long event_return;
73
74 struct list_head gang_list;
75 struct spu_gang *gang;
76};
77
78struct spu_gang {
79 struct list_head list;
80 struct mutex mutex;
81 struct kref kref;
82 int contexts;
71}; 83};
72 84
73struct mfc_dma_command { 85struct mfc_dma_command {
@@ -115,6 +127,7 @@ extern struct spu_context_ops spu_backing_ops;
115 127
116struct spufs_inode_info { 128struct spufs_inode_info {
117 struct spu_context *i_ctx; 129 struct spu_context *i_ctx;
130 struct spu_gang *i_gang;
118 struct inode vfs_inode; 131 struct inode vfs_inode;
119}; 132};
120#define SPUFS_I(inode) \ 133#define SPUFS_I(inode) \
@@ -125,12 +138,19 @@ extern struct tree_descr spufs_dir_contents[];
125/* system call implementation */ 138/* system call implementation */
126long spufs_run_spu(struct file *file, 139long spufs_run_spu(struct file *file,
127 struct spu_context *ctx, u32 *npc, u32 *status); 140 struct spu_context *ctx, u32 *npc, u32 *status);
128long spufs_create_thread(struct nameidata *nd, 141long spufs_create(struct nameidata *nd,
129 unsigned int flags, mode_t mode); 142 unsigned int flags, mode_t mode);
130extern struct file_operations spufs_context_fops; 143extern struct file_operations spufs_context_fops;
131 144
145/* gang management */
146struct spu_gang *alloc_spu_gang(void);
147struct spu_gang *get_spu_gang(struct spu_gang *gang);
148int put_spu_gang(struct spu_gang *gang);
149void spu_gang_remove_ctx(struct spu_gang *gang, struct spu_context *ctx);
150void spu_gang_add_ctx(struct spu_gang *gang, struct spu_context *ctx);
151
132/* context management */ 152/* context management */
133struct spu_context * alloc_spu_context(void); 153struct spu_context * alloc_spu_context(struct spu_gang *gang);
134void destroy_spu_context(struct kref *kref); 154void destroy_spu_context(struct kref *kref);
135struct spu_context * get_spu_context(struct spu_context *ctx); 155struct spu_context * get_spu_context(struct spu_context *ctx);
136int put_spu_context(struct spu_context *ctx); 156int put_spu_context(struct spu_context *ctx);
diff --git a/arch/powerpc/platforms/cell/spufs/syscalls.c b/arch/powerpc/platforms/cell/spufs/syscalls.c
index ef33a0ca2036..a6d1ae4dc2a3 100644
--- a/arch/powerpc/platforms/cell/spufs/syscalls.c
+++ b/arch/powerpc/platforms/cell/spufs/syscalls.c
@@ -90,7 +90,7 @@ asmlinkage long sys_spu_create(const char __user *pathname,
90 ret = path_lookup(tmp, LOOKUP_PARENT| 90 ret = path_lookup(tmp, LOOKUP_PARENT|
91 LOOKUP_OPEN|LOOKUP_CREATE, &nd); 91 LOOKUP_OPEN|LOOKUP_CREATE, &nd);
92 if (!ret) { 92 if (!ret) {
93 ret = spufs_create_thread(&nd, flags, mode); 93 ret = spufs_create(&nd, flags, mode);
94 path_release(&nd); 94 path_release(&nd);
95 } 95 }
96 putname(tmp); 96 putname(tmp);