/* sched_plugin.c -- core infrastructure for the scheduler plugin system
*
* This file includes the initialization of the plugin system, the no-op Linux
* scheduler plugin, some dummy functions, and some helper functions.
*/
#include <linux/list.h>
#include <linux/spinlock.h>
#include <linux/sched.h>
#include <litmus/litmus.h>
#include <litmus/sched_plugin.h>
#include <litmus/preempt.h>
#include <litmus/jobs.h>
#ifdef CONFIG_LITMUS_NVIDIA
#include <litmus/nvidia_info.h>
#endif
/*
* Generic function to trigger preemption on either local or remote cpu
* from scheduler plugins. The key feature is that this function is
* non-preemptive section aware and does not invoke the scheduler / send
* IPIs if the to-be-preempted task is actually non-preemptive.
*/
void preempt_if_preemptable(struct task_struct* t, int cpu)
{
/* t is the real-time task executing on CPU on_cpu If t is NULL, then
* on_cpu is currently scheduling background work.
*/
int reschedule = 0;
if (!t)
/* move non-real-time task out of the way */
reschedule = 1;
else {
if (smp_processor_id() == cpu) {
/* local CPU case */
/* check if we need to poke userspace */
if (is_user_np(t))
/* Yes, poke it. This doesn't have to be atomic since
* the task is definitely not executing. */
request_exit_np(t);
else if (!is_kernel_np(t))
/* only if we are allowed to preempt the
* currently-executing task */
reschedule = 1;
} else {
/* Remote CPU case. Only notify if it's not a kernel
* NP section and if we didn't set the userspace
* flag. */
reschedule = !(is_kernel_np(t) || request_exit_np_atomic(t));
}
}
if (likely(reschedule))
litmus_reschedule(cpu);
}
/*************************************************************
* Dummy plugin functions *
*************************************************************/
static void litmus_dummy_finish_switch(struct task_struct * prev)
{
}
static struct task_struct* litmus_dummy_schedule(struct task_struct * prev)
{
sched_state_task_picked();
return NULL;
}
static void litmus_dummy_tick(struct task_struct* tsk)
{
}
static long litmus_dummy_admit_task(struct task_struct* tsk)
{
printk(KERN_CRIT "LITMUS^RT: Linux plugin rejects %s/%d.\n",
tsk->comm, tsk->pid);
return -EINVAL;
}
static void litmus_dummy_task_new(struct task_struct *t, int on_rq, int running)
{
}
static void litmus_dummy_task_wake_up(struct task_struct *task)
{
}
static void litmus_dummy_task_block(struct task_struct *task)
{
}
static void litmus_dummy_task_exit(struct task_struct *task)
{
}
static long litmus_dummy_complete_job(void)
{
return -ENOSYS;
}
static long litmus_dummy_activate_plugin(void)
{
#ifdef CONFIG_LITMUS_NVIDIA
shutdown_nvidia_info();
#endif
return 0;
}
static long litmus_dummy_deactivate_plugin(void)
{
return 0;
}
static int litmus_dummy_compare(struct task_struct* a, struct task_struct* b)
{
TRACE_CUR("WARNING: Dummy compare function called!\n");
return 0;
}
#ifdef CONFIG_LITMUS_LOCKING
static long litmus_dummy_allocate_lock(struct litmus_lock **lock, int type,
void* __user config)
{
return -ENXIO;
}
static void litmus_dummy_increase_prio(struct task_struct* t, struct task_struct* prio_inh)
{
}
static void litmus_dummy_decrease_prio(struct task_struct* t, struct task_struct* prio_inh)
{
}
#endif
#ifdef CONFIG_LITMUS_SOFTIRQD
static void litmus_dummy_increase_prio_klitirq(struct task_struct* klitirqd,
struct task_struct* old_owner,
struct task_struct* new_owner)
{
}
static void litmus_dummy_decrease_prio_klitirqd(struct task_struct* klitirqd,
struct task_struct* old_owner)
{
}
#endif
#ifdef CONFIG_LITMUS_PAI_SOFTIRQD
static int litmus_dummy_enqueue_pai_tasklet(struct tasklet_struct* t)
{
TRACE("%s: PAI Tasklet unsupported in this plugin!!!!!!\n", __FUNCTION__);
return(0); // failure.
}
static void litmus_dummy_change_prio_pai_tasklet(struct task_struct *old_prio,
struct task_struct *new_prio)
{
TRACE("%s: PAI Tasklet unsupported in this plugin!!!!!!\n", __FUNCTION__);
}
static void litmus_dummy_run_tasklets(struct task_struct* t)
{
//TRACE("%s: PAI Tasklet unsupported in this plugin!!!!!!\n", __FUNCTION__);
}
#endif
#ifdef CONFIG_LITMUS_NESTED_LOCKING
static void litmus_dummy_nested_increase_prio(struct task_struct* t, struct task_struct* prio_inh,
raw_spinlock_t *to_unlock, unsigned long irqflags)
{
}
static void litmus_dummy_nested_decrease_prio(struct task_struct* t, struct task_struct* prio_inh,
raw_spinlock_t *to_unlock, unsigned long irqflags)
{
}
static int litmus_dummy___compare(struct task_struct* a, comparison_mode_t a_mod,
struct task_struct* b, comparison_mode_t b_mode)
{
TRACE_CUR("WARNING: Dummy compare function called!\n");
return 0;
}
#endif
#ifdef CONFIG_LITMUS_DGL_SUPPORT
static raw_spinlock_t* litmus_dummy_get_dgl_spinlock(struct task_struct *t)
{
return NULL;
}
#endif
#ifdef CONFIG_LITMUS_AFFINITY_LOCKING
static long litmus_dummy_allocate_aff_obs(struct affinity_observer **aff_obs,
int type,
void* __user config)
{
return -ENXIO;
}
#endif
/* The default scheduler plugin. It doesn't do anything and lets Linux do its
* job.
*/
struct sched_plugin linux_sched_plugin = {
.plugin_name = "Linux",
.tick = litmus_dummy_tick,
.task_new = litmus_dummy_task_new,
.task_exit = litmus_dummy_task_exit,
.task_wake_up = litmus_dummy_task_wake_up,
.task_block = litmus_dummy_task_block,
.complete_job = litmus_dummy_complete_job,
.schedule = litmus_dummy_schedule,
.finish_switch = litmus_dummy_finish_switch,
.activate_plugin = litmus_dummy_activate_plugin,
.deactivate_plugin = litmus_dummy_deactivate_plugin,
.compare = litmus_dummy_compare,
#ifdef CONFIG_LITMUS_LOCKING
.allocate_lock = litmus_dummy_allocate_lock,
.increase_prio = litmus_dummy_increase_prio,
.decrease_prio = litmus_dummy_decrease_prio,
#endif
#ifdef CONFIG_LITMUS_NESTED_LOCKING
.nested_increase_prio = litmus_dummy_nested_increase_prio,
.nested_decrease_prio = litmus_dummy_nested_decrease_prio,
.__compare = litmus_dummy___compare,
#endif
#ifdef CONFIG_LITMUS_SOFTIRQD
.increase_prio_klitirqd = litmus_dummy_increase_prio_klitirqd,
.decrease_prio_klitirqd = litmus_dummy_decrease_prio_klitirqd,
#endif
#ifdef CONFIG_LITMUS_PAI_SOFTIRQD
.enqueue_pai_tasklet = litmus_dummy_enqueue_pai_tasklet,
.change_prio_pai_tasklet = litmus_dummy_change_prio_pai_tasklet,
.run_tasklets = litmus_dummy_run_tasklets,
#endif
#ifdef CONFIG_LITMUS_DGL_SUPPORT
.get_dgl_spinlock = litmus_dummy_get_dgl_spinlock,
#endif
#ifdef CONFIG_LITMUS_AFFINITY_LOCKING
.allocate_aff_obs = litmus_dummy_allocate_aff_obs,
#endif
.admit_task = litmus_dummy_admit_task
};
/*
* The reference to current plugin that is used to schedule tasks within
* the system. It stores references to actual function implementations
* Should be initialized by calling "init_***_plugin()"
*/
struct sched_plugin *litmus = &linux_sched_plugin;
/* the list of registered scheduling plugins */
static LIST_HEAD(sched_plugins);
static DEFINE_RAW_SPINLOCK(sched_plugins_lock);
#define CHECK(func) {\
if (!plugin->func) \
plugin->func = litmus_dummy_ ## func;}
/* FIXME: get reference to module */
int register_sched_plugin(struct sched_plugin* plugin)
{
printk(KERN_INFO "Registering LITMUS^RT plugin %s.\n",
plugin->plugin_name);
/* make sure we don't trip over null pointers later */
CHECK(finish_switch);
CHECK(schedule);
CHECK(tick);
CHECK(task_wake_up);
CHECK(task_exit);
CHECK(task_block);
CHECK(task_new);
CHECK(complete_job);
CHECK(activate_plugin);
CHECK(deactivate_plugin);
CHECK(compare);
#ifdef CONFIG_LITMUS_LOCKING
CHECK(allocate_lock);
CHECK(increase_prio);
CHECK(decrease_prio);
#endif
#ifdef CONFIG_LITMUS_NESTED_LOCKING
CHECK(nested_increase_prio);
CHECK(nested_decrease_prio);
CHECK(__compare);
#endif
#ifdef CONFIG_LITMUS_SOFTIRQD
CHECK(increase_prio_klitirqd);
CHECK(decrease_prio_klitirqd);
#endif
#ifdef CONFIG_LITMUS_PAI_SOFTIRQD
CHECK(enqueue_pai_tasklet);
CHECK(change_prio_pai_tasklet);
CHECK(run_tasklets);
#endif
#ifdef CONFIG_LITMUS_DGL_SUPPORT
CHECK(get_dgl_spinlock);
#endif
#ifdef CONFIG_LITMUS_AFFINITY_LOCKING
CHECK(allocate_aff_obs);
#endif
CHECK(admit_task);
if (!plugin->release_at)
plugin->release_at = release_at;
raw_spin_lock(&sched_plugins_lock);
list_add(&plugin->list, &sched_plugins);
raw_spin_unlock(&sched_plugins_lock);
return 0;
}
/* FIXME: reference counting, etc. */
struct sched_plugin* find_sched_plugin(const char* name)
{
struct list_head *pos;
struct sched_plugin *plugin;
raw_spin_lock(&sched_plugins_lock);
list_for_each(pos, &sched_plugins) {
plugin = list_entry(pos, struct sched_plugin, list);
if (!strcmp(plugin->plugin_name, name))
goto out_unlock;
}
plugin = NULL;
out_unlock:
raw_spin_unlock(&sched_plugins_lock);
return plugin;
}
int print_sched_plugins(char* buf, int max)
{
int count = 0;
struct list_head *pos;
struct sched_plugin *plugin;
raw_spin_lock(&sched_plugins_lock);
list_for_each(pos, &sched_plugins) {
plugin = list_entry(pos, struct sched_plugin, list);
count += snprintf(buf + count, max - count, "%s\n", plugin->plugin_name);
if (max - count <= 0)
break;
}
raw_spin_unlock(&sched_plugins_lock);
return count;
}