Merge branch 'linux-2.6'
authorPaul Mackerras <paulus@samba.org>
Wed, 23 Jan 2008 23:07:21 +0000 (10:07 +1100)
committerPaul Mackerras <paulus@samba.org>
Wed, 23 Jan 2008 23:07:21 +0000 (10:07 +1100)
1  2 
Documentation/kernel-parameters.txt
arch/powerpc/kernel/iommu.c
arch/powerpc/mm/slb.c
arch/powerpc/platforms/cell/Makefile
arch/powerpc/platforms/cell/spufs/context.c
arch/powerpc/platforms/cell/spufs/sched.c
include/asm-powerpc/spu.h

@@@ -527,29 -527,30 +527,30 @@@ and is between 256 and 4096 characters
                        Format: <area>[,<node>]
                        See also Documentation/networking/decnet.txt.
  
-       default_blu=    [VT]
+       vt.default_blu= [VT]
                        Format: <blue0>,<blue1>,<blue2>,...,<blue15>
                        Change the default blue palette of the console.
                        This is a 16-member array composed of values
                        ranging from 0-255.
  
-       default_grn=    [VT]
+       vt.default_grn= [VT]
                        Format: <green0>,<green1>,<green2>,...,<green15>
                        Change the default green palette of the console.
                        This is a 16-member array composed of values
                        ranging from 0-255.
  
-       default_red=    [VT]
+       vt.default_red= [VT]
                        Format: <red0>,<red1>,<red2>,...,<red15>
                        Change the default red palette of the console.
                        This is a 16-member array composed of values
                        ranging from 0-255.
  
-       default_utf8=   [VT]
+       vt.default_utf8=
+                       [VT]
                        Format=<0|1>
                        Set system-wide default UTF-8 mode for all tty's.
-                       Default is 0 and by setting to 1, it enables UTF-8
-                       mode for all newly opened or allocated terminals.
+                       Default is 1, i.e. UTF-8 mode is enabled for all
+                       newly opened terminals.
  
        dhash_entries=  [KNL]
                        Set number of hash buckets for dentry cache.
                        See Documentation/isdn/README.HiSax.
  
        hugepages=      [HW,X86-32,IA-64] Maximal number of HugeTLB pages.
 +      hugepagesz=     [HW,IA-64,PPC] The size of the HugeTLB pages.
  
        i8042.direct    [HW] Put keyboard port into non-translated mode
        i8042.dumbkbd   [HW] Pretend that controller can only read data from
        lapic_timer_c2_ok       [X86-32,x86-64,APIC] trust the local apic timer in
                        C2 power state.
  
+       libata.dma=     [LIBATA] DMA control
+                       libata.dma=0      Disable all PATA and SATA DMA
+                       libata.dma=1      PATA and SATA Disk DMA only
+                       libata.dma=2      ATAPI (CDROM) DMA only
+                       libata.dma=4      Compact Flash DMA only 
+                       Combinations also work, so libata.dma=3 enables DMA
+                       for disks and CDROMs, but not CFs.
        libata.noacpi   [LIBATA] Disables use of ACPI in libata suspend/resume
                        when set.
                        Format: <int>
@@@ -278,6 -278,7 +278,7 @@@ int iommu_map_sg(struct iommu_table *tb
        unsigned long flags;
        struct scatterlist *s, *outs, *segstart;
        int outcount, incount, i;
+       unsigned int align;
        unsigned long handle;
  
        BUG_ON(direction == DMA_NONE);
                /* Allocate iommu entries for that segment */
                vaddr = (unsigned long) sg_virt(s);
                npages = iommu_num_pages(vaddr, slen);
-               entry = iommu_range_alloc(tbl, npages, &handle, mask >> IOMMU_PAGE_SHIFT, 0);
+               align = 0;
+               if (IOMMU_PAGE_SHIFT < PAGE_SHIFT && slen >= PAGE_SIZE &&
+                   (vaddr & ~PAGE_MASK) == 0)
+                       align = PAGE_SHIFT - IOMMU_PAGE_SHIFT;
+               entry = iommu_range_alloc(tbl, npages, &handle,
+                                         mask >> IOMMU_PAGE_SHIFT, align);
  
                DBG("  - vaddr: %lx, size: %lx\n", vaddr, slen);
  
@@@ -526,14 -532,16 +532,14 @@@ struct iommu_table *iommu_init_table(st
        return tbl;
  }
  
 -void iommu_free_table(struct device_node *dn)
 +void iommu_free_table(struct iommu_table *tbl, const char *node_name)
  {
 -      struct pci_dn *pdn = dn->data;
 -      struct iommu_table *tbl = pdn->iommu_table;
        unsigned long bitmap_sz, i;
        unsigned int order;
  
        if (!tbl || !tbl->it_map) {
                printk(KERN_ERR "%s: expected TCE map for %s\n", __FUNCTION__,
 -                              dn->full_name);
 +                              node_name);
                return;
        }
  
        for (i = 0; i < (tbl->it_size/64); i++) {
                if (tbl->it_map[i] != 0) {
                        printk(KERN_WARNING "%s: Unexpected TCEs for %s\n",
 -                              __FUNCTION__, dn->full_name);
 +                              __FUNCTION__, node_name);
                        break;
                }
        }
