Uthread arch-dependent code uses user_contexts
authorBarret Rhoden <brho@cs.berkeley.edu>
Mon, 15 Apr 2013 21:28:26 +0000 (14:28 -0700)
committerBarret Rhoden <brho@cs.berkeley.edu>
Mon, 15 Apr 2013 21:28:26 +0000 (14:28 -0700)
Still use HW contexts in all architectures, but each arch can make the
change to SW contexts (for uthreads and syscalls) at their leisure.

16 files changed:
Documentation/async_events.txt
Documentation/process-internals.txt
Documentation/processes.txt
kern/arch/i686/ros/bits/syscall.h
kern/src/process.c
kern/src/syscall.c
tests/eth_audio.c
tests/msr_get_cores.c
tests/msr_get_singlecore.c
user/c3po/threads/ucontext.c
user/parlib/include/i686/vcore.h
user/parlib/include/riscv/vcore.h
user/parlib/include/sparc/vcore.h
user/parlib/include/uthread.h
user/parlib/uthread.c
user/pthread/pthread.c

index 9d3fee4..0e4778a 100644 (file)
@@ -441,7 +441,7 @@ Worth mentioning is the difference between 'notif_pending' and VC_CAN_RCV_MSG.
 VC_CAN_RCV_MSG is the process saying it will check for messages.
 'notif_pending' is when the kernel says it *has* sent a message.
 'notif_pending' is also used by the kernel in proc_yield() and the 2LS in
-pop_ros_tf() to make sure the sent message is not missed.
+pop_user_ctx() to make sure the sent message is not missed.
 
 Also, in case this comes up, there's a slight race on changing the mbox* and the
 vcore number within the event_q.  The message could have gone to the wrong (old)
index 94bf091..2bb5338 100644 (file)
@@ -551,14 +551,14 @@ userspace can toggle this, so they can handle the notifications from a different
 core if it likes, or they can independently send a notification.
 
 Note we use notif_pending to detect if an IPI was missed while notifs were
-disabled (this is done in pop_ros_tf() by userspace).  The overall meaning of
-notif_pending is that a vcore wants to be IPI'd.  The IPI could be in-flight, or
-it could be missed.  Since notification IPIs can be spurious, when we have
-potential races, we err on the side of sending.  This happens when pop_ros_tf()
-notifies itself, and when the kernel makes sure to start a vcore in vcore
-context if a notif was pending.  This was simplified a bit over the years by
-having uthreads always get saved into the uthread_ctx (formerly the notif_tf),
-instead of in the old preempt_tf (which is now the vcore_ctx).
+disabled (this is done in pop_user_ctx() by userspace).  The overall meaning
+of notif_pending is that a vcore wants to be IPI'd.  The IPI could be
+in-flight, or it could be missed.  Since notification IPIs can be spurious,
+when we have potential races, we err on the side of sending.  This happens
+when pop_user_ctx() notifies itself, and when the kernel makes sure to start a
+vcore in vcore context if a notif was pending.  This was simplified a bit over
+the years by having uthreads always get saved into the uthread_ctx (formerly
+the notif_tf), instead of in the old preempt_tf (which is now the vcore_ctx).
 
 If a vcore has a preempt_pending, we will still send the active notification
 (IPI).  The core ought to get a notification for the preemption anyway, so we
index 8593fd0..a843890 100644 (file)
@@ -474,7 +474,7 @@ without fear of missing a notif.  If it is not clear, it needs to manually
 notify itself (sys_self_notify) so that it can process the notification that it
 missed and for which it wanted to receive an IPI.  Before it does this, it needs
 to clear notif_pending, so the kernel will send it an IPI.  These last parts are
-handled in pop_ros_tf().
+handled in pop_user_ctx().
 
 4.3: Preemption Specifics
 -------------------------------
