aboutsummaryrefslogblamecommitdiffstats
path: root/litmus/sched_litmus.c
blob: c1fc7748e590d3f2b0d5ea2c2b268850138b286a (plain) (tree)





















                                                                    



                                                           

                                                             

                             

                                          

                         
                        

               

 

                                                        

                            

                                 

                                 

                                     
                                      

                                                                        
                                          
                                                 

                                                                     











                                                                               



                                                                    

                                                         
                                                               

                                    


                                                                              







                                                                                
                                                                         
                                                          
                                            


                                                     
                                            


                                  


                                                               




















                                                                                
                                                       







                                                                          
                                                              
                                         
                                                                           
                                                        

                                                     
                                           
                                    



                                                                     





                                                      








                                                                     

                                        









                                                                                

                                        






                                                                           






                                                                        







                                                                                      



                                                                      










                                                                        

                                                               
















                                                           



                                                                              

                                                                         








































































                                                                                 
                                                      









                                                         
/* This file is included from kernel/sched.c */

#include <litmus/litmus.h>
#include <litmus/sched_plugin.h>

static void update_time_litmus(struct rq *rq, struct task_struct *p)
{
	u64 delta = rq->clock - p->se.exec_start;
	if (unlikely((s64)delta < 0))
		delta = 0;
	/* per job counter */
	p->rt_param.job_params.exec_time += delta;
	/* task counter */
	p->se.sum_exec_runtime += delta;
	/* sched_clock() */
	p->se.exec_start = rq->clock;
	cpuacct_charge(p, delta);
}

static void double_rq_lock(struct rq *rq1, struct rq *rq2);
static void double_rq_unlock(struct rq *rq1, struct rq *rq2);

/*
 * litmus_tick gets called by scheduler_tick() with HZ freq
 * Interrupts are disabled
 */
static void litmus_tick(struct rq *rq, struct task_struct *p)
{
	TS_PLUGIN_TICK_START;

	if (is_realtime(p))
		update_time_litmus(rq, p);

	/* plugin tick */
	litmus->tick(p);

	return;
}

static struct task_struct *
litmus_schedule(struct rq *rq, struct task_struct *prev)
{
	struct rq* other_rq;
	struct task_struct *next;

	long was_running;
	lt_t _maybe_deadlock = 0;

	/* let the plugin schedule */
	next = litmus->schedule(prev);

	/* check if a global plugin pulled a task from a different RQ */
	if (next && task_rq(next) != rq) {
		/* we need to migrate the task */
		other_rq = task_rq(next);
		TRACE_TASK(next, "migrate from %d\n", other_rq->cpu);

		/* while we drop the lock, the prev task could change its
		 * state
		 */
		was_running = is_running(prev);
		mb();
		spin_unlock(&rq->lock);

		/* Don't race with a concurrent switch.  This could deadlock in
		 * the case of cross or circular migrations.  It's the job of
		 * the plugin to make sure that doesn't happen.
		 */
		TRACE_TASK(next, "stack_in_use=%d\n",
			   next->rt_param.stack_in_use);
		if (next->rt_param.stack_in_use != NO_CPU) {
			TRACE_TASK(next, "waiting to deschedule\n");
			_maybe_deadlock = litmus_clock();
		}
		while (next->rt_param.stack_in_use != NO_CPU) {
			cpu_relax();
			mb();
			if (next->rt_param.stack_in_use == NO_CPU)
				TRACE_TASK(next,"descheduled. Proceeding.\n");

			if (lt_before(_maybe_deadlock + 10000000,
				      litmus_clock())) {
				/* We've been spinning for 10ms.
				 * Something can't be right!
				 * Let's abandon the task and bail out; at least
				 * we will have debug info instead of a hard
				 * deadlock.
				 */
				TRACE_TASK(next,"stack too long in use. "
					   "Deadlock?\n");
				next = NULL;

				/* bail out */
				spin_lock(&rq->lock);
				return next;
			}
		}
#ifdef  __ARCH_WANT_UNLOCKED_CTXSW
		if (next->oncpu)
			TRACE_TASK(next, "waiting for !oncpu");
		while (next->oncpu) {
			cpu_relax();
			mb();
		}
#endif
		double_rq_lock(rq, other_rq);
		mb();
		if (is_realtime(prev) && is_running(prev) != was_running) {
			TRACE_TASK(prev,
				   "state changed while we dropped"
				   " the lock: is_running=%d, was_running=%d\n",
				   is_running(prev), was_running);
			if (is_running(prev) && !was_running) {
				/* prev task became unblocked
				 * we need to simulate normal sequence of events
				 * to scheduler plugins.
				 */
				litmus->task_block(prev);
				litmus->task_wake_up(prev);
			}
		}

		set_task_cpu(next, smp_processor_id());

		/* DEBUG: now that we have the lock we need to make sure a
		 *  couple of things still hold:
		 *  - it is still a real-time task
		 *  - it is still runnable (could have been stopped)
		 * If either is violated, then the active plugin is
		 * doing something wrong.
		 */
		if (!is_realtime(next) || !is_running(next)) {
			/* BAD BAD BAD */
			TRACE_TASK(next,"BAD: migration invariant FAILED: "
				   "rt=%d running=%d\n",
				   is_realtime(next),
				   is_running(next));
			/* drop the task */
			next = NULL;
		}
		/* release the other CPU's runqueue, but keep ours */
		spin_unlock(&other_rq->lock);
	}
	if (next) {
		next->rt_param.stack_in_use = rq->cpu;
		next->se.exec_start = rq->clock;
	}

	return next;
}