@@@ -570,7 -578,7 +576,7 @@@ dma_addr_t iommu_map_single(struct iomm
  {
        dma_addr_t dma_handle = DMA_ERROR_CODE;
        unsigned long uaddr;
-       unsigned int npages;
+       unsigned int npages, align;
  
        BUG_ON(direction == DMA_NONE);
  
        npages = iommu_num_pages(uaddr, size);
  
        if (tbl) {
+               align = 0;
+               if (IOMMU_PAGE_SHIFT < PAGE_SHIFT && size >= PAGE_SIZE &&
+                   ((unsigned long)vaddr & ~PAGE_MASK) == 0)
+                       align = PAGE_SHIFT - IOMMU_PAGE_SHIFT;
                dma_handle = iommu_alloc(tbl, vaddr, npages, direction,
-                                        mask >> IOMMU_PAGE_SHIFT, 0);
+                                        mask >> IOMMU_PAGE_SHIFT, align);
                if (dma_handle == DMA_ERROR_CODE) {
                        if (printk_ratelimit())  {
                                printk(KERN_INFO "iommu_alloc failed, "
diff --combined arch/powerpc/mm/slb.c
@@@ -256,7 -256,6 +256,7 @@@ void slb_initialize(void
        static int slb_encoding_inited;
        extern unsigned int *slb_miss_kernel_load_linear;
        extern unsigned int *slb_miss_kernel_load_io;
 +      extern unsigned int *slb_compare_rr_to_size;
  
        /* Prepare our SLB miss handler based on our page size */
        linear_llp = mmu_psize_defs[mmu_linear_psize].sllp;
                                   SLB_VSID_KERNEL | linear_llp);
                patch_slb_encoding(slb_miss_kernel_load_io,
                                   SLB_VSID_KERNEL | io_llp);
 +              patch_slb_encoding(slb_compare_rr_to_size,
 +                                 mmu_slb_size);
  
                DBG("SLB: linear  LLP = %04x\n", linear_llp);
                DBG("SLB: io      LLP = %04x\n", io_llp);
  
        create_shadowed_slbe(VMALLOC_START, mmu_kernel_ssize, vflags, 1);
  
+       slb_shadow_clear(2);
        /* We don't bolt the stack for the time being - we're in boot,
         * so the stack is in the bolted segment.  By the time it goes
         * elsewhere, we'll call _switch() which will bolt in the new
@@@ -19,7 -19,8 +19,8 @@@ spu-manage-$(CONFIG_PPC_CELLEB)               += spu
  spu-manage-$(CONFIG_PPC_CELL_NATIVE)  += spu_manage.o
  
  obj-$(CONFIG_SPU_BASE)                        += spu_callbacks.o spu_base.o \
 -                                         spu_syscalls.o \
+                                          spu_notify.o \
 +                                         spu_syscalls.o spu_fault.o \
                                           $(spu-priv1-y) \
                                           $(spu-manage-y) \
                                           spufs/
@@@ -52,7 -52,6 +52,7 @@@ struct spu_context *alloc_spu_context(s
        init_waitqueue_head(&ctx->wbox_wq);
        init_waitqueue_head(&ctx->stop_wq);
        init_waitqueue_head(&ctx->mfc_wq);
 +      init_waitqueue_head(&ctx->run_wq);
        ctx->state = SPU_STATE_SAVED;
        ctx->ops = &spu_backing_ops;
        ctx->owner = get_task_mm(current);
@@@ -106,17 -105,7 +106,17 @@@ int put_spu_context(struct spu_context 
  void spu_forget(struct spu_context *ctx)
  {
        struct mm_struct *mm;
 -      spu_acquire_saved(ctx);
 +
 +      /*
 +       * This is basically an open-coded spu_acquire_saved, except that
 +       * we don't acquire the state mutex interruptible.
 +       */
 +      mutex_lock(&ctx->state_mutex);
 +      if (ctx->state != SPU_STATE_SAVED) {
 +              set_bit(SPU_SCHED_WAS_ACTIVE, &ctx->sched_flags);
 +              spu_deactivate(ctx);
 +      }
 +
        mm = ctx->owner;
        ctx->owner = NULL;
        mmput(mm);
@@@ -144,23 -133,47 +144,23 @@@ void spu_unmap_mappings(struct spu_cont
  }
  
  /**
 - * spu_acquire_runnable - lock spu contex and make sure it is in runnable state
 + * spu_acquire_saved - lock spu contex and make sure it is in saved state
   * @ctx:      spu contex to lock
 - *
 - * Note:
 - *    Returns 0 and with the context locked on success
 - *    Returns negative error and with the context _unlocked_ on failure.
   */
 -int spu_acquire_runnable(struct spu_context *ctx, unsigned long flags)
 +int spu_acquire_saved(struct spu_context *ctx)
  {
 -      int ret = -EINVAL;
 -
 -      spu_acquire(ctx);
 -      if (ctx->state == SPU_STATE_SAVED) {
 -              /*
 -               * Context is about to be freed, so we can't acquire it anymore.
 -               */
 -              if (!ctx->owner)
 -                      goto out_unlock;
 -              ret = spu_activate(ctx, flags);
 -              if (ret)
 -                      goto out_unlock;
 -      }
 +      int ret;
  
 -      return 0;
 -
 - out_unlock:
 -      spu_release(ctx);
 -      return ret;
 -}
 +      ret = spu_acquire(ctx);
 +      if (ret)
 +              return ret;
  
 -/**
 - * spu_acquire_saved - lock spu contex and make sure it is in saved state
 - * @ctx:      spu contex to lock
 - */
 -void spu_acquire_saved(struct spu_context *ctx)
 -{
 -      spu_acquire(ctx);
        if (ctx->state != SPU_STATE_SAVED) {
                set_bit(SPU_SCHED_WAS_ACTIVE, &ctx->sched_flags);
                spu_deactivate(ctx);
        }
 +
 +      return 0;
  }
  
  /**
@@@ -177,19 -190,3 +177,3 @@@ void spu_release_saved(struct spu_conte
        spu_release(ctx);
  }
  
- void spu_set_profile_private_kref(struct spu_context *ctx,
-                                 struct kref *prof_info_kref,
-                                 void ( * prof_info_release) (struct kref *kref))
- {
-       ctx->prof_priv_kref = prof_info_kref;
-       ctx->prof_priv_release = prof_info_release;
- }
- EXPORT_SYMBOL_GPL(spu_set_profile_private_kref);
- void *spu_get_profile_private_kref(struct spu_context *ctx)
- {
-       return ctx->prof_priv_kref;
- }
- EXPORT_SYMBOL_GPL(spu_get_profile_private_kref);
@@@ -58,7 -58,6 +58,7 @@@ static unsigned long spu_avenrun[3]
  static struct spu_prio_array *spu_prio;
  static struct task_struct *spusched_task;
  static struct timer_list spusched_timer;
 +static struct timer_list spuloadavg_timer;
  
  /*
   * Priority of a normal, non-rt, non-niced'd process (aka nice level 0).
@@@ -106,21 -105,15 +106,21 @@@ void spu_set_timeslice(struct spu_conte
  void __spu_update_sched_info(struct spu_context *ctx)
  {
        /*
 -       * 32-Bit assignment are atomic on powerpc, and we don't care about
 -       * memory ordering here because retriving the controlling thread is
 -       * per defintion racy.
 +       * assert that the context is not on the runqueue, so it is safe
 +       * to change its scheduling parameters.
 +       */
 +      BUG_ON(!list_empty(&ctx->rq));
 +
 +      /*
 +       * 32-Bit assignments are atomic on powerpc, and we don't care about
 +       * memory ordering here because retrieving the controlling thread is
 +       * per definition racy.
         */
        ctx->tid = current->pid;
  
        /*
         * We do our own priority calculations, so we normally want
 -       * ->static_prio to start with. Unfortunately thies field
 +       * ->static_prio to start with. Unfortunately this field
         * contains junk for threads with a realtime scheduling
         * policy so we have to look at ->prio in this case.
         */
        ctx->policy = current->policy;
  
        /*
 -       * A lot of places that don't hold list_mutex poke into
 -       * cpus_allowed, including grab_runnable_context which
 -       * already holds the runq_lock.  So abuse runq_lock
 -       * to protect this field aswell.
 +       * TO DO: the context may be loaded, so we may need to activate
 +       * it again on a different node. But it shouldn't hurt anything
 +       * to update its parameters, because we know that the scheduler
 +       * is not actively looking at this field, since it is not on the
 +       * runqueue. The context will be rescheduled on the proper node
 +       * if it is timesliced or preempted.
         */
 -      spin_lock(&spu_prio->runq_lock);
        ctx->cpus_allowed = current->cpus_allowed;
 -      spin_unlock(&spu_prio->runq_lock);
  }
  
  void spu_update_sched_info(struct spu_context *ctx)
  {
 -      int node = ctx->spu->node;
 +      int node;
  
 -      mutex_lock(&cbe_spu_info[node].list_mutex);
 -      __spu_update_sched_info(ctx);
 -      mutex_unlock(&cbe_spu_info[node].list_mutex);
 +      if (ctx->state == SPU_STATE_RUNNABLE) {
 +              node = ctx->spu->node;
 +
 +              /*
 +               * Take list_mutex to sync with find_victim().
 +               */
 +              mutex_lock(&cbe_spu_info[node].list_mutex);
 +              __spu_update_sched_info(ctx);
 +              mutex_unlock(&cbe_spu_info[node].list_mutex);
 +      } else {
 +              __spu_update_sched_info(ctx);
 +      }
  }
  
  static int __node_allowed(struct spu_context *ctx, int node)
@@@ -182,15 -166,7 +182,7 @@@ static int node_allowed(struct spu_cont
        return rval;
  }
  
- static BLOCKING_NOTIFIER_HEAD(spu_switch_notifier);
- void spu_switch_notify(struct spu *spu, struct spu_context *ctx)
- {
-       blocking_notifier_call_chain(&spu_switch_notifier,
-                           ctx ? ctx->object_id : 0, spu);
- }
- static void notify_spus_active(void)
+ void do_notify_spus_active(void)
  {
        int node;
  
         * Wake up the active spu_contexts.
         *
         * When the awakened processes see their "notify_active" flag is set,
 -       * they will call spu_switch_notify();
 +       * they will call spu_switch_notify().
         */
        for_each_online_node(node) {
                struct spu *spu;
        }
  }
  
- int spu_switch_event_register(struct notifier_block * n)
- {
-       int ret;
-       ret = blocking_notifier_chain_register(&spu_switch_notifier, n);
-       if (!ret)
-               notify_spus_active();
-       return ret;
- }
- EXPORT_SYMBOL_GPL(spu_switch_event_register);
- int spu_switch_event_unregister(struct notifier_block * n)
- {
-       return blocking_notifier_chain_unregister(&spu_switch_notifier, n);
- }
- EXPORT_SYMBOL_GPL(spu_switch_event_unregister);
  /**
   * spu_bind_context - bind spu context to physical spu
   * @spu:      physical spu to bind to
@@@ -261,6 -221,7 +237,6 @@@ static void spu_bind_context(struct sp
        spu->wbox_callback = spufs_wbox_callback;
        spu->stop_callback = spufs_stop_callback;
        spu->mfc_callback = spufs_mfc_callback;
 -      spu->dma_callback = spufs_dma_callback;
        mb();
        spu_unmap_mappings(ctx);
        spu_restore(&ctx->csa, spu);
@@@ -448,6 -409,7 +424,6 @@@ static void spu_unbind_context(struct s
        spu->wbox_callback = NULL;
        spu->stop_callback = NULL;
        spu->mfc_callback = NULL;
 -      spu->dma_callback = NULL;
        spu_associate_mm(spu, NULL);
        spu->pid = 0;
        spu->tgid = 0;
@@@ -492,13 -454,6 +468,13 @@@ static void __spu_add_to_rq(struct spu_
        }
  }
  
 +static void spu_add_to_rq(struct spu_context *ctx)
 +{
 +      spin_lock(&spu_prio->runq_lock);
 +      __spu_add_to_rq(ctx);
 +      spin_unlock(&spu_prio->runq_lock);
 +}
 +
  static void __spu_del_from_rq(struct spu_context *ctx)
  {
        int prio = ctx->prio;
        }
  }
  
 +void spu_del_from_rq(struct spu_context *ctx)
 +{
 +      spin_lock(&spu_prio->runq_lock);
 +      __spu_del_from_rq(ctx);
 +      spin_unlock(&spu_prio->runq_lock);
 +}
 +
  static void spu_prio_wait(struct spu_context *ctx)
  {
        DEFINE_WAIT(wait);
  
 +      /*
 +       * The caller must explicitly wait for a context to be loaded
 +       * if the nosched flag is set.  If NOSCHED is not set, the caller
 +       * queues the context and waits for an spu event or error.
 +       */
 +      BUG_ON(!(ctx->flags & SPU_CREATE_NOSCHED));
 +
        spin_lock(&spu_prio->runq_lock);
        prepare_to_wait_exclusive(&ctx->stop_wq, &wait, TASK_INTERRUPTIBLE);
        if (!signal_pending(current)) {
@@@ -614,7 -555,7 +590,7 @@@ static struct spu *find_victim(struct s
        /*
         * Look for a possible preemption candidate on the local node first.
         * If there is no candidate look at the other nodes.  This isn't
 -       * exactly fair, but so far the whole spu schedule tries to keep
 +       * exactly fair, but so far the whole spu scheduler tries to keep
         * a strong node affinity.  We might want to fine-tune this in
         * the future.
         */
                        struct spu_context *tmp = spu->ctx;
  
                        if (tmp && tmp->prio > ctx->prio &&
 +                          !(tmp->flags & SPU_CREATE_NOSCHED) &&
                            (!victim || tmp->prio > victim->prio))
                                victim = spu->ctx;
                }
                         * higher priority contexts before lower priority
                         * ones, so this is safe until we introduce
                         * priority inheritance schemes.
 +                       *
 +                       * XXX if the highest priority context is locked,
 +                       * this can loop a long time.  Might be better to
 +                       * look at another context or give up after X retries.
                         */
                        if (!mutex_trylock(&victim->state_mutex)) {
                                victim = NULL;
                        }
  
                        spu = victim->spu;
 -                      if (!spu) {
 +                      if (!spu || victim->prio <= ctx->prio) {
                                /*
                                 * This race can happen because we've dropped
 -                               * the active list mutex.  No a problem, just
 +                               * the active list mutex.  Not a problem, just
                                 * restart the search.
                                 */
                                mutex_unlock(&victim->state_mutex);
  
                        victim->stats.invol_ctx_switch++;
                        spu->stats.invol_ctx_switch++;
 +                      spu_add_to_rq(victim);
 +
                        mutex_unlock(&victim->state_mutex);
 -                      /*
 -                       * We need to break out of the wait loop in spu_run
 -                       * manually to ensure this context gets put on the
 -                       * runqueue again ASAP.
 -                       */
 -                      wake_up(&victim->stop_wq);
 +
                        return spu;
                }
        }
        return NULL;
  }
  
 +static void __spu_schedule(struct spu *spu, struct spu_context *ctx)
 +{
 +      int node = spu->node;
 +      int success = 0;
 +
 +      spu_set_timeslice(ctx);
 +
 +      mutex_lock(&cbe_spu_info[node].list_mutex);
 +      if (spu->ctx == NULL) {
 +              spu_bind_context(spu, ctx);
 +              cbe_spu_info[node].nr_active++;
 +              spu->alloc_state = SPU_USED;
 +              success = 1;
 +      }
 +      mutex_unlock(&cbe_spu_info[node].list_mutex);
 +
 +      if (success)
 +              wake_up_all(&ctx->run_wq);
 +      else
 +              spu_add_to_rq(ctx);
 +}
 +
 +static void spu_schedule(struct spu *spu, struct spu_context *ctx)
 +{
 +      /* not a candidate for interruptible because it's called either
 +         from the scheduler thread or from spu_deactivate */
 +      mutex_lock(&ctx->state_mutex);
 +      __spu_schedule(spu, ctx);
 +      spu_release(ctx);
 +}
 +
 +static void spu_unschedule(struct spu *spu, struct spu_context *ctx)
 +{
 +      int node = spu->node;
 +
 +      mutex_lock(&cbe_spu_info[node].list_mutex);
 +      cbe_spu_info[node].nr_active--;
 +      spu->alloc_state = SPU_FREE;
 +      spu_unbind_context(spu, ctx);
 +      ctx->stats.invol_ctx_switch++;
 +      spu->stats.invol_ctx_switch++;
 +      mutex_unlock(&cbe_spu_info[node].list_mutex);
 +}
 +
  /**
   * spu_activate - find a free spu for a context and execute it
   * @ctx:      spu context to schedule
   */
  int spu_activate(struct spu_context *ctx, unsigned long flags)
  {
 -      do {
 -              struct spu *spu;
 +      struct spu *spu;
  
 -              /*
 -               * If there are multiple threads waiting for a single context
 -               * only one actually binds the context while the others will
 -               * only be able to acquire the state_mutex once the context
 -               * already is in runnable state.
 -               */
 -              if (ctx->spu)
 -                      return 0;
 +      /*
 +       * If there are multiple threads waiting for a single context
 +       * only one actually binds the context while the others will
 +       * only be able to acquire the state_mutex once the context
 +       * already is in runnable state.
 +       */
 +      if (ctx->spu)
 +              return 0;
  
 -              spu = spu_get_idle(ctx);
 -              /*
 -               * If this is a realtime thread we try to get it running by
 -               * preempting a lower priority thread.
 -               */
 -              if (!spu && rt_prio(ctx->prio))
 -                      spu = find_victim(ctx);
 -              if (spu) {
 -                      int node = spu->node;
 +spu_activate_top:
 +      if (signal_pending(current))
 +              return -ERESTARTSYS;
  
 -                      mutex_lock(&cbe_spu_info[node].list_mutex);
 -                      spu_bind_context(spu, ctx);
 -                      cbe_spu_info[node].nr_active++;
 -                      mutex_unlock(&cbe_spu_info[node].list_mutex);
 -                      return 0;
 -              }
 +      spu = spu_get_idle(ctx);
 +      /*
 +       * If this is a realtime thread we try to get it running by
 +       * preempting a lower priority thread.
 +       */
 +      if (!spu && rt_prio(ctx->prio))
 +              spu = find_victim(ctx);
 +      if (spu) {
 +              unsigned long runcntl;
 +
 +              runcntl = ctx->ops->runcntl_read(ctx);
 +              __spu_schedule(spu, ctx);
 +              if (runcntl & SPU_RUNCNTL_RUNNABLE)
 +                      spuctx_switch_state(ctx, SPU_UTIL_USER);
  
 +              return 0;
 +      }
 +
 +      if (ctx->flags & SPU_CREATE_NOSCHED) {
                spu_prio_wait(ctx);
 -      } while (!signal_pending(current));
 +              goto spu_activate_top;
 +      }
  
 -      return -ERESTARTSYS;
 +      spu_add_to_rq(ctx);
 +
 +      return 0;
  }
  
  /**
@@@ -819,19 -706,21 +795,19 @@@ static int __spu_deactivate(struct spu_
        if (spu) {
                new = grab_runnable_context(max_prio, spu->node);
                if (new || force) {
 -                      int node = spu->node;
 -
 -                      mutex_lock(&cbe_spu_info[node].list_mutex);
 -                      spu_unbind_context(spu, ctx);
 -                      spu->alloc_state = SPU_FREE;
 -                      cbe_spu_info[node].nr_active--;
 -                      mutex_unlock(&cbe_spu_info[node].list_mutex);
 -
 -                      ctx->stats.vol_ctx_switch++;
 -                      spu->stats.vol_ctx_switch++;
 -
 -                      if (new)
 -                              wake_up(&new->stop_wq);
 +                      spu_unschedule(spu, ctx);
 +                      if (new) {
 +                              if (new->flags & SPU_CREATE_NOSCHED)
 +                                      wake_up(&new->stop_wq);
 +                              else {
 +                                      spu_release(ctx);
 +                                      spu_schedule(spu, new);
 +                                      /* this one can't easily be made
 +                                         interruptible */
 +                                      mutex_lock(&ctx->state_mutex);
 +                              }
 +                      }
                }
 -
        }
  
        return new != NULL;
