]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
ARC: [ASID] Remove asid-to-mm reverse map and track ASID alloc cycle
authorVineet Gupta <vgupta@synopsys.com>
Thu, 25 Jul 2013 22:45:50 +0000 (15:45 -0700)
committerVineet Gupta <vgupta@synopsys.com>
Tue, 30 Jul 2013 08:57:34 +0000 (01:57 -0700)
ARC ASID allocator used asid_mm_map[ ] to find a mm corresponding to an
ASID. This would help identify an existing assignee (from a prev
allocation cycle) and force it to be marked as unallocated so that it
would know to refresh it's ASID when it's mm is switched.

Without the reverse map, it is not possible to reach out to current
assignee so we track the ASID allocations generations and on every
switch mm, refresh the ASID if it is from prev generation.

The motivation for this change is SMP ASID allocatino which wiuld have
required a multi-dimensional asid-to-mmp[ ][ ].

(Based loosely on arch/sh implementation)

Signed-off-by: Vineet Gupta <vgupta@synopsys.com>
arch/arc/include/asm/mmu.h
arch/arc/include/asm/mmu_context.h
arch/arc/mm/tlb.c

index 7165f25c09105cdeb0eb1a2d645673e852436f1c..3529a46859b1cd8d130a6f8cbc35f28d0c84bace 100644 (file)
@@ -48,7 +48,7 @@
 #ifndef __ASSEMBLY__
 
 typedef struct {
-       unsigned long asid;     /* Pvt Addr-Space ID for mm */
+       unsigned long asid;     /* Hw PID + Generation cycle */
 } mm_context_t;
 
 #ifdef CONFIG_ARC_DBG_TLB_PARANOIA
index f3a0cb7eb9bc5b324396523ccb6bc59fdecbbbf3..cf1d7c145ef66e76f6b0620a80ef9b6ee891b27c 100644 (file)
  * When it reaches max 255, the allocation cycle starts afresh by flushing
  * the entire TLB and wrapping ASID back to zero.
  *
- * For book-keeping, Linux uses a couple of data-structures:
- *  -mm_struct has an @asid field to keep a note of task's ASID (needed at the
- *   time of say switch_mm( )
- *  -An array of mm structs @asid_mm_map[] for asid->mm the reverse mapping,
- *  given an ASID, finding the mm struct associated.
- *
- * The round-robin allocation algorithm allows for ASID stealing.
- * If asid tracker is at "x-1", a new req will allocate "x", even if "x" was
- * already assigned to another (switched-out) task. Obviously the prev owner
- * is marked with an invalid ASID to make it request for a new ASID when it
- * gets scheduled next time. However its TLB entries (with ASID "x") could
- * exist, which must be cleared before the same ASID is used by the new owner.
- * Flushing them would be plausible but costly solution. Instead we force a
- * allocation policy quirk, which ensures that a stolen ASID won't have any
- * TLB entries associates, alleviating the need to flush.
- * The quirk essentially is not allowing ASID allocated in prev cycle
- * to be used past a roll-over in the next cycle.
- * When this happens (i.e. task ASID > asid tracker), task needs to refresh
- * its ASID, aligning it to current value of tracker. If the task doesn't get
- * scheduled past a roll-over, hence its ASID is not yet realigned with
- * tracker, such ASID is anyways safely reusable because it is
- * gauranteed that TLB entries with that ASID wont exist.
+ * A new allocation cycle, post rollover, could potentially reassign an ASID
+ * to a different task. Thus the rule is to refresh the ASID in a new cycle.
+ * The 32 bit @asid_cache (and mm->asid) have 8 bits MMU PID and rest 24 bits
+ * serve as cycle/generation indicator and natural 32 bit unsigned math
+ * automagically increments the generation when lower 8 bits rollover.
  */
 
-#define FIRST_ASID  0
-#define MAX_ASID    255                        /* 8 bit PID field in PID Aux reg */
-#define NO_ASID     (MAX_ASID + 1)     /* ASID Not alloc to mmu ctxt */
-#define NUM_ASID    ((MAX_ASID - FIRST_ASID) + 1)
+#define MM_CTXT_ASID_MASK      0x000000ff /* MMU PID reg :8 bit PID */
+#define MM_CTXT_CYCLE_MASK     (~MM_CTXT_ASID_MASK)
+
+#define MM_CTXT_FIRST_CYCLE    (MM_CTXT_ASID_MASK + 1)
+#define MM_CTXT_NO_ASID                0UL
 
