sched.c - vx32 - Local 9vx git repository for patches.
(HTM) git clone git://r-36.net/vx32
(DIR) Log
(DIR) Files
(DIR) Refs
---
sched.c (5116B)
---
1 /*
2 * Plan 9 VX scheduler
3 *
4 * Allocate new processors (host threads) for each kproc.
5 * Everyone else gets scheduled via the regular scheduler
6 * on cpu0. It is tempting to use multiple threads and
7 * multiple vx32 instances on an SMP to get parallelism of
8 * user execution, but we can't: there's only one user address space.
9 * (The kprocs are okay because they never touch user memory.)
10 */
11
12 #define WANT_M
13
14 #include "u.h"
15 #include <pthread.h>
16 #include <sys/poll.h>
17 #include <sched.h>
18 #include "lib.h"
19 #include "mem.h"
20 #include "dat.h"
21 #include "fns.h"
22 #include "error.h"
23 #include "trace.h"
24
25 /*
26 * The cpu0 scheduler calls idlehands when there is
27 * nothing left on the main runqueue (runproc
28 * is returning nil). Instead of chewing up the
29 * host CPU spinning, we go to sleep using pthreads,
30 * but then if some other kproc readies a normal
31 * proc, it needs to call noidlehands to kick cpu0.
32 */
33 static int idlewakeup;
34 static Psleep idling;
35
36 void
37 idlehands(void)
38 {
39 int nbad;
40
41 plock(&idling);
42 nbad = 0;
43 while(!idlewakeup){
44 if(traceprocs)
45 iprint("cpu%d: idlehands\n", m->machno);
46 psleep(&idling);
47 if(traceprocs)
48 iprint("cpu%d: busy hands\n", m->machno);
49 if(!idlewakeup && ++nbad%1000 == 0)
50 iprint("idlehands spurious wakeup\n");
51 }
52 idlewakeup = 0;
53 if(traceprocs)
54 iprint("cpu%d: idlehands returning\n", m->machno);
55 punlock(&idling);
56 }
57
58 void
59 noidlehands(void)
60 {
61 if(m->machno == 0)
62 return;
63 plock(&idling);
64 idlewakeup = 1;
65 pwakeup(&idling);
66 punlock(&idling);
67 }
68
69 /*
70 * Special run queue for kprocs.
71 */
72 static Schedq kprocq;
73 static int nrunproc;
74 static Psleep run;
75
76 /*
77 * Ready the proc p.
78 * If it's a normal proc, it goes to the normal scheduler.
79 * Otherwise it gets put on the kproc run queue, and
80 * maybe a new "cpu" gets forked to run the kproc.
81 */
82 void
83 ready(Proc *p)
84 {
85 if(p->kp == 0){
86 _ready(p);
87 noidlehands(); /* kick cpu0 if it is sleeping */
88 return;
89 }
90 plock(&run);
91 lock(&kprocq.lk); /* redundant but fine */
92 p->state = Ready;
93 p->rnext = 0;
94 if(kprocq.tail)
95 kprocq.tail->rnext = p;
96 else
97 kprocq.head = p;
98 kprocq.tail = p;
99 /*
100 * If there are more kprocs on the queue
101 * than there are cpus waiting to run kprocs,
102 * kick off a new one.
103 */
104 kprocq.n++;
105 if(kprocq.n > nrunproc){
106 if(traceprocs)
107 iprint("create new cpu: kprocq.n=%d nrunproc=%d\n", kprocq.n, nrunproc);
108 nrunproc++;
109 newmach();
110 }
111 if(traceprocs)
112 iprint("cpu%d: ready %ld %s; wakeup kproc cpus\n", m->machno, p->pid, p->text);
113 pwakeup(&run);
114 unlock(&kprocq.lk);
115 punlock(&run);
116 }
117
118 /*
119 * Get a new proc to run.
120 * If we're running on cpu0, use the normal scheduler
121 * to get a normal proc.
122 */
123 Proc*
124 runproc(void)
125 {
126 int nbad;
127 Proc *p;
128
129 if(m->machno == 0)
130 return _runproc();
131
132 nbad = 0;
133 plock(&run);
134 lock(&kprocq.lk); /* redundant but fine */
135 if(m->new){
136 nrunproc--;
137 m->new = 0;
138 }
139 while((p = kprocq.head) == nil){
140 nrunproc++;
141 unlock(&kprocq.lk);
142 if(traceprocs)
143 iprint("cpu%d: runproc psleep %d %d\n", m->machno, kprocq.n, nrunproc);
144 psleep(&run);
145 lock(&kprocq.lk);
146 if(kprocq.head == nil && ++nbad%1000 == 0)
147 iprint("cpu%d: runproc spurious wakeup\n", m->machno);
148 if(traceprocs)
149 iprint("cpu%d: runproc awake\n", m->machno);
150 nrunproc--;
151 }
152 kprocq.head = p->rnext;
153 if(kprocq.head == 0)
154 kprocq.tail = nil;
155 kprocq.n--;
156 if(traceprocs)
157 iprint("cpu%d: runproc %ld %s [%d %d]\n",
158 m->machno, p->pid, p->text, kprocq.n, nrunproc);
159 unlock(&kprocq.lk);
160 punlock(&run);
161 /*
162 * To avoid the "double sleep" bug. See:
163 * http://9fans.net/archive/2010/06/71
164 */
165 while (p->mach)
166 sched_yield();
167 return p;
168 }
169
170 /*
171 * Limit CPU usage going to sleep while holding the run lock
172 */
173 void
174 plimitproc(void *v)
175 {
176 int lim;
177 uint sleeping, working;
178
179 lim = *((int*)v);
180 sleeping = 100000 * (100 - lim) / 100;
181 working = 100000 * lim / 100;
182
183 for(;;){
184 usleep(working);
185 plock(&run);
186 usleep(sleeping);
187 punlock(&run);
188 }
189 }
190
191 /*
192 * Host OS process sleep and wakeup.
193 */
194 static pthread_mutex_t initmutex = PTHREAD_MUTEX_INITIALIZER;
195
196 struct Pwaiter
197 {
198 pthread_cond_t cond;
199 Pwaiter *next;
200 int awake;
201 };
202
203 void
204 __plock(Psleep *p)
205 {
206 int r;
207
208 if(!p->init){
209 if((r = pthread_mutex_lock(&initmutex)) != 0)
210 panic("pthread_mutex_lock initmutex: %d", r);
211 if(!p->init){
212 p->init = 1;
213 pthread_mutex_init(&p->mutex, nil);
214 }
215 if((r = pthread_mutex_unlock(&initmutex)) != 0)
216 panic("pthread_mutex_unlock initmutex: %d", r);
217 }
218 if((r = pthread_mutex_lock(&p->mutex)) != 0)
219 panic("pthread_mutex_lock: %d", r);
220 }
221
222 void
223 __punlock(Psleep *p)
224 {
225 int r;
226
227 if((r = pthread_mutex_unlock(&p->mutex)) != 0)
228 panic("pthread_mutex_unlock: %d", r);
229 }
230
231 void
232 __psleep(Psleep *p)
233 {
234 int r;
235 Pwaiter w;
236
237 memset(&w, 0, sizeof w);
238 pthread_cond_init(&w.cond, nil);
239 w.next = p->waiter;
240 p->waiter = &w;
241 while(!w.awake)
242 if((r = pthread_cond_wait(&w.cond, &p->mutex)) != 0)
243 panic("pthread_cond_wait: %d", r);
244 pthread_cond_destroy(&w.cond);
245 }
246
247 void
248 __pwakeup(Psleep *p)
249 {
250 int r;
251 Pwaiter *w;
252
253 w = p->waiter;
254 if(w){
255 p->waiter = w->next;
256 w->awake = 1;
257 if((r = pthread_cond_signal(&w->cond)) != 0)
258 panic("pthread_cond_signal: %d", r);
259 }
260 }
261