aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc
diff options
context:
space:
mode:
authorChristoph Hellwig <hch@lst.de>2008-04-29 03:08:38 -0400
committerJeremy Kerr <jk@ozlabs.org>2008-04-30 01:01:54 -0400
commit5158e9b5218bd3799c9fa8c401ad24d7f0c0a0a1 (patch)
tree4ed17672fe0582c46f594217ba410ec92aebfb45 /arch/powerpc
parent14b3ca4022f050f8622ed282b734ddf445464583 (diff)
[POWERPC] spufs: add context switch notification log
There are userspace instrumentation tools that need to monitor spu context switches. This patch adds a new file called 'switch_log' to each spufs context directory that can be used to monitor the context switches. Context switch in/out and exit from spu_run are monitored after the file was first opened and can be read from it. Signed-off-by: Christoph Hellwig <hch@lst.de> Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
Diffstat (limited to 'arch/powerpc')
-rw-r--r--arch/powerpc/platforms/cell/spufs/context.c1
-rw-r--r--arch/powerpc/platforms/cell/spufs/file.c166
-rw-r--r--arch/powerpc/platforms/cell/spufs/run.c2
-rw-r--r--arch/powerpc/platforms/cell/spufs/sched.c2
-rw-r--r--arch/powerpc/platforms/cell/spufs/spufs.h29
5 files changed, 200 insertions, 0 deletions
diff --git a/arch/powerpc/platforms/cell/spufs/context.c b/arch/powerpc/platforms/cell/spufs/context.c
index 0ad83aeb70b1..91f2d4d9aefd 100644
--- a/arch/powerpc/platforms/cell/spufs/context.c
+++ b/arch/powerpc/platforms/cell/spufs/context.c
@@ -88,6 +88,7 @@ void destroy_spu_context(struct kref *kref)
88 kref_put(ctx->prof_priv_kref, ctx->prof_priv_release); 88 kref_put(ctx->prof_priv_kref, ctx->prof_priv_release);
89 BUG_ON(!list_empty(&ctx->rq)); 89 BUG_ON(!list_empty(&ctx->rq));
90 atomic_dec(&nr_spu_contexts); 90 atomic_dec(&nr_spu_contexts);
91 kfree(ctx->switch_log);
91 kfree(ctx); 92 kfree(ctx);
92} 93}
93 94
diff --git a/arch/powerpc/platforms/cell/spufs/file.c b/arch/powerpc/platforms/cell/spufs/file.c
index ba791e931bd6..b5c1505cf58b 100644
--- a/arch/powerpc/platforms/cell/spufs/file.c
+++ b/arch/powerpc/platforms/cell/spufs/file.c
@@ -2387,6 +2387,171 @@ static const struct file_operations spufs_stat_fops = {
2387 .release = single_release, 2387 .release = single_release,
2388}; 2388};
2389 2389
2390static inline int spufs_switch_log_used(struct spu_context *ctx)
2391{
2392 return (ctx->switch_log->head - ctx->switch_log->tail) %
2393 SWITCH_LOG_BUFSIZE;
2394}
2395
2396static inline int spufs_switch_log_avail(struct spu_context *ctx)
2397{
2398 return SWITCH_LOG_BUFSIZE - spufs_switch_log_used(ctx);
2399}
2400
2401static int spufs_switch_log_open(struct inode *inode, struct file *file)
2402{
2403 struct spu_context *ctx = SPUFS_I(inode)->i_ctx;
2404
2405 /*
2406 * We (ab-)use the mapping_lock here because it serves the similar
2407 * purpose for synchronizing open/close elsewhere. Maybe it should
2408 * be renamed eventually.
2409 */
2410 mutex_lock(&ctx->mapping_lock);
2411 if (ctx->switch_log) {
2412 spin_lock(&ctx->switch_log->lock);
2413 ctx->switch_log->head = 0;
2414 ctx->switch_log->tail = 0;
2415 spin_unlock(&ctx->switch_log->lock);
2416 } else {
2417 /*
2418 * We allocate the switch log data structures on first open.
2419 * They will never be free because we assume a context will
2420 * be traced until it goes away.
2421 */
2422 ctx->switch_log = kzalloc(sizeof(struct switch_log) +
2423 SWITCH_LOG_BUFSIZE * sizeof(struct switch_log_entry),
2424 GFP_KERNEL);
2425 if (!ctx->switch_log)
2426 goto out;
2427 spin_lock_init(&ctx->switch_log->lock);
2428 init_waitqueue_head(&ctx->switch_log->wait);
2429 }
2430 mutex_unlock(&ctx->mapping_lock);
2431
2432 return 0;
2433 out:
2434 mutex_unlock(&ctx->mapping_lock);
2435 return -ENOMEM;
2436}
2437
2438static int switch_log_sprint(struct spu_context *ctx, char *tbuf, int n)
2439{
2440 struct switch_log_entry *p;
2441
2442 p = ctx->switch_log->log + ctx->switch_log->tail % SWITCH_LOG_BUFSIZE;
2443
2444 return snprintf(tbuf, n, "%u.%09u %d %u %u %llu\n",
2445 (unsigned int) p->tstamp.tv_sec,
2446 (unsigned int) p->tstamp.tv_nsec,
2447 p->spu_id,
2448 (unsigned int) p->type,
2449 (unsigned int) p->val,
2450 (unsigned long long) p->timebase);
2451}
2452
2453static ssize_t spufs_switch_log_read(struct file *file, char __user *buf,
2454 size_t len, loff_t *ppos)
2455{
2456 struct inode *inode = file->f_path.dentry->d_inode;
2457 struct spu_context *ctx = SPUFS_I(inode)->i_ctx;
2458 int error = 0, cnt = 0;
2459
2460 if (!buf || len < 0)
2461 return -EINVAL;
2462
2463 while (cnt < len) {
2464 char tbuf[128];
2465 int width;
2466
2467 if (file->f_flags & O_NONBLOCK) {
2468 if (spufs_switch_log_used(ctx) <= 0)
2469 return cnt ? cnt : -EAGAIN;
2470 } else {
2471 /* Wait for data in buffer */
2472 error = wait_event_interruptible(ctx->switch_log->wait,
2473 spufs_switch_log_used(ctx) > 0);
2474 if (error)
2475 break;
2476 }
2477
2478 spin_lock(&ctx->switch_log->lock);
2479 if (ctx->switch_log->head == ctx->switch_log->tail) {
2480 /* multiple readers race? */
2481 spin_unlock(&ctx->switch_log->lock);
2482 continue;
2483 }
2484
2485 width = switch_log_sprint(ctx, tbuf, sizeof(tbuf));
2486 if (width < len) {
2487 ctx->switch_log->tail =
2488 (ctx->switch_log->tail + 1) %
2489 SWITCH_LOG_BUFSIZE;
2490 }
2491
2492 spin_unlock(&ctx->switch_log->lock);
2493
2494 /*
2495 * If the record is greater than space available return
2496 * partial buffer (so far)
2497 */
2498 if (width >= len)
2499 break;
2500
2501 error = copy_to_user(buf + cnt, tbuf, width);
2502 if (error)
2503 break;
2504 cnt += width;
2505 }
2506
2507 return cnt == 0 ? error : cnt;
2508}
2509
2510static unsigned int spufs_switch_log_poll(struct file *file, poll_table *wait)
2511{
2512 struct inode *inode = file->f_path.dentry->d_inode;
2513 struct spu_context *ctx = SPUFS_I(inode)->i_ctx;
2514 unsigned int mask = 0;
2515
2516 poll_wait(file, &ctx->switch_log->wait, wait);
2517
2518 if (spufs_switch_log_used(ctx) > 0)
2519 mask |= POLLIN;
2520
2521 return mask;
2522}
2523
2524static const struct file_operations spufs_switch_log_fops = {
2525 .owner = THIS_MODULE,
2526 .open = spufs_switch_log_open,
2527 .read = spufs_switch_log_read,
2528 .poll = spufs_switch_log_poll,
2529};
2530
2531void spu_switch_log_notify(struct spu *spu, struct spu_context *ctx,
2532 u32 type, u32 val)
2533{
2534 if (!ctx->switch_log)
2535 return;
2536
2537 spin_lock(&ctx->switch_log->lock);
2538 if (spufs_switch_log_avail(ctx) > 1) {
2539 struct switch_log_entry *p;
2540
2541 p = ctx->switch_log->log + ctx->switch_log->head;
2542 ktime_get_ts(&p->tstamp);
2543 p->timebase = get_tb();
2544 p->spu_id = spu ? spu->number : -1;
2545 p->type = type;
2546 p->val = val;
2547
2548 ctx->switch_log->head =
2549 (ctx->switch_log->head + 1) % SWITCH_LOG_BUFSIZE;
2550 }
2551 spin_unlock(&ctx->switch_log->lock);
2552
2553 wake_up(&ctx->switch_log->wait);
2554}
2390 2555
2391struct tree_descr spufs_dir_contents[] = { 2556struct tree_descr spufs_dir_contents[] = {
2392 { "capabilities", &spufs_caps_fops, 0444, }, 2557 { "capabilities", &spufs_caps_fops, 0444, },
@@ -2423,6 +2588,7 @@ struct tree_descr spufs_dir_contents[] = {
2423 { "proxydma_info", &spufs_proxydma_info_fops, 0444, }, 2588 { "proxydma_info", &spufs_proxydma_info_fops, 0444, },
2424 { "tid", &spufs_tid_fops, 0444, }, 2589 { "tid", &spufs_tid_fops, 0444, },
2425 { "stat", &spufs_stat_fops, 0444, }, 2590 { "stat", &spufs_stat_fops, 0444, },
2591 { "switch_log", &spufs_switch_log_fops, 0444 },
2426 {}, 2592 {},
2427}; 2593};
2428 2594
diff --git a/arch/powerpc/platforms/cell/spufs/run.c b/arch/powerpc/platforms/cell/spufs/run.c
index 96bf7c2b86fc..a9c35b7b719f 100644
--- a/arch/powerpc/platforms/cell/spufs/run.c
+++ b/arch/powerpc/platforms/cell/spufs/run.c
@@ -405,6 +405,8 @@ long spufs_run_spu(struct spu_context *ctx, u32 *npc, u32 *event)
405 ret = spu_run_fini(ctx, npc, &status); 405 ret = spu_run_fini(ctx, npc, &status);
406 spu_yield(ctx); 406 spu_yield(ctx);
407 407
408 spu_switch_log_notify(NULL, ctx, SWITCH_LOG_EXIT, status);
409
408 if ((status & SPU_STATUS_STOPPED_BY_STOP) && 410 if ((status & SPU_STATUS_STOPPED_BY_STOP) &&
409 (((status >> SPU_STOP_STATUS_SHIFT) & 0x3f00) == 0x2100)) 411 (((status >> SPU_STOP_STATUS_SHIFT) & 0x3f00) == 0x2100))
410 ctx->stats.libassist++; 412 ctx->stats.libassist++;
diff --git a/arch/powerpc/platforms/cell/spufs/sched.c b/arch/powerpc/platforms/cell/spufs/sched.c
index 00528ef84ad2..31e9d85761c2 100644
--- a/arch/powerpc/platforms/cell/spufs/sched.c
+++ b/arch/powerpc/platforms/cell/spufs/sched.c
@@ -240,6 +240,7 @@ static void spu_bind_context(struct spu *spu, struct spu_context *ctx)
240 spu->mfc_callback = spufs_mfc_callback; 240 spu->mfc_callback = spufs_mfc_callback;
241 mb(); 241 mb();
242 spu_unmap_mappings(ctx); 242 spu_unmap_mappings(ctx);
243 spu_switch_log_notify(spu, ctx, SWITCH_LOG_START, 0);
243 spu_restore(&ctx->csa, spu); 244 spu_restore(&ctx->csa, spu);
244 spu->timestamp = jiffies; 245 spu->timestamp = jiffies;
245 spu_cpu_affinity_set(spu, raw_smp_processor_id()); 246 spu_cpu_affinity_set(spu, raw_smp_processor_id());
@@ -419,6 +420,7 @@ static void spu_unbind_context(struct spu *spu, struct spu_context *ctx)
419 spu_switch_notify(spu, NULL); 420 spu_switch_notify(spu, NULL);
420 spu_unmap_mappings(ctx); 421 spu_unmap_mappings(ctx);
421 spu_save(&ctx->csa, spu); 422 spu_save(&ctx->csa, spu);
423 spu_switch_log_notify(spu, ctx, SWITCH_LOG_STOP, 0);
422 spu->timestamp = jiffies; 424 spu->timestamp = jiffies;
423 ctx->state = SPU_STATE_SAVED; 425 ctx->state = SPU_STATE_SAVED;
424 spu->ibox_callback = NULL; 426 spu->ibox_callback = NULL;
diff --git a/arch/powerpc/platforms/cell/spufs/spufs.h b/arch/powerpc/platforms/cell/spufs/spufs.h
index cdc515182f82..dd63b16bb072 100644
--- a/arch/powerpc/platforms/cell/spufs/spufs.h
+++ b/arch/powerpc/platforms/cell/spufs/spufs.h
@@ -47,6 +47,30 @@ enum {
47 SPU_SCHED_SPU_RUN, /* context is within spu_run */ 47 SPU_SCHED_SPU_RUN, /* context is within spu_run */
48}; 48};
49 49
50enum {
51 SWITCH_LOG_BUFSIZE = 4096,
52};
53
54enum {
55 SWITCH_LOG_START,
56 SWITCH_LOG_STOP,
57 SWITCH_LOG_EXIT,
58};
59
60struct switch_log {
61 spinlock_t lock;
62 wait_queue_head_t wait;
63 unsigned long head;
64 unsigned long tail;
65 struct switch_log_entry {
66 struct timespec tstamp;
67 s32 spu_id;
68 u32 type;
69 u32 val;
70 u64 timebase;
71 } log[];
72};
73
50struct spu_context { 74struct spu_context {
51 struct spu *spu; /* pointer to a physical SPU */ 75 struct spu *spu; /* pointer to a physical SPU */
52 struct spu_state csa; /* SPU context save area. */ 76 struct spu_state csa; /* SPU context save area. */
@@ -116,6 +140,9 @@ struct spu_context {
116 unsigned long long libassist; 140 unsigned long long libassist;
117 } stats; 141 } stats;
118 142
143 /* context switch log */
144 struct switch_log *switch_log;
145
119 struct list_head aff_list; 146 struct list_head aff_list;
120 int aff_head; 147 int aff_head;
121 int aff_offset; 148 int aff_offset;
@@ -256,6 +283,8 @@ int spu_activate(struct spu_context *ctx, unsigned long flags);
256void spu_deactivate(struct spu_context *ctx); 283void spu_deactivate(struct spu_context *ctx);
257void spu_yield(struct spu_context *ctx); 284void spu_yield(struct spu_context *ctx);
258void spu_switch_notify(struct spu *spu, struct spu_context *ctx); 285void spu_switch_notify(struct spu *spu, struct spu_context *ctx);
286void spu_switch_log_notify(struct spu *spu, struct spu_context *ctx,
287 u32 type, u32 val);
259void spu_set_timeslice(struct spu_context *ctx); 288void spu_set_timeslice(struct spu_context *ctx);
260void spu_update_sched_info(struct spu_context *ctx); 289void spu_update_sched_info(struct spu_context *ctx);
261void __spu_update_sched_info(struct spu_context *ctx); 290void __spu_update_sched_info(struct spu_context *ctx);