index f073d67..1fc420a 100644 (file)
@@ -35,7 +35,7 @@ static inline intreg_t __syscall_trap(uintreg_t a0, uintreg_t a1)
 {
        intreg_t ret;
 
-       /* If you change this, change pop_ros_tf() */
+       /* If you change this, change pop_user_ctx() */
        asm volatile("int %1"
                     : "=a" (ret)
                     : "i" (T_SYSCALL),
index dc443f9..5554556 100644 (file)
@@ -1024,8 +1024,9 @@ void proc_yield(struct proc *SAFE p, bool being_nice)
                        goto out_failed;
        }
        /* Don't let them yield if they are missing a notification.  Userspace must
-        * not leave vcore context without dealing with notif_pending.  pop_ros_tf()
-        * handles leaving via uthread context.  This handles leaving via a yield.
+        * not leave vcore context without dealing with notif_pending.
+        * pop_user_ctx() handles leaving via uthread context.  This handles leaving
+        * via a yield.
         *
         * This early check is an optimization.  The real check is below when it
         * works with the online_vcs list (syncing with event.c and INDIR/IPI
index 27304d4..9b368a8 100644 (file)
@@ -825,7 +825,8 @@ static int sys_notify(struct proc *p, int target_pid, unsigned int ev_type,
 }
 
 /* Will notify the calling process on the given vcore, independently of WANTED
- * or advertised vcoreid.  If you change the parameters, change pop_ros_tf() */
+ * or advertised vcoreid.  If you change the parameters, change pop_user_ctx().
+ */
 static int sys_self_notify(struct proc *p, uint32_t vcoreid,
                            unsigned int ev_type, struct event_msg *u_msg,
                            bool priv)
index e31d6a7..2c0fc23 100644 (file)
@@ -121,7 +121,7 @@ void vcore_entry(void)
                set_tls_desc(core0_tls, 0);
                assert(__vcoreid == 0); /* in case anyone uses this */
                /* Load silly state (Floating point) too */
-               pop_ros_tf(&vcpd->uthread_ctx.tf.hw_tf, vcoreid);
+               pop_user_ctx(&vcpd->uthread_ctx, vcoreid);
                printf("should never see me!");
        }       
        /* unmask notifications once you can let go of the uthread_ctx and it is
index d23d7d7..77460cd 100644 (file)
@@ -102,7 +102,7 @@ void vcore_entry(void)
                set_tls_desc(core0_tls, 0);
                assert(__vcoreid == 0); /* in case anyone uses this */
                /* Load silly state (Floating point) too */
-               pop_ros_tf(&vcpd->uthread_ctx.tf.hw_tf, vcoreid);
+               pop_user_ctx(&vcpd->uthread_ctx, vcoreid);
                panic("should never see me!");
        }       
 /* end: stuff userspace needs to do to handle notifications */
index 7a2e84a..6e45d08 100644 (file)
@@ -103,7 +103,7 @@ void vcore_entry(void)
                set_tls_desc(core0_tls, 0);
                assert(__vcoreid == 0); /* in case anyone uses this */
                /* Load silly state (Floating point) too */
-               pop_ros_tf(&vcpd->uthread_ctx.tf.hw_tf, vcoreid);
+               pop_user_ctx(&vcpd->uthread_ctx, vcoreid);
                panic("should never see me!");
        }       
 /* end: stuff userspace needs to do to handle notifications */
index eed3d4b..ebc9870 100644 (file)
@@ -53,14 +53,14 @@ struct u_context* create_context(thread_t *t, void *entry_pt, void *stack_top)
        uc->thread = t;
 
        /* Initialize the trapframe for this context */
-       init_user_tf(&uc->u_ctx.tf.hw_tf, (uint32_t)entry_pt, (uint32_t)stack_top);
+       init_user_ctx(&uc->u_ctx, (uint32_t)entry_pt, (uint32_t)stack_top);
        return uc;
 }
 
 void save_context(struct u_context *uc)
 {
        /* Save the trapframe for this context */
-       save_ros_tf(&uc->u_ctx.tf.hw_tf);
+       save_user_ctx(&uc->u_ctx);
 }
 
 void restore_context(struct u_context *uc)
@@ -75,7 +75,7 @@ void restore_context(struct u_context *uc)
        /* Tell the uthread which vcore it is on */
        __vcoreid = vcoreid;
        /* Pop the trapframe */
-       pop_ros_tf(&uc->u_ctx.tf.hw_tf, vcoreid);
+       pop_user_ctx(&uc->u_ctx, vcoreid);
 }
 
 void destroy_context(struct u_context *uc)
index ae92e45..ea77c76 100644 (file)
@@ -8,9 +8,8 @@
 #include <ros/arch/mmu.h>
 #include <sys/vcore-tls.h>
 
