*/
 #define MAX_EA_BITS_PER_CONTEXT                46
 
+/*
+ * Our page table limit us to 64TB. Hence for the kernel mapping,
+ * each MAP area is limited to 16 TB.
+ * The four map areas are:  linear mapping, vmap, IO and vmemmap
+ */
+#define H_KERN_MAP_SIZE                (ASM_CONST(1) << (MAX_EA_BITS_PER_CONTEXT - 2))
+
+/*
+ * Define the address range of the kernel non-linear virtual area
+ * 16TB
+ */
+#define H_KERN_VIRT_START      ASM_CONST(0xc000100000000000)
+
 #ifndef __ASSEMBLY__
 #define H_PTE_TABLE_SIZE       (sizeof(pte_t) << H_PTE_INDEX_SIZE)
 #define H_PMD_TABLE_SIZE       (sizeof(pmd_t) << H_PMD_INDEX_SIZE)
 
  */
 #define MAX_EA_BITS_PER_CONTEXT                49
 
+/*
+ * We use one context for each MAP area.
+ */
+#define H_KERN_MAP_SIZE                (1UL << MAX_EA_BITS_PER_CONTEXT)
+
+/*
+ * Define the address range of the kernel non-linear virtual area
+ * 2PB
+ */
+#define H_KERN_VIRT_START      ASM_CONST(0xc008000000000000)
+
 /*
  * 64k aligned address free up few of the lower bits of RPN for us
  * We steal that here. For more deatils look at pte_pfn/pfn_pte()
 
 #define H_PGTABLE_EADDR_SIZE   (H_PTE_INDEX_SIZE + H_PMD_INDEX_SIZE + \
                                 H_PUD_INDEX_SIZE + H_PGD_INDEX_SIZE + PAGE_SHIFT)
 #define H_PGTABLE_RANGE                (ASM_CONST(1) << H_PGTABLE_EADDR_SIZE)
+/*
+ * Top 2 bits are ignored in page table walk.
+ */
+#define EA_MASK                        (~(0xcUL << 60))
 
 /*
  * We store the slot details in the second half of page table.
 #endif
 
 /*
- * Define the address range of the kernel non-linear virtual area. In contrast
- * to the linear mapping, this is managed using the kernel page tables and then
- * inserted into the hash page table to actually take effect, similarly to user
- * mappings.
+ * +------------------------------+
+ * |                              |
+ * |                              |
+ * |                              |
+ * +------------------------------+  Kernel virtual map end (0xc00e000000000000)
+ * |                              |
+ * |                              |
+ * |      512TB/16TB of vmemmap   |
+ * |                              |
+ * |                              |
+ * +------------------------------+  Kernel vmemmap  start
+ * |                              |
+ * |      512TB/16TB of IO map    |
+ * |                              |
+ * +------------------------------+  Kernel IO map start
+ * |                              |
+ * |      512TB/16TB of vmap      |
+ * |                              |
+ * +------------------------------+  Kernel virt start (0xc008000000000000)
+ * |                              |
+ * |                              |
+ * |                              |
+ * +------------------------------+  Kernel linear (0xc.....)
  */
-#define H_KERN_VIRT_START ASM_CONST(0xD000000000000000)
 
-/*
- * Allow virtual mapping of one context size.
- * 512TB for 64K page size
- * 64TB for 4K page size
- */
-#define H_KERN_VIRT_SIZE (1UL << MAX_EA_BITS_PER_CONTEXT)
+#define H_VMALLOC_START                H_KERN_VIRT_START
+#define H_VMALLOC_SIZE         H_KERN_MAP_SIZE
+#define H_VMALLOC_END          (H_VMALLOC_START + H_VMALLOC_SIZE)
 
-/*
- * 8TB IO mapping size
- */
-#define H_KERN_IO_SIZE ASM_CONST(0x80000000000) /* 8T */
-
-/*
- * The vmalloc space starts at the beginning of the kernel non-linear virtual
- * region, and occupies 504T (64K) or 56T (4K)
- */
-#define H_VMALLOC_START H_KERN_VIRT_START
-#define H_VMALLOC_SIZE (H_KERN_VIRT_SIZE - H_KERN_IO_SIZE)
-#define H_VMALLOC_END  (H_VMALLOC_START + H_VMALLOC_SIZE)
+#define H_KERN_IO_START                H_VMALLOC_END
+#define H_KERN_IO_SIZE         H_KERN_MAP_SIZE
+#define H_KERN_IO_END          (H_KERN_IO_START + H_KERN_IO_SIZE)
 