@@@ -868,38 -757,43 +844,38 @@@ void spu_yield(struct spu_context *ctx
  
  static noinline void spusched_tick(struct spu_context *ctx)
  {
 +      struct spu_context *new = NULL;
 +      struct spu *spu = NULL;
 +      u32 status;
 +
 +      if (spu_acquire(ctx))
 +              BUG();  /* a kernel thread never has signals pending */
 +
 +      if (ctx->state != SPU_STATE_RUNNABLE)
 +              goto out;
 +      if (spu_stopped(ctx, &status))
 +              goto out;
        if (ctx->flags & SPU_CREATE_NOSCHED)
 -              return;
 +              goto out;
        if (ctx->policy == SCHED_FIFO)
 -              return;
 +              goto out;
  
        if (--ctx->time_slice)
 -              return;
 +              goto out;
  
 -      /*
 -       * Unfortunately list_mutex ranks outside of state_mutex, so
 -       * we have to trylock here.  If we fail give the context another
 -       * tick and try again.
 -       */
 -      if (mutex_trylock(&ctx->state_mutex)) {
 -              struct spu *spu = ctx->spu;
 -              struct spu_context *new;
 -
 -              new = grab_runnable_context(ctx->prio + 1, spu->node);
 -              if (new) {
 -                      spu_unbind_context(spu, ctx);
 -                      ctx->stats.invol_ctx_switch++;
 -                      spu->stats.invol_ctx_switch++;
 -                      spu->alloc_state = SPU_FREE;
 -                      cbe_spu_info[spu->node].nr_active--;
 -                      wake_up(&new->stop_wq);
 -                      /*
 -                       * We need to break out of the wait loop in
 -                       * spu_run manually to ensure this context
 -                       * gets put on the runqueue again ASAP.
 -                       */
 -                      wake_up(&ctx->stop_wq);
 -              }
 -              spu_set_timeslice(ctx);
 -              mutex_unlock(&ctx->state_mutex);
 +      spu = ctx->spu;
 +      new = grab_runnable_context(ctx->prio + 1, spu->node);
 +      if (new) {
 +              spu_unschedule(spu, ctx);
 +              spu_add_to_rq(ctx);
        } else {
                ctx->time_slice++;
        }
 +out:
 +      spu_release(ctx);
 +
 +      if (new)
 +              spu_schedule(spu, new);
  }
  
  /**
@@@ -923,31 -817,35 +899,31 @@@ static unsigned long count_active_conte
  }
  
  /**
 - * spu_calc_load - given tick count, update the avenrun load estimates.
 - * @tick:     tick count
 + * spu_calc_load - update the avenrun load estimates.
   *
   * No locking against reading these values from userspace, as for
   * the CPU loadavg code.
   */
 -static void spu_calc_load(unsigned long ticks)
 +static void spu_calc_load(void)
  {
        unsigned long active_tasks; /* fixed-point */
 -      static int count = LOAD_FREQ;
 -
 -      count -= ticks;
 -
 -      if (unlikely(count < 0)) {
 -              active_tasks = count_active_contexts() * FIXED_1;
 -              do {
 -                      CALC_LOAD(spu_avenrun[0], EXP_1, active_tasks);
 -                      CALC_LOAD(spu_avenrun[1], EXP_5, active_tasks);
 -                      CALC_LOAD(spu_avenrun[2], EXP_15, active_tasks);
 -                      count += LOAD_FREQ;
 -              } while (count < 0);
 -      }
 +
 +      active_tasks = count_active_contexts() * FIXED_1;
 +      CALC_LOAD(spu_avenrun[0], EXP_1, active_tasks);
 +      CALC_LOAD(spu_avenrun[1], EXP_5, active_tasks);
 +      CALC_LOAD(spu_avenrun[2], EXP_15, active_tasks);
  }
  
  static void spusched_wake(unsigned long data)
  {
        mod_timer(&spusched_timer, jiffies + SPUSCHED_TICK);
        wake_up_process(spusched_task);
 -      spu_calc_load(SPUSCHED_TICK);
 +}
 +
 +static void spuloadavg_wake(unsigned long data)
 +{
 +      mod_timer(&spuloadavg_timer, jiffies + LOAD_FREQ);
 +      spu_calc_load();
  }
  
  static int spusched_thread(void *unused)
                set_current_state(TASK_INTERRUPTIBLE);
                schedule();
                for (node = 0; node < MAX_NUMNODES; node++) {
 -                      mutex_lock(&cbe_spu_info[node].list_mutex);
 -                      list_for_each_entry(spu, &cbe_spu_info[node].spus, cbe_list)
 -                              if (spu->ctx)
 -                                      spusched_tick(spu->ctx);
 -                      mutex_unlock(&cbe_spu_info[node].list_mutex);
 +                      struct mutex *mtx = &cbe_spu_info[node].list_mutex;
 +
 +                      mutex_lock(mtx);
 +                      list_for_each_entry(spu, &cbe_spu_info[node].spus,
 +                                      cbe_list) {
 +                              struct spu_context *ctx = spu->ctx;
 +
 +                              if (ctx) {
 +                                      mutex_unlock(mtx);
 +                                      spusched_tick(ctx);
 +                                      mutex_lock(mtx);
 +                              }
 +                      }
 +                      mutex_unlock(mtx);
                }
        }
  
        return 0;
  }
  
 +void spuctx_switch_state(struct spu_context *ctx,
 +              enum spu_utilization_state new_state)
 +{
 +      unsigned long long curtime;
 +      signed long long delta;
 +      struct timespec ts;
 +      struct spu *spu;
 +      enum spu_utilization_state old_state;
 +
 +      ktime_get_ts(&ts);
 +      curtime = timespec_to_ns(&ts);
 +      delta = curtime - ctx->stats.tstamp;
 +
 +      WARN_ON(!mutex_is_locked(&ctx->state_mutex));
 +      WARN_ON(delta < 0);
 +
 +      spu = ctx->spu;
 +      old_state = ctx->stats.util_state;
 +      ctx->stats.util_state = new_state;
 +      ctx->stats.tstamp = curtime;
 +
 +      /*
 +       * Update the physical SPU utilization statistics.
 +       */
 +      if (spu) {
 +              ctx->stats.times[old_state] += delta;
 +              spu->stats.times[old_state] += delta;
 +              spu->stats.util_state = new_state;
 +              spu->stats.tstamp = curtime;
 +      }
 +}
 +
  #define LOAD_INT(x) ((x) >> FSHIFT)
  #define LOAD_FRAC(x) LOAD_INT(((x) & (FIXED_1-1)) * 100)
  
