| @@ -2128,8 +2128,6 @@ void activate_task(struct rq *rq, struct task_struct *p, int flags) · 2127 unmodified lines |
| 2128 | 2128 | { |
| 2129 | 2129 | if (task_on_rq_migrating(p)) |
| 2130 | 2130 | flags |= ENQUEUE_MIGRATED; |
| 2131 | | − if (flags & ENQUEUE_MIGRATED) |
| 2132 | | − sched_mm_cid_migrate_to(rq, p); |
| 2133 | 2131 | |
| 2134 | 2132 | enqueue_task(rq, p, flags); |
| 2135 | 2133 | |
| @@ -3329,7 +3327,6 @@ void set_task_cpu(struct task_struct *p, unsigned int new_cpu) · 1193 unmodified lines |
| 3329 | 3327 | if (p->sched_class->migrate_task_rq) |
| 3330 | 3328 | p->sched_class->migrate_task_rq(p, new_cpu); |
| 3331 | 3329 | p->se.nr_migrations++; |
| 3332 | | − sched_mm_cid_migrate_from(p); |
| 3333 | 3330 | perf_event_task_migrate(p); |
| 3334 | 3331 | } |
| 3335 | 3332 | |
| @@ -5280,9 +5277,6 @@ context_switch(struct rq *rq, struct task_struct *prev, · 1944 unmodified lines |
| 5280 | 5277 | * |
| 5281 | 5278 | * kernel -> user switch + mmdrop_lazy_tlb() active |
| 5282 | 5279 | * user -> user switch |
| 5283 | | − * |
| 5284 | | − * switch_mm_cid() needs to be updated if the barriers provided |
| 5285 | | − * by context_switch() are modified. |
| 5286 | 5280 | */ |
| 5287 | 5281 | if (!next->mm) { // to kernel |
| 5288 | 5282 | enter_lazy_tlb(prev->active_mm, next); |
| @@ -5312,8 +5306,7 @@ context_switch(struct rq *rq, struct task_struct *prev, · 23 unmodified lines |
| 5312 | 5306 | } |
| 5313 | 5307 | } |
| 5314 | 5308 | |
| 5315 | | − /* switch_mm_cid() requires the memory barriers above. */ |
| 5316 | | − switch_mm_cid(rq, prev, next); |
| 5309 | + switch_mm_cid(prev, next); |
| 5317 | 5310 | |
| 5318 | 5311 | /* |
| 5319 | 5312 | * Tell rseq that the task was scheduled in. Must be after |
| @@ -5604,7 +5597,6 @@ void sched_tick(void) · 284 unmodified lines |
| 5604 | 5597 | resched_latency = cpu_resched_latency(rq); |
| 5605 | 5598 | calc_global_load_tick(rq); |
| 5606 | 5599 | sched_core_tick(rq); |
| 5607 | | − task_tick_mm_cid(rq, donor); |
| 5608 | 5600 | scx_tick(rq); |
| 5609 | 5601 | |
| 5610 | 5602 | rq_unlock(rq, &rf); |
| @@ -10376,522 +10368,47 @@ void call_trace_sched_update_nr_running(struct rq *rq, int count) · 4765 unmodified lines |
| 10376 | 10368 | } |
| 10377 | 10369 | |
| 10378 | 10370 | #ifdef CONFIG_SCHED_MM_CID |
| 10379 | | − |
| 10380 | 10371 | /* |
| 10381 | | − * @cid_lock: Guarantee forward-progress of cid allocation. |
| 10382 | | − * |
| 10383 | | − * Concurrency ID allocation within a bitmap is mostly lock-free. The cid_lock |
| 10384 | | − * is only used when contention is detected by the lock-free allocation so |
| 10385 | | − * forward progress can be guaranteed. |
| 10386 | | − */ |
| 10387 | | −DEFINE_RAW_SPINLOCK(cid_lock); |
| 10388 | | − |
| 10389 | | −/* |
| 10390 | | − * @use_cid_lock: Select cid allocation behavior: lock-free vs spinlock. |
| 10391 | | − * |
| 10392 | | − * When @use_cid_lock is 0, the cid allocation is lock-free. When contention is |
| 10393 | | − * detected, it is set to 1 to ensure that all newly coming allocations are |
| 10394 | | − * serialized by @cid_lock until the allocation which detected contention |
| 10395 | | − * completes and sets @use_cid_lock back to 0. This guarantees forward progress |
| 10396 | | − * of a cid allocation. |
| 10397 | | − */ |
| 10398 | | −int use_cid_lock; |
| 10399 | | − |
| 10400 | | −/* |
| 10401 | | − * mm_cid remote-clear implements a lock-free algorithm to clear per-mm/cpu cid |
| 10402 | | − * concurrently with respect to the execution of the source runqueue context |
| 10403 | | − * switch. |
| 10404 | | − * |
| 10405 | | − * There is one basic properties we want to guarantee here: |
| 10406 | | − * |
| 10407 | | − * (1) Remote-clear should _never_ mark a per-cpu cid UNSET when it is actively |
| 10408 | | − * used by a task. That would lead to concurrent allocation of the cid and |
| 10409 | | − * userspace corruption. |
| 10410 | | − * |
| 10411 | | − * Provide this guarantee by introducing a Dekker memory ordering to guarantee |
| 10412 | | − * that a pair of loads observe at least one of a pair of stores, which can be |
| 10413 | | − * shown as: |
| 10414 | | − * |
| 10415 | | − * X = Y = 0 |
| 10416 | | − * |
| 10417 | | − * w[X]=1 w[Y]=1 |
| 10418 | | − * MB MB |
| 10419 | | − * r[Y]=y r[X]=x |
| 10420 | | − * |
| 10421 | | − * Which guarantees that x==0 && y==0 is impossible. But rather than using |
| 10422 | | − * values 0 and 1, this algorithm cares about specific state transitions of the |
| 10423 | | − * runqueue current task (as updated by the scheduler context switch), and the |
| 10424 | | − * per-mm/cpu cid value. |
| 10425 | | − * |
| 10426 | | − * Let's introduce task (Y) which has task->mm == mm and task (N) which has |
| 10427 | | − * task->mm != mm for the rest of the discussion. There are two scheduler state |
| 10428 | | − * transitions on context switch we care about: |
| 10429 | | − * |
| 10430 | | − * (TSA) Store to rq->curr with transition from (N) to (Y) |
| 10431 | | − * |
| 10432 | | − * (TSB) Store to rq->curr with transition from (Y) to (N) |
| 10433 | | − * |
| 10434 | | − * On the remote-clear side, there is one transition we care about: |
| 10435 | | − * |
| 10436 | | − * (TMA) cmpxchg to *pcpu_cid to set the LAZY flag |
| 10437 | | − * |
| 10438 | | − * There is also a transition to UNSET state which can be performed from all |
| 10439 | | − * sides (scheduler, remote-clear). It is always performed with a cmpxchg which |
| 10440 | | − * guarantees that only a single thread will succeed: |
| 10441 | | − * |
| 10442 | | − * (TMB) cmpxchg to *pcpu_cid to mark UNSET |
| 10443 | | − * |
| 10444 | | − * Just to be clear, what we do _not_ want to happen is a transition to UNSET |
| 10445 | | − * when a thread is actively using the cid (property (1)). |
| 10446 | | − * |
| 10447 | | − * Let's looks at the relevant combinations of TSA/TSB, and TMA transitions. |
| 10448 | | − * |
| 10449 | | − * Scenario A) (TSA)+(TMA) (from next task perspective) |
| 10450 | | − * |
| 10451 | | − * CPU0 CPU1 |
| 10452 | | − * |
| 10453 | | − * Context switch CS-1 Remote-clear |
| 10454 | | − * - store to rq->curr: (N)->(Y) (TSA) - cmpxchg to *pcpu_id to LAZY (TMA) |
| 10455 | | − * (implied barrier after cmpxchg) |
| 10456 | | − * - switch_mm_cid() |
| 10457 | | − * - memory barrier (see switch_mm_cid() |
| 10458 | | − * comment explaining how this barrier |
| 10459 | | − * is combined with other scheduler |
| 10460 | | − * barriers) |
| 10461 | | − * - mm_cid_get (next) |
| 10462 | | − * - READ_ONCE(*pcpu_cid) - rcu_dereference(src_rq->curr) |
| 10463 | | − * |
| 10464 | | − * This Dekker ensures that either task (Y) is observed by the |
| 10465 | | − * rcu_dereference() or the LAZY flag is observed by READ_ONCE(), or both are |
| 10466 | | − * observed. |
| 10467 | | − * |
| 10468 | | − * If task (Y) store is observed by rcu_dereference(), it means that there is |
| 10469 | | − * still an active task on the cpu. Remote-clear will therefore not transition |
| 10470 | | − * to UNSET, which fulfills property (1). |
| 10471 | | − * |
| 10472 | | − * If task (Y) is not observed, but the lazy flag is observed by READ_ONCE(), |
| 10473 | | − * it will move its state to UNSET, which clears the percpu cid perhaps |
| 10474 | | − * uselessly (which is not an issue for correctness). Because task (Y) is not |
| 10475 | | − * observed, CPU1 can move ahead to set the state to UNSET. Because moving |
| 10476 | | − * state to UNSET is done with a cmpxchg expecting that the old state has the |
| 10477 | | − * LAZY flag set, only one thread will successfully UNSET. |
| 10478 | | − * |
| 10479 | | − * If both states (LAZY flag and task (Y)) are observed, the thread on CPU0 |
| 10480 | | − * will observe the LAZY flag and transition to UNSET (perhaps uselessly), and |
| 10481 | | − * CPU1 will observe task (Y) and do nothing more, which is fine. |
| 10482 | | − * |
| 10483 | | − * What we are effectively preventing with this Dekker is a scenario where |
| 10484 | | − * neither LAZY flag nor store (Y) are observed, which would fail property (1) |
| 10485 | | − * because this would UNSET a cid which is actively used. |
| 10372 | + * When a task exits, the MM CID held by the task is not longer required as |
| 10373 | + * the task cannot return to user space. |
| 10486 | 10374 | */ |
| 10487 | | − |
| 10488 | | −void sched_mm_cid_migrate_from(struct task_struct *t) |
| 10489 | | −{ |
| 10490 | | − t->migrate_from_cpu = task_cpu(t); |
| 10491 | | −} |
| 10492 | | − |
| 10493 | | −static |
| 10494 | | −int __sched_mm_cid_migrate_from_fetch_cid(struct rq *src_rq, |
| 10495 | | − struct task_struct *t, |
| 10496 | | − struct mm_cid *src_pcpu_cid) |
| 10497 | | −{ |
| 10498 | | − struct mm_struct *mm = t->mm; |
| 10499 | | − struct task_struct *src_task; |
| 10500 | | − int src_cid, last_mm_cid; |
| 10501 | | − |
| 10502 | | − if (!mm) |
| 10503 | | − return -1; |
| 10504 | | − |
| 10505 | | − last_mm_cid = t->last_mm_cid; |
| 10506 | | − /* |
| 10507 | | − * If the migrated task has no last cid, or if the current |
| 10508 | | − * task on src rq uses the cid, it means the source cid does not need |
| 10509 | | − * to be moved to the destination cpu. |
| 10510 | | − */ |
| 10511 | | − if (last_mm_cid == -1) |
| 10512 | | − return -1; |
| 10513 | | − src_cid = READ_ONCE(src_pcpu_cid->cid); |
| 10514 | | − if (!mm_cid_is_valid(src_cid) || last_mm_cid != src_cid) |
| 10515 | | − return -1; |
| 10516 | | − |
| 10517 | | − /* |
| 10518 | | − * If we observe an active task using the mm on this rq, it means we |
| 10519 | | − * are not the last task to be migrated from this cpu for this mm, so |
| 10520 | | − * there is no need to move src_cid to the destination cpu. |
| 10521 | | − */ |
| 10522 | | − guard(rcu)(); |
| 10523 | | − src_task = rcu_dereference(src_rq->curr); |
| 10524 | | − if (READ_ONCE(src_task->mm_cid_active) && src_task->mm == mm) { |
| 10525 | | − t->last_mm_cid = -1; |
| 10526 | | − return -1; |
| 10527 | | − } |
| 10528 | | − |
| 10529 | | − return src_cid; |
| 10530 | | −} |
| 10531 | | − |
| 10532 | | −static |
| 10533 | | −int __sched_mm_cid_migrate_from_try_steal_cid(struct rq *src_rq, |
| 10534 | | − struct task_struct *t, |
| 10535 | | − struct mm_cid *src_pcpu_cid, |
| 10536 | | − int src_cid) |
| 10537 | | −{ |
| 10538 | | − struct task_struct *src_task; |
| 10539 | | − struct mm_struct *mm = t->mm; |
| 10540 | | − int lazy_cid; |
| 10541 | | − |
| 10542 | | − if (src_cid == -1) |
| 10543 | | − return -1; |
| 10544 | | − |
| 10545 | | − /* |
| 10546 | | − * Attempt to clear the source cpu cid to move it to the destination |
| 10547 | | − * cpu. |
| 10548 | | − */ |
| 10549 | | − lazy_cid = mm_cid_set_lazy_put(src_cid); |
| 10550 | | − if (!try_cmpxchg(&src_pcpu_cid->cid, &src_cid, lazy_cid)) |
| 10551 | | − return -1; |
| 10552 | | − |
| 10553 | | − /* |
| 10554 | | − * The implicit barrier after cmpxchg per-mm/cpu cid before loading |
| 10555 | | − * rq->curr->mm matches the scheduler barrier in context_switch() |
| 10556 | | − * between store to rq->curr and load of prev and next task's |
| 10557 | | − * per-mm/cpu cid. |
| 10558 | | − * |
| 10559 | | − * The implicit barrier after cmpxchg per-mm/cpu cid before loading |
| 10560 | | − * rq->curr->mm_cid_active matches the barrier in |
| 10561 | | − * sched_mm_cid_exit_signals(), sched_mm_cid_before_execve(), and |
| 10562 | | − * sched_mm_cid_after_execve() between store to t->mm_cid_active and |
| 10563 | | − * load of per-mm/cpu cid. |
| 10564 | | − */ |
| 10565 | | − |
| 10566 | | − /* |
| 10567 | | − * If we observe an active task using the mm on this rq after setting |
| 10568 | | − * the lazy-put flag, this task will be responsible for transitioning |
| 10569 | | − * from lazy-put flag set to MM_CID_UNSET. |
| 10570 | | − */ |
| 10571 | | − scoped_guard (rcu) { |
| 10572 | | − src_task = rcu_dereference(src_rq->curr); |
| 10573 | | − if (READ_ONCE(src_task->mm_cid_active) && src_task->mm == mm) { |
| 10574 | | − /* |
| 10575 | | − * We observed an active task for this mm, there is therefore |
| 10576 | | − * no point in moving this cid to the destination cpu. |
| 10577 | | − */ |
| 10578 | | − t->last_mm_cid = -1; |
| 10579 | | − return -1; |
| 10580 | | − } |
| 10581 | | − } |
| 10582 | | − |
| 10583 | | − /* |
| 10584 | | − * The src_cid is unused, so it can be unset. |
| 10585 | | − */ |
| 10586 | | − if (!try_cmpxchg(&src_pcpu_cid->cid, &lazy_cid, MM_CID_UNSET)) |
| 10587 | | − return -1; |
| 10588 | | − WRITE_ONCE(src_pcpu_cid->recent_cid, MM_CID_UNSET); |
| 10589 | | − return src_cid; |
| 10590 | | −} |
| 10591 | | − |
| 10592 | | −/* |
| 10593 | | − * Migration to dst cpu. Called with dst_rq lock held. |
| 10594 | | − * Interrupts are disabled, which keeps the window of cid ownership without the |
| 10595 | | − * source rq lock held small. |
| 10596 | | − */ |
| 10597 | | −void sched_mm_cid_migrate_to(struct rq *dst_rq, struct task_struct *t) |
| 10598 | | −{ |
| 10599 | | − struct mm_cid *src_pcpu_cid, *dst_pcpu_cid; |
| 10600 | | − struct mm_struct *mm = t->mm; |
| 10601 | | − int src_cid, src_cpu; |
| 10602 | | − bool dst_cid_is_set; |
| 10603 | | − struct rq *src_rq; |
| 10604 | | − |
| 10605 | | − lockdep_assert_rq_held(dst_rq); |
| 10606 | | − |
| 10607 | | − if (!mm) |
| 10608 | | − return; |
| 10609 | | − src_cpu = t->migrate_from_cpu; |
| 10610 | | − if (src_cpu == -1) { |
| 10611 | | − t->last_mm_cid = -1; |
| 10612 | | − return; |
| 10613 | | − } |
| 10614 | | − /* |
| 10615 | | − * Move the src cid if the dst cid is unset. This keeps id |
| 10616 | | − * allocation closest to 0 in cases where few threads migrate around |
| 10617 | | − * many CPUs. |
| 10618 | | − * |
| 10619 | | − * If destination cid or recent cid is already set, we may have |
| 10620 | | − * to just clear the src cid to ensure compactness in frequent |
| 10621 | | − * migrations scenarios. |
| 10622 | | − * |
| 10623 | | − * It is not useful to clear the src cid when the number of threads is |
| 10624 | | − * greater or equal to the number of allowed CPUs, because user-space |
| 10625 | | − * can expect that the number of allowed cids can reach the number of |
| 10626 | | − * allowed CPUs. |
| 10627 | | − */ |
| 10628 | | − dst_pcpu_cid = per_cpu_ptr(mm->pcpu_cid, cpu_of(dst_rq)); |
| 10629 | | − dst_cid_is_set = !mm_cid_is_unset(READ_ONCE(dst_pcpu_cid->cid)) || |
| 10630 | | − !mm_cid_is_unset(READ_ONCE(dst_pcpu_cid->recent_cid)); |
| 10631 | | − if (dst_cid_is_set && atomic_read(&mm->mm_users) >= READ_ONCE(mm->nr_cpus_allowed)) |
| 10632 | | − return; |
| 10633 | | − src_pcpu_cid = per_cpu_ptr(mm->pcpu_cid, src_cpu); |
| 10634 | | − src_rq = cpu_rq(src_cpu); |
| 10635 | | − src_cid = __sched_mm_cid_migrate_from_fetch_cid(src_rq, t, src_pcpu_cid); |
| 10636 | | − if (src_cid == -1) |
| 10637 | | − return; |
| 10638 | | − src_cid = __sched_mm_cid_migrate_from_try_steal_cid(src_rq, t, src_pcpu_cid, |
| 10639 | | − src_cid); |
| 10640 | | − if (src_cid == -1) |
| 10641 | | − return; |
| 10642 | | − if (dst_cid_is_set) { |
| 10643 | | − __mm_cid_put(mm, src_cid); |
| 10644 | | − return; |
| 10645 | | − } |
| 10646 | | − /* Move src_cid to dst cpu. */ |
| 10647 | | − mm_cid_snapshot_time(dst_rq, mm); |
| 10648 | | − WRITE_ONCE(dst_pcpu_cid->cid, src_cid); |
| 10649 | | − WRITE_ONCE(dst_pcpu_cid->recent_cid, src_cid); |
| 10650 | | −} |
| 10651 | | − |
| 10652 | | −static void sched_mm_cid_remote_clear(struct mm_struct *mm, struct mm_cid *pcpu_cid, |
| 10653 | | − int cpu) |
| 10654 | | −{ |
| 10655 | | − struct rq *rq = cpu_rq(cpu); |
| 10656 | | − struct task_struct *t; |
| 10657 | | − int cid, lazy_cid; |
| 10658 | | − |
| 10659 | | − cid = READ_ONCE(pcpu_cid->cid); |
| 10660 | | − if (!mm_cid_is_valid(cid)) |
| 10661 | | − return; |
| 10662 | | − |
| 10663 | | − /* |
| 10664 | | − * Clear the cpu cid if it is set to keep cid allocation compact. If |
| 10665 | | − * there happens to be other tasks left on the source cpu using this |
| 10666 | | − * mm, the next task using this mm will reallocate its cid on context |
| 10667 | | − * switch. |
| 10668 | | − */ |
| 10669 | | − lazy_cid = mm_cid_set_lazy_put(cid); |
| 10670 | | − if (!try_cmpxchg(&pcpu_cid->cid, &cid, lazy_cid)) |
| 10671 | | − return; |
| 10672 | | − |
| 10673 | | − /* |
| 10674 | | − * The implicit barrier after cmpxchg per-mm/cpu cid before loading |
| 10675 | | − * rq->curr->mm matches the scheduler barrier in context_switch() |
| 10676 | | − * between store to rq->curr and load of prev and next task's |
| 10677 | | − * per-mm/cpu cid. |
| 10678 | | − * |
| 10679 | | − * The implicit barrier after cmpxchg per-mm/cpu cid before loading |
| 10680 | | − * rq->curr->mm_cid_active matches the barrier in |
| 10681 | | − * sched_mm_cid_exit_signals(), sched_mm_cid_before_execve(), and |
| 10682 | | − * sched_mm_cid_after_execve() between store to t->mm_cid_active and |
| 10683 | | − * load of per-mm/cpu cid. |
| 10684 | | − */ |
| 10685 | | − |
| 10686 | | − /* |
| 10687 | | − * If we observe an active task using the mm on this rq after setting |
| 10688 | | − * the lazy-put flag, that task will be responsible for transitioning |
| 10689 | | − * from lazy-put flag set to MM_CID_UNSET. |
| 10690 | | − */ |
| 10691 | | − scoped_guard (rcu) { |
| 10692 | | − t = rcu_dereference(rq->curr); |
| 10693 | | − if (READ_ONCE(t->mm_cid_active) && t->mm == mm) |
| 10694 | | − return; |
| 10695 | | − } |
| 10696 | | − |
| 10697 | | − /* |
| 10698 | | − * The cid is unused, so it can be unset. |
| 10699 | | − * Disable interrupts to keep the window of cid ownership without rq |
| 10700 | | − * lock small. |
| 10701 | | − */ |
| 10702 | | − scoped_guard (irqsave) { |
| 10703 | | − if (try_cmpxchg(&pcpu_cid->cid, &lazy_cid, MM_CID_UNSET)) |
| 10704 | | − __mm_cid_put(mm, cid); |
| 10705 | | − } |
| 10706 | | −} |
| 10707 | | − |
| 10708 | | −static void sched_mm_cid_remote_clear_old(struct mm_struct *mm, int cpu) |
| 10709 | | −{ |
| 10710 | | − struct rq *rq = cpu_rq(cpu); |
| 10711 | | − struct mm_cid *pcpu_cid; |
| 10712 | | − struct task_struct *curr; |
| 10713 | | − u64 rq_clock; |
| 10714 | | − |
| 10715 | | − /* |
| 10716 | | − * rq->clock load is racy on 32-bit but one spurious clear once in a |
| 10717 | | − * while is irrelevant. |
| 10718 | | − */ |
| 10719 | | − rq_clock = READ_ONCE(rq->clock); |
| 10720 | | − pcpu_cid = per_cpu_ptr(mm->pcpu_cid, cpu); |
| 10721 | | − |
| 10722 | | − /* |
| 10723 | | − * In order to take care of infrequently scheduled tasks, bump the time |
| 10724 | | − * snapshot associated with this cid if an active task using the mm is |
| 10725 | | − * observed on this rq. |
| 10726 | | − */ |
| 10727 | | − scoped_guard (rcu) { |
| 10728 | | − curr = rcu_dereference(rq->curr); |
| 10729 | | − if (READ_ONCE(curr->mm_cid_active) && curr->mm == mm) { |
| 10730 | | − WRITE_ONCE(pcpu_cid->time, rq_clock); |
| 10731 | | − return; |
| 10732 | | − } |
| 10733 | | − } |
| 10734 | | − |
| 10735 | | − if (rq_clock < pcpu_cid->time + SCHED_MM_CID_PERIOD_NS) |
| 10736 | | − return; |
| 10737 | | − sched_mm_cid_remote_clear(mm, pcpu_cid, cpu); |
| 10738 | | −} |
| 10739 | | − |
| 10740 | | −static void sched_mm_cid_remote_clear_weight(struct mm_struct *mm, int cpu, |
| 10741 | | − int weight) |
| 10742 | | −{ |
| 10743 | | − struct mm_cid *pcpu_cid; |
| 10744 | | − int cid; |
| 10745 | | − |
| 10746 | | − pcpu_cid = per_cpu_ptr(mm->pcpu_cid, cpu); |
| 10747 | | − cid = READ_ONCE(pcpu_cid->cid); |
| 10748 | | − if (!mm_cid_is_valid(cid) || cid < weight) |
| 10749 | | − return; |
| 10750 | | − sched_mm_cid_remote_clear(mm, pcpu_cid, cpu); |
| 10751 | | −} |
| 10752 | | − |
| 10753 | | −static void task_mm_cid_work(struct callback_head *work) |
| 10754 | | −{ |
| 10755 | | − unsigned long now = jiffies, old_scan, next_scan; |
| 10756 | | − struct task_struct *t = current; |
| 10757 | | − struct cpumask *cidmask; |
| 10758 | | − struct mm_struct *mm; |
| 10759 | | − int weight, cpu; |
| 10760 | | − |
| 10761 | | − WARN_ON_ONCE(t != container_of(work, struct task_struct, cid_work)); |
| 10762 | | − |
| 10763 | | − work->next = work; /* Prevent double-add */ |
| 10764 | | − if (t->flags & PF_EXITING) |
| 10765 | | − return; |
| 10766 | | − mm = t->mm; |
| 10767 | | − if (!mm) |
| 10768 | | − return; |
| 10769 | | − old_scan = READ_ONCE(mm->mm_cid_next_scan); |
| 10770 | | − next_scan = now + msecs_to_jiffies(MM_CID_SCAN_DELAY); |
| 10771 | | − if (!old_scan) { |
| 10772 | | − unsigned long res; |
| 10773 | | − |
| 10774 | | − res = cmpxchg(&mm->mm_cid_next_scan, old_scan, next_scan); |
| 10775 | | − if (res != old_scan) |
| 10776 | | − old_scan = res; |
| 10777 | | − else |
| 10778 | | − old_scan = next_scan; |
| 10779 | | − } |
| 10780 | | − if (time_before(now, old_scan)) |
| 10781 | | − return; |
| 10782 | | − if (!try_cmpxchg(&mm->mm_cid_next_scan, &old_scan, next_scan)) |
| 10783 | | − return; |
| 10784 | | − cidmask = mm_cidmask(mm); |
| 10785 | | − /* Clear cids that were not recently used. */ |
| 10786 | | − for_each_possible_cpu(cpu) |
| 10787 | | − sched_mm_cid_remote_clear_old(mm, cpu); |
| 10788 | | − weight = cpumask_weight(cidmask); |
| 10789 | | − /* |
| 10790 | | − * Clear cids that are greater or equal to the cidmask weight to |
| 10791 | | − * recompact it. |
| 10792 | | − */ |
| 10793 | | − for_each_possible_cpu(cpu) |
| 10794 | | − sched_mm_cid_remote_clear_weight(mm, cpu, weight); |
| 10795 | | −} |
| 10796 | | − |
| 10797 | | −void init_sched_mm_cid(struct task_struct *t) |
| 10798 | | −{ |
| 10799 | | − struct mm_struct *mm = t->mm; |
| 10800 | | − int mm_users = 0; |
| 10801 | | − |
| 10802 | | − if (mm) { |
| 10803 | | − mm_users = atomic_read(&mm->mm_users); |
| 10804 | | − if (mm_users == 1) |
| 10805 | | − mm->mm_cid_next_scan = jiffies + msecs_to_jiffies(MM_CID_SCAN_DELAY); |
| 10806 | | − } |
| 10807 | | − t->cid_work.next = &t->cid_work; /* Protect against double add */ |
| 10808 | | − init_task_work(&t->cid_work, task_mm_cid_work); |
| 10809 | | −} |
| 10810 | | − |
| 10811 | | −void task_tick_mm_cid(struct rq *rq, struct task_struct *curr) |
| 10812 | | −{ |
| 10813 | | − struct callback_head *work = &curr->cid_work; |
| 10814 | | − unsigned long now = jiffies; |
| 10815 | | − |
| 10816 | | − if (!curr->mm || (curr->flags & (PF_EXITING | PF_KTHREAD)) || |
| 10817 | | − work->next != work) |
| 10818 | | − return; |
| 10819 | | − if (time_before(now, READ_ONCE(curr->mm->mm_cid_next_scan))) |
| 10820 | | − return; |
| 10821 | | − |
| 10822 | | − /* No page allocation under rq lock */ |
| 10823 | | − task_work_add(curr, work, TWA_RESUME); |
| 10824 | | −} |
| 10825 | | − |
| 10826 | 10375 | void sched_mm_cid_exit_signals(struct task_struct *t) |
| 10827 | 10376 | { |
| 10828 | 10377 | struct mm_struct *mm = t->mm; |
| 10829 | | − struct rq *rq; |
| 10830 | 10378 | |
| 10831 | | − if (!mm) |
| 10379 | + if (!mm || !t->mm_cid_active) |
| 10832 | 10380 | return; |
| 10833 | 10381 | |
| 10834 | | − preempt_disable(); |
| 10835 | | − rq = this_rq(); |
| 10836 | | − guard(rq_lock_irqsave)(rq); |
| 10837 | | − preempt_enable_no_resched(); /* holding spinlock */ |
| 10838 | | − WRITE_ONCE(t->mm_cid_active, 0); |
| 10839 | | − /* |
| 10840 | | − * Store t->mm_cid_active before loading per-mm/cpu cid. |
| 10841 | | − * Matches barrier in sched_mm_cid_remote_clear_old(). |
| 10842 | | − */ |
| 10843 | | − smp_mb(); |
| 10844 | | − mm_cid_put(mm); |
| 10845 | | − t->last_mm_cid = t->mm_cid = -1; |
| 10382 | + guard(preempt)(); |
| 10383 | + t->mm_cid_active = 0; |
| 10384 | + if (t->mm_cid != MM_CID_UNSET) { |
| 10385 | + cpumask_clear_cpu(t->mm_cid, mm_cidmask(mm)); |
| 10386 | + t->mm_cid = MM_CID_UNSET; |
| 10387 | + } |
| 10846 | 10388 | } |
| 10847 | 10389 | |
| 10390 | +/* Deactivate MM CID allocation across execve() */ |
| 10848 | 10391 | void sched_mm_cid_before_execve(struct task_struct *t) |
| 10849 | 10392 | { |
| 10850 | | − struct mm_struct *mm = t->mm; |
| 10851 | | − struct rq *rq; |
| 10852 | | − |
| 10853 | | − if (!mm) |
| 10854 | | − return; |
| 10855 | | − |
| 10856 | | − preempt_disable(); |
| 10857 | | − rq = this_rq(); |
| 10858 | | − guard(rq_lock_irqsave)(rq); |
| 10859 | | − preempt_enable_no_resched(); /* holding spinlock */ |
| 10860 | | − WRITE_ONCE(t->mm_cid_active, 0); |
| 10861 | | − /* |
| 10862 | | − * Store t->mm_cid_active before loading per-mm/cpu cid. |
| 10863 | | − * Matches barrier in sched_mm_cid_remote_clear_old(). |
| 10864 | | − */ |
| 10865 | | − smp_mb(); |
| 10866 | | − mm_cid_put(mm); |
| 10867 | | − t->last_mm_cid = t->mm_cid = -1; |
| 10393 | + sched_mm_cid_exit_signals(t); |
| 10868 | 10394 | } |
| 10869 | 10395 | |
| 10396 | +/* Reactivate MM CID after successful execve() */ |
| 10870 | 10397 | void sched_mm_cid_after_execve(struct task_struct *t) |
| 10871 | 10398 | { |
| 10872 | 10399 | struct mm_struct *mm = t->mm; |
| 10873 | | − struct rq *rq; |
| 10874 | 10400 | |
| 10875 | 10401 | if (!mm) |
| 10876 | 10402 | return; |
| 10877 | 10403 | |
| 10878 | | − preempt_disable(); |
| 10879 | | − rq = this_rq(); |
| 10880 | | − scoped_guard (rq_lock_irqsave, rq) { |
| 10881 | | − preempt_enable_no_resched(); /* holding spinlock */ |
| 10882 | | − WRITE_ONCE(t->mm_cid_active, 1); |
| 10883 | | − /* |
| 10884 | | − * Store t->mm_cid_active before loading per-mm/cpu cid. |
| 10885 | | − * Matches barrier in sched_mm_cid_remote_clear_old(). |
| 10886 | | − */ |
| 10887 | | − smp_mb(); |
| 10888 | | − t->last_mm_cid = t->mm_cid = mm_cid_get(rq, t, mm); |
| 10889 | | − } |
| 10404 | + guard(preempt)(); |
| 10405 | + t->mm_cid_active = 1; |
| 10406 | + mm_cid_select(t); |
| 10890 | 10407 | } |
| 10891 | 10408 | |
| 10892 | 10409 | void sched_mm_cid_fork(struct task_struct *t) |
| 10893 | 10410 | { |
| 10894 | | − WARN_ON_ONCE(!t->mm || t->mm_cid != -1); |
| 10411 | + WARN_ON_ONCE(!t->mm || t->mm_cid != MM_CID_UNSET); |
| 10895 | 10412 | t->mm_cid_active = 1; |
| 10896 | 10413 | } |
| 10897 | 10414 | #endif /* CONFIG_SCHED_MM_CID */ |