#include <stdio.h>

volatile unsigned long jiffies;
#define ULONG_MAX       (~0UL)
#define ULONG_CMP_GE(a, b)      (ULONG_MAX / 2 >= (a) - (b))
#define ULONG_CMP_LT(a, b)      (ULONG_MAX / 2 < (a) - (b))

struct task_struct {
	int boost_kthread_status;
	int rcu_wake_cond_been_called;
};

struct task_struct t1;
struct task_struct t2;

struct rcu_node {
	void *exp_tasks;
	void *gp_tasks;
	void *boost_tasks;
	unsigned long qsmask;
	unsigned long boost_time;
	int boost_kthread_status;
	struct task_struct *boost_kthread_task;
	int rcu_initiate_boost_trace_been_called;
};

struct rcu_node rn1;
struct rcu_node rn2;

void rcu_wake_cond(struct task_struct *t, int bks)
{
	t->rcu_wake_cond_been_called = 1;
	t->boost_kthread_status = bks;
}

void rcu_initiate_boost_trace(struct rcu_node *rnp)
{
	rnp->rcu_initiate_boost_trace_been_called = 1;
}

void initialize(char *argv[], struct task_struct *t, struct rcu_node *rnp)
{
	rnp->exp_tasks = argv[0];
	rnp->gp_tasks = argv[1];
	rnp->boost_tasks = argv[2];
	rnp->qsmask = (unsigned long)argv[3];
	rnp->boost_time = (unsigned long)argv[4];
	rnp->boost_kthread_status = (int)argv[5];
	rnp->boost_kthread_task = t;
	rnp->rcu_initiate_boost_trace_been_called = 0;
	t->rcu_wake_cond_been_called = 0;
}

void do_old_if(char *argv[], struct task_struct *t_in, struct rcu_node *rnp)
{
	struct task_struct *t;

	/* --- Code under test, original. --- */
	if (rnp->exp_tasks != NULL ||
	    (rnp->gp_tasks != NULL &&
	     rnp->boost_tasks == NULL &&
	     rnp->qsmask == 0 &&
	     ULONG_CMP_GE(jiffies, rnp->boost_time))) {
		if (rnp->exp_tasks == NULL)
			rnp->boost_tasks = rnp->gp_tasks;
		/* raw_spin_unlock_irqrestore(&rnp->lock, flags); */
		t = rnp->boost_kthread_task;
		if (t)
			rcu_wake_cond(t, rnp->boost_kthread_status);
	} else {
		rcu_initiate_boost_trace(rnp);
		/* raw_spin_unlock_irqrestore(&rnp->lock, flags); */
	}
}

void do_new_if(char *argv[], struct task_struct *t_in, struct rcu_node *rnp)
{
	struct task_struct *t;

	/* --- Code under test, new. --- */
	if (rnp->exp_tasks == NULL &&
	    (rnp->gp_tasks == NULL ||
	     rnp->boost_tasks != NULL ||
	     rnp->qsmask != 0 ||
	     ULONG_CMP_LT(jiffies, rnp->boost_time))) {
		rcu_initiate_boost_trace(rnp);
		/* raw_spin_unlock_irqrestore(&rnp->lock, flags); */
	} else {
		if (rnp->exp_tasks == NULL)
			rnp->boost_tasks = rnp->gp_tasks;
		/* raw_spin_unlock_irqrestore(&rnp->lock, flags); */
		t = rnp->boost_kthread_task;
		if (t)
			rcu_wake_cond(t, rnp->boost_kthread_status);
	}
}

void check(void)
{
	assert(rn1.exp_tasks == rn2.exp_tasks);
	assert(rn1.gp_tasks == rn2.gp_tasks);
	assert(rn1.boost_tasks == rn2.boost_tasks);
	assert(rn1.qsmask == rn2.qsmask);
	assert(rn1.boost_time == rn2.boost_time);
	assert(rn1.boost_kthread_status == rn2.boost_kthread_status);
	assert(rn1.rcu_initiate_boost_trace_been_called ==
	       rn2.rcu_initiate_boost_trace_been_called);
	assert(t1.boost_kthread_status == t2.boost_kthread_status);
	assert(t1.rcu_wake_cond_been_called == t2.rcu_wake_cond_been_called);
}

int main(int argc, char *argv[])
{
	initialize(argv, &t1, &rn1);
	initialize(argv, &t2, &rn2);
	check();

	do_old_if(argv, &t1, &rn1);
	do_new_if(argv, &t2, &rn2);
	check();
}