summaryrefslogtreecommitdiffstats
path: root/modules/spl/spl-thread.c
blob: 1cfa369ba1d0650e58fc94c5bcdb4769c82a5d0d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
#include <sys/thread.h>

/*
 * Thread interfaces
 */
typedef struct thread_priv_s {
	unsigned long tp_magic;		/* Magic */
	void (*tp_func)(void *);	/* Registered function */
	void *tp_args;			/* Args to be passed to function */
	size_t tp_len;			/* Len to be passed to function */
	int tp_state;			/* State to start thread at */
	pri_t tp_pri;			/* Priority to start threat at */
	volatile kthread_t *tp_task;	/* Task pointer for new thread */
	spinlock_t tp_lock;		/* Syncronization lock */
        wait_queue_head_t tp_waitq;	/* Syncronization wait queue */
} thread_priv_t;

static int
thread_generic_wrapper(void *arg)
{
	thread_priv_t *tp = (thread_priv_t *)arg;
	void (*func)(void *);
	void *args;

        spin_lock(&tp->tp_lock);
	BUG_ON(tp->tp_magic != TP_MAGIC);
	func = tp->tp_func;
	args = tp->tp_args;
	tp->tp_task = get_current();
	set_current_state(tp->tp_state);
	set_user_nice((kthread_t *)tp->tp_task, PRIO_TO_NICE(tp->tp_pri));

        spin_unlock(&tp->tp_lock);
	wake_up(&tp->tp_waitq);

	/* DO NOT USE 'ARG' AFTER THIS POINT, EVER, EVER, EVER!
	 * Local variables are used here because after the calling thread
	 * has been woken up it will exit and this memory will no longer
	 * be safe to access since it was declared on the callers stack. */
	if (func)
		func(args);

	return 0;
}

void
__thread_exit(void)
{
	do_exit(0);
	return;
}
EXPORT_SYMBOL(__thread_exit);

/* thread_create() may block forever if it cannot create a thread or
 * allocate memory.  This is preferable to returning a NULL which Solaris
 * style callers likely never check for... since it can't fail. */
kthread_t *
__thread_create(caddr_t stk, size_t  stksize, thread_func_t func,
		const char *name, void *args, size_t len, int *pp,
		int state, pri_t pri)
{
	thread_priv_t tp;
	DEFINE_WAIT(wait);
	struct task_struct *tsk;

	/* Option pp is simply ignored */
	/* Variable stack size unsupported */
	BUG_ON(stk != NULL);
	BUG_ON(stk != 0);

	/* Variable tp is located on the stack and not the heap because I want
	 * to minimize any chance of a failure, since the Solaris code is designed
	 * such that this function cannot fail.  This is a little dangerous since
	 * we're passing a stack address to a new thread but correct locking was
	 * added to ensure the callee can use the data safely until wake_up(). */
	tp.tp_magic = TP_MAGIC;
	tp.tp_func  = func;
	tp.tp_args  = args;
	tp.tp_len   = len;
	tp.tp_state = state;
	tp.tp_pri   = pri;
	tp.tp_task  = NULL;
	spin_lock_init(&tp.tp_lock);
        init_waitqueue_head(&tp.tp_waitq);

	spin_lock(&tp.tp_lock);

	tsk = kthread_create(thread_generic_wrapper, (void *)&tp, "%s", name);
	if (IS_ERR(tsk)) {
		printk("spl: Failed to create thread: %ld\n", PTR_ERR(tsk));
		return NULL;
	}

	wake_up_process(tsk);

	/* All signals are ignored due to sleeping TASK_UNINTERRUPTIBLE */
	for (;;) {
		prepare_to_wait(&tp.tp_waitq, &wait, TASK_UNINTERRUPTIBLE);
		if (tp.tp_task != NULL)
			break;

		spin_unlock(&tp.tp_lock);
		schedule();
		spin_lock(&tp.tp_lock);
	}

	BUG_ON(tsk != tp.tp_task); /* Extra paranoia */
	spin_unlock(&tp.tp_lock);

	return (kthread_t *)tp.tp_task;
}
EXPORT_SYMBOL(__thread_create);