static void enqueue_task_litmus(struct rq *rq, struct task_struct *p,
				int wakeup)
{
	if (wakeup) {
		sched_trace_task_resume(p);
		tsk_rt(p)->present = 1;
		litmus->task_wake_up(p);

		rq->litmus.nr_running++;
	} else
		TRACE_TASK(p, "ignoring an enqueue, not a wake up.\n");
}

static void dequeue_task_litmus(struct rq *rq, struct task_struct *p, int sleep)
{
	if (sleep) {
		litmus->task_block(p);
		tsk_rt(p)->present = 0;
		sched_trace_task_block(p);

		rq->litmus.nr_running--;
	} else
		TRACE_TASK(p, "ignoring a dequeue, not going to sleep.\n");
}

static void yield_task_litmus(struct rq *rq)
{
	BUG_ON(rq->curr != current);
	/* sched_yield() is called to trigger delayed preemptions.
	 * Thus, mark the current task as needing to be rescheduled.
	 * This will cause the scheduler plugin to be invoked, which can
	 * then determine if a preemption is still required.
	 */
	clear_exit_np(current);
	set_tsk_need_resched(current);
}

/* Plugins are responsible for this.
 */
static void check_preempt_curr_litmus(struct rq *rq, struct task_struct *p, int flags)
{
}

static void put_prev_task_litmus(struct rq *rq, struct task_struct *p)
{
}

static void pre_schedule_litmus(struct rq *rq, struct task_struct *prev)
{
	update_time_litmus(rq, prev);
	if (!is_running(prev))
		tsk_rt(prev)->present = 0;
}

/* pick_next_task_litmus() - litmus_schedule() function
 *
 * return the next task to be scheduled
 */
static struct task_struct *pick_next_task_litmus(struct rq *rq)
{
	/* get the to-be-switched-out task (prev) */
	struct task_struct *prev = rq->litmus.prev;
	struct task_struct *next;

	/* if not called from schedule() but from somewhere
	 * else (e.g., migration), return now!
	 */
	if(!rq->litmus.prev)
		return NULL;

	rq->litmus.prev = NULL;

	TS_PLUGIN_SCHED_START;
	next = litmus_schedule(rq, prev);
	TS_PLUGIN_SCHED_END;

	return next;
}

static void task_tick_litmus(struct rq *rq, struct task_struct *p, int queued)
{
	/* nothing to do; tick related tasks are done by litmus_tick() */
	return;
}

static void switched_to_litmus(struct rq *rq, struct task_struct *p, int running)
{
}

static void prio_changed_litmus(struct rq *rq, struct task_struct *p,
				int oldprio, int running)
{
}

unsigned int get_rr_interval_litmus(struct task_struct *p)
{
	/* return infinity */
	return 0;
}

/* This is called when a task became a real-time task, either due to a SCHED_*
 * class transition or due to PI mutex inheritance. We don't handle Linux PI
 * mutex inheritance yet (and probably never will). Use LITMUS provided
 * synchronization primitives instead.
 */
static void set_curr_task_litmus(struct rq *rq)
{
	rq->curr->se.exec_start = rq->clock;
}


#ifdef CONFIG_SMP
/* execve tries to rebalance task in this scheduling domain */
static int select_task_rq_litmus(struct task_struct *p, int sd_flag, int flags)
{
	/* preemption is already disabled.
	 * We don't want to change cpu here
	 */
	return smp_processor_id();
}

/* we don't repartition at runtime */

static unsigned long
load_balance_litmus(struct rq *this_rq, int this_cpu, struct rq *busiest,
		unsigned long max_load_move,
		struct sched_domain *sd, enum cpu_idle_type idle,
		int *all_pinned, int *this_best_prio)
{
	return 0;
}

static int
move_one_task_litmus(struct rq *this_rq, int this_cpu, struct rq *busiest,
		 struct sched_domain *sd, enum cpu_idle_type idle)
{
	return 0;
}
#endif

const struct sched_class litmus_sched_class = {
	.next			= &rt_sched_class,
	.enqueue_task		= enqueue_task_litmus,
	.dequeue_task		= dequeue_task_litmus,
	.yield_task		= yield_task_litmus,

	.check_preempt_curr	= check_preempt_curr_litmus,

	.pick_next_task		= pick_next_task_litmus,
	.put_prev_task		= put_prev_task_litmus,

#ifdef CONFIG_SMP
	.select_task_rq		= select_task_rq_litmus,

	.load_balance		= load_balance_litmus,
	.move_one_task		= move_one_task_litmus,
	.pre_schedule		= pre_schedule_litmus,
#endif

	.set_curr_task          = set_curr_task_litmus,
	.task_tick		= task_tick_litmus,

	.get_rr_interval	= get_rr_interval_litmus,

	.prio_changed		= prio_changed_litmus,
	.switched_to		= switched_to_litmus,
};