]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
sparc64: Restrict number of processes
authorSanath Kumar <sanath.s.kumar@oracle.com>
Fri, 9 Dec 2016 20:26:09 +0000 (14:26 -0600)
committerChuck Anderson <chuck.anderson@oracle.com>
Thu, 6 Apr 2017 07:13:56 +0000 (00:13 -0700)
Orabug: 24523680

If the number of processes exceeds the number of supported context IDs
then there are random segfaults seen in the user programs.

The data collected when debugging this bug showed that two processes
with the same context IDs were present in the TLB shared between them
thus resulting in incorrect translations. Since the bug occurs after the
kernel hits the max context ID supported by the processor, the context
wraparound code found in get_new_mmu_context(...) and
smp_new_mmu_context_version_client(...) are under suspicion.

The plan is that this will get fixed when we implement "context domain"
feature for sparc in the kernel.  For now this patch temporarily
restricts the number of processes allowed by the kernel based on the
number of context IDs supported by the processor. This way we never reach
that condition of having incorrect translations.

For non root users fork will fail if the number of existing processes is
greater than (max_user_nctx - 100). Where max_user_nctx is the maximum
number of context IDs supported by the processor. For root user the fork
will fail if the the number of existing processes reaches max_user_nctx.
Extra context IDs are given to root to recover the system if users reach
their limit and cannot recover the system (i.e cannot even execute
'kill' to reduce the number of processes).

Signed-off-by: Sanath Kumar <sanath.s.kumar@oracle.com>
Reviewed-by: Rob Gardner <rob.gardner@oracle.com>
Signed-off-by: Allen Pais <allen.pais@oracle.com>
arch/sparc/include/asm/mmu_context_64.h
arch/sparc/mm/init_64.c
arch/sparc/mm/tsb.c

index ad4d7b2b77115f566c9e3bd517021c19ed84690d..cba84144c6df435bdd656a337c1a3b4c9708caf4 100644 (file)
@@ -17,6 +17,7 @@ static inline void enter_lazy_tlb(struct mm_struct *mm, struct task_struct *tsk)
 extern spinlock_t ctx_alloc_lock;
 extern unsigned long tlb_context_cache;
 extern unsigned long ctx_nr_bits;
+extern int max_user_nctx;
 extern unsigned long mmu_context_bmap[];
 
 void get_new_mmu_context(struct mm_struct *mm);
index 33a715050a4b1b37eded1fb80aa2ed95d1859663..11c31c9176f447d1a8e563c916f3b352b4b5b053 100644 (file)
@@ -29,6 +29,7 @@
 #include <linux/gfp.h>
 #include <linux/kexec.h>
 #include <linux/crash_dump.h>
+#include <linux/ratelimit.h>
 
 #include <asm/head.h>
 #include <asm/page.h>
@@ -57,6 +58,7 @@
 #include "init_64.h"
 
 unsigned long ctx_nr_bits = DEFAULT_CTX_NR_BITS;
+int max_user_nctx;
 unsigned long kern_linear_pte_xor[4] __read_mostly;
 static unsigned long page_cache4v_flag;
 
@@ -985,8 +987,12 @@ out:
        mm->context.sparc64_ctx_val = new_ctx | orig_pgsz_bits;
        spin_unlock(&ctx_alloc_lock);
 
-       if (unlikely(new_version))
+       if (unlikely(new_version)) {
+               pr_err_ratelimited("Context ID wrapped: %s(%d) CPU%d\n",
+                                    current->comm, task_pid_nr(current),
+                                    smp_processor_id());
                smp_new_mmu_context_version();
+       }
 }
 
 static int numa_enabled = 1;
@@ -2751,6 +2757,8 @@ void __init paging_init(void)
                sun4u_linear_pte_xor_finalize();
        }
 
+       max_user_nctx = (1UL << ctx_nr_bits) - 1;
+
        /* Flush the TLBs and the 4M TSB so that the updated linear
         * pte XOR settings are realized for all mappings.
         */
index 40dc22681e1a6451762b2e831a05be075a52ec63..7665162370654e46706d8decc73e3d368adb0aed 100644 (file)
@@ -13,6 +13,7 @@
 #include <asm/tsb.h>
 #include <asm/tlb.h>
 #include <asm/oplib.h>
+#include <linux/ratelimit.h>
 
 extern struct tsb swapper_tsb[KERNEL_TSB_NENTRIES];
 
@@ -560,11 +561,31 @@ captured_hugepage_pte_count_grow_tsb(struct mm_struct *mm,
                                     unsigned long *capture_huge_pte_count) {}
 #endif /* CONFIG_HUGETLB_PAGE || CONFIG_TRANSPARENT_HUGEPAGE */
 
+static atomic_t nctxs = ATOMIC_INIT(0);
+
 int init_new_context(struct task_struct *tsk, struct mm_struct *mm)
 {
        unsigned long capture_huge_pte_count[MM_NUM_HUGEPAGE_SIZES];
        unsigned long saved_thp_pte_count;
        unsigned int i;
+       int max_nctx = max_user_nctx;
+       int ret = 0;
+       int uid = current_cred()->uid.val;
+
+       /*
+        * In the worst case, user(s) might use up all contexts and make the
+        * system unusable.  Give root extra 100 grace ctxs to recover the
+        * system. E.g by killing some user processes.
+        */
+       if (uid != 0)
+               max_nctx -= 100;
+
+       if (unlikely(max_nctx <= atomic_inc_return(&nctxs))) {
+               pr_warn_ratelimited("Reached max(%d) number of processes for %s\n",
+                                   max_nctx, uid ? "users" : "root");
+               ret = -EAGAIN;
+               goto error;
+       }
 
        spin_lock_init(&mm->context.lock);
 
@@ -593,10 +614,15 @@ int init_new_context(struct task_struct *tsk, struct mm_struct *mm)
        captured_hugepage_pte_count_grow_tsb(mm, &saved_thp_pte_count,
                                             capture_huge_pte_count);
 
-       if (unlikely(!mm->context.tsb_block[MM_TSB_BASE].tsb))
-               return -ENOMEM;
+       if (unlikely(!mm->context.tsb_block[MM_TSB_BASE].tsb)) {
+               ret = -ENOMEM;
+               goto error;
+       }
 
-       return 0;
+       return ret;
+error:
+       atomic_dec(&nctxs);
+       return ret;
 }
 
 static void tsb_destroy_one(struct tsb_config *tp)
@@ -618,6 +644,8 @@ void destroy_context(struct mm_struct *mm)
        for (i = 0; i < MM_NUM_TSBS; i++)
                tsb_destroy_one(&mm->context.tsb_block[i]);
 
+       atomic_dec(&nctxs);
+
        spin_lock_irqsave(&ctx_alloc_lock, flags);
 
        if (CTX_VALID(mm->context)) {