diff options
| author | Christoph Hellwig <hch@lst.de> | 2008-04-29 03:08:38 -0400 |
|---|---|---|
| committer | Jeremy Kerr <jk@ozlabs.org> | 2008-04-30 01:01:54 -0400 |
| commit | 5158e9b5218bd3799c9fa8c401ad24d7f0c0a0a1 (patch) | |
| tree | 4ed17672fe0582c46f594217ba410ec92aebfb45 /arch/powerpc/platforms | |
| parent | 14b3ca4022f050f8622ed282b734ddf445464583 (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/platforms')
| -rw-r--r-- | arch/powerpc/platforms/cell/spufs/context.c | 1 | ||||
| -rw-r--r-- | arch/powerpc/platforms/cell/spufs/file.c | 166 | ||||
| -rw-r--r-- | arch/powerpc/platforms/cell/spufs/run.c | 2 | ||||
| -rw-r--r-- | arch/powerpc/platforms/cell/spufs/sched.c | 2 | ||||
| -rw-r--r-- | arch/powerpc/platforms/cell/spufs/spufs.h | 29 |
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 | ||
| 2390 | static 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 | |||
| 2396 | static inline int spufs_switch_log_avail(struct spu_context *ctx) | ||
| 2397 | { | ||
| 2398 | return SWITCH_LOG_BUFSIZE - spufs_switch_log_used(ctx); | ||
| 2399 | } | ||
| 2400 | |||
| 2401 | static 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 | |||
| 2438 | static 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 | |||
| 2453 | static 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 | |||
| 2510 | static 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 | |||
| 2524 | static 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 | |||
| 2531 | void 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 | ||
| 2391 | struct tree_descr spufs_dir_contents[] = { | 2556 | struct 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 | ||
| 50 | enum { | ||
| 51 | SWITCH_LOG_BUFSIZE = 4096, | ||
| 52 | }; | ||
| 53 | |||
| 54 | enum { | ||
| 55 | SWITCH_LOG_START, | ||
| 56 | SWITCH_LOG_STOP, | ||
| 57 | SWITCH_LOG_EXIT, | ||
| 58 | }; | ||
| 59 | |||
| 60 | struct 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 | |||
| 50 | struct spu_context { | 74 | struct 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); | |||
| 256 | void spu_deactivate(struct spu_context *ctx); | 283 | void spu_deactivate(struct spu_context *ctx); |
| 257 | void spu_yield(struct spu_context *ctx); | 284 | void spu_yield(struct spu_context *ctx); |
| 258 | void spu_switch_notify(struct spu *spu, struct spu_context *ctx); | 285 | void spu_switch_notify(struct spu *spu, struct spu_context *ctx); |
| 286 | void spu_switch_log_notify(struct spu *spu, struct spu_context *ctx, | ||
| 287 | u32 type, u32 val); | ||
| 259 | void spu_set_timeslice(struct spu_context *ctx); | 288 | void spu_set_timeslice(struct spu_context *ctx); |
| 260 | void spu_update_sched_info(struct spu_context *ctx); | 289 | void spu_update_sched_info(struct spu_context *ctx); |
| 261 | void __spu_update_sched_info(struct spu_context *ctx); | 290 | void __spu_update_sched_info(struct spu_context *ctx); |
