aboutsummaryrefslogtreecommitdiffstats
path: root/arch/mips/tx4938/common/irq.c
Commit message (Expand)AuthorAge
* [MIPS] TXx9: Reorganize codeAtsushi Nemoto2008-07-15
* [MIPS] Cleanup TX39/TX49 irq codeAtsushi Nemoto2007-08-26
* [MIPS] use name instead of typename for each irq_chipAtsushi Nemoto2007-02-06
* [MIPS] Compile __do_IRQ() when really neededFranck Bui-Huu2006-12-06
* [MIPS] use generic_handle_irq, handle_level_irq, handle_percpu_irqAtsushi Nemoto2006-11-29
* [MIPS] IRQ cleanupsAtsushi Nemoto2006-11-29
* [MIPS] Fix build errors related to wbflush.h on tx4927/tx4938.Atsushi Nemoto2006-10-09
* [MIPS] Complete fixes after removal of pt_regs argument to int handlers.Ralf Baechle2006-10-07
* fix file specification in commentsUwe Zeisberger2006-10-03
* [MIPS] Eleminate interrupt migration helper use.Ralf Baechle2006-07-13
* [PATCH] genirq: rename desc->handler to desc->chipIngo Molnar2006-06-29
* [MIPS] Rewrite all the assembler interrupt handlers to C.Ralf Baechle2006-04-18
* Support for Toshiba's RBHMA4500 eval board for the TX4938.Ralf Baechle2005-10-29
ef='#n143'>143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215
/* litmus/sync.c - Support for synchronous and asynchronous task system releass.
 */

#include <asm/atomic.h>
#include <asm/uaccess.h>
#include <linux/spinlock.h>
#include <linux/list.h>
#include <linux/sched.h>
#include <linux/completion.h>

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

#include <litmus/sched_trace.h>
#include <litmus/budget.h>

struct ts_release_wait {
	struct list_head list;
	struct completion completion;
	lt_t ts_release_time;
};

#define DECLARE_TS_RELEASE_WAIT(symb)					\
	struct ts_release_wait symb =					\
	{								\
		LIST_HEAD_INIT(symb.list),				\
		COMPLETION_INITIALIZER_ONSTACK(symb.completion),	\
		0							\
	}

static LIST_HEAD(task_release_list);
static DEFINE_MUTEX(task_release_lock);

static long do_wait_for_ts_release(struct timespec *wake)
{
	DECLARE_TS_RELEASE_WAIT(wait);

	long ret = -ERESTARTSYS;

	struct task_struct *t = current;
	int is_rt = is_realtime(t);

#if defined(CONFIG_REALTIME_AUX_TASKS) || defined(CONFIG_LITMUS_NVIDIA)
	DECLARE_WORKER_VIS_FLAGS(vis_flags);
#endif

	if (mutex_lock_interruptible(&task_release_lock))
		goto out;

	list_add(&wait.list, &task_release_list);

	mutex_unlock(&task_release_lock);

	if (is_rt) {
#if defined(CONFIG_REALTIME_AUX_TASKS) || defined(CONFIG_LITMUS_NVIDIA)
		hide_from_workers(t, &vis_flags);
#endif
		bt_flag_set(t, BTF_WAITING_FOR_RELEASE);
		mb();
		budget_state_machine(t, on_exit); /* TODO: maybe call in schedule() */
	}

	TRACE_TASK(t, "waiting for ts release.\n");

	if (is_rt)
		BUG_ON(!bt_flag_is_set(t, BTF_WAITING_FOR_RELEASE));

	/* We are enqueued, now we wait for someone to wake us up. */
	ret = wait_for_completion_interruptible(&wait.completion);

	TRACE_TASK(t, "released by ts release!\n");

	if (is_rt) {
		bt_flag_clear(t, BTF_WAITING_FOR_RELEASE);
#if defined(CONFIG_REALTIME_AUX_TASKS) || defined(CONFIG_LITMUS_NVIDIA)
		show_to_workers(t, &vis_flags);
#endif
	}

	if (!ret) {
		if (is_rt) {
			lt_t phasedRelease = wait.ts_release_time
					+ t->rt_param.task_params.phase;
			*wake = ns_to_timespec(phasedRelease);

			/* Setting this flag before releasing ensures that this CPU
			 * will be the next CPU to requeue the task on a ready or
			 * release queue. Cleared by prepare_for_next_period()
			 */
			tsk_rt(current)->dont_requeue = 1;
			tsk_rt(t)->completed = 1;
			tsk_rt(t)->job_params.backlog = 0;
			tsk_rt(t)->job_params.is_backlogged_job = 0;
			tsk_rt(t)->budget.suspend_timestamp = 0;
			bt_flag_clear(t, BTF_BUDGET_EXHAUSTED);
			mb();

			/* completion succeeded, set up release. subtract off
			 * period because schedule()->job_completion() will
			 * advances us to the correct time */
			/* TODO: pfair might require pass through release_at... */
			setup_release(t, phasedRelease - t->rt_param.task_params.period);
			schedule();
		}
		else {
			/* sleep until our release time */
			if (litmus_clock() < wait.ts_release_time) {
				ktime_t remaining =
						ns_to_ktime(wait.ts_release_time - litmus_clock());
				schedule_hrtimeout(&remaining, HRTIMER_MODE_REL);
			}
			*wake = ns_to_timespec(wait.ts_release_time);
		}
	} else {
		/* We were interrupted, must cleanup list. */
		mutex_lock(&task_release_lock);
		if (!wait.completion.done)
			list_del(&wait.list);
		mutex_unlock(&task_release_lock);
	}

out:
	return ret;
}

int count_tasks_waiting_for_release(void)
{
	int task_count = 0;
	struct list_head *pos;

	mutex_lock(&task_release_lock);

	list_for_each(pos, &task_release_list) {
		task_count++;
	}

	mutex_unlock(&task_release_lock);


	return task_count;
}

static long do_release_ts(lt_t start)
{
	long  task_count = 0;

	struct list_head	*pos, *safe;
	struct ts_release_wait	*wait;

	if (mutex_lock_interruptible(&task_release_lock)) {
		task_count = -ERESTARTSYS;
		goto out;
	}

	TRACE("<<<<<< synchronous task system release >>>>>>\n");
	sched_trace_sys_release(&start);

	task_count = 0;
	list_for_each_safe(pos, safe, &task_release_list) {
		wait = (struct ts_release_wait*)
			list_entry(pos, struct ts_release_wait, list);

		task_count++;

		wait->ts_release_time = start;
		complete(&wait->completion);
	}

	/* clear stale list */
	INIT_LIST_HEAD(&task_release_list);

	mutex_unlock(&task_release_lock);

out:
	return task_count;
}


asmlinkage long sys_wait_for_ts_release(struct timespec __user *__wake)
{
	struct timespec wake;
	long ret = -EPERM;

	ret = do_wait_for_ts_release(&wake);

	if (__wake && access_ok(VERIFY_WRITE, __wake, sizeof(struct timespec)))
		__copy_to_user(__wake, &wake, sizeof(wake));

	return ret;
}

#define ONE_MS 1000000

asmlinkage long sys_release_ts(lt_t __user *__delay)
{
	long ret = 0;
	lt_t delay = 0;
	lt_t start_time;

	/* FIXME: check capabilities... */

	if (__delay)
		ret = copy_from_user(&delay, __delay, sizeof(delay));

	if (ret == 0) {
		/* round up to next larger integral millisecond */
		start_time = litmus_clock();
		do_div(start_time, ONE_MS);
		start_time *= ONE_MS;
		ret = do_release_ts(start_time + delay);
	}

	return ret;
}