-#define H_KERN_IO_START        H_VMALLOC_END
-#define H_KERN_IO_END  (H_KERN_VIRT_START + H_KERN_VIRT_SIZE)
+#define H_VMEMMAP_START                H_KERN_IO_END
+#define H_VMEMMAP_SIZE         H_KERN_MAP_SIZE
+#define H_VMEMMAP_END          (H_VMEMMAP_START + H_VMEMMAP_SIZE)
 
 /*
  * Region IDs
  */
-#define REGION_SHIFT           60UL
-#define REGION_MASK            (0xfUL << REGION_SHIFT)
-#define REGION_ID(ea)          (((unsigned long)(ea)) >> REGION_SHIFT)
-
-#define VMALLOC_REGION_ID      (REGION_ID(H_VMALLOC_START))
-#define KERNEL_REGION_ID       (REGION_ID(PAGE_OFFSET))
-#define VMEMMAP_REGION_ID      (0xfUL) /* Server only */
-#define USER_REGION_ID         (0UL)
+#define USER_REGION_ID         1
+#define KERNEL_REGION_ID       2
+#define VMALLOC_REGION_ID      3
+#define IO_REGION_ID           4
+#define VMEMMAP_REGION_ID      5
 
 /*
  * Defines the address of the vmemap area, in its own region on
  * hash table CPUs.
  */
-#define H_VMEMMAP_BASE         (VMEMMAP_REGION_ID << REGION_SHIFT)
 
 #ifdef CONFIG_PPC_MM_SLICES
 #define HAVE_ARCH_UNMAPPED_AREA
 #define H_PUD_BAD_BITS         (PMD_TABLE_SIZE-1)
 
 #ifndef __ASSEMBLY__
+static inline int get_region_id(unsigned long ea)
+{
+       int id = (ea >> 60UL);
+
+       if (id == 0)
+               return USER_REGION_ID;
+
+       VM_BUG_ON(id != 0xc);
+       VM_BUG_ON(ea >= H_VMEMMAP_END);
+
+       if (ea >= H_VMEMMAP_START)
+               return VMEMMAP_REGION_ID;
+       else if (ea >= H_KERN_IO_START)
+               return IO_REGION_ID;
+       else if (ea >= H_VMALLOC_START)
+               return VMALLOC_REGION_ID;
+
+       return KERNEL_REGION_ID;
+}
+
 #define        hash__pmd_bad(pmd)              (pmd_val(pmd) & H_PMD_BAD_BITS)
 #define        hash__pud_bad(pud)              (pud_val(pud) & H_PUD_BAD_BITS)
 static inline int hash__pgd_bad(pgd_t pgd)
 
 #endif
 
 #define MAX_VMALLOC_CTX_CNT    1
-#define MAX_MEMMAP_CTX_CNT     1
+#define MAX_IO_CTX_CNT         1
+#define MAX_VMEMMAP_CTX_CNT    1
 
 /*
  * 256MB segment
  * would give a protovsid of 0x1fffffffff. That will result in a VSID 0
  * because of the modulo operation in vsid scramble.
  *
- * We add one extra context to MIN_USER_CONTEXT so that we can map kernel
- * context easily. The +1 is to map the unused 0xe region mapping.
  */
 #define MAX_USER_CONTEXT       ((ASM_CONST(1) << CONTEXT_BITS) - 2)
 #define MIN_USER_CONTEXT       (MAX_KERNEL_CTX_CNT + MAX_VMALLOC_CTX_CNT + \
-                                MAX_MEMMAP_CTX_CNT + 2)
-
+                                MAX_IO_CTX_CNT + MAX_VMEMMAP_CTX_CNT)
 /*
  * For platforms that support on 65bit VA we limit the context bits
  */
        /*
         * Bad address. We return VSID 0 for that
         */
-       if ((ea & ~REGION_MASK) >= H_PGTABLE_RANGE)
+       if ((ea & EA_MASK)  >= H_PGTABLE_RANGE)
                return 0;
 
        if (!mmu_has_feature(MMU_FTR_68_BIT_VA))
  * 0x00002 -  [ 0xc002000000000000 - 0xc003ffffffffffff]
  * 0x00003 -  [ 0xc004000000000000 - 0xc005ffffffffffff]
  * 0x00004 -  [ 0xc006000000000000 - 0xc007ffffffffffff]
