@@ -330,6 +330,8 @@ static __always_inline void exit_to_user_mode_prepare(struct pt_regs *regs)
arch_exit_to_user_mode_prepare(regs, ti_work);
+ rseq_delay_resched_fini(ti_work);
+
/* Ensure that kernel state is sane for a return to userspace */
kmap_assert_nomap();
lockdep_assert_irqs_disabled();
@@ -967,6 +967,9 @@ struct task_struct {
#ifdef CONFIG_RT_MUTEXES
unsigned sched_rt_mutex:1;
#endif
+#if defined(CONFIG_RSEQ) && defined(CONFIG_SCHED_HRTICK)
+ unsigned rseq_sched_delay:1;
+#endif
/* Bit to tell TOMOYO we're in execve(): */
unsigned in_execve:1;
@@ -2206,16 +2209,22 @@ static inline bool owner_on_cpu(struct task_struct *owner)
unsigned long sched_cpu_util(int cpu);
#endif /* CONFIG_SMP */
-#ifdef CONFIG_RSEQ
+#if defined(CONFIG_RSEQ) && defined(CONFIG_SCHED_HRTICK)
extern bool rseq_delay_resched(void);
+extern void rseq_delay_resched_fini(unsigned long ti_work);
+extern void rseq_delay_resched_tick(void);
#else
static inline bool rseq_delay_resched(void) { return false; }
+extern inline void rseq_delay_resched_fini(unsigned long ti_work) { }
+static inline void rseq_delay_resched_tick(void) { }
#endif
+extern void hrtick_local_start(u64 delay);
+
#ifdef CONFIG_SCHED_CORE
extern void sched_core_free(struct task_struct *tsk);
extern void sched_core_fork(struct task_struct *p);
@@ -142,7 +142,7 @@ __always_inline unsigned long exit_to_user_mode_loop(struct pt_regs *regs,
}
/* Return the latest work state for arch_exit_to_user_mode() */
- return ti_work;
+ return ti_work | ignore_mask;
}
/*
@@ -339,35 +339,101 @@ void __rseq_handle_notify_resume(struct ksignal *ksig, struct pt_regs *regs)
force_sigsegv(sig);
}
+#ifdef CONFIG_SCHED_HRTICK
+void rseq_delay_resched_fini(unsigned long ti_work)
+{
+ extern void hrtick_local_start(u64 delay);
+ struct task_struct *t = current;
+
+ if (!t->rseq)
+ return;
+
+ if (!(ti_work & _TIF_NEED_RESCHED_LAZY)) {
+ /* Clear any previous setting of rseq_sched_delay */
+ t->rseq_sched_delay = 0;
+ return;
+ }
+
+ /* No need to start the timer if it is already started */
+ if (t->rseq_sched_delay)
+ return;
+
+ /*
+ * IRQs off, guaranteed to return to userspace, start timer on this CPU
+ * to limit the resched-overdraft.
+ *
+ * If your critical section is longer than 50 us you get to keep the
+ * pieces.
+ */
+
+ t->rseq_sched_delay = 1;
+ hrtick_local_start(50 * NSEC_PER_USEC);
+}
+
bool rseq_delay_resched(void)
{
struct task_struct *t = current;
u32 flags;
if (!t->rseq)
- return false;
+ goto nodelay;
/* Make sure the cr_counter exists */
if (current->rseq_len <= offsetof(struct rseq, cr_counter))
- return false;
+ goto nodelay;
/* If this were to fault, it would likely cause a schedule anyway */
if (copy_from_user_nofault(&flags, &t->rseq->cr_counter, sizeof(flags)))
- return false;
+ goto nodelay;
if (!(flags & RSEQ_CR_FLAG_IN_CRITICAL_SECTION_MASK))
- return false;
+ goto nodelay;
trace_printk("Extend time slice\n");
flags |= RSEQ_CR_FLAG_KERNEL_REQUEST_SCHED;
if (copy_to_user_nofault(&t->rseq->cr_counter, &flags, sizeof(flags))) {
trace_printk("Faulted writing rseq\n");
- return false;
+ goto nodelay;
}
return true;
+
+nodelay:
+ t->rseq_sched_delay = 0;
+ return false;
+}
+
+void rseq_delay_resched_tick(void)
+{
+ struct task_struct *t = current;
+
+ if (t->rseq_sched_delay) {
+ u32 flags;
+
+ set_tsk_need_resched(t);
+ t->rseq_sched_delay = 0;
+ trace_printk("timeout -- force resched\n");
+
+ /*
+ * Now remove the that it was extended, as this will
+ * force a schedule and user space no longer needs to.
+ */
+
+ /* Just in case user space unregistered its rseq */
+ if (!t->rseq)
+ return;
+
+ if (copy_from_user_nofault(&flags, &t->rseq->cr_counter, sizeof(flags)))
+ return;
+
+ flags &= ~RSEQ_CR_FLAG_KERNEL_REQUEST_SCHED;
+
+ if (copy_to_user_nofault(&t->rseq->cr_counter, &flags, sizeof(flags)))
+ return;
+ }
}
+#endif /* CONFIG_SCHED_HRTICK */
#ifdef CONFIG_DEBUG_RSEQ
@@ -815,6 +815,7 @@ void update_rq_clock(struct rq *rq)
static void hrtick_clear(struct rq *rq)
{
+ rseq_delay_resched_tick();
if (hrtimer_active(&rq->hrtick_timer))
hrtimer_cancel(&rq->hrtick_timer);
}
@@ -830,6 +831,8 @@ static enum hrtimer_restart hrtick(struct hrtimer *timer)
WARN_ON_ONCE(cpu_of(rq) != smp_processor_id());
+ rseq_delay_resched_tick();
+
rq_lock(rq, &rf);
update_rq_clock(rq);
rq->donor->sched_class->task_tick(rq, rq->curr, 1);
@@ -903,6 +906,16 @@ void hrtick_start(struct rq *rq, u64 delay)
#endif /* CONFIG_SMP */
+void hrtick_local_start(u64 delay)
+{
+ struct rq *rq = this_rq();
+ struct rq_flags rf;
+
+ rq_lock(rq, &rf);
+ hrtick_start(rq, delay);
+ rq_unlock(rq, &rf);
+}
+
static void hrtick_rq_init(struct rq *rq)
{
#ifdef CONFIG_SMP
@@ -6711,6 +6724,9 @@ static void __sched notrace __schedule(int sched_mode)
picked:
clear_tsk_need_resched(prev);
clear_preempt_need_resched();
+#ifdef CONFIG_RSEQ
+ prev->rseq_sched_delay = 0;
+#endif
#ifdef CONFIG_SCHED_DEBUG
rq->last_seen_need_resched_ns = 0;
#endif
@@ -1379,6 +1379,12 @@ static void do_sched_yield(void)
*/
SYSCALL_DEFINE0(sched_yield)
{
+ if (current->rseq_sched_delay) {
+ trace_printk("yield -- made it\n");
+ schedule();
+ return 0;
+ }
+
do_sched_yield();
return 0;
}