]> Pileus Git - ~andy/linux/blobdiff - fs/proc/task_mmu.c
Merge tag 'gpio-fixes-for-v3.7' of git://git.kernel.org/pub/scm/linux/kernel/git...
[~andy/linux] / fs / proc / task_mmu.c
index 79827ce03e3bc00208aced44caa7ae9fd2d08bd3..90c63f9392a595318c5d1e03e1d69305bf9413fa 100644 (file)
@@ -90,10 +90,55 @@ static void pad_len_spaces(struct seq_file *m, int len)
        seq_printf(m, "%*c", len, ' ');
 }
 
+#ifdef CONFIG_NUMA
+/*
+ * These functions are for numa_maps but called in generic **maps seq_file
+ * ->start(), ->stop() ops.
+ *
+ * numa_maps scans all vmas under mmap_sem and checks their mempolicy.
+ * Each mempolicy object is controlled by reference counting. The problem here
+ * is how to avoid accessing dead mempolicy object.
+ *
+ * Because we're holding mmap_sem while reading seq_file, it's safe to access
+ * each vma's mempolicy, no vma objects will never drop refs to mempolicy.
+ *
+ * A task's mempolicy (task->mempolicy) has different behavior. task->mempolicy
+ * is set and replaced under mmap_sem but unrefed and cleared under task_lock().
+ * So, without task_lock(), we cannot trust get_vma_policy() because we cannot
+ * gurantee the task never exits under us. But taking task_lock() around
+ * get_vma_plicy() causes lock order problem.
+ *
+ * To access task->mempolicy without lock, we hold a reference count of an
+ * object pointed by task->mempolicy and remember it. This will guarantee
+ * that task->mempolicy points to an alive object or NULL in numa_maps accesses.
+ */
+static void hold_task_mempolicy(struct proc_maps_private *priv)
+{
+       struct task_struct *task = priv->task;
+
+       task_lock(task);
+       priv->task_mempolicy = task->mempolicy;
+       mpol_get(priv->task_mempolicy);
+       task_unlock(task);
+}
+static void release_task_mempolicy(struct proc_maps_private *priv)
+{
+       mpol_put(priv->task_mempolicy);
+}
+#else
+static void hold_task_mempolicy(struct proc_maps_private *priv)
+{
+}
+static void release_task_mempolicy(struct proc_maps_private *priv)
+{
+}
+#endif
+
 static void vma_stop(struct proc_maps_private *priv, struct vm_area_struct *vma)
 {
        if (vma && vma != priv->tail_vma) {
                struct mm_struct *mm = vma->vm_mm;
+               release_task_mempolicy(priv);
                up_read(&mm->mmap_sem);
                mmput(mm);
        }
@@ -132,7 +177,7 @@ static void *m_start(struct seq_file *m, loff_t *pos)
 
        tail_vma = get_gate_vma(priv->task->mm);
        priv->tail_vma = tail_vma;
-
+       hold_task_mempolicy(priv);
        /* Start with last addr hint */
        vma = find_vma(mm, last_addr);
        if (last_addr && vma) {
@@ -159,6 +204,7 @@ out:
        if (vma)
                return vma;
 
+       release_task_mempolicy(priv);
        /* End of vmas has been reached */
        m->version = (tail_vma != NULL)? 0: -1UL;
        up_read(&mm->mmap_sem);
@@ -1158,6 +1204,7 @@ static int show_numa_map(struct seq_file *m, void *v, int is_pid)
        struct vm_area_struct *vma = v;
        struct numa_maps *md = &numa_priv->md;
        struct file *file = vma->vm_file;
+       struct task_struct *task = proc_priv->task;
        struct mm_struct *mm = vma->vm_mm;
        struct mm_walk walk = {};
        struct mempolicy *pol;
@@ -1177,7 +1224,7 @@ static int show_numa_map(struct seq_file *m, void *v, int is_pid)
        walk.private = md;
        walk.mm = mm;
 
-       pol = get_vma_policy(proc_priv->task, vma, vma->vm_start);
+       pol = get_vma_policy(task, vma, vma->vm_start);
        mpol_to_str(buffer, sizeof(buffer), pol, 0);
        mpol_cond_put(pol);
 
@@ -1189,7 +1236,7 @@ static int show_numa_map(struct seq_file *m, void *v, int is_pid)
        } else if (vma->vm_start <= mm->brk && vma->vm_end >= mm->start_brk) {
                seq_printf(m, " heap");
        } else {
-               pid_t tid = vm_is_stack(proc_priv->task, vma, is_pid);
+               pid_t tid = vm_is_stack(task, vma, is_pid);
                if (tid != 0) {
                        /*
                         * Thread stack in /proc/PID/task/TID/maps or