-
- * 0x00005 -  [ 0xd000000000000000 - 0xd001ffffffffffff ]
- * 0x00006 -  Not used - Can map 0xe000000000000000 range.
- * 0x00007 -  [ 0xf000000000000000 - 0xf001ffffffffffff ]
  *
- * So we can compute the context from the region (top nibble) by
- * subtracting 11, or 0xc - 1.
+ * vmap, IO, vmemap
+ *
+ * 0x00005 -  [ 0xc008000000000000 - 0xc009ffffffffffff]
+ * 0x00006 -  [ 0xc00a000000000000 - 0xc00bffffffffffff]
+ * 0x00007 -  [ 0xc00c000000000000 - 0xc00dffffffffffff]
+ *
  */
 static inline unsigned long get_kernel_context(unsigned long ea)
 {
-       unsigned long region_id = REGION_ID(ea);
+       unsigned long region_id = get_region_id(ea);
        unsigned long ctx;
        /*
-        * For linear mapping we do support multiple context
+        * Depending on Kernel config, kernel region can have one context
+        * or more.
         */
        if (region_id == KERNEL_REGION_ID) {
                /*
                 * We already verified ea to be not beyond the addr limit.
                 */
-               ctx =  1 + ((ea & ~REGION_MASK) >> MAX_EA_BITS_PER_CONTEXT);
+               ctx =  1 + ((ea & EA_MASK) >> MAX_EA_BITS_PER_CONTEXT);
        } else
-               ctx = (region_id - 0xc) + MAX_KERNEL_CTX_CNT;
+               ctx = region_id + MAX_KERNEL_CTX_CNT - 2;
        return ctx;
 }
 
 
 extern unsigned long __kernel_io_start;
 extern unsigned long __kernel_io_end;
 #define KERN_VIRT_START __kernel_virt_start
-#define KERN_VIRT_SIZE  __kernel_virt_size
 #define KERN_IO_START  __kernel_io_start
 #define KERN_IO_END __kernel_io_end
 
 
  * |                              |
  * |                              |
  * |                              |
- * +------------------------------+  Kernel IO map end (0xc010000000000000)
+ * +------------------------------+  Kernel vmemmap end (0xc010000000000000)
  * |                              |
+ * |           512TB             |
  * |                              |
- * |      1/2 of virtual map      |
+ * +------------------------------+  Kernel IO map end/vmemap start
  * |                              |
+ * |           512TB             |
  * |                              |
- * +------------------------------+  Kernel IO map start
+ * +------------------------------+  Kernel vmap end/ IO map start
  * |                              |
- * |      1/4 of virtual map      |
- * |                              |
- * +------------------------------+  Kernel vmemap start
- * |                              |
- * |     1/4 of virtual map       |
+ * |           512TB             |
  * |                              |
  * +------------------------------+  Kernel virt start (0xc008000000000000)
  * |                              |
  * +------------------------------+  Kernel linear (0xc.....)
  */
 
-#define RADIX_KERN_VIRT_START ASM_CONST(0xc008000000000000)
-#define RADIX_KERN_VIRT_SIZE  ASM_CONST(0x0008000000000000)
-
+#define RADIX_KERN_VIRT_START  ASM_CONST(0xc008000000000000)
 /*
- * The vmalloc space starts at the beginning of that region, and
- * occupies a quarter of it on radix config.
- * (we keep a quarter for the virtual memmap)
+ * 49 =  MAX_EA_BITS_PER_CONTEXT (hash specific). To make sure we pick
+ * the same value as hash.
  */
+#define RADIX_KERN_MAP_SIZE    (1UL << 49)
+
 #define RADIX_VMALLOC_START    RADIX_KERN_VIRT_START
-#define RADIX_VMALLOC_SIZE     (RADIX_KERN_VIRT_SIZE >> 2)
+#define RADIX_VMALLOC_SIZE     RADIX_KERN_MAP_SIZE
 #define RADIX_VMALLOC_END      (RADIX_VMALLOC_START + RADIX_VMALLOC_SIZE)