-/* ASID to mm struct mapping */
-extern struct mm_struct *asid_mm_map[NUM_ASID + 1];
+#define hw_pid(mm)             (mm->context.asid & MM_CTXT_ASID_MASK)
 
-extern int asid_cache;
+extern unsigned int asid_cache;
 
 /*
  * Get a new ASID if task doesn't have a valid one (unalloc or from prev cycle)
@@ -74,7 +57,6 @@ extern int asid_cache;
  */
 static inline void get_new_mmu_context(struct mm_struct *mm)
 {
-       struct mm_struct *prev_owner;
        unsigned long flags;
 
        local_irq_save(flags);
@@ -86,46 +68,26 @@ static inline void get_new_mmu_context(struct mm_struct *mm)
         * context, setting it to invalid value, which the check below would
         * catch too
         */
-       if (mm->context.asid <= asid_cache)
+       if (!((mm->context.asid ^ asid_cache) & MM_CTXT_CYCLE_MASK))
                goto set_hw;
 
-       /*
-        * Relinquish the currently owned ASID (if any).
-        * Doing unconditionally saves a cmp-n-branch; for already unused
-        * ASID slot, the value was/remains NULL
-        */
-       asid_mm_map[mm->context.asid] = (struct mm_struct *)NULL;
-
        /* move to new ASID */
-       if (++asid_cache > MAX_ASID) {  /* ASID roll-over */
-               asid_cache = FIRST_ASID;
+       if (!(++asid_cache & MM_CTXT_ASID_MASK)) {      /* ASID roll-over */
                flush_tlb_all();
        }
 
-       /*
-        * Is next ASID already owned by some-one else (we are stealing it).
-        * If so, let the orig owner be aware of this, so when it runs, it
-        * asks for a brand new ASID. This would only happen for a long-lived
-        * task with ASID from prev allocation cycle (before ASID roll-over).
-        *
-        * This might look wrong - if we are re-using some other task's ASID,
-        * won't we use it's stale TLB entries too. Actually the algorithm takes
-        * care of such a case: it ensures that task with ASID from prev alloc
-        * cycle, when scheduled will refresh it's ASID
-        * The stealing scenario described here will only happen if that task
-        * didn't get a chance to refresh it's ASID - implying stale entries
-        * won't exist.
+       /* Above was rollover of 8 bit ASID in 32 bit container.
+        * If the container itself wrapped around, set it to a non zero
+        * "generation" to distinguish from no context
         */
-       prev_owner = asid_mm_map[asid_cache];
-       if (prev_owner)
-               prev_owner->context.asid = NO_ASID;
+       if (!asid_cache)
+               asid_cache = MM_CTXT_FIRST_CYCLE;
 
        /* Assign new ASID to tsk */
-       asid_mm_map[asid_cache] = mm;
        mm->context.asid = asid_cache;
 
 set_hw:
-       write_aux_reg(ARC_REG_PID, mm->context.asid | MMU_ENABLE);
+       write_aux_reg(ARC_REG_PID, hw_pid(mm) | MMU_ENABLE);
 
        local_irq_restore(flags);
 }
@@ -137,7 +99,7 @@ set_hw:
 static inline int
 init_new_context(struct task_struct *tsk, struct mm_struct *mm)
 {
-       mm->context.asid = NO_ASID;
+       mm->context.asid = MM_CTXT_NO_ASID;
        return 0;
 }
 
@@ -166,14 +128,7 @@ static inline void switch_mm(struct mm_struct *prev, struct mm_struct *next,
 
 static inline void destroy_context(struct mm_struct *mm)
 {
-       unsigned long flags;
-
-       local_irq_save(flags);
-
-       asid_mm_map[mm->context.asid] = NULL;
-       mm->context.asid = NO_ASID;
-
-       local_irq_restore(flags);
+       mm->context.asid = MM_CTXT_NO_ASID;
 }
 
 /* it seemed that deactivate_mm( ) is a reasonable place to do book-keeping
index 53c4101dd35295daf596ae04144c735acc015321..ecf5a113335ca240a20281e1b191eb6ee5132810 100644 (file)
 
 
 /* A copy of the ASID from the PID reg is kept in asid_cache */
-int asid_cache = FIRST_ASID;
-
-/* ASID to mm struct mapping. We have one extra entry corresponding to
- * NO_ASID to save us a compare when clearing the mm entry for old asid
- * see get_new_mmu_context (asm-arc/mmu_context.h)
- */
-struct mm_struct *asid_mm_map[NUM_ASID + 1];
+unsigned int asid_cache = MM_CTXT_FIRST_CYCLE;
 
 /*
  * Utility Routine to erase a J-TLB entry
@@ -282,7 +276,6 @@ void local_flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
                           unsigned long end)
 {
        unsigned long flags;
-       unsigned int asid;
 
        /* If range @start to @end is more than 32 TLB entries deep,
         * its better to move to a new ASID rather than searching for
@@ -304,11 +297,10 @@ void local_flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
        start &= PAGE_MASK;
 
        local_irq_save(flags);
-       asid = vma->vm_mm->context.asid;
 
-       if (asid != NO_ASID) {
+       if (vma->vm_mm->context.asid != MM_CTXT_NO_ASID) {
                while (start < end) {
-                       tlb_entry_erase(start | (asid & 0xff));
+                       tlb_entry_erase(start | hw_pid(vma->vm_mm));
                        start += PAGE_SIZE;
                }
        }
@@ -362,9 +354,8 @@ void local_flush_tlb_page(struct vm_area_struct *vma, unsigned long page)
         */
        local_irq_save(flags);
 
-       if (vma->vm_mm->context.asid != NO_ASID) {
-               tlb_entry_erase((page & PAGE_MASK) |
-                               (vma->vm_mm->context.asid & 0xff));
+       if (vma->vm_mm->context.asid != MM_CTXT_NO_ASID) {
+               tlb_entry_erase((page & PAGE_MASK) | hw_pid(vma->vm_mm));
                utlb_invalidate();
        }
 
@@ -410,7 +401,7 @@ void create_tlb(struct vm_area_struct *vma, unsigned long address, pte_t *ptep)
 
        local_irq_save(flags);
 
-       tlb_paranoid_check(vma->vm_mm->context.asid, address);
+       tlb_paranoid_check(hw_pid(vma->vm_mm), address);
 
        address &= PAGE_MASK;
 
@@ -572,11 +563,6 @@ void arc_mmu_init(void)
        if (mmu->pg_sz != PAGE_SIZE)
                panic("MMU pg size != PAGE_SIZE (%luk)\n", TO_KB(PAGE_SIZE));
 
-       /*
-        * ASID mgmt data structures are compile time init
-        *  asid_cache = FIRST_ASID and asid_mm_map[] all zeroes
-        */
-
        local_flush_tlb_all();
 
        /* Enable the MMU */
@@ -693,7 +679,7 @@ void do_tlb_overlap_fault(unsigned long cause, unsigned long address,
 void print_asid_mismatch(int is_fast_path)
 {
        int pid_sw, pid_hw;
-       pid_sw = current->active_mm->context.asid;
+       pid_sw = hw_pid(current->active_mm);
        pid_hw = read_aux_reg(ARC_REG_PID) & 0xff;
 
        pr_emerg("ASID Mismatch in %s Path Handler: sw-pid=0x%x hw-pid=0x%x\n",
@@ -708,7 +694,8 @@ void tlb_paranoid_check(unsigned int pid_sw, unsigned long addr)
 
        pid_hw = read_aux_reg(ARC_REG_PID) & 0xff;
 
-       if (addr < 0x70000000 && ((pid_hw != pid_sw) || (pid_sw == NO_ASID)))
+       if (addr < 0x70000000 && ((pid_hw != pid_sw) ||
+           (pid_sw == MM_CTXT_NO_ASID)))
                print_asid_mismatch(0);
 }
 #endif