Merge git://git.kernel.org/pub/scm/linux/kernel/git/jk/spufs
This commit is contained in:
2
arch/powerpc/platforms/cell/spufs/.gitignore
vendored
Normal file
2
arch/powerpc/platforms/cell/spufs/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
spu_save_dump.h
|
||||||
|
spu_restore_dump.h
|
@@ -78,6 +78,7 @@ void destroy_spu_context(struct kref *kref)
|
|||||||
{
|
{
|
||||||
struct spu_context *ctx;
|
struct spu_context *ctx;
|
||||||
ctx = container_of(kref, struct spu_context, kref);
|
ctx = container_of(kref, struct spu_context, kref);
|
||||||
|
spu_context_nospu_trace(destroy_spu_context__enter, ctx);
|
||||||
mutex_lock(&ctx->state_mutex);
|
mutex_lock(&ctx->state_mutex);
|
||||||
spu_deactivate(ctx);
|
spu_deactivate(ctx);
|
||||||
mutex_unlock(&ctx->state_mutex);
|
mutex_unlock(&ctx->state_mutex);
|
||||||
@@ -88,6 +89,7 @@ void destroy_spu_context(struct kref *kref)
|
|||||||
kref_put(ctx->prof_priv_kref, ctx->prof_priv_release);
|
kref_put(ctx->prof_priv_kref, ctx->prof_priv_release);
|
||||||
BUG_ON(!list_empty(&ctx->rq));
|
BUG_ON(!list_empty(&ctx->rq));
|
||||||
atomic_dec(&nr_spu_contexts);
|
atomic_dec(&nr_spu_contexts);
|
||||||
|
kfree(ctx->switch_log);
|
||||||
kfree(ctx);
|
kfree(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -150,6 +152,8 @@ int spu_acquire_saved(struct spu_context *ctx)
|
|||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
spu_context_nospu_trace(spu_acquire_saved__enter, ctx);
|
||||||
|
|
||||||
ret = spu_acquire(ctx);
|
ret = spu_acquire(ctx);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
@@ -2386,6 +2386,171 @@ static const struct file_operations spufs_stat_fops = {
|
|||||||
.release = single_release,
|
.release = single_release,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static inline int spufs_switch_log_used(struct spu_context *ctx)
|
||||||
|
{
|
||||||
|
return (ctx->switch_log->head - ctx->switch_log->tail) %
|
||||||
|
SWITCH_LOG_BUFSIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int spufs_switch_log_avail(struct spu_context *ctx)
|
||||||
|
{
|
||||||
|
return SWITCH_LOG_BUFSIZE - spufs_switch_log_used(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int spufs_switch_log_open(struct inode *inode, struct file *file)
|
||||||
|
{
|
||||||
|
struct spu_context *ctx = SPUFS_I(inode)->i_ctx;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We (ab-)use the mapping_lock here because it serves the similar
|
||||||
|
* purpose for synchronizing open/close elsewhere. Maybe it should
|
||||||
|
* be renamed eventually.
|
||||||
|
*/
|
||||||
|
mutex_lock(&ctx->mapping_lock);
|
||||||
|
if (ctx->switch_log) {
|
||||||
|
spin_lock(&ctx->switch_log->lock);
|
||||||
|
ctx->switch_log->head = 0;
|
||||||
|
ctx->switch_log->tail = 0;
|
||||||
|
spin_unlock(&ctx->switch_log->lock);
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* We allocate the switch log data structures on first open.
|
||||||
|
* They will never be free because we assume a context will
|
||||||
|
* be traced until it goes away.
|
||||||
|
*/
|
||||||
|
ctx->switch_log = kzalloc(sizeof(struct switch_log) +
|
||||||
|
SWITCH_LOG_BUFSIZE * sizeof(struct switch_log_entry),
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (!ctx->switch_log)
|
||||||
|
goto out;
|
||||||
|
spin_lock_init(&ctx->switch_log->lock);
|
||||||
|
init_waitqueue_head(&ctx->switch_log->wait);
|
||||||
|
}
|
||||||
|
mutex_unlock(&ctx->mapping_lock);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
out:
|
||||||
|
mutex_unlock(&ctx->mapping_lock);
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int switch_log_sprint(struct spu_context *ctx, char *tbuf, int n)
|
||||||
|
{
|
||||||
|
struct switch_log_entry *p;
|
||||||
|
|
||||||
|
p = ctx->switch_log->log + ctx->switch_log->tail % SWITCH_LOG_BUFSIZE;
|
||||||
|
|
||||||
|
return snprintf(tbuf, n, "%u.%09u %d %u %u %llu\n",
|
||||||
|
(unsigned int) p->tstamp.tv_sec,
|
||||||
|
(unsigned int) p->tstamp.tv_nsec,
|
||||||
|
p->spu_id,
|
||||||
|
(unsigned int) p->type,
|
||||||
|
(unsigned int) p->val,
|
||||||
|
(unsigned long long) p->timebase);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t spufs_switch_log_read(struct file *file, char __user *buf,
|
||||||
|
size_t len, loff_t *ppos)
|
||||||
|
{
|
||||||
|
struct inode *inode = file->f_path.dentry->d_inode;
|
||||||
|
struct spu_context *ctx = SPUFS_I(inode)->i_ctx;
|
||||||
|
int error = 0, cnt = 0;
|
||||||
|
|
||||||
|
if (!buf || len < 0)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
while (cnt < len) {
|
||||||
|
char tbuf[128];
|
||||||
|
int width;
|
||||||
|
|
||||||
|
if (file->f_flags & O_NONBLOCK) {
|
||||||
|
if (spufs_switch_log_used(ctx) <= 0)
|
||||||
|
return cnt ? cnt : -EAGAIN;
|
||||||
|
} else {
|
||||||
|
/* Wait for data in buffer */
|
||||||
|
error = wait_event_interruptible(ctx->switch_log->wait,
|
||||||
|
spufs_switch_log_used(ctx) > 0);
|
||||||
|
if (error)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_lock(&ctx->switch_log->lock);
|
||||||
|
if (ctx->switch_log->head == ctx->switch_log->tail) {
|
||||||
|
/* multiple readers race? */
|
||||||
|
spin_unlock(&ctx->switch_log->lock);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
width = switch_log_sprint(ctx, tbuf, sizeof(tbuf));
|
||||||
|
if (width < len) {
|
||||||
|
ctx->switch_log->tail =
|
||||||
|
(ctx->switch_log->tail + 1) %
|
||||||
|
SWITCH_LOG_BUFSIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_unlock(&ctx->switch_log->lock);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the record is greater than space available return
|
||||||
|
* partial buffer (so far)
|
||||||
|
*/
|
||||||
|
if (width >= len)
|
||||||
|
break;
|
||||||
|
|
||||||
|
error = copy_to_user(buf + cnt, tbuf, width);
|
||||||
|
if (error)
|
||||||
|
break;
|
||||||
|
cnt += width;
|
||||||
|
}
|
||||||
|
|
||||||
|
return cnt == 0 ? error : cnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned int spufs_switch_log_poll(struct file *file, poll_table *wait)
|
||||||
|
{
|
||||||
|
struct inode *inode = file->f_path.dentry->d_inode;
|
||||||
|
struct spu_context *ctx = SPUFS_I(inode)->i_ctx;
|
||||||
|
unsigned int mask = 0;
|
||||||
|
|
||||||
|
poll_wait(file, &ctx->switch_log->wait, wait);
|
||||||
|
|
||||||
|
if (spufs_switch_log_used(ctx) > 0)
|
||||||
|
mask |= POLLIN;
|
||||||
|
|
||||||
|
return mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct file_operations spufs_switch_log_fops = {
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
.open = spufs_switch_log_open,
|
||||||
|
.read = spufs_switch_log_read,
|
||||||
|
.poll = spufs_switch_log_poll,
|
||||||
|
};
|
||||||
|
|
||||||
|
void spu_switch_log_notify(struct spu *spu, struct spu_context *ctx,
|
||||||
|
u32 type, u32 val)
|
||||||
|
{
|
||||||
|
if (!ctx->switch_log)
|
||||||
|
return;
|
||||||
|
|
||||||
|
spin_lock(&ctx->switch_log->lock);
|
||||||
|
if (spufs_switch_log_avail(ctx) > 1) {
|
||||||
|
struct switch_log_entry *p;
|
||||||
|
|
||||||
|
p = ctx->switch_log->log + ctx->switch_log->head;
|
||||||
|
ktime_get_ts(&p->tstamp);
|
||||||
|
p->timebase = get_tb();
|
||||||
|
p->spu_id = spu ? spu->number : -1;
|
||||||
|
p->type = type;
|
||||||
|
p->val = val;
|
||||||
|
|
||||||
|
ctx->switch_log->head =
|
||||||
|
(ctx->switch_log->head + 1) % SWITCH_LOG_BUFSIZE;
|
||||||
|
}
|
||||||
|
spin_unlock(&ctx->switch_log->lock);
|
||||||
|
|
||||||
|
wake_up(&ctx->switch_log->wait);
|
||||||
|
}
|
||||||
|
|
||||||
struct tree_descr spufs_dir_contents[] = {
|
struct tree_descr spufs_dir_contents[] = {
|
||||||
{ "capabilities", &spufs_caps_fops, 0444, },
|
{ "capabilities", &spufs_caps_fops, 0444, },
|
||||||
@@ -2422,6 +2587,7 @@ struct tree_descr spufs_dir_contents[] = {
|
|||||||
{ "proxydma_info", &spufs_proxydma_info_fops, 0444, },
|
{ "proxydma_info", &spufs_proxydma_info_fops, 0444, },
|
||||||
{ "tid", &spufs_tid_fops, 0444, },
|
{ "tid", &spufs_tid_fops, 0444, },
|
||||||
{ "stat", &spufs_stat_fops, 0444, },
|
{ "stat", &spufs_stat_fops, 0444, },
|
||||||
|
{ "switch_log", &spufs_switch_log_fops, 0444 },
|
||||||
{},
|
{},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -405,6 +405,8 @@ long spufs_run_spu(struct spu_context *ctx, u32 *npc, u32 *event)
|
|||||||
ret = spu_run_fini(ctx, npc, &status);
|
ret = spu_run_fini(ctx, npc, &status);
|
||||||
spu_yield(ctx);
|
spu_yield(ctx);
|
||||||
|
|
||||||
|
spu_switch_log_notify(NULL, ctx, SWITCH_LOG_EXIT, status);
|
||||||
|
|
||||||
if ((status & SPU_STATUS_STOPPED_BY_STOP) &&
|
if ((status & SPU_STATUS_STOPPED_BY_STOP) &&
|
||||||
(((status >> SPU_STOP_STATUS_SHIFT) & 0x3f00) == 0x2100))
|
(((status >> SPU_STOP_STATUS_SHIFT) & 0x3f00) == 0x2100))
|
||||||
ctx->stats.libassist++;
|
ctx->stats.libassist++;
|
||||||
|
@@ -240,6 +240,7 @@ static void spu_bind_context(struct spu *spu, struct spu_context *ctx)
|
|||||||
spu->mfc_callback = spufs_mfc_callback;
|
spu->mfc_callback = spufs_mfc_callback;
|
||||||
mb();
|
mb();
|
||||||
spu_unmap_mappings(ctx);
|
spu_unmap_mappings(ctx);
|
||||||
|
spu_switch_log_notify(spu, ctx, SWITCH_LOG_START, 0);
|
||||||
spu_restore(&ctx->csa, spu);
|
spu_restore(&ctx->csa, spu);
|
||||||
spu->timestamp = jiffies;
|
spu->timestamp = jiffies;
|
||||||
spu_cpu_affinity_set(spu, raw_smp_processor_id());
|
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)
|
|||||||
spu_switch_notify(spu, NULL);
|
spu_switch_notify(spu, NULL);
|
||||||
spu_unmap_mappings(ctx);
|
spu_unmap_mappings(ctx);
|
||||||
spu_save(&ctx->csa, spu);
|
spu_save(&ctx->csa, spu);
|
||||||
|
spu_switch_log_notify(spu, ctx, SWITCH_LOG_STOP, 0);
|
||||||
spu->timestamp = jiffies;
|
spu->timestamp = jiffies;
|
||||||
ctx->state = SPU_STATE_SAVED;
|
ctx->state = SPU_STATE_SAVED;
|
||||||
spu->ibox_callback = NULL;
|
spu->ibox_callback = NULL;
|
||||||
@@ -591,7 +593,7 @@ static struct spu *find_victim(struct spu_context *ctx)
|
|||||||
struct spu *spu;
|
struct spu *spu;
|
||||||
int node, n;
|
int node, n;
|
||||||
|
|
||||||
spu_context_nospu_trace(spu_find_vitim__enter, ctx);
|
spu_context_nospu_trace(spu_find_victim__enter, ctx);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Look for a possible preemption candidate on the local node first.
|
* Look for a possible preemption candidate on the local node first.
|
||||||
|
@@ -47,6 +47,30 @@ enum {
|
|||||||
SPU_SCHED_SPU_RUN, /* context is within spu_run */
|
SPU_SCHED_SPU_RUN, /* context is within spu_run */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
SWITCH_LOG_BUFSIZE = 4096,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
SWITCH_LOG_START,
|
||||||
|
SWITCH_LOG_STOP,
|
||||||
|
SWITCH_LOG_EXIT,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct switch_log {
|
||||||
|
spinlock_t lock;
|
||||||
|
wait_queue_head_t wait;
|
||||||
|
unsigned long head;
|
||||||
|
unsigned long tail;
|
||||||
|
struct switch_log_entry {
|
||||||
|
struct timespec tstamp;
|
||||||
|
s32 spu_id;
|
||||||
|
u32 type;
|
||||||
|
u32 val;
|
||||||
|
u64 timebase;
|
||||||
|
} log[];
|
||||||
|
};
|
||||||
|
|
||||||
struct spu_context {
|
struct spu_context {
|
||||||
struct spu *spu; /* pointer to a physical SPU */
|
struct spu *spu; /* pointer to a physical SPU */
|
||||||
struct spu_state csa; /* SPU context save area. */
|
struct spu_state csa; /* SPU context save area. */
|
||||||
@@ -116,6 +140,9 @@ struct spu_context {
|
|||||||
unsigned long long libassist;
|
unsigned long long libassist;
|
||||||
} stats;
|
} stats;
|
||||||
|
|
||||||
|
/* context switch log */
|
||||||
|
struct switch_log *switch_log;
|
||||||
|
|
||||||
struct list_head aff_list;
|
struct list_head aff_list;
|
||||||
int aff_head;
|
int aff_head;
|
||||||
int aff_offset;
|
int aff_offset;
|
||||||
@@ -256,6 +283,8 @@ int spu_activate(struct spu_context *ctx, unsigned long flags);
|
|||||||
void spu_deactivate(struct spu_context *ctx);
|
void spu_deactivate(struct spu_context *ctx);
|
||||||
void spu_yield(struct spu_context *ctx);
|
void spu_yield(struct spu_context *ctx);
|
||||||
void spu_switch_notify(struct spu *spu, struct spu_context *ctx);
|
void spu_switch_notify(struct spu *spu, struct spu_context *ctx);
|
||||||
|
void spu_switch_log_notify(struct spu *spu, struct spu_context *ctx,
|
||||||
|
u32 type, u32 val);
|
||||||
void spu_set_timeslice(struct spu_context *ctx);
|
void spu_set_timeslice(struct spu_context *ctx);
|
||||||
void spu_update_sched_info(struct spu_context *ctx);
|
void spu_update_sched_info(struct spu_context *ctx);
|
||||||
void __spu_update_sched_info(struct spu_context *ctx);
|
void __spu_update_sched_info(struct spu_context *ctx);
|
||||||
@@ -330,8 +359,8 @@ extern void spuctx_switch_state(struct spu_context *ctx,
|
|||||||
enum spu_utilization_state new_state);
|
enum spu_utilization_state new_state);
|
||||||
|
|
||||||
#define spu_context_trace(name, ctx, spu) \
|
#define spu_context_trace(name, ctx, spu) \
|
||||||
trace_mark(name, "%p %p", ctx, spu);
|
trace_mark(name, "ctx %p spu %p", ctx, spu);
|
||||||
#define spu_context_nospu_trace(name, ctx) \
|
#define spu_context_nospu_trace(name, ctx) \
|
||||||
trace_mark(name, "%p", ctx);
|
trace_mark(name, "ctx %p", ctx);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@@ -171,24 +171,24 @@ static void spu_context_nospu_event(void *probe_private, void *call_data,
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct spu_probe spu_probes[] = {
|
struct spu_probe spu_probes[] = {
|
||||||
{ "spu_bind_context__enter", "%p %p", spu_context_event },
|
{ "spu_bind_context__enter", "ctx %p spu %p", spu_context_event },
|
||||||
{ "spu_unbind_context__enter", "%p %p", spu_context_event },
|
{ "spu_unbind_context__enter", "ctx %p spu %p", spu_context_event },
|
||||||
{ "spu_get_idle__enter", "%p", spu_context_nospu_event },
|
{ "spu_get_idle__enter", "ctx %p", spu_context_nospu_event },
|
||||||
{ "spu_get_idle__found", "%p %p", spu_context_event },
|
{ "spu_get_idle__found", "ctx %p spu %p", spu_context_event },
|
||||||
{ "spu_get_idle__not_found", "%p", spu_context_nospu_event },
|
{ "spu_get_idle__not_found", "ctx %p", spu_context_nospu_event },
|
||||||
{ "spu_find_victim__enter", "%p", spu_context_nospu_event },
|
{ "spu_find_victim__enter", "ctx %p", spu_context_nospu_event },
|
||||||
{ "spusched_tick__preempt", "%p %p", spu_context_event },
|
{ "spusched_tick__preempt", "ctx %p spu %p", spu_context_event },
|
||||||
{ "spusched_tick__newslice", "%p", spu_context_nospu_event },
|
{ "spusched_tick__newslice", "ctx %p", spu_context_nospu_event },
|
||||||
{ "spu_yield__enter", "%p", spu_context_nospu_event },
|
{ "spu_yield__enter", "ctx %p", spu_context_nospu_event },
|
||||||
{ "spu_deactivate__enter", "%p", spu_context_nospu_event },
|
{ "spu_deactivate__enter", "ctx %p", spu_context_nospu_event },
|
||||||
{ "__spu_deactivate__unload", "%p %p", spu_context_event },
|
{ "__spu_deactivate__unload", "ctx %p spu %p", spu_context_event },
|
||||||
{ "spufs_ps_nopfn__enter", "%p", spu_context_nospu_event },
|
{ "spufs_ps_nopfn__enter", "ctx %p", spu_context_nospu_event },
|
||||||
{ "spufs_ps_nopfn__sleep", "%p", spu_context_nospu_event },
|
{ "spufs_ps_nopfn__sleep", "ctx %p", spu_context_nospu_event },
|
||||||
{ "spufs_ps_nopfn__wake", "%p %p", spu_context_event },
|
{ "spufs_ps_nopfn__wake", "ctx %p spu %p", spu_context_event },
|
||||||
{ "spufs_ps_nopfn__insert", "%p %p", spu_context_event },
|
{ "spufs_ps_nopfn__insert", "ctx %p spu %p", spu_context_event },
|
||||||
{ "spu_acquire_saved__enter", "%p", spu_context_nospu_event },
|
{ "spu_acquire_saved__enter", "ctx %p", spu_context_nospu_event },
|
||||||
{ "destroy_spu_context__enter", "%p", spu_context_nospu_event },
|
{ "destroy_spu_context__enter", "ctx %p", spu_context_nospu_event },
|
||||||
{ "spufs_stop_callback__enter", "%p %p", spu_context_event },
|
{ "spufs_stop_callback__enter", "ctx %p spu %p", spu_context_event },
|
||||||
};
|
};
|
||||||
|
|
||||||
static int __init sputrace_init(void)
|
static int __init sputrace_init(void)
|
||||||
|
Reference in New Issue
Block a user