summaryrefslogtreecommitdiffstats
path: root/src/spl/linux-thread.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/spl/linux-thread.c')
-rw-r--r--src/spl/linux-thread.c113
1 files changed, 113 insertions, 0 deletions
diff --git a/src/spl/linux-thread.c b/src/spl/linux-thread.c
new file mode 100644
index 000000000..ad036471a
--- /dev/null
+++ b/src/spl/linux-thread.c
@@ -0,0 +1,113 @@
+#include <sys/linux-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;
+
+int
+thread_generic_wrapper(void *arg)
+{
+ thread_priv_t *tp = (thread_priv_t *)arg;
+ void (*func)(void *);
+ void *args;
+ char name[16];
+
+ /* Use the truncated function name as thread name */
+ snprintf(name, sizeof(name), "%s", "kthread");
+ daemonize(name);
+
+ 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)
+{
+ return;
+}
+
+/* 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, void (*proc)(void *),
+ void *args, size_t len, proc_t *pp, int state, pri_t pri)
+{
+ thread_priv_t tp;
+ DEFINE_WAIT(wait);
+ kthread_t *task;
+ long pid;
+
+ /* 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 = proc;
+ 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);
+
+ /* Solaris says this must never fail so we try forever */
+ while ((pid = kernel_thread(thread_generic_wrapper, (void *)&tp, 0)) < 0)
+ printk(KERN_ERR "linux-thread: Unable to create thread; "
+ "pid = %ld\n", pid);
+
+ /* 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);
+ }
+
+ /* Verify the pid retunred matches the pid in the task struct */
+ BUG_ON(pid != (tp.tp_task)->pid);
+
+ spin_unlock(&tp.tp_lock);
+
+ return (kthread_t *)tp.tp_task;
+}