-/*
- * Defines the address of the vmemap area, in its own region on
- * hash table CPUs.
- */
-#define RADIX_VMEMMAP_BASE             (RADIX_VMALLOC_END)
 
-#define RADIX_KERN_IO_START    (RADIX_KERN_VIRT_START + (RADIX_KERN_VIRT_SIZE >> 1))
-#define RADIX_KERN_IO_END       (RADIX_KERN_VIRT_START + RADIX_KERN_VIRT_SIZE)
+#define RADIX_KERN_IO_START    RADIX_VMALLOC_END
+#define RADIX_KERN_IO_SIZE     RADIX_KERN_MAP_SIZE
+#define RADIX_KERN_IO_END      (RADIX_KERN_IO_START + RADIX_KERN_IO_SIZE)
+
+#define RADIX_VMEMMAP_START    RADIX_KERN_IO_END
+#define RADIX_VMEMMAP_SIZE     RADIX_KERN_MAP_SIZE
+#define RADIX_VMEMMAP_END      (RADIX_VMEMMAP_START + RADIX_VMEMMAP_SIZE)
 
 #ifndef __ASSEMBLY__
 #define RADIX_PTE_TABLE_SIZE   (sizeof(pte_t) << RADIX_PTE_INDEX_SIZE)
 
  * return true for some vmalloc addresses, which is incorrect. So explicitly
  * check that the address is in the kernel region.
  */
-#define virt_addr_valid(kaddr) (REGION_ID(kaddr) == KERNEL_REGION_ID && \
+/* may be can drop get_region_id */
+#define virt_addr_valid(kaddr) (get_region_id((unsigned long)kaddr) == KERNEL_REGION_ID && \
                                pfn_valid(virt_to_pfn(kaddr)))
 #else
 #define virt_addr_valid(kaddr) pfn_valid(virt_to_pfn(kaddr))
 
        raddr = per_cpu_ptr(addr, cpu);
        l = (unsigned long)raddr;
 
