/**
* struct mm_mm_cid - Storage for per MM CID data
* @pcpu: Per CPU storage for CIDs associated to a CPU
+ * @max_cids: The exclusive maximum CID value for allocation and convergence
* @nr_cpus_allowed: The number of CPUs in the per MM allowed CPUs map. The map
* is growth only.
+ * @users: The number of tasks sharing this MM. Separate from mm::mm_users
+ * as that is modified by mmget()/mm_put() by other entities which
+ * do not actually share the MM.
* @lock: Spinlock to protect all fields except @pcpu. It also protects
* the MM cid cpumask and the MM cidmask bitmap.
*/
struct mm_mm_cid {
struct mm_cid_pcpu __percpu *pcpu;
+ unsigned int max_cids;
unsigned int nr_cpus_allowed;
+ unsigned int users;
raw_spinlock_t lock;
}____cacheline_aligned_in_smp;
#else /* CONFIG_SCHED_MM_CID */
init_numa_balancing(clone_flags, p);
p->wake_entry.u_flags = CSD_TYPE_TTWU;
p->migration_pending = NULL;
- init_sched_mm_cid(p);
}
DEFINE_STATIC_KEY_FALSE(sched_numa_balancing);
#ifdef CONFIG_SCHED_MM_CID
/*
- * When a task exits, the MM CID held by the task is not longer required as
- * the task cannot return to user space.
+ * Update the CID range properties when the constraints change. Invoked via
+ * fork(), exit() and affinity changes
*/
+static void mm_update_max_cids(struct mm_struct *mm)
+{
+ struct mm_mm_cid *mc = &mm->mm_cid;
+ unsigned int max_cids;
+
+ lockdep_assert_held(&mm->mm_cid.lock);
+
+ /* Calculate the new maximum constraint */
+ max_cids = min(mc->nr_cpus_allowed, mc->users);
+ WRITE_ONCE(mc->max_cids, max_cids);
+}
+
static inline void mm_update_cpus_allowed(struct mm_struct *mm, const struct cpumask *affmsk)
{
struct cpumask *mm_allowed;
unsigned int weight;
- if (!mm)
+ if (!mm || !READ_ONCE(mm->mm_cid.users))
return;
/*
guard(raw_spinlock)(&mm->mm_cid.lock);
mm_allowed = mm_cpus_allowed(mm);
weight = cpumask_weighted_or(mm_allowed, mm_allowed, affmsk);
+ if (weight == mm->mm_cid.nr_cpus_allowed)
+ return;
WRITE_ONCE(mm->mm_cid.nr_cpus_allowed, weight);
+ mm_update_max_cids(mm);
+}
+
+void sched_mm_cid_fork(struct task_struct *t)
+{
+ struct mm_struct *mm = t->mm;
+
+ WARN_ON_ONCE(!mm || t->mm_cid.cid != MM_CID_UNSET);
+
+ guard(raw_spinlock)(&mm->mm_cid.lock);
+ t->mm_cid.active = 1;
+ mm->mm_cid.users++;
+ /* Preset last_cid for mm_cid_select() */
+ t->mm_cid.last_cid = READ_ONCE(mm->mm_cid.max_cids) - 1;
+ mm_update_max_cids(mm);
}
+/*
+ * When a task exits, the MM CID held by the task is not longer required as
+ * the task cannot return to user space.
+ */
void sched_mm_cid_exit(struct task_struct *t)
{
struct mm_struct *mm = t->mm;
if (!mm || !t->mm_cid.active)
return;
- guard(preempt)();
+ guard(raw_spinlock)(&mm->mm_cid.lock);
t->mm_cid.active = 0;
+ mm->mm_cid.users--;
if (t->mm_cid.cid != MM_CID_UNSET) {
clear_bit(t->mm_cid.cid, mm_cidmask(mm));
t->mm_cid.cid = MM_CID_UNSET;
}
+ mm_update_max_cids(mm);
}
/* Deactivate MM CID allocation across execve() */
/* Reactivate MM CID after successful execve() */
void sched_mm_cid_after_execve(struct task_struct *t)
{
- struct mm_struct *mm = t->mm;
-
- if (!mm)
- return;
-
+ sched_mm_cid_fork(t);
guard(preempt)();
- t->mm_cid.active = 1;
mm_cid_select(t);
}
-void sched_mm_cid_fork(struct task_struct *t)
-{
- WARN_ON_ONCE(!t->mm || t->mm_cid.cid != MM_CID_UNSET);
- t->mm_cid.active = 1;
-}
-
void mm_init_cid(struct mm_struct *mm, struct task_struct *p)
{
struct mm_cid_pcpu __percpu *pcpu = mm->mm_cid.pcpu;
for_each_possible_cpu(cpu)
per_cpu_ptr(pcpu, cpu)->cid = MM_CID_UNSET;
+ mm->mm_cid.max_cids = 0;
mm->mm_cid.nr_cpus_allowed = p->nr_cpus_allowed;
+ mm->mm_cid.users = 0;
raw_spin_lock_init(&mm->mm_cid.lock);
cpumask_copy(mm_cpus_allowed(mm), &p->cpus_mask);
bitmap_zero(mm_cidmask(mm), num_possible_cpus());
struct mm_struct *mm = t->mm;
unsigned int max_cids;
- max_cids = min_t(int, READ_ONCE(mm->mm_cid.nr_cpus_allowed), atomic_read(&mm->mm_users));
+ max_cids = READ_ONCE(mm->mm_cid.max_cids);
/* Try to reuse the last CID of this task */
if (__mm_cid_get(t, t->mm_cid.last_cid, max_cids))
}
#else /* !CONFIG_SCHED_MM_CID: */
-static inline void init_sched_mm_cid(struct task_struct *t) { }
static inline void mm_cid_select(struct task_struct *t) { }
static inline void switch_mm_cid(struct task_struct *prev, struct task_struct *next) { }
#endif /* !CONFIG_SCHED_MM_CID */