/* Operations and structures for dealing with generic priority inheritance. */

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

#include <litmus/sched_plugin.h>

/* Add a record to the pi sem history stack.
 *
 * Recommend that inh == tsk for "no inheritance", though this
 * is up to the pi sem protocol implementation.  inh could be
 * NULL as long as the protocol implementation expects it.
 */
void push_pi_sem(
	struct task_struct *tsk,
	struct task_struct *inh,
	struct pi_semaphore *sem)
{
	sem->stack_node.inh_task = inh;

	/* list_add() needs node to be initialized properly.  Node may
	 * not be initialized yet.
	 */
	INIT_LIST_HEAD(&sem->stack_node.list);

	list_add_tail(&tsk->rt_param.pi_sem_stack, &sem->stack_node.list);

	TRACE_TASK(tsk,
		"Pushed semaphore %p with inheritance from task %d\n",
		sem, inh->pid);
}

/* Removes the last semaphore record. */
void pop_pi_sem(struct task_struct *tsk)
{
	int empty = list_empty(&tsk->rt_param.pi_sem_stack);

	WARN_ON(empty);

	if(!empty)
	{
		pi_sem_record_t *rec =
			list_entry(tsk->rt_param.pi_sem_stack.prev, pi_sem_record_t, list);
		list_del(&rec->list);

		TRACE_TASK(tsk, "Popped sem/inh record %p / %d.\n",
				container_of(rec, struct pi_semaphore, stack_node),
				rec->inh_task->pid);
	}
}

/* Get a pointer to the last semaphore pushed on to the stack.
 * Returns NULL if there is no record.
 */
pi_sem_record_t* peek_pi_sem(struct task_struct *tsk)
{
	if(!list_empty(&tsk->rt_param.pi_sem_stack))
	{
		pi_sem_record_t *rec =
			list_entry(tsk->rt_param.pi_sem_stack.prev,
				pi_sem_record_t, list);
		return rec;
	}
	return NULL;
}


/* Update priority inheritance. */
int update_pi_sem(
	struct task_struct *tsk,
	struct task_struct *inh,
	struct pi_semaphore *sem)
{
	int success = 0;
	struct list_head *pos, *next;
	pi_sem_record_t *rec;

	/* Though we have a reference to the semaphore (sem), we need
	 * to search through tsk's stack to validate that tsk actually
	 * holds sem.  If tsk holds sem, then sem will be in its stack.
	 *
	 * TODO: Add function pointer hooks to pi_semaphore for operations
	 * such as "int is_holder(struct task_struct *tsk)".  pi_semaphore
	 * does not track its holder(s), though concrete protocols may,
	 * providing more efficient validation methods than this stack search.
	 * Another function may be "pi_sem_record_t* get_record(...)" if the
	 * semaphore protocol supports multiple holders (though
	 * pi_semaphore.stack_node will probably go unused or replaced in
	 * such cases).
	 */

	/* Assuming a sane locking order, the lock we're looking for
	 * is probably towards (at) the end.
	 */
	list_for_each_prev_safe(pos, next, &tsk->rt_param.pi_sem_stack)
	{
		struct pi_semaphore* which_sem;
		rec = list_entry(pos, pi_sem_record_t, list);

		which_sem = container_of(rec, struct pi_semaphore, stack_node);
		if(which_sem == sem)
		{
			TRACE_TASK(tsk, "Updating inheritance from %d to %d on sem %p\n",
				rec->inh_task->pid, inh->pid, sem);

			rec->inh_task = inh;
			success = 1;
			break;
		}
	}

	if(!success)
	{
		TRACE_TASK(tsk, "Could not find record for sem %p to update inheritance.\n",
			sem);
	}

	return success;
}

/* Removes a record from the semaphore stack if it exists.
 *
 * Note: pop_pi_sem() may be more appropriate if total ordering can be
 * guaranteed.
 */
int remove_pi_sem(struct task_struct *tsk, struct pi_semaphore *sem)
{
	int success = 0;
	struct list_head *pos, *next;
	pi_sem_record_t *rec;

	/* Though we have a reference to the semaphore (sem), we need
	 * to search through tsk's stack to validate that tsk actually
	 * holds sem.  If tsk holds sem, then sem will be in its stack.
	 *
	 * TODO: Add function pointer hooks to pi_semaphore for operations
	 * such as "int is_holder(struct task_struct *tsk)".  pi_semaphore
	 * does not track its holder(s), though concrete protocols may,
	 * providing more efficient validation methods than this stack search.
	 * Another function may be "pi_sem_record_t* get_record(...)" if the
	 * semaphore protocol supports multiple holders (though
	 * pi_semaphore.stack_node will probably go unused or replaced in
	 * such cases).
	 */

	/* Assuming a sane locking order, the lock we're looking for
	 * is probably towards (at) the end.
	 */
	list_for_each_prev_safe(pos, next, &tsk->rt_param.pi_sem_stack)
	{   
		struct pi_semaphore* which_sem;
		rec = list_entry(pos, pi_sem_record_t, list);

		which_sem = container_of(rec, struct pi_semaphore, stack_node);
		if(which_sem == sem)
		{
			TRACE_TASK(tsk, "Removing semaphore stack record from %d with sem %p\n",
				rec->inh_task->pid, sem);

			list_del(&rec->list);
			success = 1;
			break;
		}   
	}   

	if(!success)
	{   
		TRACE_TASK(tsk, "Could not find record for sem %p to remove.\n",
			sem);
	}

	return success;	
}

/* Returns true of tsk holds ANY pi semaphores (as reportable by the stack).
 * Function will return false if the protocol in use does not use the stack,
 * such as FMLP-Long, which uses rt_params.eff_priority.
 */
int has_pi_sem(struct task_struct *tsk)
{
	return !list_empty(&tsk->rt_param.pi_sem_stack);
}