Gitweb:     
http://git.kernel.org/git/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=684bd614015188561197342fd336292e9e2ce196
Commit:     684bd614015188561197342fd336292e9e2ce196
Parent:     f6eb7d7ffef3e2fa40b0161c30486cb87203758d
Author:     Jeremy Kerr <[EMAIL PROTECTED]>
AuthorDate: Wed Dec 5 13:49:31 2007 +1100
Committer:  Arnd Bergmann <[EMAIL PROTECTED]>
CommitDate: Wed Dec 19 01:00:05 2007 +0100

    [POWERPC] cell: handle SPE kernel mappings that cross segment boundaries
    
    Currently, we have a possibilty that the SLBs setup during context
    switch don't cover the entirety of the necessary lscsa and code
    regions, if these regions cross a segment boundary.
    
    This change checks the start and end of each region, and inserts a SLB
    entry for each, if unique. We also remove the assumption that the
    spu_save_code and spu_restore_code reside in the same segment, by using
    the specific code array for save and restore.
    
    Signed-off-by: Jeremy Kerr <[EMAIL PROTECTED]>
    Signed-off-by: Arnd Bergmann <[EMAIL PROTECTED]>
---
 arch/powerpc/platforms/cell/spu_base.c     |   50 ++++++++++++++++++++++++----
 arch/powerpc/platforms/cell/spufs/switch.c |   11 ++++--
 include/asm-powerpc/spu.h                  |    4 +-
 3 files changed, 52 insertions(+), 13 deletions(-)

diff --git a/arch/powerpc/platforms/cell/spu_base.c 
b/arch/powerpc/platforms/cell/spu_base.c
index 95001cd..ee37e0e 100644
--- a/arch/powerpc/platforms/cell/spu_base.c
+++ b/arch/powerpc/platforms/cell/spu_base.c
@@ -275,19 +275,55 @@ static void __spu_kernel_slb(void *addr, struct spu_slb 
*slb)
 }
 
 /**
+ * Given an array of @nr_slbs SLB entries, @slbs, return non-zero if the
+ * address @new_addr is present.
+ */
+static inline int __slb_present(struct spu_slb *slbs, int nr_slbs,
+               void *new_addr)
+{
+       unsigned long ea = (unsigned long)new_addr;
+       int i;
+
+       for (i = 0; i < nr_slbs; i++)
+               if (!((slbs[i].esid ^ ea) & ESID_MASK))
+                       return 1;
+
+       return 0;
+}
+
+/**
  * Setup the SPU kernel SLBs, in preparation for a context save/restore. We
  * need to map both the context save area, and the save/restore code.
+ *
+ * Because the lscsa and code may cross segment boundaires, we check to see
+ * if mappings are required for the start and end of each range. We currently
+ * assume that the mappings are smaller that one segment - if not, something
+ * is seriously wrong.
  */
-void spu_setup_kernel_slbs(struct spu *spu, struct spu_lscsa *lscsa, void 
*code)
+void spu_setup_kernel_slbs(struct spu *spu, struct spu_lscsa *lscsa,
+               void *code, int code_size)
 {
-       struct spu_slb code_slb, lscsa_slb;
+       struct spu_slb slbs[4];
+       int i, nr_slbs = 0;
+       /* start and end addresses of both mappings */
+       void *addrs[] = {
+               lscsa, (void *)lscsa + sizeof(*lscsa) - 1,
+               code, code + code_size - 1
+       };
+
+       /* check the set of addresses, and create a new entry in the slbs array
+        * if there isn't already a SLB for that address */
+       for (i = 0; i < ARRAY_SIZE(addrs); i++) {
+               if (__slb_present(slbs, nr_slbs, addrs[i]))
+                       continue;
 
-       __spu_kernel_slb(lscsa, &lscsa_slb);
-       __spu_kernel_slb(code, &code_slb);
+               __spu_kernel_slb(addrs[i], &slbs[nr_slbs]);
+               nr_slbs++;
+       }
 
-       spu_load_slb(spu, 0, &lscsa_slb);
-       if (lscsa_slb.esid != code_slb.esid)
-               spu_load_slb(spu, 1, &code_slb);
+       /* Add the set of SLBs */
+       for (i = 0; i < nr_slbs; i++)
+               spu_load_slb(spu, i, &slbs[i]);
 }
 EXPORT_SYMBOL_GPL(spu_setup_kernel_slbs);
 
diff --git a/arch/powerpc/platforms/cell/spufs/switch.c 
b/arch/powerpc/platforms/cell/spufs/switch.c
index 96f5514..8cbc657 100644
--- a/arch/powerpc/platforms/cell/spufs/switch.c
+++ b/arch/powerpc/platforms/cell/spufs/switch.c
@@ -691,7 +691,8 @@ static inline void resume_mfc_queue(struct spu_state *csa, 
struct spu *spu)
        out_be64(&priv2->mfc_control_RW, MFC_CNTL_RESUME_DMA_QUEUE);
 }
 
-static inline void setup_mfc_slbs(struct spu_state *csa, struct spu *spu)
+static inline void setup_mfc_slbs(struct spu_state *csa, struct spu *spu,
+               unsigned int *code, int code_size)
 {
        /* Save, Step 47:
         * Restore, Step 30.
@@ -708,7 +709,7 @@ static inline void setup_mfc_slbs(struct spu_state *csa, 
struct spu *spu)
         *     translation is desired by OS environment).
         */
        spu_invalidate_slbs(spu);
-       spu_setup_kernel_slbs(spu, csa->lscsa, &spu_save_code);
+       spu_setup_kernel_slbs(spu, csa->lscsa, code, code_size);
 }
 
 static inline void set_switch_active(struct spu_state *csa, struct spu *spu)
@@ -1835,7 +1836,8 @@ static void save_lscsa(struct spu_state *prev, struct spu 
*spu)
         */
 
        resume_mfc_queue(prev, spu);    /* Step 46. */
-       setup_mfc_slbs(prev, spu);      /* Step 47. */
+       /* Step 47. */
+       setup_mfc_slbs(prev, spu, spu_save_code, sizeof(spu_save_code));
        set_switch_active(prev, spu);   /* Step 48. */
        enable_interrupts(prev, spu);   /* Step 49. */
        save_ls_16kb(prev, spu);        /* Step 50. */
@@ -1940,7 +1942,8 @@ static void restore_lscsa(struct spu_state *next, struct 
spu *spu)
        setup_spu_status_part1(next, spu);      /* Step 27. */
        setup_spu_status_part2(next, spu);      /* Step 28. */
        restore_mfc_rag(next, spu);             /* Step 29. */
-       setup_mfc_slbs(next, spu);              /* Step 30. */
+       /* Step 30. */
+       setup_mfc_slbs(next, spu, spu_restore_code, sizeof(spu_restore_code));
        set_spu_npc(next, spu);                 /* Step 31. */
        set_signot1(next, spu);                 /* Step 32. */
        set_signot2(next, spu);                 /* Step 33. */
diff --git a/include/asm-powerpc/spu.h b/include/asm-powerpc/spu.h
index 3308ed4..314aad3 100644
--- a/include/asm-powerpc/spu.h
+++ b/include/asm-powerpc/spu.h
@@ -201,8 +201,8 @@ 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);
+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);
-
To unsubscribe from this list: send the line "unsubscribe git-commits-head" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to