-/* Pops an ROS kernel-provided TF, reanabling notifications at the same time.
- * A Userspace scheduler can call this when transitioning off the transition
- * stack.
+/* Pops a user context, reanabling notifications at the same time.  A Userspace
+ * scheduler can call this when transitioning off the transition stack.
  *
  * Make sure you clear the notif_pending flag, and then check the queue before
  * calling this.  If notif_pending is not clear, this will self_notify this
@@ -64,8 +63,10 @@ struct restart_helper {
        uint32_t                                        eip;
 };
 
-static inline void pop_ros_tf(struct hw_trapframe *tf, uint32_t vcoreid)
+static inline void pop_user_ctx(struct user_context *ctx, uint32_t vcoreid)
 {
+       struct hw_trapframe *tf = &ctx->tf.hw_tf;
+       assert(ctx->type == ROS_HW_CTX);
        struct restart_helper *rst;
        struct preempt_data *vcpd = &__procdata.vcore_preempt_data[vcoreid];
        if (!tf->tf_cs) { /* sysenter TF.  esp and eip are in other regs. */
@@ -126,10 +127,12 @@ static inline void pop_ros_tf(struct hw_trapframe *tf, uint32_t vcoreid)
                      : "memory");
 }
 
-/* Like the regular pop_ros_tf, but this one doesn't check or clear
+/* Like the regular pop_user_ctx, but this one doesn't check or clear
  * notif_pending. */
-static inline void pop_ros_tf_raw(struct hw_trapframe *tf, uint32_t vcoreid)
+static inline void pop_user_ctx_raw(struct user_context *ctx, uint32_t vcoreid)
 {
+       struct hw_trapframe *tf = &ctx->tf.hw_tf;
+       assert(ctx->type == ROS_HW_CTX);
        struct restart_helper *rst;
        struct preempt_data *vcpd = &__procdata.vcore_preempt_data[vcoreid];
        if (!tf->tf_cs) { /* sysenter TF.  esp and eip are in other regs. */
@@ -156,8 +159,8 @@ static inline void pop_ros_tf_raw(struct hw_trapframe *tf, uint32_t vcoreid)
                      "subl %%eax,%%esp;     " /* move to notif_en_loc slot */
                      "popl %%eax;           " /* load notif_disabled addr */
                      "movb $0x00,(%%eax);   " /* enable notifications */
-                                 /* Here's where we differ from the regular pop_ros_tf().  We
-                                  * do the same pops/esp moves, just to keep things similar
+                                 /* Here's where we differ from the regular pop_user_ctx().
+                                  * We do the same pops/esp moves, just to keep things similar
                                   * and simple, but don't do test, clear notif_pending, or
                                   * call a syscall. */
                                  /* From here down, we can get interrupted and restarted */
@@ -172,11 +175,13 @@ static inline void pop_ros_tf_raw(struct hw_trapframe *tf, uint32_t vcoreid)
                      : "memory");
 }
 
-/* Save the current context/registers into the given tf, setting the pc of the
+/* Save the current context/registers into the given ctx, setting the pc of the
  * tf to the end of this function.  You only need to save that which you later
- * restore with pop_ros_tf(). */
-static inline void save_ros_tf(struct hw_trapframe *tf)
+ * restore with pop_user_ctx(). */
+static inline void save_user_ctx(struct user_context *ctx)
 {
+       struct hw_trapframe *tf = &ctx->tf.hw_tf;
+       ctx->type = ROS_HW_CTX;
        memset(tf, 0, sizeof(struct hw_trapframe)); /* sanity */
        /* set CS and make sure eflags is okay */
        tf->tf_cs = GD_UT | 3;
@@ -198,10 +203,11 @@ static inline void save_ros_tf(struct hw_trapframe *tf)
                     : "eax", "memory", "cc");
 }
 
