Allocate subpage protect related variables only if we use the feature.
This helps in reducing the hash related mm context struct by around 4K
Before the patch
sizeof(struct hash_mm_context)  = 8288
After the patch
sizeof(struct hash_mm_context) = 4160
Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@linux.ibm.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
 #define SBP_L3_SHIFT           (SBP_L2_SHIFT + SBP_L2_BITS)
 
 extern void subpage_prot_free(struct mm_struct *mm);
-extern void subpage_prot_init_new_context(struct mm_struct *mm);
 #else
 static inline void subpage_prot_free(struct mm_struct *mm) {}
-static inline void subpage_prot_init_new_context(struct mm_struct *mm) { }
 #endif /* CONFIG_PPC_SUBPAGE_PROT */
 
 /*
 #endif
 
 #ifdef CONFIG_PPC_SUBPAGE_PROT
-       struct subpage_prot_table spt;
+       struct subpage_prot_table *spt;
 #endif /* CONFIG_PPC_SUBPAGE_PROT */
 };
 
 
 #ifdef CONFIG_PPC_SUBPAGE_PROT
 static inline struct subpage_prot_table *mm_ctx_subpage_prot(mm_context_t *ctx)
 {
-       return &ctx->hash_context->spt;
+       return ctx->hash_context->spt;
 }
 #endif
 
 
        u32 spp = 0;
        u32 **sbpm, *sbpp;
 
+       if (!spt)
+               return 0;
+
        if (ea >= spt->maxaddr)
                return 0;
        if (ea < 0x100000000UL) {
 
        if (index < 0)
                return index;
 
-       mm->context.hash_context = kmalloc(sizeof(struct hash_mm_context), GFP_KERNEL);
+       mm->context.hash_context = kmalloc(sizeof(struct hash_mm_context),
+                                          GFP_KERNEL);
        if (!mm->context.hash_context) {
                ida_free(&mmu_context_ida, index);
                return -ENOMEM;
        } else {
                /* This is fork. Copy hash_context details from current->mm */
                memcpy(mm->context.hash_context, current->mm->context.hash_context, sizeof(struct hash_mm_context));
+#ifdef CONFIG_PPC_SUBPAGE_PROT
+               /* inherit subpage prot detalis if we have one. */
+               if (current->mm->context.hash_context->spt) {
+                       mm->context.hash_context->spt = kmalloc(sizeof(struct subpage_prot_table),
+                                                               GFP_KERNEL);
+                       if (!mm->context.hash_context->spt) {
+                               ida_free(&mmu_context_ida, index);
+                               kfree(mm->context.hash_context);
+                               return -ENOMEM;
+                       }
+               }
+#endif
 
        }
 
-       subpage_prot_init_new_context(mm);
-
        pkey_mm_init(mm);
        return index;
 }
 
        unsigned long i, j, addr;
        u32 **p;
 
+       if (!spt)
+               return;
+
        for (i = 0; i < 4; ++i) {
                if (spt->low_prot[i]) {
                        free_page((unsigned long)spt->low_prot[i]);
                free_page((unsigned long)p);
        }
        spt->maxaddr = 0;
-}
-
-void subpage_prot_init_new_context(struct mm_struct *mm)
-{
-       struct subpage_prot_table *spt = mm_ctx_subpage_prot(&mm->context);
-
-       memset(spt, 0, sizeof(*spt));
+       kfree(spt);
 }
 
 static void hpte_flush_range(struct mm_struct *mm, unsigned long addr,
        size_t nw;
        unsigned long next, limit;
 
+       if (!spt)
+               return ;
+
        down_write(&mm->mmap_sem);
        limit = addr + len;
        if (limit > spt->maxaddr)
                return -EFAULT;
 
        down_write(&mm->mmap_sem);
+
+       if (!spt) {
+               /*
+                * Allocate subpage prot table if not already done.
+                * Do this with mmap_sem held
+                */
+               spt = kzalloc(sizeof(struct subpage_prot_table), GFP_KERNEL);
+               if (!spt) {
+                       err = -ENOMEM;
+                       goto out;
+               }
+               mm->context.hash_context->spt = spt;
+       }
+
        subpage_mark_vma_nohuge(mm, addr, len);
        for (limit = addr + len; addr < limit; addr = next) {
                next = pmd_addr_end(addr, limit);