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/cell | |
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/cell')
-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); |