-       if (REGION_ID(l) == VMALLOC_REGION_ID) {
+       if (get_region_id(l) == VMALLOC_REGION_ID) {
                l = vmalloc_to_phys(raddr);
                raddr = (unsigned int *)l;
        }
 
        u64 vsid, vsidkey;
        int psize, ssize;
 
-       switch (REGION_ID(ea)) {
+       switch (get_region_id(ea)) {
        case USER_REGION_ID:
                pr_devel("%s: 0x%llx -- USER_REGION_ID\n", __func__, ea);
                if (mm == NULL)
                break;
        case VMALLOC_REGION_ID:
                pr_devel("%s: 0x%llx -- VMALLOC_REGION_ID\n", __func__, ea);
-               if (ea < VMALLOC_END)
-                       psize = mmu_vmalloc_psize;
-               else
-                       psize = mmu_io_psize;
+               psize = mmu_vmalloc_psize;
+               ssize = mmu_kernel_ssize;
+               vsid = get_kernel_vsid(ea, mmu_kernel_ssize);
+               vsidkey = SLB_VSID_KERNEL;
+               break;
+       case IO_REGION_ID:
+               pr_devel("%s: 0x%llx -- IO_REGION_ID\n", __func__, ea);
+               psize = mmu_io_psize;
                ssize = mmu_kernel_ssize;
                vsid = get_kernel_vsid(ea, mmu_kernel_ssize);
                vsidkey = SLB_VSID_KERNEL;
 
        __pgd_val_bits = HASH_PGD_VAL_BITS;
 
        __kernel_virt_start = H_KERN_VIRT_START;
-       __kernel_virt_size = H_KERN_VIRT_SIZE;
        __vmalloc_start = H_VMALLOC_START;
        __vmalloc_end = H_VMALLOC_END;
        __kernel_io_start = H_KERN_IO_START;
        __kernel_io_end = H_KERN_IO_END;
-       vmemmap = (struct page *)H_VMEMMAP_BASE;
+       vmemmap = (struct page *)H_VMEMMAP_START;
        ioremap_bot = IOREMAP_BASE;
 
 #ifdef CONFIG_PCI
        trace_hash_fault(ea, access, trap);
 
        /* Get region & vsid */
-       switch (REGION_ID(ea)) {
+       switch (get_region_id(ea)) {
        case USER_REGION_ID:
                user_region = 1;
                if (! mm) {
                break;
        case VMALLOC_REGION_ID:
                vsid = get_kernel_vsid(ea, mmu_kernel_ssize);
-               if (ea < VMALLOC_END)
-                       psize = mmu_vmalloc_psize;
-               else
-                       psize = mmu_io_psize;
+               psize = mmu_vmalloc_psize;
+               ssize = mmu_kernel_ssize;
+               break;
+
+       case IO_REGION_ID:
+               vsid = get_kernel_vsid(ea, mmu_kernel_ssize);
+               psize = mmu_io_psize;
                ssize = mmu_kernel_ssize;
                break;
        default:
        unsigned long flags = 0;
        struct mm_struct *mm = current->mm;
 
-       if (REGION_ID(ea) == VMALLOC_REGION_ID)
+       if ((get_region_id(ea) == VMALLOC_REGION_ID) ||
+           (get_region_id(ea) == IO_REGION_ID))
                mm = &init_mm;
 
        if (dsisr & DSISR_NOHPTE)
        unsigned long access = _PAGE_PRESENT | _PAGE_READ;
        unsigned long flags = 0;
        struct mm_struct *mm = current->mm;
+       unsigned int region_id = get_region_id(ea);
 
-       if (REGION_ID(ea) == VMALLOC_REGION_ID)
+       if ((region_id == VMALLOC_REGION_ID) || (region_id == IO_REGION_ID))
                mm = &init_mm;
 
        if (dsisr & DSISR_NOHPTE)
         * 2) user space access kernel space.
         */
        access |= _PAGE_PRIVILEGED;
-       if ((msr & MSR_PR) || (REGION_ID(ea) == USER_REGION_ID))
+       if ((msr & MSR_PR) || (region_id == USER_REGION_ID))
                access &= ~_PAGE_PRIVILEGED;
 
        if (trap == 0x400)
        int rc, ssize, update_flags = 0;
        unsigned long access = _PAGE_PRESENT | _PAGE_READ | (is_exec ? _PAGE_EXEC : 0);
 
-       BUG_ON(REGION_ID(ea) != USER_REGION_ID);
+       BUG_ON(get_region_id(ea) != USER_REGION_ID);
 
        if (!should_hash_preload(mm, ea))
                return;
 
         */
        BUILD_BUG_ON(TASK_SIZE_USER64 > RADIX_PGTABLE_RANGE);
 
+#ifdef CONFIG_PPC_64K_PAGES
+       BUILD_BUG_ON(RADIX_KERN_MAP_SIZE != (1UL << MAX_EA_BITS_PER_CONTEXT));
+#endif
+
        if (unlikely(!slab_is_available()))
                return early_map_kernel_page(ea, pa, flags, map_page_size,
                                                nid, region_start, region_end);
        __pgd_val_bits = RADIX_PGD_VAL_BITS;
 
        __kernel_virt_start = RADIX_KERN_VIRT_START;
-       __kernel_virt_size = RADIX_KERN_VIRT_SIZE;
        __vmalloc_start = RADIX_VMALLOC_START;
        __vmalloc_end = RADIX_VMALLOC_END;
        __kernel_io_start = RADIX_KERN_IO_START;
        __kernel_io_end = RADIX_KERN_IO_END;
-       vmemmap = (struct page *)RADIX_VMEMMAP_BASE;
+       vmemmap = (struct page *)RADIX_VMEMMAP_START;
        ioremap_bot = IOREMAP_BASE;
 
 #ifdef CONFIG_PCI
 
 EXPORT_SYMBOL(__pgd_val_bits);
 unsigned long __kernel_virt_start;
 EXPORT_SYMBOL(__kernel_virt_start);
-unsigned long __kernel_virt_size;
-EXPORT_SYMBOL(__kernel_virt_size);
 unsigned long __vmalloc_start;
 EXPORT_SYMBOL(__vmalloc_start);
 unsigned long __vmalloc_end;
 
        address_markers[7].start_address = IOREMAP_BASE;
        address_markers[8].start_address = IOREMAP_END;
 #ifdef CONFIG_PPC_BOOK3S_64
-       address_markers[9].start_address =  H_VMEMMAP_BASE;
+       address_markers[9].start_address =  H_VMEMMAP_START;
 #else
        address_markers[9].start_address =  VMEMMAP_BASE;
 #endif
 
        address_markers[i++].start_address = PHB_IO_END;
        address_markers[i++].start_address = IOREMAP_BASE;
        address_markers[i++].start_address = IOREMAP_END;
+       /* What is the ifdef about? */
 #ifdef CONFIG_PPC_BOOK3S_64
-       address_markers[i++].start_address =  H_VMEMMAP_BASE;
+       address_markers[i++].start_address =  H_VMEMMAP_START;
 #else
        address_markers[i++].start_address =  VMEMMAP_BASE;
 #endif
 
        if (id == KERNEL_REGION_ID) {
 
                /* We only support upto MAX_PHYSMEM_BITS */
-               if ((ea & ~REGION_MASK) > (1UL << MAX_PHYSMEM_BITS))
+               if ((ea & EA_MASK) > (1UL << MAX_PHYSMEM_BITS))
                        return -EFAULT;
 
                flags = SLB_VSID_KERNEL | mmu_psize_defs[mmu_linear_psize].sllp;
 #ifdef CONFIG_SPARSEMEM_VMEMMAP
        } else if (id == VMEMMAP_REGION_ID) {
 
-               if ((ea & ~REGION_MASK) >= (1ULL << MAX_EA_BITS_PER_CONTEXT))
+               if (ea >= H_VMEMMAP_END)
                        return -EFAULT;
 
                flags = SLB_VSID_KERNEL | mmu_psize_defs[mmu_vmemmap_psize].sllp;
 #endif
        } else if (id == VMALLOC_REGION_ID) {
 
-               if ((ea & ~REGION_MASK) >= (1ULL << MAX_EA_BITS_PER_CONTEXT))
+               if (ea >= H_VMALLOC_END)
                        return -EFAULT;
 
-               if (ea < H_VMALLOC_END)
-                       flags = local_paca->vmalloc_sllp;
-               else
-                       flags = SLB_VSID_KERNEL | mmu_psize_defs[mmu_io_psize].sllp;
+               flags = local_paca->vmalloc_sllp;
+
+       } else if (id == IO_REGION_ID) {
+
+               if (ea >= H_KERN_IO_END)
+                       return -EFAULT;
+
+               flags = SLB_VSID_KERNEL | mmu_psize_defs[mmu_io_psize].sllp;
+
        } else {
                return -EFAULT;
        }
                ssize = MMU_SEGSIZE_256M;
 
        context = get_kernel_context(ea);
+
        return slb_insert_entry(ea, context, flags, ssize, true);
 }
 
 
 long do_slb_fault(struct pt_regs *regs, unsigned long ea)
 {
-       unsigned long id = REGION_ID(ea);
+       unsigned long id = get_region_id(ea);
 
        /* IRQs are not reconciled here, so can't check irqs_disabled */
        VM_WARN_ON(mfmsr() & MSR_EE);
 
         * faults need to be deferred to process context.
         */
        if ((dsisr & MFC_DSISR_PTE_NOT_FOUND) &&
-           (REGION_ID(ea) != USER_REGION_ID)) {
+           (get_region_id(ea) != USER_REGION_ID)) {
 
                spin_unlock(&spu->register_lock);
                ret = hash_page(ea,
        unsigned long ea = (unsigned long)addr;
        u64 llp;
 
-       if (REGION_ID(ea) == KERNEL_REGION_ID)
+       if (get_region_id(ea) == KERNEL_REGION_ID)
                llp = mmu_psize_defs[mmu_linear_psize].sllp;
        else
                llp = mmu_psize_defs[mmu_virtual_psize].sllp;
 
                if (dsisr & CXL_PSL_DSISR_An_S)
                        access |= _PAGE_WRITE;
 
-               if (!mm && (REGION_ID(dar) != USER_REGION_ID))
+               if (!mm && (get_region_id(dar) != USER_REGION_ID))
                        access |= _PAGE_PRIVILEGED;
 
                if (dsisr & DSISR_NOHPTE)
 
                if (fault->dsisr & SPA_XSL_S)
                        access |= _PAGE_WRITE;
 
-               if (REGION_ID(fault->dar) != USER_REGION_ID)
+               if (get_region_id(fault->dar) != USER_REGION_ID)
                        access |= _PAGE_PRIVILEGED;
 
                local_irq_save(flags);