aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJeremy Kerr <jk@ozlabs.org>2008-10-15 19:03:46 -0400
committerJeremy Kerr <jk@ozlabs.org>2008-10-20 20:13:19 -0400
commitf5ed0eb6fe131e8f3847323b4aa569a6f7b36f56 (patch)
tree7da2dc8897685843430e61c10a0921ed7a8a499d
parente869446bb6db209e6092f7cba6cdfc2a5e637177 (diff)
powerpc/spufs: Use state_mutex for switch_log locking, and prevent multiple openers
Currently, we use ctx->mapping_lock and ctx->switch_log->lock for the context switch log. The mapping lock only prevents concurrent open()s, so we require the switch_lock->lock for reads. Since writes to the switch log buffer occur on context switches, we're better off synchronising with the state_mutex, which is held during a switch. Since we're serialised througout the buffer reads and writes, we can use the state mutex to protect open and release too, and can now kfree() the log buffer on release. This allows us to perform the switch log notify without taking any extra locks. Because the buffer is only present while the file is open, we can use it to prevent multiple simultaneous openers. Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
-rw-r--r--arch/powerpc/platforms/cell/spufs/file.c133
-rw-r--r--arch/powerpc/platforms/cell/spufs/run.c3
-rw-r--r--arch/powerpc/platforms/cell/spufs/spufs.h1
3 files changed, 81 insertions, 56 deletions
diff --git a/arch/powerpc/platforms/cell/spufs/file.c b/arch/powerpc/platforms/cell/spufs/file.c
index 010a51f59796..adb5abb9af5d 100644
--- a/arch/powerpc/platforms/cell/spufs/file.c
+++ b/arch/powerpc/platforms/cell/spufs/file.c
@@ -2426,38 +2426,48 @@ static inline int spufs_switch_log_avail(struct spu_context *ctx)
2426static int spufs_switch_log_open(struct inode *inode, struct file *file) 2426static int spufs_switch_log_open(struct inode *inode, struct file *file)
2427{ 2427{
2428 struct spu_context *ctx = SPUFS_I(inode)->i_ctx; 2428 struct spu_context *ctx = SPUFS_I(inode)->i_ctx;
2429 int rc;
2430
2431 rc = spu_acquire(ctx);
2432 if (rc)
2433 return rc;
2429 2434
2430 /*
2431 * We (ab-)use the mapping_lock here because it serves the similar
2432 * purpose for synchronizing open/close elsewhere. Maybe it should
2433 * be renamed eventually.
2434 */
2435 mutex_lock(&ctx->mapping_lock);
2436 if (ctx->switch_log) { 2435 if (ctx->switch_log) {
2437 spin_lock(&ctx->switch_log->lock); 2436 rc = -EBUSY;
2438 ctx->switch_log->head = 0; 2437 goto out;
2439 ctx->switch_log->tail = 0;
2440 spin_unlock(&ctx->switch_log->lock);
2441 } else {
2442 /*
2443 * We allocate the switch log data structures on first open.
2444 * They will never be free because we assume a context will
2445 * be traced until it goes away.
2446 */
2447 ctx->switch_log = kzalloc(sizeof(struct switch_log) +
2448 SWITCH_LOG_BUFSIZE * sizeof(struct switch_log_entry),
2449 GFP_KERNEL);
2450 if (!ctx->switch_log)
2451 goto out;
2452 spin_lock_init(&ctx->switch_log->lock);
2453 init_waitqueue_head(&ctx->switch_log->wait);
2454 } 2438 }
2455 mutex_unlock(&ctx->mapping_lock); 2439
2440 ctx->switch_log = kzalloc(sizeof(struct switch_log) +
2441 SWITCH_LOG_BUFSIZE * sizeof(struct switch_log_entry),
2442 GFP_KERNEL);
2443
2444 if (!ctx->switch_log) {
2445 rc = -ENOMEM;
2446 goto out;
2447 }
2448
2449 init_waitqueue_head(&ctx->switch_log->wait);
2450 rc = 0;
2451
2452out:
2453 spu_release(ctx);
2454 return rc;
2455}
2456
2457static int spufs_switch_log_release(struct inode *inode, struct file *file)
2458{
2459 struct spu_context *ctx = SPUFS_I(inode)->i_ctx;
2460 int rc;
2461
2462 rc = spu_acquire(ctx);
2463 if (rc)
2464 return rc;
2465
2466 kfree(ctx->switch_log);
2467 ctx->switch_log = NULL;
2468 spu_release(ctx);
2456 2469
2457 return 0; 2470 return 0;
2458 out:
2459 mutex_unlock(&ctx->mapping_lock);
2460 return -ENOMEM;
2461} 2471}
2462 2472
2463static int switch_log_sprint(struct spu_context *ctx, char *tbuf, int n) 2473static int switch_log_sprint(struct spu_context *ctx, char *tbuf, int n)
@@ -2485,42 +2495,46 @@ static ssize_t spufs_switch_log_read(struct file *file, char __user *buf,
2485 if (!buf || len < 0) 2495 if (!buf || len < 0)
2486 return -EINVAL; 2496 return -EINVAL;
2487 2497
2498 error = spu_acquire(ctx);
2499 if (error)
2500 return error;
2501
2488 while (cnt < len) { 2502 while (cnt < len) {
2489 char tbuf[128]; 2503 char tbuf[128];
2490 int width; 2504 int width;
2491 2505
2492 if (file->f_flags & O_NONBLOCK) { 2506 if (file->f_flags & O_NONBLOCK) {
2493 if (spufs_switch_log_used(ctx) <= 0) 2507 if (spufs_switch_log_used(ctx) == 0) {
2494 return cnt ? cnt : -EAGAIN; 2508 error = -EAGAIN;
2509 break;
2510 }
2495 } else { 2511 } else {
2496 /* Wait for data in buffer */ 2512 /* spufs_wait will drop the mutex and re-acquire,
2497 error = wait_event_interruptible(ctx->switch_log->wait, 2513 * but since we're in read(), the file cannot be
2514 * _released (and so ctx->switch_log is stable).
2515 */
2516 error = spufs_wait(ctx->switch_log->wait,
2498 spufs_switch_log_used(ctx) > 0); 2517 spufs_switch_log_used(ctx) > 0);
2518
2519 /* On error, spufs_wait returns without the
2520 * state mutex held */
2499 if (error) 2521 if (error)
2500 break; 2522 return error;
2501 } 2523 }
2502 2524
2503 spin_lock(&ctx->switch_log->lock); 2525 /* We may have had entries read from underneath us while we
2504 if (ctx->switch_log->head == ctx->switch_log->tail) { 2526 * dropped the mutex in spufs_wait, so re-check */
2505 /* multiple readers race? */ 2527 if (ctx->switch_log->head == ctx->switch_log->tail)
2506 spin_unlock(&ctx->switch_log->lock);
2507 continue; 2528 continue;
2508 }
2509 2529
2510 width = switch_log_sprint(ctx, tbuf, sizeof(tbuf)); 2530 width = switch_log_sprint(ctx, tbuf, sizeof(tbuf));
2511 if (width < len) { 2531 if (width < len)
2512 ctx->switch_log->tail = 2532 ctx->switch_log->tail =
2513 (ctx->switch_log->tail + 1) % 2533 (ctx->switch_log->tail + 1) %
2514 SWITCH_LOG_BUFSIZE; 2534 SWITCH_LOG_BUFSIZE;
2515 } 2535 else
2516 2536 /* If the record is greater than space available return
2517 spin_unlock(&ctx->switch_log->lock); 2537 * partial buffer (so far) */
2518
2519 /*
2520 * If the record is greater than space available return
2521 * partial buffer (so far)
2522 */
2523 if (width >= len)
2524 break; 2538 break;
2525 2539
2526 error = copy_to_user(buf + cnt, tbuf, width); 2540 error = copy_to_user(buf + cnt, tbuf, width);
@@ -2529,6 +2543,8 @@ static ssize_t spufs_switch_log_read(struct file *file, char __user *buf,
2529 cnt += width; 2543 cnt += width;
2530 } 2544 }
2531 2545
2546 spu_release(ctx);
2547
2532 return cnt == 0 ? error : cnt; 2548 return cnt == 0 ? error : cnt;
2533} 2549}
2534 2550
@@ -2537,29 +2553,41 @@ static unsigned int spufs_switch_log_poll(struct file *file, poll_table *wait)
2537 struct inode *inode = file->f_path.dentry->d_inode; 2553 struct inode *inode = file->f_path.dentry->d_inode;
2538 struct spu_context *ctx = SPUFS_I(inode)->i_ctx; 2554 struct spu_context *ctx = SPUFS_I(inode)->i_ctx;
2539 unsigned int mask = 0; 2555 unsigned int mask = 0;
2556 int rc;
2540 2557
2541 poll_wait(file, &ctx->switch_log->wait, wait); 2558 poll_wait(file, &ctx->switch_log->wait, wait);
2542 2559
2560 rc = spu_acquire(ctx);
2561 if (rc)
2562 return rc;
2563
2543 if (spufs_switch_log_used(ctx) > 0) 2564 if (spufs_switch_log_used(ctx) > 0)
2544 mask |= POLLIN; 2565 mask |= POLLIN;
2545 2566
2567 spu_release(ctx);
2568
2546 return mask; 2569 return mask;
2547} 2570}
2548 2571
2549static const struct file_operations spufs_switch_log_fops = { 2572static const struct file_operations spufs_switch_log_fops = {
2550 .owner = THIS_MODULE, 2573 .owner = THIS_MODULE,
2551 .open = spufs_switch_log_open, 2574 .open = spufs_switch_log_open,
2552 .read = spufs_switch_log_read, 2575 .read = spufs_switch_log_read,
2553 .poll = spufs_switch_log_poll, 2576 .poll = spufs_switch_log_poll,
2577 .release = spufs_switch_log_release,
2554}; 2578};
2555 2579
2580/**
2581 * Log a context switch event to a switch log reader.
2582 *
2583 * Must be called with ctx->state_mutex held.
2584 */
2556void spu_switch_log_notify(struct spu *spu, struct spu_context *ctx, 2585void spu_switch_log_notify(struct spu *spu, struct spu_context *ctx,
2557 u32 type, u32 val) 2586 u32 type, u32 val)
2558{ 2587{
2559 if (!ctx->switch_log) 2588 if (!ctx->switch_log)
2560 return; 2589 return;
2561 2590
2562 spin_lock(&ctx->switch_log->lock);
2563 if (spufs_switch_log_avail(ctx) > 1) { 2591 if (spufs_switch_log_avail(ctx) > 1) {
2564 struct switch_log_entry *p; 2592 struct switch_log_entry *p;
2565 2593
@@ -2573,7 +2601,6 @@ void spu_switch_log_notify(struct spu *spu, struct spu_context *ctx,
2573 ctx->switch_log->head = 2601 ctx->switch_log->head =
2574 (ctx->switch_log->head + 1) % SWITCH_LOG_BUFSIZE; 2602 (ctx->switch_log->head + 1) % SWITCH_LOG_BUFSIZE;
2575 } 2603 }
2576 spin_unlock(&ctx->switch_log->lock);
2577 2604
2578 wake_up(&ctx->switch_log->wait); 2605 wake_up(&ctx->switch_log->wait);
2579} 2606}
diff --git a/arch/powerpc/platforms/cell/spufs/run.c b/arch/powerpc/platforms/cell/spufs/run.c
index c9bb7cfd3dca..c58bd36b0c5b 100644
--- a/arch/powerpc/platforms/cell/spufs/run.c
+++ b/arch/powerpc/platforms/cell/spufs/run.c
@@ -249,6 +249,7 @@ static int spu_run_fini(struct spu_context *ctx, u32 *npc,
249 249
250 spuctx_switch_state(ctx, SPU_UTIL_IDLE_LOADED); 250 spuctx_switch_state(ctx, SPU_UTIL_IDLE_LOADED);
251 clear_bit(SPU_SCHED_SPU_RUN, &ctx->sched_flags); 251 clear_bit(SPU_SCHED_SPU_RUN, &ctx->sched_flags);
252 spu_switch_log_notify(NULL, ctx, SWITCH_LOG_EXIT, *status);
252 spu_release(ctx); 253 spu_release(ctx);
253 254
254 if (signal_pending(current)) 255 if (signal_pending(current))
@@ -417,8 +418,6 @@ long spufs_run_spu(struct spu_context *ctx, u32 *npc, u32 *event)
417 ret = spu_run_fini(ctx, npc, &status); 418 ret = spu_run_fini(ctx, npc, &status);
418 spu_yield(ctx); 419 spu_yield(ctx);
419 420
420 spu_switch_log_notify(NULL, ctx, SWITCH_LOG_EXIT, status);
421
422 if ((status & SPU_STATUS_STOPPED_BY_STOP) && 421 if ((status & SPU_STATUS_STOPPED_BY_STOP) &&
423 (((status >> SPU_STOP_STATUS_SHIFT) & 0x3f00) == 0x2100)) 422 (((status >> SPU_STOP_STATUS_SHIFT) & 0x3f00) == 0x2100))
424 ctx->stats.libassist++; 423 ctx->stats.libassist++;
diff --git a/arch/powerpc/platforms/cell/spufs/spufs.h b/arch/powerpc/platforms/cell/spufs/spufs.h
index 8ae8ef9dfc22..15c62d3ca129 100644
--- a/arch/powerpc/platforms/cell/spufs/spufs.h
+++ b/arch/powerpc/platforms/cell/spufs/spufs.h
@@ -65,7 +65,6 @@ enum {
65}; 65};
66 66
67struct switch_log { 67struct switch_log {
68 spinlock_t lock;
69 wait_queue_head_t wait; 68 wait_queue_head_t wait;
70 unsigned long head; 69 unsigned long head;
71 unsigned long tail; 70 unsigned long tail;