|
|
1.1 root 1: #define _DDI_DKI 1
2: #define _SYSV4 1
3:
4: /*
5: * This file contains various miscellaneous STREAMS library functions that
6: * have been moved into a file on their own. Mostly, this stuff deals with
7: * scheduling STREAMS service procedures, running STREAMS service procedures,
8: * invoking bufcall ()/esbbcall () events, and similar stuff.
9: */
10:
11: #include <common/ccompat.h>
12: #include <kernel/strmlib.h>
13: #include <sys/debug.h>
14: #include <sys/types.h>
15: #include <sys/kmem.h>
16: #include <sys/stream.h>
17:
18:
19: /*
20: * Allocate and initialize a schedule.
21: *
22: * Callable only from base level. This function may sleep.
23: */
24:
25: #if __USE_PROTO__
26: ssched_t * (QSCHED_ALLOC) (void)
27: #else
28: ssched_t *
29: QSCHED_ALLOC __ARGS (())
30: #endif
31: {
32: ssched_t * sched;
33:
34: if ((sched = (ssched_t *) kmem_alloc (sizeof (* sched), KM_SLEEP))
35: != NULL) {
36:
37: if (SCHLOCK_INIT (sched, KM_SLEEP) == 0) {
38:
39: kmem_free (sched, sizeof (* sched));
40: return NULL;
41: }
42:
43: sched->ss_head = sched->ss_tail = NULL;
44: }
45:
46: return sched;
47: }
48:
49:
50: /*
51: * Destroy a schedule.
52: *
53: * The schedule most not have any STREAMS queues threaded on it.
54: */
55:
56: #if __USE_PROTO__
57: void (QSCHED_FREE) (ssched_t * sched)
58: #else
59: void
60: QSCHED_FREE __ARGS ((sched))
61: ssched_t * sched;
62: #endif
63: {
64: ASSERT (sched != NULL);
65: ASSERT (sched->ss_head == NULL && sched->ss_tail == NULL);
66:
67: SCHLOCK_DESTROY (sched);
68:
69: kmem_free (sched, sizeof (* sched));
70: }
71:
72:
73: /*
74: * Remove a queue from a schedule. This function is available for modules and
75: * drivers to call. Note that STREAMS itself will only call this function at
76: * queue destruction time.
77: */
78:
79: #if __USE_PROTO__
80: void (QSCHED_UNSCHEDULE) (queue_t * q, ssched_t * sched)
81: #else
82: void
83: QSCHED_UNSCHEDULE __ARGS ((q, sched))
84: queue_t * q;
85: ssched_t * sched;
86: #endif
87: {
88: pl_t lock_pl;
89: queue_t * scan;
90: queue_t * prev;
91:
92: ASSERT (sched != NULL);
93: QUEUE_TRACE (q, "QUEUE_UNSCHEDULE");
94:
95: lock_pl = SCHLOCK_LOCK (sched, "QUEUE_UNSCHEDULE");
96:
97: for (scan = sched->ss_head, prev = NULL ; scan != NULL ;
98: scan = (prev = scan)->q_link) {
99:
100: if (scan == q) {
101:
102: if (prev == NULL)
103: sched->ss_head = q->q_next;
104: else
105: prev->q_link = q->q_next;
106:
107: if (sched->ss_tail == q)
108: sched->ss_tail = prev;
109: break;
110: }
111: }
112:
113: SCHLOCK_UNLOCK (str_mem->sm_sched, lock_pl);
114: }
115:
116:
117: /*
118: * Dequeue and return the first stream in the schedule.
119: */
120:
121: #if __USE_PROTO__
122: queue_t * (QSCHED_GETFIRST) (ssched_t * sched)
123: #else
124: queue_t *
125: QSCHED_GETFIRST __ARGS ((sched))
126: ssched_t * sched;
127: #endif
128: {
129: pl_t prev_pl;
130: queue_t * q;
131:
132: ASSERT (sched != NULL);
133:
134: prev_pl = SCHLOCK_LOCK (sched, "QSCHED_GETFIRST");
135:
136: if ((q = sched->ss_head) != NULL) {
137:
138: if ((sched->ss_head = q->q_link) == NULL)
139: sched->ss_tail = NULL;
140: }
141:
142: SCHLOCK_UNLOCK (sched, prev_pl);
143:
144: return q;
145: }
146:
147:
148: /*
149: * Add a queue to a schedule. This function is available for use by modules
150: * and drivers. It is callable from base or interrupt level and does not
151: * sleep.
152: *
153: * The return value is 0 for success, and -1 if it is unable to schedule the
154: * queue.
155: */
156:
157: #if __USE_PROTO__
158: int (QSCHED_SCHEDULE) (queue_t * q, ssched_t * sched)
159: #else
160: int
161: QSCHED_SCHEDULE __ARGS ((q, sched))
162: queue_t * q;
163: ssched_t * sched;
164: #endif
165: {
166: pl_t lock_pl;
167:
168: QUEUE_TRACE (q, "QSCHED_SCHEDULE");
169:
170: /*
171: * Link this queue into the tail of the scheduling
172: * list after locking the scheduling list.
173: */
174:
175: lock_pl = SCHLOCK_LOCK (sched, "QSCHED_SCHEDULE");
176:
177: q->q_link = NULL;
178:
179: if (sched->ss_tail == NULL)
180: sched->ss_head = q;
181: else
182: sched->ss_tail->q_link = q;
183:
184: sched->ss_tail = q;
185:
186: SCHLOCK_UNLOCK (str_mem->sm_sched, lock_pl);
187:
188:
189: /*
190: * Under this implementation, it is not really possible for a request
191: * to fail, but because we are using STREAMS internal data here, we
192: * allow for a portable implementation that may fail.
193: */
194:
195: return 0;
196: }
197:
198:
199: /*
200: * Code for checking which bufcall events to call, in a function by itself to
201: * keep things clear and maintainable.
202: *
203: * We take a snapshot of the available memory, then loop over the bufcall ()/
204: * esbbcall () lists and give away memory until our notion of what the memory
205: * pool size ought to be drops to a level where there are cells we think we
206: * should hold off enabling... if we make that decision, we mark things so
207: * that next time through here we look again, then we exit.
208: */
209:
210: #if __USE_PROTO__
211: void (RUN_BUFCALLS) (void)
212: #else
213: void
214: RUN_BUFCALLS __ARGS (())
215: #endif
216: {
217: size_t mem_level;
218: pl_t prev_pl;
219: sevent_t * runlist;
220: sevent_t * seventp;
221: int i;
222:
223: /*
224: * Take a snapshot of the amount of STREAMS memory that is in use, so
225: * we can parcel it out to the bufcall routines. We clear the bufcall
226: * defer flag now so that if STREAMS memory is made available after
227: * our snapshot we will be run again.
228: */
229:
230: ATOMIC_STORE_UCHAR (ddi_global_data ()->dg_run_bufcalls, 0);
231:
232: mem_level = str_mem->sm_used;
233: runlist = NULL;
234:
235: for (i = N_PRI_LEVELS ; i -- > 0 ; ) {
236: selist_t * elistp;
237: unsigned long max = str_mem->sm_max [i];
238:
239: elistp = & str_mem->sm_bcevents [i];
240:
241: prev_pl = SELIST_LOCK (elistp);
242:
243: while ((seventp = elistp->sl_head) != NULL) {
244: /*
245: * If the amount of memory requested in this cell is
246: * enough to push the memory level over the top for
247: * this allocation priority, then give up.
248: */
249:
250: if ((mem_level += seventp->se_size) > max)
251: goto give_up;
252:
253: /*
254: * Dequeue the event from the event list and add it to
255: * our work list.
256: */
257:
258: elistp->sl_head = seventp->se_next;
259:
260: seventp->se_next = runlist;
261: runlist = seventp;
262: }
263:
264: #if _FIFO_BUFCALL
265: /*
266: * If we have drained all the events in this cell...
267: */
268:
269: elistp->sl_tail = NULL;
270: #endif
271:
272: give_up:
273: SELIST_UNLOCK (elistp, prev_pl);
274:
275: if (seventp != NULL)
276: break;
277: }
278:
279:
280: /*
281: * Now use 'i' as a boolean to remember whether we have any
282: * outstanding event cells we though it better not to run.
283: */
284:
285: i = seventp != NULL;
286:
287:
288: /*
289: * Now walk over our little work list...
290: */
291:
292: while (runlist != NULL) {
293:
294: seventp = runlist;
295: runlist = seventp->se_next;
296:
297: prev_pl = splstr ();
298:
299: (* seventp->se_func) (seventp->se_arg);
300:
301: (void) splx (prev_pl);
302:
303: kmem_free (seventp, sizeof (* seventp));
304: }
305:
306:
307: /*
308: * If we found anything worth running, try again after we have given
309: * the bufcall functions a chance to claim the memory that was free
310: * first time through. We queue another defer request so that we give
311: * a chance to other deferred routines.
312: */
313:
314: if (i)
315: SCHEDULE_BUFCALLS ();
316: }
317:
318:
319: /*
320: * Streams scheduling routine, invoked before return to user level. Runs any
321: * service procedures that have been enabled, calls bufcall ()/esbbcall ()
322: * events if memory is sitting around, and also wakes up processes sleeping
323: * in kmem_alloc ()/kmem_zalloc () for memory to become available.
324: */
325:
326: #if __USE_PROTO__
327: void (RUN_STREAMS) (void)
328: #else
329: void
330: RUN_STREAMS __ARGS (())
331: #endif
332: {
333: queue_t * q;
334:
335: /*
336: * Before we start running through the queues that we need to service,
337: * turn off the "deferred" flag for this routine to avoid race
338: * conditions.
339: */
340:
341: ATOMIC_STORE_UCHAR (ddi_global_data ()->dg_run_strsched, 0);
342:
343: while ((q = QSCHED_GETFIRST (str_mem->sm_sched)) != NULL) {
344: pl_t prev_pl;
345:
346: /*
347: * Before we run the service procedure, we must ensure
348: * that we are not going to re-enter the service
349: * routine. After that, we can turn off the 'enabled'
350: * flag.
351: */
352:
353: prev_pl = QFREEZE_TRACE (q, "STREAMS_SCHEDULER");
354:
355: if ((q->q_flag & QSRVACTIVE) != 0) {
356: /*
357: * Put the queue back and look for something
358: * else to do.
359: */
360:
361: QUNFREEZE_TRACE (q, prev_pl);
362: QSCHED_SCHEDULE (q, str_mem->sm_sched);
363:
364: continue;
365: }
366:
367: q->q_flag = (q->q_flag & ~ QENAB) | QSRVACTIVE;
368:
369: if ((q->q_flag & QPROCSOFF) != 0) {
370: /*
371: * The service procedure of this queue has
372: * been disabled, so we leave things alone.
373: */
374:
375: goto srvdone;
376: }
377:
378: q->q_active ++;
379:
380: QUNFREEZE_TRACE (q, prev_pl);
381:
382:
383: (* q->q_qinfo->qi_srvp) (q);
384:
385:
386: prev_pl = QFREEZE_TRACE (q, "STREAMS_SCHEDULER");
387:
388: q->q_flag &= ~ QSRVACTIVE;
389:
390: if (-- q->q_active == 0 && (q->q_flag & QPROCSOFF) != 0) {
391: /*
392: * Wake up the qprocoffs () procedure waiting
393: * for us to go exit.
394: */
395:
396: (void) LOCK (str_mem->sm_proc_lock, plstr);
397: SV_BROADCAST (str_mem->sm_proc_sv, 0);
398: UNLOCK (str_mem->sm_proc_lock, plstr);
399: }
400: srvdone:
401: QUNFREEZE_TRACE (q, prev_pl);
402: }
403: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.