[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>

authored by Arnd Bergmann and committed by Paul Mackerras 6263203e 9add11da

+303 -42
+1 -1
arch/powerpc/platforms/cell/spufs/Makefile
··· 2 2 3 3 obj-$(CONFIG_SPU_FS) += spufs.o 4 4 spufs-y += inode.o file.o context.o syscalls.o 5 - spufs-y += sched.o backing_ops.o hw_ops.o run.o 5 + spufs-y += sched.o backing_ops.o hw_ops.o run.o gang.o 6 6 7 7 # Rules to build switch.o with the help of SPU tool chain 8 8 SPU_CROSS := spu-
+5 -1
arch/powerpc/platforms/cell/spufs/context.c
··· 27 27 #include <asm/spu_csa.h> 28 28 #include "spufs.h" 29 29 30 - struct spu_context *alloc_spu_context(void) 30 + struct spu_context *alloc_spu_context(struct spu_gang *gang) 31 31 { 32 32 struct spu_context *ctx; 33 33 ctx = kzalloc(sizeof *ctx, GFP_KERNEL); ··· 51 51 ctx->state = SPU_STATE_SAVED; 52 52 ctx->ops = &spu_backing_ops; 53 53 ctx->owner = get_task_mm(current); 54 + if (gang) 55 + spu_gang_add_ctx(gang, ctx); 54 56 goto out; 55 57 out_free: 56 58 kfree(ctx); ··· 69 67 spu_deactivate(ctx); 70 68 up_write(&ctx->state_sema); 71 69 spu_fini_csa(&ctx->csa); 70 + if (ctx->gang) 71 + spu_gang_remove_ctx(ctx->gang, ctx); 72 72 kfree(ctx); 73 73 } 74 74
+81
arch/powerpc/platforms/cell/spufs/gang.c
··· 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 + 28 + struct 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 + 40 + out: 41 + return gang; 42 + } 43 + 44 + static 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 + 52 + struct spu_gang *get_spu_gang(struct spu_gang *gang) 53 + { 54 + kref_get(&gang->kref); 55 + return gang; 56 + } 57 + 58 + int put_spu_gang(struct spu_gang *gang) 59 + { 60 + return kref_put(&gang->kref, &destroy_spu_gang); 61 + } 62 + 63 + void 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 + 72 + void 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 + }
+189 -36
arch/powerpc/platforms/cell/spufs/inode.c
··· 50 50 ei = kmem_cache_alloc(spufs_inode_cache, SLAB_KERNEL); 51 51 if (!ei) 52 52 return NULL; 53 + 54 + ei->i_gang = NULL; 55 + ei->i_ctx = NULL; 56 + 53 57 return &ei->vfs_inode; 54 58 } 55 59 ··· 132 128 static void 133 129 spufs_delete_inode(struct inode *inode) 134 130 { 135 - if (SPUFS_I(inode)->i_ctx) 136 - put_spu_context(SPUFS_I(inode)->i_ctx); 131 + struct spufs_inode_info *ei = SPUFS_I(inode); 132 + 133 + if (ei->i_ctx) 134 + put_spu_context(ei->i_ctx); 135 + if (ei->i_gang) 136 + put_spu_gang(ei->i_gang); 137 137 clear_inode(inode); 138 138 } 139 139 140 140 static void spufs_prune_dir(struct dentry *dir) 141 141 { 142 142 struct dentry *dentry, *tmp; 143 + 143 144 mutex_lock(&dir->d_inode->i_mutex); 144 145 list_for_each_entry_safe(dentry, tmp, &dir->d_subdirs, d_u.d_child) { 145 146 spin_lock(&dcache_lock); ··· 165 156 mutex_unlock(&dir->d_inode->i_mutex); 166 157 } 167 158 168 - /* Caller must hold root->i_mutex */ 169 - static int spufs_rmdir(struct inode *root, struct dentry *dir_dentry) 159 + /* Caller must hold parent->i_mutex */ 160 + static int spufs_rmdir(struct inode *parent, struct dentry *dir) 170 161 { 171 162 /* remove all entries */ 172 - spufs_prune_dir(dir_dentry); 163 + spufs_prune_dir(dir); 173 164 174 - return simple_rmdir(root, dir_dentry); 165 + return simple_rmdir(parent, dir); 175 166 } 176 167 177 168 static int spufs_fill_dir(struct dentry *dir, struct tree_descr *files, ··· 200 191 static int spufs_dir_close(struct inode *inode, struct file *file) 201 192 { 202 193 struct spu_context *ctx; 203 - struct inode *dir; 204 - struct dentry *dentry; 194 + struct inode *parent; 195 + struct dentry *dir; 205 196 int ret; 206 197 207 - dentry = file->f_dentry; 208 - dir = dentry->d_parent->d_inode; 209 - ctx = SPUFS_I(dentry->d_inode)->i_ctx; 198 + dir = file->f_dentry; 199 + parent = dir->d_parent->d_inode; 200 + ctx = SPUFS_I(dir->d_inode)->i_ctx; 210 201 211 - mutex_lock(&dir->i_mutex); 212 - ret = spufs_rmdir(dir, dentry); 213 - mutex_unlock(&dir->i_mutex); 202 + mutex_lock(&parent->i_mutex); 203 + ret = spufs_rmdir(parent, dir); 204 + mutex_unlock(&parent->i_mutex); 214 205 WARN_ON(ret); 215 206 216 207 /* We have to give up the mm_struct */ ··· 249 240 inode->i_gid = dir->i_gid; 250 241 inode->i_mode &= S_ISGID; 251 242 } 252 - ctx = alloc_spu_context(); 243 + ctx = alloc_spu_context(SPUFS_I(dir)->i_gang); /* XXX gang */ 253 244 SPUFS_I(inode)->i_ctx = ctx; 254 245 if (!ctx) 255 246 goto out_iput; ··· 301 292 return ret; 302 293 } 303 294 295 + static int spufs_create_context(struct inode *inode, 296 + struct dentry *dentry, 297 + struct vfsmount *mnt, int flags, int mode) 298 + { 299 + int ret; 300 + 301 + ret = spufs_mkdir(inode, dentry, flags, mode & S_IRWXUGO); 302 + if (ret) 303 + goto out_unlock; 304 + 305 + /* 306 + * get references for dget and mntget, will be released 307 + * in error path of *_open(). 308 + */ 309 + ret = spufs_context_open(dget(dentry), mntget(mnt)); 310 + if (ret < 0) { 311 + WARN_ON(spufs_rmdir(inode, dentry)); 312 + mutex_unlock(&inode->i_mutex); 313 + spu_forget(SPUFS_I(dentry->d_inode)->i_ctx); 314 + goto out; 315 + } 316 + 317 + out_unlock: 318 + mutex_unlock(&inode->i_mutex); 319 + out: 320 + dput(dentry); 321 + return ret; 322 + } 323 + 324 + static int spufs_rmgang(struct inode *root, struct dentry *dir) 325 + { 326 + /* FIXME: this fails if the dir is not empty, 327 + which causes a leak of gangs. */ 328 + return simple_rmdir(root, dir); 329 + } 330 + 331 + static int spufs_gang_close(struct inode *inode, struct file *file) 332 + { 333 + struct inode *parent; 334 + struct dentry *dir; 335 + int ret; 336 + 337 + dir = file->f_dentry; 338 + parent = dir->d_parent->d_inode; 339 + 340 + ret = spufs_rmgang(parent, dir); 341 + WARN_ON(ret); 342 + 343 + return dcache_dir_close(inode, file); 344 + } 345 + 346 + struct file_operations spufs_gang_fops = { 347 + .open = dcache_dir_open, 348 + .release = spufs_gang_close, 349 + .llseek = dcache_dir_lseek, 350 + .read = generic_read_dir, 351 + .readdir = dcache_readdir, 352 + .fsync = simple_sync_file, 353 + }; 354 + 355 + static int 356 + spufs_mkgang(struct inode *dir, struct dentry *dentry, int mode) 357 + { 358 + int ret; 359 + struct inode *inode; 360 + struct spu_gang *gang; 361 + 362 + ret = -ENOSPC; 363 + inode = spufs_new_inode(dir->i_sb, mode | S_IFDIR); 364 + if (!inode) 365 + goto out; 366 + 367 + ret = 0; 368 + if (dir->i_mode & S_ISGID) { 369 + inode->i_gid = dir->i_gid; 370 + inode->i_mode &= S_ISGID; 371 + } 372 + gang = alloc_spu_gang(); 373 + SPUFS_I(inode)->i_ctx = NULL; 374 + SPUFS_I(inode)->i_gang = gang; 375 + if (!gang) 376 + goto out_iput; 377 + 378 + inode->i_op = &spufs_dir_inode_operations; 379 + inode->i_fop = &simple_dir_operations; 380 + 381 + d_instantiate(dentry, inode); 382 + dget(dentry); 383 + dir->i_nlink++; 384 + dentry->d_inode->i_nlink++; 385 + return ret; 386 + 387 + out_iput: 388 + iput(inode); 389 + out: 390 + return ret; 391 + } 392 + 393 + static int spufs_gang_open(struct dentry *dentry, struct vfsmount *mnt) 394 + { 395 + int ret; 396 + struct file *filp; 397 + 398 + ret = get_unused_fd(); 399 + if (ret < 0) { 400 + dput(dentry); 401 + mntput(mnt); 402 + goto out; 403 + } 404 + 405 + filp = dentry_open(dentry, mnt, O_RDONLY); 406 + if (IS_ERR(filp)) { 407 + put_unused_fd(ret); 408 + ret = PTR_ERR(filp); 409 + goto out; 410 + } 411 + 412 + filp->f_op = &spufs_gang_fops; 413 + fd_install(ret, filp); 414 + out: 415 + return ret; 416 + } 417 + 418 + static int spufs_create_gang(struct inode *inode, 419 + struct dentry *dentry, 420 + struct vfsmount *mnt, int mode) 421 + { 422 + int ret; 423 + 424 + ret = spufs_mkgang(inode, dentry, mode & S_IRWXUGO); 425 + if (ret) 426 + goto out; 427 + 428 + /* 429 + * get references for dget and mntget, will be released 430 + * in error path of *_open(). 431 + */ 432 + ret = spufs_gang_open(dget(dentry), mntget(mnt)); 433 + if (ret < 0) 434 + WARN_ON(spufs_rmgang(inode, dentry)); 435 + 436 + out: 437 + mutex_unlock(&inode->i_mutex); 438 + dput(dentry); 439 + return ret; 440 + } 441 + 442 + 304 443 static struct file_system_type spufs_type; 305 444 306 - long spufs_create_thread(struct nameidata *nd, 307 - unsigned int flags, mode_t mode) 445 + long spufs_create(struct nameidata *nd, unsigned int flags, mode_t mode) 308 446 { 309 447 struct dentry *dentry; 310 448 int ret; 311 449 312 - /* need to be at the root of spufs */ 313 450 ret = -EINVAL; 314 - if (nd->dentry->d_sb->s_type != &spufs_type || 315 - nd->dentry != nd->dentry->d_sb->s_root) 451 + /* check if we are on spufs */ 452 + if (nd->dentry->d_sb->s_type != &spufs_type) 316 453 goto out; 317 454 318 - /* all flags are reserved */ 455 + /* don't accept undefined flags */ 319 456 if (flags & (~SPU_CREATE_FLAG_ALL)) 320 457 goto out; 458 + 459 + /* only threads can be underneath a gang */ 460 + if (nd->dentry != nd->dentry->d_sb->s_root) { 461 + if ((flags & SPU_CREATE_GANG) || 462 + !SPUFS_I(nd->dentry->d_inode)->i_gang) 463 + goto out; 464 + } 321 465 322 466 dentry = lookup_create(nd, 1); 323 467 ret = PTR_ERR(dentry); ··· 482 320 goto out_dput; 483 321 484 322 mode &= ~current->fs->umask; 485 - ret = spufs_mkdir(nd->dentry->d_inode, dentry, flags, mode & S_IRWXUGO); 486 - if (ret) 487 - goto out_dput; 488 323 489 - /* 490 - * get references for dget and mntget, will be released 491 - * in error path of *_open(). 492 - */ 493 - ret = spufs_context_open(dget(dentry), mntget(nd->mnt)); 494 - if (ret < 0) { 495 - WARN_ON(spufs_rmdir(nd->dentry->d_inode, dentry)); 496 - mutex_unlock(&nd->dentry->d_inode->i_mutex); 497 - spu_forget(SPUFS_I(dentry->d_inode)->i_ctx); 498 - dput(dentry); 499 - goto out; 500 - } 324 + if (flags & SPU_CREATE_GANG) 325 + return spufs_create_gang(nd->dentry->d_inode, 326 + dentry, nd->mnt, mode); 327 + else 328 + return spufs_create_context(nd->dentry->d_inode, 329 + dentry, nd->mnt, flags, mode); 501 330 502 331 out_dput: 503 332 dput(dentry);
+22 -2
arch/powerpc/platforms/cell/spufs/spufs.h
··· 39 39 40 40 #define SPU_CONTEXT_PREEMPT 0UL 41 41 42 + struct spu_gang; 43 + 42 44 struct spu_context { 43 45 struct spu *spu; /* pointer to a physical SPU */ 44 46 struct spu_state csa; /* SPU context save area. */ ··· 70 68 struct work_struct reap_work; 71 69 unsigned long flags; 72 70 unsigned long event_return; 71 + 72 + struct list_head gang_list; 73 + struct spu_gang *gang; 74 + }; 75 + 76 + struct spu_gang { 77 + struct list_head list; 78 + struct mutex mutex; 79 + struct kref kref; 80 + int contexts; 73 81 }; 74 82 75 83 struct mfc_dma_command { ··· 127 115 128 116 struct spufs_inode_info { 129 117 struct spu_context *i_ctx; 118 + struct spu_gang *i_gang; 130 119 struct inode vfs_inode; 131 120 }; 132 121 #define SPUFS_I(inode) \ ··· 138 125 /* system call implementation */ 139 126 long spufs_run_spu(struct file *file, 140 127 struct spu_context *ctx, u32 *npc, u32 *status); 141 - long spufs_create_thread(struct nameidata *nd, 128 + long spufs_create(struct nameidata *nd, 142 129 unsigned int flags, mode_t mode); 143 130 extern struct file_operations spufs_context_fops; 144 131 132 + /* gang management */ 133 + struct spu_gang *alloc_spu_gang(void); 134 + struct spu_gang *get_spu_gang(struct spu_gang *gang); 135 + int put_spu_gang(struct spu_gang *gang); 136 + void spu_gang_remove_ctx(struct spu_gang *gang, struct spu_context *ctx); 137 + void spu_gang_add_ctx(struct spu_gang *gang, struct spu_context *ctx); 138 + 145 139 /* context management */ 146 - struct spu_context * alloc_spu_context(void); 140 + struct spu_context * alloc_spu_context(struct spu_gang *gang); 147 141 void destroy_spu_context(struct kref *kref); 148 142 struct spu_context * get_spu_context(struct spu_context *ctx); 149 143 int put_spu_context(struct spu_context *ctx);
+1 -1
arch/powerpc/platforms/cell/spufs/syscalls.c
··· 90 90 ret = path_lookup(tmp, LOOKUP_PARENT| 91 91 LOOKUP_OPEN|LOOKUP_CREATE, &nd); 92 92 if (!ret) { 93 - ret = spufs_create_thread(&nd, flags, mode); 93 + ret = spufs_create(&nd, flags, mode); 94 94 path_release(&nd); 95 95 } 96 96 putname(tmp);
+4 -1
include/asm-powerpc/spu.h
··· 181 181 * Flags for sys_spu_create. 182 182 */ 183 183 #define SPU_CREATE_EVENTS_ENABLED 0x0001 184 - #define SPU_CREATE_FLAG_ALL 0x0001 /* mask of all valid flags */ 184 + #define SPU_CREATE_GANG 0x0002 185 + 186 + #define SPU_CREATE_FLAG_ALL 0x0003 /* mask of all valid flags */ 187 + 185 188 186 189 #ifdef CONFIG_SPU_FS_MODULE 187 190 int register_spu_syscalls(struct spufs_calls *calls);