Major libcaps work to support userland threading. Stage 1/2.
[dragonfly.git] / lib / libcaps / uthread.c
1 /*
2  * Copyright (c) 2003 Galen Sampson <galen_sampson@yahoo.com>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $DragonFly: src/lib/libcaps/uthread.c,v 1.1 2003/12/04 22:06:19 dillon Exp $
27  */
28
29 /*
30  * Each cpu in a system has its own self-contained light weight kernel
31  * thread scheduler, which means that generally speaking we only need
32  * to use a critical section to avoid problems.  Foreign thread 
33  * scheduling is queued via (async) IPIs.
34  *
35  * NOTE: on UP machines smp_active is defined to be 0.  On SMP machines
36  * smp_active is 0 prior to SMP activation, then it is 1.  The LWKT module
37  * uses smp_active to optimize UP builds and to avoid sending IPIs during
38  * early boot (primarily interrupt and network thread initialization).
39  */
40
41 #include "defs.h"
42
43 void cpu_lwkt_switch(thread_t);
44
45 /*
46  * system message port for the system call interface
47  */
48 lwkt_port_t             sysport;
49
50 static void
51 lwkt_idleloop(void *dummy)
52 {
53     for (;;) {
54         /*
55          * if the idle thread and the main thread are the only remaining
56          * threads then switch to single threaded mode.  Accomplish this
57          * by switching to the main thread.  XXX
58          */
59         globaldata_t gd = mycpu;
60         if (gd->gd_num_threads == 0) {  /* XXX not working yet */
61             lwkt_schedule(&main_td);
62         }
63         lwkt_switch();
64     }
65 }
66
67 /*
68  * Userland override of lwkt_init_thread. The only difference is
69  * the manipulation of gd->gd_num_threads;
70  */
71 static void
72 lwkt_init_thread_remote(void *arg)
73
74     thread_t td = arg;
75      
76     TAILQ_INSERT_TAIL(&td->td_gd->gd_tdallq, td, td_allq);
77 }
78
79 void
80 lwkt_init_thread(thread_t td, void *stack, int flags, struct globaldata *gd)
81 {
82     bzero(td, sizeof(struct thread));
83     td->td_kstack = stack;
84     td->td_flags |= flags;
85     td->td_gd = gd;
86     td->td_pri = TDPRI_KERN_DAEMON + TDPRI_CRIT;
87     lwkt_initport(&td->td_msgport, td);
88     cpu_init_thread(td);
89     if (smp_active == 0 || gd == mycpu) {
90         crit_enter();
91         TAILQ_INSERT_TAIL(&gd->gd_tdallq, td, td_allq);
92         gd->gd_num_threads++;       /* Userland specific */
93         crit_exit();
94     } else {
95         lwkt_send_ipiq(gd->gd_cpuid, lwkt_init_thread_remote, td);
96     }
97 }
98
99 /*
100  * Userland override of lwkt_exit. The only difference is
101  * the manipulation of gd->gd_num_threads;
102  */
103 void
104 lwkt_exit(void)
105 {
106     thread_t td = curthread;
107
108     if (td->td_flags & TDF_VERBOSE)
109         printf("kthread %p %s has exited\n", td, td->td_comm);
110     crit_enter();
111     lwkt_deschedule_self();
112     ++mycpu->gd_tdfreecount;
113     --mycpu->gd_num_threads;        /* Userland specific */
114     TAILQ_INSERT_TAIL(&mycpu->gd_tdfreeq, td, td_threadq);
115     cpu_thread_exit();
116 }
117
118 /*
119  * Userland override of lwkt_gdinit.  Called from mi_gdinit().
120  */
121 void
122 lwkt_gdinit(struct globaldata *gd)
123 {
124     /* Kernel Version of lwkt_gdinit() */
125     int i;
126
127     for (i = 0; i < sizeof(gd->gd_tdrunq)/sizeof(gd->gd_tdrunq[0]); ++i)
128         TAILQ_INIT(&gd->gd_tdrunq[i]);
129     gd->gd_runqmask = 0;
130     gd->gd_curthread = &gd->gd_idlethread;
131     TAILQ_INIT(&gd->gd_tdallq);
132
133     /* Set up this cpu's idle thread */
134     lwkt_init_thread(&gd->gd_idlethread, libcaps_alloc_stack(THREAD_STACK), 0, gd);
135     cpu_set_thread_handler(&gd->gd_idlethread, lwkt_exit, lwkt_idleloop, NULL);
136
137     /*
138      * lwkt_init_thread added threads to gd->gd_tdallq and incrementented
139      * gd->gd_num_threads accordingly.  Reset the count to zero here.
140      * The reason gd_num_threads exists is so a check can be performed
141      * to see if there are any non bookkeeping threads running on this
142      * virtual cpu.  We have created some threads for bookkeeping here that
143      * shouldn't be counted. Resetting the count to 0 allows the test
144      * if(mycpu->gd_num_threads == 0) to correctly test if there are any
145      * non-bookeeping threads running on this virtual cpu.
146      */
147     gd->gd_num_threads = 0;
148 }
149
150 /*
151  * Start threading.
152  */
153 void
154 lwkt_start_threading(thread_t td)
155 {
156     lwkt_switch();
157 }