Commit e107be36 authored by Avi Kivity's avatar Avi Kivity Committed by Ingo Molnar

[PATCH] sched: arch preempt notifier mechanism

This adds a general mechanism whereby a task can request the scheduler to
notify it whenever it is preempted or scheduled back in.  This allows the
task to swap any special-purpose registers like the fpu or Intel's VT
registers.
Signed-off-by: default avatarAvi Kivity <avi@qumranet.com>
[ mingo@elte.hu: fixes, cleanups ]
Signed-off-by: default avatarIngo Molnar <mingo@elte.hu>
parent b47e8608
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include <linux/thread_info.h> #include <linux/thread_info.h>
#include <linux/linkage.h> #include <linux/linkage.h>
#include <linux/list.h>
#ifdef CONFIG_DEBUG_PREEMPT #ifdef CONFIG_DEBUG_PREEMPT
extern void fastcall add_preempt_count(int val); extern void fastcall add_preempt_count(int val);
...@@ -60,4 +61,47 @@ do { \ ...@@ -60,4 +61,47 @@ do { \
#endif #endif
#ifdef CONFIG_PREEMPT_NOTIFIERS
struct preempt_notifier;
/**
* preempt_ops - notifiers called when a task is preempted and rescheduled
* @sched_in: we're about to be rescheduled:
* notifier: struct preempt_notifier for the task being scheduled
* cpu: cpu we're scheduled on
* @sched_out: we've just been preempted
* notifier: struct preempt_notifier for the task being preempted
* next: the task that's kicking us out
*/
struct preempt_ops {
void (*sched_in)(struct preempt_notifier *notifier, int cpu);
void (*sched_out)(struct preempt_notifier *notifier,
struct task_struct *next);
};
/**
* preempt_notifier - key for installing preemption notifiers
* @link: internal use
* @ops: defines the notifier functions to be called
*
* Usually used in conjunction with container_of().
*/
struct preempt_notifier {
struct hlist_node link;
struct preempt_ops *ops;
};
void preempt_notifier_register(struct preempt_notifier *notifier);
void preempt_notifier_unregister(struct preempt_notifier *notifier);
static inline void preempt_notifier_init(struct preempt_notifier *notifier,
struct preempt_ops *ops)
{
INIT_HLIST_NODE(&notifier->link);
notifier->ops = ops;
}
#endif
#endif /* __LINUX_PREEMPT_H */ #endif /* __LINUX_PREEMPT_H */
...@@ -935,6 +935,11 @@ struct task_struct { ...@@ -935,6 +935,11 @@ struct task_struct {
struct sched_class *sched_class; struct sched_class *sched_class;
struct sched_entity se; struct sched_entity se;
#ifdef CONFIG_PREEMPT_NOTIFIERS
/* list of struct preempt_notifier: */
struct hlist_head preempt_notifiers;
#endif
unsigned short ioprio; unsigned short ioprio;
#ifdef CONFIG_BLK_DEV_IO_TRACE #ifdef CONFIG_BLK_DEV_IO_TRACE
unsigned int btrace_seq; unsigned int btrace_seq;
......
...@@ -63,3 +63,6 @@ config PREEMPT_BKL ...@@ -63,3 +63,6 @@ config PREEMPT_BKL
Say Y here if you are building a kernel for a desktop system. Say Y here if you are building a kernel for a desktop system.
Say N if you are unsure. Say N if you are unsure.
config PREEMPT_NOTIFIERS
bool
...@@ -1592,6 +1592,10 @@ static void __sched_fork(struct task_struct *p) ...@@ -1592,6 +1592,10 @@ static void __sched_fork(struct task_struct *p)
INIT_LIST_HEAD(&p->run_list); INIT_LIST_HEAD(&p->run_list);
p->se.on_rq = 0; p->se.on_rq = 0;
#ifdef CONFIG_PREEMPT_NOTIFIERS
INIT_HLIST_HEAD(&p->preempt_notifiers);
#endif
/* /*
* We mark the process as running here, but have not actually * We mark the process as running here, but have not actually
* inserted it onto the runqueue yet. This guarantees that * inserted it onto the runqueue yet. This guarantees that
...@@ -1673,6 +1677,63 @@ void fastcall wake_up_new_task(struct task_struct *p, unsigned long clone_flags) ...@@ -1673,6 +1677,63 @@ void fastcall wake_up_new_task(struct task_struct *p, unsigned long clone_flags)
task_rq_unlock(rq, &flags); task_rq_unlock(rq, &flags);
} }
#ifdef CONFIG_PREEMPT_NOTIFIERS
/**
* preempt_notifier_register - tell me when current is being being preempted
* and rescheduled
*/
void preempt_notifier_register(struct preempt_notifier *notifier)
{
hlist_add_head(&notifier->link, &current->preempt_notifiers);
}
EXPORT_SYMBOL_GPL(preempt_notifier_register);
/**
* preempt_notifier_unregister - no longer interested in preemption notifications
*
* This is safe to call from within a preemption notifier.
*/
void preempt_notifier_unregister(struct preempt_notifier *notifier)
{
hlist_del(&notifier->link);
}
EXPORT_SYMBOL_GPL(preempt_notifier_unregister);
static void fire_sched_in_preempt_notifiers(struct task_struct *curr)
{
struct preempt_notifier *notifier;
struct hlist_node *node;
hlist_for_each_entry(notifier, node, &curr->preempt_notifiers, link)
notifier->ops->sched_in(notifier, raw_smp_processor_id());
}
static void
fire_sched_out_preempt_notifiers(struct task_struct *curr,
struct task_struct *next)
{
struct preempt_notifier *notifier;
struct hlist_node *node;
hlist_for_each_entry(notifier, node, &curr->preempt_notifiers, link)
notifier->ops->sched_out(notifier, next);
}
#else
static void fire_sched_in_preempt_notifiers(struct task_struct *curr)
{
}
static void
fire_sched_out_preempt_notifiers(struct task_struct *curr,
struct task_struct *next)
{
}
#endif
/** /**
* prepare_task_switch - prepare to switch tasks * prepare_task_switch - prepare to switch tasks
* @rq: the runqueue preparing to switch * @rq: the runqueue preparing to switch
...@@ -1685,8 +1746,11 @@ void fastcall wake_up_new_task(struct task_struct *p, unsigned long clone_flags) ...@@ -1685,8 +1746,11 @@ void fastcall wake_up_new_task(struct task_struct *p, unsigned long clone_flags)
* prepare_task_switch sets up locking and calls architecture specific * prepare_task_switch sets up locking and calls architecture specific
* hooks. * hooks.
*/ */
static inline void prepare_task_switch(struct rq *rq, struct task_struct *next) static inline void
prepare_task_switch(struct rq *rq, struct task_struct *prev,
struct task_struct *next)
{ {
fire_sched_out_preempt_notifiers(prev, next);
prepare_lock_switch(rq, next); prepare_lock_switch(rq, next);
prepare_arch_switch(next); prepare_arch_switch(next);
} }
...@@ -1728,6 +1792,7 @@ static inline void finish_task_switch(struct rq *rq, struct task_struct *prev) ...@@ -1728,6 +1792,7 @@ static inline void finish_task_switch(struct rq *rq, struct task_struct *prev)
prev_state = prev->state; prev_state = prev->state;
finish_arch_switch(prev); finish_arch_switch(prev);
finish_lock_switch(rq, prev); finish_lock_switch(rq, prev);
fire_sched_in_preempt_notifiers(current);
if (mm) if (mm)
mmdrop(mm); mmdrop(mm);
if (unlikely(prev_state == TASK_DEAD)) { if (unlikely(prev_state == TASK_DEAD)) {
...@@ -1768,7 +1833,7 @@ context_switch(struct rq *rq, struct task_struct *prev, ...@@ -1768,7 +1833,7 @@ context_switch(struct rq *rq, struct task_struct *prev,
{ {
struct mm_struct *mm, *oldmm; struct mm_struct *mm, *oldmm;
prepare_task_switch(rq, next); prepare_task_switch(rq, prev, next);
mm = next->mm; mm = next->mm;
oldmm = prev->active_mm; oldmm = prev->active_mm;
/* /*
...@@ -6335,6 +6400,10 @@ void __init sched_init(void) ...@@ -6335,6 +6400,10 @@ void __init sched_init(void)
set_load_weight(&init_task); set_load_weight(&init_task);
#ifdef CONFIG_PREEMPT_NOTIFIERS
INIT_HLIST_HEAD(&init_task.preempt_notifiers);
#endif
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
nr_cpu_ids = highest_cpu + 1; nr_cpu_ids = highest_cpu + 1;
open_softirq(SCHED_SOFTIRQ, run_rebalance_domains, NULL); open_softirq(SCHED_SOFTIRQ, run_rebalance_domains, NULL);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment