/* 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 #include #include #include #include #include #include /* * 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) { 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; }