diff options
author | Lachlan McIlroy <lachlan@redback.melbourne.sgi.com> | 2008-05-19 15:09:05 +1000 |
---|---|---|
committer | Lachlan McIlroy <lachlan@redback.melbourne.sgi.com> | 2008-05-19 15:09:05 +1000 |
commit | c203e45f069af47ca7623e4dcd8c00bfba2722e4 (patch) | |
tree | 4563115b6565dcfd97015c1c9366fb3d07cabf19 /arch/powerpc/platforms/cell | |
parent | a94477da38e0b261a7ecea71f4c95a3bcd5be69c (diff) | |
parent | b8291ad07a7f3b5b990900f0001198ac23ba893e (diff) |
Merge git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6 into for-linus
Diffstat (limited to 'arch/powerpc/platforms/cell')
-rw-r--r-- | arch/powerpc/platforms/cell/interrupt.c | 53 | ||||
-rw-r--r-- | arch/powerpc/platforms/cell/io-workarounds.c | 6 | ||||
-rw-r--r-- | arch/powerpc/platforms/cell/io-workarounds.h | 6 | ||||
-rw-r--r-- | arch/powerpc/platforms/cell/spu_base.c | 31 | ||||
-rw-r--r-- | arch/powerpc/platforms/cell/spu_priv1_mmio.c | 16 | ||||
-rw-r--r-- | arch/powerpc/platforms/cell/spufs/.gitignore | 2 | ||||
-rw-r--r-- | arch/powerpc/platforms/cell/spufs/context.c | 4 | ||||
-rw-r--r-- | arch/powerpc/platforms/cell/spufs/coredump.c | 1 | ||||
-rw-r--r-- | arch/powerpc/platforms/cell/spufs/fault.c | 17 | ||||
-rw-r--r-- | arch/powerpc/platforms/cell/spufs/file.c | 167 | ||||
-rw-r--r-- | arch/powerpc/platforms/cell/spufs/inode.c | 10 | ||||
-rw-r--r-- | arch/powerpc/platforms/cell/spufs/run.c | 40 | ||||
-rw-r--r-- | arch/powerpc/platforms/cell/spufs/sched.c | 14 | ||||
-rw-r--r-- | arch/powerpc/platforms/cell/spufs/spufs.h | 36 | ||||
-rw-r--r-- | arch/powerpc/platforms/cell/spufs/sputrace.c | 39 | ||||
-rw-r--r-- | arch/powerpc/platforms/cell/spufs/switch.c | 71 |
16 files changed, 420 insertions, 93 deletions
diff --git a/arch/powerpc/platforms/cell/interrupt.c b/arch/powerpc/platforms/cell/interrupt.c index 04f74f9f9ab..5bf7df14602 100644 --- a/arch/powerpc/platforms/cell/interrupt.c +++ b/arch/powerpc/platforms/cell/interrupt.c @@ -35,6 +35,7 @@ #include <linux/percpu.h> #include <linux/types.h> #include <linux/ioport.h> +#include <linux/kernel_stat.h> #include <asm/io.h> #include <asm/pgtable.h> @@ -231,6 +232,54 @@ static int iic_host_match(struct irq_host *h, struct device_node *node) "IBM,CBEA-Internal-Interrupt-Controller"); } +extern int noirqdebug; + +static void handle_iic_irq(unsigned int irq, struct irq_desc *desc) +{ + const unsigned int cpu = smp_processor_id(); + + spin_lock(&desc->lock); + + desc->status &= ~(IRQ_REPLAY | IRQ_WAITING); + + /* + * If we're currently running this IRQ, or its disabled, + * we shouldn't process the IRQ. Mark it pending, handle + * the necessary masking and go out + */ + if (unlikely((desc->status & (IRQ_INPROGRESS | IRQ_DISABLED)) || + !desc->action)) { + desc->status |= IRQ_PENDING; + goto out_eoi; + } + + kstat_cpu(cpu).irqs[irq]++; + + /* Mark the IRQ currently in progress.*/ + desc->status |= IRQ_INPROGRESS; + + do { + struct irqaction *action = desc->action; + irqreturn_t action_ret; + + if (unlikely(!action)) + goto out_eoi; + + desc->status &= ~IRQ_PENDING; + spin_unlock(&desc->lock); + action_ret = handle_IRQ_event(irq, action); + if (!noirqdebug) + note_interrupt(irq, desc, action_ret); + spin_lock(&desc->lock); + + } while ((desc->status & (IRQ_PENDING | IRQ_DISABLED)) == IRQ_PENDING); + + desc->status &= ~IRQ_INPROGRESS; +out_eoi: + desc->chip->eoi(irq); + spin_unlock(&desc->lock); +} + static int iic_host_map(struct irq_host *h, unsigned int virq, irq_hw_number_t hw) { @@ -240,10 +289,10 @@ static int iic_host_map(struct irq_host *h, unsigned int virq, break; case IIC_IRQ_TYPE_IOEXC: set_irq_chip_and_handler(virq, &iic_ioexc_chip, - handle_fasteoi_irq); + handle_iic_irq); break; default: - set_irq_chip_and_handler(virq, &iic_chip, handle_fasteoi_irq); + set_irq_chip_and_handler(virq, &iic_chip, handle_iic_irq); } return 0; } diff --git a/arch/powerpc/platforms/cell/io-workarounds.c b/arch/powerpc/platforms/cell/io-workarounds.c index 3b84e8be314..b5f84e8f089 100644 --- a/arch/powerpc/platforms/cell/io-workarounds.c +++ b/arch/powerpc/platforms/cell/io-workarounds.c @@ -118,7 +118,7 @@ static void iowa_##name at \ #undef DEF_PCI_AC_RET #undef DEF_PCI_AC_NORET -static struct ppc_pci_io __initdata iowa_pci_io = { +static const struct ppc_pci_io __devinitconst iowa_pci_io = { #define DEF_PCI_AC_RET(name, ret, at, al, space, aa) .name = iowa_##name, #define DEF_PCI_AC_NORET(name, at, al, space, aa) .name = iowa_##name, @@ -146,7 +146,7 @@ static void __iomem *iowa_ioremap(unsigned long addr, unsigned long size, } /* Regist new bus to support workaround */ -void __init iowa_register_bus(struct pci_controller *phb, +void __devinit iowa_register_bus(struct pci_controller *phb, struct ppc_pci_io *ops, int (*initfunc)(struct iowa_bus *, void *), void *data) { @@ -173,7 +173,7 @@ void __init iowa_register_bus(struct pci_controller *phb, } /* enable IO workaround */ -void __init io_workaround_init(void) +void __devinit io_workaround_init(void) { static int io_workaround_inited; diff --git a/arch/powerpc/platforms/cell/io-workarounds.h b/arch/powerpc/platforms/cell/io-workarounds.h index 79d8ed3d510..6efc7782ebf 100644 --- a/arch/powerpc/platforms/cell/io-workarounds.h +++ b/arch/powerpc/platforms/cell/io-workarounds.h @@ -31,9 +31,9 @@ struct iowa_bus { void *private; }; -void __init io_workaround_init(void); -void __init iowa_register_bus(struct pci_controller *, struct ppc_pci_io *, - int (*)(struct iowa_bus *, void *), void *); +void __devinit io_workaround_init(void); +void __devinit iowa_register_bus(struct pci_controller *, struct ppc_pci_io *, + int (*)(struct iowa_bus *, void *), void *); struct iowa_bus *iowa_mem_find_bus(const PCI_IO_ADDR); struct iowa_bus *iowa_pio_find_bus(unsigned long); diff --git a/arch/powerpc/platforms/cell/spu_base.c b/arch/powerpc/platforms/cell/spu_base.c index 6bab44b7716..70c660121ec 100644 --- a/arch/powerpc/platforms/cell/spu_base.c +++ b/arch/powerpc/platforms/cell/spu_base.c @@ -141,6 +141,10 @@ static void spu_restart_dma(struct spu *spu) if (!test_bit(SPU_CONTEXT_SWITCH_PENDING, &spu->flags)) out_be64(&priv2->mfc_control_RW, MFC_CNTL_RESTART_DMA_COMMAND); + else { + set_bit(SPU_CONTEXT_FAULT_PENDING, &spu->flags); + mb(); + } } static inline void spu_load_slb(struct spu *spu, int slbe, struct spu_slb *slb) @@ -226,11 +230,13 @@ static int __spu_trap_data_map(struct spu *spu, unsigned long ea, u64 dsisr) return 0; } - spu->class_0_pending = 0; - spu->dar = ea; - spu->dsisr = dsisr; + spu->class_1_dar = ea; + spu->class_1_dsisr = dsisr; + + spu->stop_callback(spu, 1); - spu->stop_callback(spu); + spu->class_1_dar = 0; + spu->class_1_dsisr = 0; return 0; } @@ -318,11 +324,15 @@ spu_irq_class_0(int irq, void *data) stat = spu_int_stat_get(spu, 0) & mask; spu->class_0_pending |= stat; - spu->dsisr = spu_mfc_dsisr_get(spu); - spu->dar = spu_mfc_dar_get(spu); + spu->class_0_dsisr = spu_mfc_dsisr_get(spu); + spu->class_0_dar = spu_mfc_dar_get(spu); spin_unlock(&spu->register_lock); - spu->stop_callback(spu); + spu->stop_callback(spu, 0); + + spu->class_0_pending = 0; + spu->class_0_dsisr = 0; + spu->class_0_dar = 0; spu_int_stat_clear(spu, 0, stat); @@ -363,6 +373,9 @@ spu_irq_class_1(int irq, void *data) if (stat & CLASS1_LS_COMPARE_SUSPEND_ON_PUT_INTR) ; + spu->class_1_dsisr = 0; + spu->class_1_dar = 0; + return stat ? IRQ_HANDLED : IRQ_NONE; } @@ -396,10 +409,10 @@ spu_irq_class_2(int irq, void *data) spu->ibox_callback(spu); if (stat & CLASS2_SPU_STOP_INTR) - spu->stop_callback(spu); + spu->stop_callback(spu, 2); if (stat & CLASS2_SPU_HALT_INTR) - spu->stop_callback(spu); + spu->stop_callback(spu, 2); if (stat & CLASS2_SPU_DMA_TAG_GROUP_COMPLETE_INTR) spu->mfc_callback(spu); diff --git a/arch/powerpc/platforms/cell/spu_priv1_mmio.c b/arch/powerpc/platforms/cell/spu_priv1_mmio.c index 67fa7247b80..906a0a2a9fe 100644 --- a/arch/powerpc/platforms/cell/spu_priv1_mmio.c +++ b/arch/powerpc/platforms/cell/spu_priv1_mmio.c @@ -28,6 +28,7 @@ #include <linux/io.h> #include <linux/mutex.h> #include <linux/device.h> +#include <linux/sched.h> #include <asm/spu.h> #include <asm/spu_priv1.h> @@ -75,8 +76,19 @@ static u64 int_stat_get(struct spu *spu, int class) static void cpu_affinity_set(struct spu *spu, int cpu) { - u64 target = iic_get_target_id(cpu); - u64 route = target << 48 | target << 32 | target << 16; + u64 target; + u64 route; + + if (nr_cpus_node(spu->node)) { + cpumask_t spumask = node_to_cpumask(spu->node); + cpumask_t cpumask = node_to_cpumask(cpu_to_node(cpu)); + + if (!cpus_intersects(spumask, cpumask)) + return; + } + + target = iic_get_target_id(cpu); + route = target << 48 | target << 32 | target << 16; out_be64(&spu->priv1->int_route_RW, route); } diff --git a/arch/powerpc/platforms/cell/spufs/.gitignore b/arch/powerpc/platforms/cell/spufs/.gitignore new file mode 100644 index 00000000000..a09ee8d84d6 --- /dev/null +++ b/arch/powerpc/platforms/cell/spufs/.gitignore @@ -0,0 +1,2 @@ +spu_save_dump.h +spu_restore_dump.h diff --git a/arch/powerpc/platforms/cell/spufs/context.c b/arch/powerpc/platforms/cell/spufs/context.c index 0ad83aeb70b..177735f7931 100644 --- a/arch/powerpc/platforms/cell/spufs/context.c +++ b/arch/powerpc/platforms/cell/spufs/context.c @@ -78,6 +78,7 @@ void destroy_spu_context(struct kref *kref) { struct spu_context *ctx; ctx = container_of(kref, struct spu_context, kref); + spu_context_nospu_trace(destroy_spu_context__enter, ctx); mutex_lock(&ctx->state_mutex); spu_deactivate(ctx); 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); BUG_ON(!list_empty(&ctx->rq)); atomic_dec(&nr_spu_contexts); + kfree(ctx->switch_log); kfree(ctx); } @@ -150,6 +152,8 @@ int spu_acquire_saved(struct spu_context *ctx) { int ret; + spu_context_nospu_trace(spu_acquire_saved__enter, ctx); + ret = spu_acquire(ctx); if (ret) return ret; diff --git a/arch/powerpc/platforms/cell/spufs/coredump.c b/arch/powerpc/platforms/cell/spufs/coredump.c index b962c3ab470..af116aadba1 100644 --- a/arch/powerpc/platforms/cell/spufs/coredump.c +++ b/arch/powerpc/platforms/cell/spufs/coredump.c @@ -22,6 +22,7 @@ #include <linux/elf.h> #include <linux/file.h> +#include <linux/fdtable.h> #include <linux/fs.h> #include <linux/list.h> #include <linux/module.h> diff --git a/arch/powerpc/platforms/cell/spufs/fault.c b/arch/powerpc/platforms/cell/spufs/fault.c index e46d300e21a..f093a581ac7 100644 --- a/arch/powerpc/platforms/cell/spufs/fault.c +++ b/arch/powerpc/platforms/cell/spufs/fault.c @@ -83,13 +83,18 @@ int spufs_handle_class0(struct spu_context *ctx) return 0; if (stat & CLASS0_DMA_ALIGNMENT_INTR) - spufs_handle_event(ctx, ctx->csa.dar, SPE_EVENT_DMA_ALIGNMENT); + spufs_handle_event(ctx, ctx->csa.class_0_dar, + SPE_EVENT_DMA_ALIGNMENT); if (stat & CLASS0_INVALID_DMA_COMMAND_INTR) - spufs_handle_event(ctx, ctx->csa.dar, SPE_EVENT_INVALID_DMA); + spufs_handle_event(ctx, ctx->csa.class_0_dar, + SPE_EVENT_INVALID_DMA); if (stat & CLASS0_SPU_ERROR_INTR) - spufs_handle_event(ctx, ctx->csa.dar, SPE_EVENT_SPE_ERROR); + spufs_handle_event(ctx, ctx->csa.class_0_dar, + SPE_EVENT_SPE_ERROR); + + ctx->csa.class_0_pending = 0; return -EIO; } @@ -119,8 +124,8 @@ int spufs_handle_class1(struct spu_context *ctx) * in time, we can still expect to get the same fault * the immediately after the context restore. */ - ea = ctx->csa.dar; - dsisr = ctx->csa.dsisr; + ea = ctx->csa.class_1_dar; + dsisr = ctx->csa.class_1_dsisr; if (!(dsisr & (MFC_DSISR_PTE_NOT_FOUND | MFC_DSISR_ACCESS_DENIED))) return 0; @@ -158,7 +163,7 @@ int spufs_handle_class1(struct spu_context *ctx) * time slicing will not preempt the context while the page fault * handler is running. Context switch code removes mappings. */ - ctx->csa.dar = ctx->csa.dsisr = 0; + ctx->csa.class_1_dar = ctx->csa.class_1_dsisr = 0; /* * If we handled the fault successfully and are in runnable diff --git a/arch/powerpc/platforms/cell/spufs/file.c b/arch/powerpc/platforms/cell/spufs/file.c index 08f44d1971a..c81341ff75b 100644 --- a/arch/powerpc/platforms/cell/spufs/file.c +++ b/arch/powerpc/platforms/cell/spufs/file.c @@ -32,6 +32,7 @@ #include <linux/marker.h> #include <asm/io.h> +#include <asm/time.h> #include <asm/spu.h> #include <asm/spu_info.h> #include <asm/uaccess.h> @@ -2386,6 +2387,171 @@ static const struct file_operations spufs_stat_fops = { .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[] = { { "capabilities", &spufs_caps_fops, 0444, }, @@ -2422,6 +2588,7 @@ struct tree_descr spufs_dir_contents[] = { { "proxydma_info", &spufs_proxydma_info_fops, 0444, }, { "tid", &spufs_tid_fops, 0444, }, { "stat", &spufs_stat_fops, 0444, }, + { "switch_log", &spufs_switch_log_fops, 0444 }, {}, }; diff --git a/arch/powerpc/platforms/cell/spufs/inode.c b/arch/powerpc/platforms/cell/spufs/inode.c index 0c32a05ab06..f407b247185 100644 --- a/arch/powerpc/platforms/cell/spufs/inode.c +++ b/arch/powerpc/platforms/cell/spufs/inode.c @@ -23,6 +23,7 @@ #include <linux/file.h> #include <linux/fs.h> +#include <linux/fsnotify.h> #include <linux/backing-dev.h> #include <linux/init.h> #include <linux/ioctl.h> @@ -223,7 +224,7 @@ static int spufs_dir_close(struct inode *inode, struct file *file) parent = dir->d_parent->d_inode; ctx = SPUFS_I(dir->d_inode)->i_ctx; - mutex_lock(&parent->i_mutex); + mutex_lock_nested(&parent->i_mutex, I_MUTEX_PARENT); ret = spufs_rmdir(parent, dir); mutex_unlock(&parent->i_mutex); WARN_ON(ret); @@ -618,12 +619,15 @@ long spufs_create(struct nameidata *nd, unsigned int flags, mode_t mode, mode &= ~current->fs->umask; if (flags & SPU_CREATE_GANG) - return spufs_create_gang(nd->path.dentry->d_inode, + ret = spufs_create_gang(nd->path.dentry->d_inode, dentry, nd->path.mnt, mode); else - return spufs_create_context(nd->path.dentry->d_inode, + ret = spufs_create_context(nd->path.dentry->d_inode, dentry, nd->path.mnt, flags, mode, filp); + if (ret >= 0) + fsnotify_mkdir(nd->path.dentry->d_inode, dentry); + return ret; out_dput: dput(dentry); diff --git a/arch/powerpc/platforms/cell/spufs/run.c b/arch/powerpc/platforms/cell/spufs/run.c index 96bf7c2b86f..b7493b86581 100644 --- a/arch/powerpc/platforms/cell/spufs/run.c +++ b/arch/powerpc/platforms/cell/spufs/run.c @@ -11,7 +11,7 @@ #include "spufs.h" /* interrupt-level stop callback function. */ -void spufs_stop_callback(struct spu *spu) +void spufs_stop_callback(struct spu *spu, int irq) { struct spu_context *ctx = spu->ctx; @@ -24,9 +24,19 @@ void spufs_stop_callback(struct spu *spu) */ if (ctx) { /* Copy exception arguments into module specific structure */ - ctx->csa.class_0_pending = spu->class_0_pending; - ctx->csa.dsisr = spu->dsisr; - ctx->csa.dar = spu->dar; + switch(irq) { + case 0 : + ctx->csa.class_0_pending = spu->class_0_pending; + ctx->csa.class_0_dsisr = spu->class_0_dsisr; + ctx->csa.class_0_dar = spu->class_0_dar; + break; + case 1 : + ctx->csa.class_1_dsisr = spu->class_1_dsisr; + ctx->csa.class_1_dar = spu->class_1_dar; + break; + case 2 : + break; + } /* ensure that the exception status has hit memory before a * thread waiting on the context's stop queue is woken */ @@ -34,11 +44,6 @@ void spufs_stop_callback(struct spu *spu) wake_up_all(&ctx->stop_wq); } - - /* Clear callback arguments from spu structure */ - spu->class_0_pending = 0; - spu->dsisr = 0; - spu->dar = 0; } int spu_stopped(struct spu_context *ctx, u32 *stat) @@ -56,7 +61,11 @@ int spu_stopped(struct spu_context *ctx, u32 *stat) if (!(*stat & SPU_STATUS_RUNNING) && (*stat & stopped)) return 1; - dsisr = ctx->csa.dsisr; + dsisr = ctx->csa.class_0_dsisr; + if (dsisr & (MFC_DSISR_PTE_NOT_FOUND | MFC_DSISR_ACCESS_DENIED)) + return 1; + + dsisr = ctx->csa.class_1_dsisr; if (dsisr & (MFC_DSISR_PTE_NOT_FOUND | MFC_DSISR_ACCESS_DENIED)) return 1; @@ -294,7 +303,7 @@ static int spu_process_callback(struct spu_context *ctx) u32 ls_pointer, npc; void __iomem *ls; long spu_ret; - int ret, ret2; + int ret; /* get syscall block from local store */ npc = ctx->ops->npc_read(ctx) & ~3; @@ -316,11 +325,9 @@ static int spu_process_callback(struct spu_context *ctx) if (spu_ret <= -ERESTARTSYS) { ret = spu_handle_restartsys(ctx, &spu_ret, &npc); } - ret2 = spu_acquire(ctx); + mutex_lock(&ctx->state_mutex); if (ret == -ERESTARTSYS) return ret; - if (ret2) - return -EINTR; } /* need to re-get the ls, as it may have changed when we released the @@ -343,13 +350,14 @@ long spufs_run_spu(struct spu_context *ctx, u32 *npc, u32 *event) if (mutex_lock_interruptible(&ctx->run_mutex)) return -ERESTARTSYS; - spu_enable_spu(ctx); ctx->event_return = 0; ret = spu_acquire(ctx); if (ret) goto out_unlock; + spu_enable_spu(ctx); + spu_update_sched_info(ctx); ret = spu_run_init(ctx, npc); @@ -405,6 +413,8 @@ long spufs_run_spu(struct spu_context *ctx, u32 *npc, u32 *event) ret = spu_run_fini(ctx, npc, &status); spu_yield(ctx); + spu_switch_log_notify(NULL, ctx, SWITCH_LOG_EXIT, status); + if ((status & SPU_STATUS_STOPPED_BY_STOP) && (((status >> SPU_STOP_STATUS_SHIFT) & 0x3f00) == 0x2100)) ctx->stats.libassist++; diff --git a/arch/powerpc/platforms/cell/spufs/sched.c b/arch/powerpc/platforms/cell/spufs/sched.c index 00528ef84ad..745dd51ec37 100644 --- a/arch/powerpc/platforms/cell/spufs/sched.c +++ b/arch/powerpc/platforms/cell/spufs/sched.c @@ -140,6 +140,9 @@ void __spu_update_sched_info(struct spu_context *ctx) * if it is timesliced or preempted. */ ctx->cpus_allowed = current->cpus_allowed; + + /* Save the current cpu id for spu interrupt routing. */ + ctx->last_ran = raw_smp_processor_id(); } void spu_update_sched_info(struct spu_context *ctx) @@ -240,9 +243,9 @@ static void spu_bind_context(struct spu *spu, struct spu_context *ctx) spu->mfc_callback = spufs_mfc_callback; mb(); spu_unmap_mappings(ctx); + spu_switch_log_notify(spu, ctx, SWITCH_LOG_START, 0); spu_restore(&ctx->csa, spu); spu->timestamp = jiffies; - spu_cpu_affinity_set(spu, raw_smp_processor_id()); spu_switch_notify(spu, ctx); ctx->state = SPU_STATE_RUNNABLE; @@ -419,6 +422,7 @@ static void spu_unbind_context(struct spu *spu, struct spu_context *ctx) spu_switch_notify(spu, NULL); spu_unmap_mappings(ctx); spu_save(&ctx->csa, spu); + spu_switch_log_notify(spu, ctx, SWITCH_LOG_STOP, 0); spu->timestamp = jiffies; ctx->state = SPU_STATE_SAVED; spu->ibox_callback = NULL; @@ -591,7 +595,7 @@ static struct spu *find_victim(struct spu_context *ctx) struct spu *spu; 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. @@ -655,7 +659,8 @@ static struct spu *find_victim(struct spu_context *ctx) victim->stats.invol_ctx_switch++; spu->stats.invol_ctx_switch++; - spu_add_to_rq(victim); + if (test_bit(SPU_SCHED_SPU_RUN, &victim->sched_flags)) + spu_add_to_rq(victim); mutex_unlock(&victim->state_mutex); @@ -1063,10 +1068,9 @@ int __init spu_sched_init(void) mod_timer(&spuloadavg_timer, 0); - entry = create_proc_entry("spu_loadavg", 0, NULL); + entry = proc_create("spu_loadavg", 0, NULL, &spu_loadavg_fops); if (!entry) goto out_stop_kthread; - entry->proc_fops = &spu_loadavg_fops; pr_debug("spusched: tick: %d, min ticks: %d, default ticks: %d\n", SPUSCHED_TICK, MIN_SPU_TIMESLICE, DEF_SPU_TIMESLICE); diff --git a/arch/powerpc/platforms/cell/spufs/spufs.h b/arch/powerpc/platforms/cell/spufs/spufs.h index cdc515182f8..454c277c145 100644 --- a/arch/powerpc/platforms/cell/spufs/spufs.h +++ b/arch/powerpc/platforms/cell/spufs/spufs.h @@ -47,6 +47,30 @@ enum { 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 *spu; /* pointer to a physical SPU */ struct spu_state csa; /* SPU context save area. */ @@ -97,6 +121,7 @@ struct spu_context { cpumask_t cpus_allowed; int policy; int prio; + int last_ran; /* statistics */ struct { @@ -116,6 +141,9 @@ struct spu_context { unsigned long long libassist; } stats; + /* context switch log */ + struct switch_log *switch_log; + struct list_head aff_list; int aff_head; int aff_offset; @@ -256,6 +284,8 @@ int spu_activate(struct spu_context *ctx, unsigned long flags); void spu_deactivate(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_log_notify(struct spu *spu, struct spu_context *ctx, + u32 type, u32 val); 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); @@ -302,7 +332,7 @@ size_t spu_ibox_read(struct spu_context *ctx, u32 *data); /* irq callback funcs. */ void spufs_ibox_callback(struct spu *spu); void spufs_wbox_callback(struct spu *spu); -void spufs_stop_callback(struct spu *spu); +void spufs_stop_callback(struct spu *spu, int irq); void spufs_mfc_callback(struct spu *spu); void spufs_dma_callback(struct spu *spu, int type); @@ -330,8 +360,8 @@ extern void spuctx_switch_state(struct spu_context *ctx, enum spu_utilization_state new_state); #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) \ - trace_mark(name, "%p", ctx); + trace_mark(name, "ctx %p", ctx); #endif diff --git a/arch/powerpc/platforms/cell/spufs/sputrace.c b/arch/powerpc/platforms/cell/spufs/sputrace.c index 79aa773f3c9..53202422ba7 100644 --- a/arch/powerpc/platforms/cell/spufs/sputrace.c +++ b/arch/powerpc/platforms/cell/spufs/sputrace.c @@ -171,24 +171,24 @@ static void spu_context_nospu_event(void *probe_private, void *call_data, } struct spu_probe spu_probes[] = { - { "spu_bind_context__enter", "%p %p", spu_context_event }, - { "spu_unbind_context__enter", "%p %p", spu_context_event }, - { "spu_get_idle__enter", "%p", spu_context_nospu_event }, - { "spu_get_idle__found", "%p %p", spu_context_event }, - { "spu_get_idle__not_found", "%p", spu_context_nospu_event }, - { "spu_find_victim__enter", "%p", spu_context_nospu_event }, - { "spusched_tick__preempt", "%p %p", spu_context_event }, - { "spusched_tick__newslice", "%p", spu_context_nospu_event }, - { "spu_yield__enter", "%p", spu_context_nospu_event }, - { "spu_deactivate__enter", "%p", spu_context_nospu_event }, - { "__spu_deactivate__unload", "%p %p", spu_context_event }, - { "spufs_ps_nopfn__enter", "%p", spu_context_nospu_event }, - { "spufs_ps_nopfn__sleep", "%p", spu_context_nospu_event }, - { "spufs_ps_nopfn__wake", "%p %p", spu_context_event }, - { "spufs_ps_nopfn__insert", "%p %p", spu_context_event }, - { "spu_acquire_saved__enter", "%p", spu_context_nospu_event }, - { "destroy_spu_context__enter", "%p", spu_context_nospu_event }, - { "spufs_stop_callback__enter", "%p %p", spu_context_event }, + { "spu_bind_context__enter", "ctx %p spu %p", spu_context_event }, + { "spu_unbind_context__enter", "ctx %p spu %p", spu_context_event }, + { "spu_get_idle__enter", "ctx %p", spu_context_nospu_event }, + { "spu_get_idle__found", "ctx %p spu %p", spu_context_event }, + { "spu_get_idle__not_found", "ctx %p", spu_context_nospu_event }, + { "spu_find_victim__enter", "ctx %p", spu_context_nospu_event }, + { "spusched_tick__preempt", "ctx %p spu %p", spu_context_event }, + { "spusched_tick__newslice", "ctx %p", spu_context_nospu_event }, + { "spu_yield__enter", "ctx %p", spu_context_nospu_event }, + { "spu_deactivate__enter", "ctx %p", spu_context_nospu_event }, + { "__spu_deactivate__unload", "ctx %p spu %p", spu_context_event }, + { "spufs_ps_nopfn__enter", "ctx %p", spu_context_nospu_event }, + { "spufs_ps_nopfn__sleep", "ctx %p", spu_context_nospu_event }, + { "spufs_ps_nopfn__wake", "ctx %p spu %p", spu_context_event }, + { "spufs_ps_nopfn__insert", "ctx %p spu %p", spu_context_event }, + { "spu_acquire_saved__enter", "ctx %p", spu_context_nospu_event }, + { "destroy_spu_context__enter", "ctx %p", spu_context_nospu_event }, + { "spufs_stop_callback__enter", "ctx %p spu %p", spu_context_event }, }; static int __init sputrace_init(void) @@ -201,10 +201,9 @@ static int __init sputrace_init(void) if (!sputrace_log) goto out; - entry = create_proc_entry("sputrace", S_IRUSR, NULL); + entry = proc_create("sputrace", S_IRUSR, NULL, &sputrace_fops); if (!entry) goto out_free_log; - entry->proc_fops = &sputrace_fops; for (i = 0; i < ARRAY_SIZE(spu_probes); i++) { struct spu_probe *p = &spu_probes[i]; diff --git a/arch/powerpc/platforms/cell/spufs/switch.c b/arch/powerpc/platforms/cell/spufs/switch.c index d2a1249d36d..3df9a36eb2f 100644 --- a/arch/powerpc/platforms/cell/spufs/switch.c +++ b/arch/powerpc/platforms/cell/spufs/switch.c @@ -132,6 +132,14 @@ static inline void disable_interrupts(struct spu_state *csa, struct spu *spu) spu_int_mask_set(spu, 2, 0ul); eieio(); spin_unlock_irq(&spu->register_lock); + + /* + * This flag needs to be set before calling synchronize_irq so + * that the update will be visible to the relevant handlers + * via a simple load. + */ + set_bit(SPU_CONTEXT_SWITCH_PENDING, &spu->flags); + clear_bit(SPU_CONTEXT_FAULT_PENDING, &spu->flags); synchronize_irq(spu->irqs[0]); synchronize_irq(spu->irqs[1]); synchronize_irq(spu->irqs[2]); @@ -166,9 +174,8 @@ static inline void set_switch_pending(struct spu_state *csa, struct spu *spu) /* Save, Step 7: * Restore, Step 5: * Set a software context switch pending flag. + * Done above in Step 3 - disable_interrupts(). */ - set_bit(SPU_CONTEXT_SWITCH_PENDING, &spu->flags); - mb(); } static inline void save_mfc_cntl(struct spu_state *csa, struct spu *spu) @@ -186,20 +193,21 @@ static inline void save_mfc_cntl(struct spu_state *csa, struct spu *spu) MFC_CNTL_SUSPEND_COMPLETE); /* fall through */ case MFC_CNTL_SUSPEND_COMPLETE: - if (csa) { + if (csa) csa->priv2.mfc_control_RW = - MFC_CNTL_SUSPEND_MASK | + in_be64(&priv2->mfc_control_RW) | MFC_CNTL_SUSPEND_DMA_QUEUE; - } break; case MFC_CNTL_NORMAL_DMA_QUEUE_OPERATION: out_be64(&priv2->mfc_control_RW, MFC_CNTL_SUSPEND_DMA_QUEUE); POLL_WHILE_FALSE((in_be64(&priv2->mfc_control_RW) & MFC_CNTL_SUSPEND_DMA_STATUS_MASK) == MFC_CNTL_SUSPEND_COMPLETE); - if (csa) { - csa->priv2.mfc_control_RW = 0; - } + if (csa) + csa->priv2.mfc_control_RW = + in_be64(&priv2->mfc_control_RW) & + ~MFC_CNTL_SUSPEND_DMA_QUEUE & + ~MFC_CNTL_SUSPEND_MASK; break; } } @@ -249,16 +257,21 @@ static inline void save_spu_status(struct spu_state *csa, struct spu *spu) } } -static inline void save_mfc_decr(struct spu_state *csa, struct spu *spu) +static inline void save_mfc_stopped_status(struct spu_state *csa, + struct spu *spu) { struct spu_priv2 __iomem *priv2 = spu->priv2; + const u64 mask = MFC_CNTL_DECREMENTER_RUNNING | + MFC_CNTL_DMA_QUEUES_EMPTY; /* Save, Step 12: * Read MFC_CNTL[Ds]. Update saved copy of * CSA.MFC_CNTL[Ds]. + * + * update: do the same with MFC_CNTL[Q]. */ - csa->priv2.mfc_control_RW |= - in_be64(&priv2->mfc_control_RW) & MFC_CNTL_DECREMENTER_RUNNING; + csa->priv2.mfc_control_RW &= ~mask; + csa->priv2.mfc_control_RW |= in_be64(&priv2->mfc_control_RW) & mask; } static inline void halt_mfc_decr(struct spu_state *csa, struct spu *spu) @@ -462,7 +475,9 @@ static inline void purge_mfc_queue(struct spu_state *csa, struct spu *spu) * Restore, Step 14. * Write MFC_CNTL[Pc]=1 (purge queue). */ - out_be64(&priv2->mfc_control_RW, MFC_CNTL_PURGE_DMA_REQUEST); + out_be64(&priv2->mfc_control_RW, + MFC_CNTL_PURGE_DMA_REQUEST | + MFC_CNTL_SUSPEND_MASK); eieio(); } @@ -725,10 +740,14 @@ static inline void set_switch_active(struct spu_state *csa, struct spu *spu) /* Save, Step 48: * Restore, Step 23. * Change the software context switch pending flag - * to context switch active. + * to context switch active. This implementation does + * not uses a switch active flag. * - * This implementation does not uses a switch active flag. + * Now that we have saved the mfc in the csa, we can add in the + * restart command if an exception occurred. */ + if (test_bit(SPU_CONTEXT_FAULT_PENDING, &spu->flags)) + csa->priv2.mfc_control_RW |= MFC_CNTL_RESTART_DMA_COMMAND; clear_bit(SPU_CONTEXT_SWITCH_PENDING, &spu->flags); mb(); } @@ -1690,6 +1709,13 @@ static inline void restore_mfc_sr1(struct spu_state *csa, struct spu *spu) eieio(); } +static inline void set_int_route(struct spu_state *csa, struct spu *spu) +{ + struct spu_context *ctx = spu->ctx; + + spu_cpu_affinity_set(spu, ctx->last_ran); +} + static inline void restore_other_spu_access(struct spu_state *csa, struct spu *spu) { @@ -1721,15 +1747,15 @@ static inline void restore_mfc_cntl(struct spu_state *csa, struct spu *spu) */ out_be64(&priv2->mfc_control_RW, csa->priv2.mfc_control_RW); eieio(); + /* - * FIXME: this is to restart a DMA that we were processing - * before the save. better remember the fault information - * in the csa instead. + * The queue is put back into the same state that was evident prior to + * the context switch. The suspend flag is added to the saved state in + * the csa, if the operational state was suspending or suspended. In + * this case, the code that suspended the mfc is responsible for + * continuing it. Note that SPE faults do not change the operational + * state of the spu. */ - if ((csa->priv2.mfc_control_RW & MFC_CNTL_SUSPEND_DMA_QUEUE_MASK)) { - out_be64(&priv2->mfc_control_RW, MFC_CNTL_RESTART_DMA_COMMAND); - eieio(); - } } static inline void enable_user_access(struct spu_state *csa, struct spu *spu) @@ -1788,7 +1814,7 @@ static int quiece_spu(struct spu_state *prev, struct spu *spu) save_spu_runcntl(prev, spu); /* Step 9. */ save_mfc_sr1(prev, spu); /* Step 10. */ save_spu_status(prev, spu); /* Step 11. */ - save_mfc_decr(prev, spu); /* Step 12. */ + save_mfc_stopped_status(prev, spu); /* Step 12. */ halt_mfc_decr(prev, spu); /* Step 13. */ save_timebase(prev, spu); /* Step 14. */ remove_other_spu_access(prev, spu); /* Step 15. */ @@ -2000,6 +2026,7 @@ static void restore_csa(struct spu_state *next, struct spu *spu) check_ppuint_mb_stat(next, spu); /* Step 67. */ spu_invalidate_slbs(spu); /* Modified Step 68. */ restore_mfc_sr1(next, spu); /* Step 69. */ + set_int_route(next, spu); /* NEW */ restore_other_spu_access(next, spu); /* Step 70. */ restore_spu_runcntl(next, spu); /* Step 71. */ restore_mfc_cntl(next, spu); /* Step 72. */ |