@@@ -1024,7 -881,7 +1000,7 @@@ static int show_spu_loadavg(struct seq_
  
        /*
         * Note that last_pid doesn't really make much sense for the
 -       * SPU loadavg (it even seems very odd on the CPU side..),
 +       * SPU loadavg (it even seems very odd on the CPU side...),
         * but we include it here to have a 100% compatible interface.
         */
        seq_printf(s, "%d.%02d %d.%02d %d.%02d %ld/%d %d\n",
@@@ -1065,7 -922,6 +1041,7 @@@ int __init spu_sched_init(void
        spin_lock_init(&spu_prio->runq_lock);
  
        setup_timer(&spusched_timer, spusched_wake, 0);
 +      setup_timer(&spuloadavg_timer, spuloadavg_wake, 0);
  
        spusched_task = kthread_run(spusched_thread, NULL, "spusched");
        if (IS_ERR(spusched_task)) {
                goto out_free_spu_prio;
        }
  
 +      mod_timer(&spuloadavg_timer, 0);
 +
        entry = create_proc_entry("spu_loadavg", 0, NULL);
        if (!entry)
                goto out_stop_kthread;
@@@ -1100,7 -954,6 +1076,7 @@@ void spu_sched_exit(void
        remove_proc_entry("spu_loadavg", NULL);
  
        del_timer_sync(&spusched_timer);
 +      del_timer_sync(&spuloadavg_timer);
        kthread_stop(spusched_task);
  
        for (node = 0; node < MAX_NUMNODES; node++) {
  
  struct spu_context;
  struct spu_runqueue;
 +struct spu_lscsa;
  struct device_node;
  
  enum spu_utilization_state {
@@@ -146,6 -145,7 +146,6 @@@ struct spu 
        void (* ibox_callback)(struct spu *spu);
        void (* stop_callback)(struct spu *spu);
        void (* mfc_callback)(struct spu *spu);
 -      void (* dma_callback)(struct spu *spu, int type);
  
        char irq_c0[8];
        char irq_c1[8];
@@@ -196,11 -196,10 +196,11 @@@ struct cbe_spu_info 
  extern struct cbe_spu_info cbe_spu_info[];
  
  void spu_init_channels(struct spu *spu);
 -int spu_irq_class_0_bottom(struct spu *spu);
 -int spu_irq_class_1_bottom(struct spu *spu);
  void spu_irq_setaffinity(struct spu *spu, int cpu);
  
 +void spu_setup_kernel_slbs(struct spu *spu, struct spu_lscsa *lscsa,
 +              void *code, int code_size);
 +
  #ifdef CONFIG_KEXEC
  void crash_register_spus(struct list_head *list);
  #else
@@@ -211,7 -210,6 +211,7 @@@ static inline void crash_register_spus(
  
  extern void spu_invalidate_slbs(struct spu *spu);
  extern void spu_associate_mm(struct spu *spu, struct mm_struct *mm);
 +int spu_64k_pages_available(void);
  
  /* Calls from the memory management to the SPU */
  struct mm_struct;
@@@ -248,6 -246,7 +248,7 @@@ struct spufs_calls 
                                                __u32 __user *ustatus);
        int (*coredump_extra_notes_size)(void);
        int (*coredump_extra_notes_write)(struct file *file, loff_t *foffset);
+       void (*notify_spus_active)(void);
        struct module *owner;
  };
  
@@@ -280,8 -279,6 +281,8 @@@ void spu_remove_sysdev_attr(struct sysd
  int spu_add_sysdev_attr_group(struct attribute_group *attrs);
  void spu_remove_sysdev_attr_group(struct attribute_group *attrs);
  
 +int spu_handle_mm_fault(struct mm_struct *mm, unsigned long ea,
 +              unsigned long dsisr, unsigned *flt);
  
  /*
   * Notifier blocks:
@@@ -302,8 -299,11 +303,11 @@@ struct notifier_block
  int spu_switch_event_register(struct notifier_block * n);
  int spu_switch_event_unregister(struct notifier_block * n);
  
+ extern void notify_spus_active(void);
+ extern void do_notify_spus_active(void);
  /*
 - * This defines the Local Store, Problem Area and Privlege Area of an SPU.
 + * This defines the Local Store, Problem Area and Privilege Area of an SPU.
   */
  
  union mfc_tag_size_class_cmd {
@@@ -524,24 -524,8 +528,24 @@@ struct spu_priv1 
  #define CLASS2_ENABLE_SPU_STOP_INTR                   0x2L
  #define CLASS2_ENABLE_SPU_HALT_INTR                   0x4L
  #define CLASS2_ENABLE_SPU_DMA_TAG_GROUP_COMPLETE_INTR 0x8L
 +#define CLASS2_ENABLE_MAILBOX_THRESHOLD_INTR          0x10L
        u8  pad_0x118_0x140[0x28];                              /* 0x118 */
        u64 int_stat_RW[3];                                     /* 0x140 */
 +#define CLASS0_DMA_ALIGNMENT_INTR                     0x1L
 +#define CLASS0_INVALID_DMA_COMMAND_INTR                       0x2L
 +#define CLASS0_SPU_ERROR_INTR                         0x4L
 +#define CLASS0_INTR_MASK                              0x7L
 +#define CLASS1_SEGMENT_FAULT_INTR                     0x1L
 +#define CLASS1_STORAGE_FAULT_INTR                     0x2L
 +#define CLASS1_LS_COMPARE_SUSPEND_ON_GET_INTR         0x4L
 +#define CLASS1_LS_COMPARE_SUSPEND_ON_PUT_INTR         0x8L
 +#define CLASS1_INTR_MASK                              0xfL
 +#define CLASS2_MAILBOX_INTR                           0x1L
 +#define CLASS2_SPU_STOP_INTR                          0x2L
 +#define CLASS2_SPU_HALT_INTR                          0x4L
 +#define CLASS2_SPU_DMA_TAG_GROUP_COMPLETE_INTR                0x8L
 +#define CLASS2_MAILBOX_THRESHOLD_INTR                 0x10L
 +#define CLASS2_INTR_MASK                              0x1fL
        u8  pad_0x158_0x180[0x28];                              /* 0x158 */
        u64 int_route_RW;                                       /* 0x180 */