-/* This assumes a user_tf looks like a regular kernel trapframe */
-static __inline void
-init_user_tf(struct hw_trapframe *u_tf, uint32_t entry_pt, uint32_t stack_top)
+static inline void init_user_ctx(struct user_context *ctx, uint32_t entry_pt,
+                                 uint32_t stack_top)
 {
+       struct hw_trapframe *u_tf = &ctx->tf.hw_tf;
+       ctx->type = ROS_HW_CTX;
        memset(u_tf, 0, sizeof(struct hw_trapframe));
        u_tf->tf_eip = entry_pt;
        u_tf->tf_cs = GD_UT | 3;
index 3b2d855..0601783 100644 (file)
@@ -57,9 +57,8 @@ static inline void __pop_ros_tf(struct hw_trapframe *tf, uint32_t vcoreid,
        __pop_ros_tf_regs(tf, vcpd, vcoreid, helper);
 }
 
-/* Pops an ROS kernel-provided TF, reanabling notifications at the same time.
- * A Userspace scheduler can call this when transitioning off the transition
- * stack.
+/* Pops a user context, reanabling notifications at the same time.  A Userspace
+ * scheduler can call this when transitioning off the transition stack.
  *
  * Make sure you clear the notif_pending flag, and then check the queue before
  * calling this.  If notif_pending is not clear, this will self_notify this
@@ -70,30 +69,37 @@ static inline void __pop_ros_tf(struct hw_trapframe *tf, uint32_t vcoreid,
  * notifications, and when it gets resumed it can ultimately run the new
  * context.  Enough state is saved in the running context and stack to continue
  * running. */
-static inline void pop_ros_tf(struct hw_trapframe *tf, uint32_t vcoreid)
+static inline void pop_user_ctx(struct user_context *ctx, uint32_t vcoreid)
 {
+       struct hw_trapframe *tf = &ctx->tf.hw_tf;
+       assert(ctx->type == ROS_HW_CTX);
        __pop_ros_tf(tf, vcoreid, &__pop_ros_tf_notifs);
 }
 
-/* Like the regular pop_ros_tf, but this one doesn't check or clear
+/* Like the regular pop_user_ctx, but this one doesn't check or clear
  * notif_pending. */
-static inline void pop_ros_tf_raw(struct hw_trapframe *tf, uint32_t vcoreid)
+static inline void pop_user_ctx_raw(struct user_context *ctx, uint32_t vcoreid)
 {
+       struct hw_trapframe *tf = &ctx->tf.hw_tf;
+       assert(ctx->type == ROS_HW_CTX);
        __pop_ros_tf(tf, vcoreid, &__pop_ros_tf_notifs_raw);
 }
 
-/* Save the current context/registers into the given tf, setting the pc of the
+/* Save the current context/registers into the given ctx, setting the pc of the
  * tf to the end of this function.  You only need to save that which you later
- * restore with pop_ros_tf(). */
-static inline void save_ros_tf(struct hw_trapframe *tf)
+ * restore with pop_user_ctx(). */
+static inline void save_user_ctx(struct user_context *ctx)
 {
+       struct hw_trapframe *tf = &ctx->tf.hw_tf;
+       ctx->type = ROS_HW_CTX;
        __save_ros_tf_regs(tf);
 }
 
-/* This assumes a user_tf looks like a regular kernel trapframe */
-static __inline void
-init_user_tf(struct hw_trapframe *u_tf, long entry_pt, long stack_top)
+static inline void init_user_ctx(struct user_context *ctx, uint32_t entry_pt,
+                                 uint32_t stack_top)
 {
+       struct hw_trapframe *u_tf = &ctx->tf.hw_tf;
+       ctx->type = ROS_HW_CTX;
        memset(u_tf, 0, sizeof(*u_tf));
        u_tf->gpr[30] = stack_top;
        u_tf->epc = entry_pt;
index c02b4f4..3b62dc8 100644 (file)
@@ -23,9 +23,8 @@ static inline void set_tls_desc(void *tls_desc, uint32_t vcoreid)
        __vcoreid = vcoreid;
 }
 
-/* Pops an ROS kernel-provided TF, reanabling notifications at the same time.
- * A Userspace scheduler can call this when transitioning off the transition
- * stack.
+/* Pops a user context, reanabling notifications at the same time.  A Userspace
+ * scheduler can call this when transitioning off the transition stack.
  *
  * Make sure you clear the notif_pending flag, and then check the queue before
  * calling this.  If notif_pending is not clear, this will self_notify this
@@ -36,8 +35,10 @@ static inline void set_tls_desc(void *tls_desc, uint32_t vcoreid)
  * notifications, and when it gets resumed it can ultimately run the new
  * context.  Enough state is saved in the running context and stack to continue
  * running. */
-static inline void pop_ros_tf(struct hw_trapframe *tf, uint32_t vcoreid)
+static inline void pop_user_ctx(struct user_context *ctx, uint32_t vcoreid)
 {
+       struct hw_trapframe *tf = &ctx->tf.hw_tf;
+       assert(ctx->type == ROS_HW_CTX);
        // since we're changing the stack, move stuff into regs for now
        register uint32_t _vcoreid = vcoreid;
        register struct hw_trapframe* _tf = tf;
@@ -62,10 +63,12 @@ static inline void pop_ros_tf(struct hw_trapframe *tf, uint32_t vcoreid)
        asm volatile ("mov %0, %%o0; ta 4" : : "r"(tf) : "memory");
 }
 
-/* Like the regular pop_ros_tf, but this one doesn't check or clear
+/* Like the regular pop_user_ctx, but this one doesn't check or clear
  * notif_pending.  TODO: someone from sparc should look at this. */
-static inline void pop_ros_tf_raw(struct hw_trapframe *tf, uint32_t vcoreid)
+static inline void pop_user_ctx_raw(struct user_context *ctx, uint32_t vcoreid)
 {
+       struct hw_trapframe *tf = &ctx->tf.hw_tf;
+       assert(ctx->type == ROS_HW_CTX);
        // since we're changing the stack, move stuff into regs for now
        register uint32_t _vcoreid = vcoreid;
        register struct hw_trapframe* _tf = tf;
@@ -90,20 +93,23 @@ static inline void pop_ros_tf_raw(struct hw_trapframe *tf, uint32_t vcoreid)
        asm volatile ("mov %0, %%o0; ta 4" : : "r"(tf) : "memory");
 }
 
-/* Save the current context/registers into the given tf, setting the pc of the
+/* Save the current context/registers into the given ctx, setting the pc of the
  * tf to the end of this function.  You only need to save that which you later
- * restore with pop_ros_tf(). */
-static inline void save_ros_tf(struct hw_trapframe *tf)
+ * restore with pop_user_ctx(). */
+static inline void save_user_ctx(struct user_context *ctx)
 {
+       struct hw_trapframe *tf = &ctx->tf.hw_tf;
+       ctx->type = ROS_HW_CTX;
        // just do it in the kernel.  since we need to flush windows anyway,
        // this isn't an egregious overhead.
        asm volatile ("mov %0, %%o0; ta 5" : : "r"(tf) : "o0","memory");
 }
 
-/* This assumes a user_tf looks like a regular kernel trapframe */
-static __inline void
-init_user_tf(struct hw_trapframe *u_tf, uint32_t entry_pt, uint32_t stack_top)
+static inline void init_user_ctx(struct user_context *ctx, uint32_t entry_pt,
+                                 uint32_t stack_top)
 {
+       struct hw_trapframe *u_tf = &ctx->tf.hw_tf;
+       ctx->type = ROS_HW_CTX;
        memset(u_tf, 0, sizeof(struct hw_trapframe));
        u_tf->gpr[14] = stack_top - 96;
        u_tf->pc = entry_pt;
index 0fc5562..d195cbb 100644 (file)
@@ -83,8 +83,7 @@ void run_uthread(struct uthread *uthread);
 static inline void init_uthread_ctx(struct uthread *uth, void (*entry)(void),
                                     void *stack_bottom, uint32_t size)
 {
-       init_user_tf(&uth->u_ctx.tf.hw_tf, (long)entry,
-                    (long)(stack_bottom) + size);
+       init_user_ctx(&uth->u_ctx, (long)entry, (long)(stack_bottom) + size);
 }
 
 #define uthread_set_tls_var(uth, name, val)                                    \
index 7415670..9f86ead 100644 (file)
@@ -270,7 +270,7 @@ void uthread_yield(bool save_state, void (*yield_func)(struct uthread*, void*),
        struct preempt_data *vcpd = vcpd_of(vcoreid);
        /* once we do this, we might miss a notif_pending, so we need to enter vcore
         * entry later.  Need to disable notifs so we don't get in weird loops with
-        * save_ros_tf() and pop_ros_tf(). */
+        * save_user_ctx() and pop_user_ctx(). */
        disable_notifs(vcoreid);
        /* take the current state and save it into t->utf when this pthread
         * restarts, it will continue from right after this, see yielding is false,
@@ -278,22 +278,22 @@ void uthread_yield(bool save_state, void (*yield_func)(struct uthread*, void*),
        if (save_state) {
                /* TODO: (HSS) Save silly state */
                // save_fp_state(&t->as);
-               save_ros_tf(&uthread->u_ctx.tf.hw_tf);
+               save_user_ctx(&uthread->u_ctx);
        }
-       cmb();  /* Force a reread of yielding. Technically save_ros_tf() is enough*/
+       cmb();  /* Force reread of yielding. Technically save_user_ctx() suffices*/
        /* Restart path doesn't matter if we're dying */
        if (!yielding)
                goto yield_return_path;
        yielding = FALSE; /* for when it starts back up */
        /* Signal the current state is in utf.  Need to do this only the first time
-        * through (not on the yield return path that comes after save_ros_tf) */
+        * through (not on the yield return path that comes after save_user_ctx) */
        if (save_state)
                uthread->flags |= UTHREAD_SAVED | UTHREAD_FPSAVED;
        /* Change to the transition context (both TLS and stack). */
        extern void** vcore_thread_control_blocks;
        set_tls_desc(vcore_thread_control_blocks[vcoreid], vcoreid);
        assert(current_uthread == uthread);
-       assert(in_vcore_context());     /* technically, we aren't fully in vcore context */
+       assert(in_vcore_context());     /* technically, we aren't fully in vcore ctx */
        /* After this, make sure you don't use local variables.  Also, make sure the
         * compiler doesn't use them without telling you (TODO).
         *
@@ -364,8 +364,8 @@ void __ros_mcp_syscall_blockon(struct syscall *sysc)
  * manages the TF, FP state, and related flags.
  *
  * This will adjust the thread's state, do one last check on notif_pending, and
- * pop the tf.  Note that the notif check is an optimization.  pop_ros_tf() will
- * definitely handle it, but it will take a syscall to do so later. */
+ * pop the ctx.  Note that the notif check is an optimization.  pop_user_ctx()
+ * will definitely handle it, but it will take a syscall to do so later. */
 static void __run_cur_uthread(void)
 {
        uint32_t vcoreid = vcore_id();
@@ -395,13 +395,13 @@ static void __run_cur_uthread(void)
        __vcoreid = vcoreid;    /* setting the uthread's TLS var */
        /* Depending on where it was saved, we pop differently.  This assumes that
         * if a uthread was not saved, that it was running in the vcpd uthread_ctx.
-        * There should never be a time that the TF is unsaved and not in the notif
-        * TF (or about to be in that TF). */
+        * There should never be a time that the ctx is unsaved and not in the VCPD
+        * uthread ctx slot (or about to be in that ctx slot). */
        if (uthread->flags & UTHREAD_SAVED) {
                uthread->flags &= ~UTHREAD_SAVED;
-               pop_ros_tf(&uthread->u_ctx.tf.hw_tf, vcoreid);
+               pop_user_ctx(&uthread->u_ctx, vcoreid);
        } else  {
-               pop_ros_tf(&vcpd->uthread_ctx.tf.hw_tf, vcoreid);
+               pop_user_ctx(&vcpd->uthread_ctx, vcoreid);
        }
 }
 
@@ -470,8 +470,7 @@ static void __run_current_uthread_raw(void)
        current_uthread->flags &= ~UTHREAD_SAVED;
        set_tls_desc(current_uthread->tls_desc, vcoreid);
        __vcoreid = vcoreid;    /* setting the uthread's TLS var */
-       /* Pop the user trap frame */
-       pop_ros_tf_raw(&vcpd->uthread_ctx.tf.hw_tf, vcoreid);
+       pop_user_ctx_raw(&vcpd->uthread_ctx, vcoreid);
        assert(0);
 }
 
index 6d938a0..f7fa6e3 100644 (file)
@@ -432,8 +432,8 @@ int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
        /* Set the u_tf to start up in __pthread_run, which will call the real
         * start_routine and pass it the arg.  Note those aren't set until later in
         * pthread_create(). */
-       init_user_tf(&pthread->uthread.u_ctx.tf.hw_tf, (long)&__pthread_run,
-                    (long)(pthread->stacktop));
+       init_user_ctx(&pthread->uthread.u_ctx, (long)&__pthread_run,
+                     (long)(pthread->stacktop));
        pthread->start_routine = start_routine;
        pthread->arg = arg;
        /* Initialize the uthread */