1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
|
.TH QIO 10.2
.SH NAME
qio: qget, qdiscard, qconsume, qpass, qproduce, qcopy, qopen, qbread, qread, qbwrite, qwrite, qiwrite, qfree, qclose, qhangup, qreopen, qlen, qwindow, qcanread, qsetlimit, qnoblock, qflush, qfull \- queued I/O for devices
.SH SYNOPSIS
.ta \w'\fLQueue* 'u
.B
Queue* qopen(int limit,int msg, void (*kick)(void*),void *arg)
.PP
.B
void qhangup(Queue *q, char *reason)
.PP
.B
void qclose(Queue *q)
.PP
.B
void qreopen(Queue *q)
.PP
.B
void qfree(Queue *q)
.PP
.B
long qbwrite(Queue *q, Block *b)
.PP
.B
long qwrite(Queue *q, void *buf, int len)
.PP
.B
int qpass(Queue *q, Block *b)
.PP
.B
int qpassnolim(Queue *q, Block *b)
.PP
.B
int qproduce(Queue *q, void *buf, int len)
.PP
.B
int qiwrite(Queue *q, void *buf, int len)
.PP
.B
Block* qbread(Queue *q, int len)
.PP
.B
long qread(Queue *q, void *buf, int len)
.PP
.B
Block* qcopy(Queue *q, int len, ulong offset)
.PP
.B
Block* qget(Queue *q)
.PP
.B
int qconsume(Queue *q, void *buf, int len)
.PP
.B
int qdiscard(Queue *q, int len)
.PP
.B
void qflush(Queue *q)
.PP
.B
int qlen(Queue *q)
.PP
.B
int qwindow(Queue *q)
.PP
.B
int qcanread(Queue *q)
.PP
.B
void qsetlimit(Queue *q, int limit)
.PP
.B
void qnoblock(Queue *q, int nonblock)
.PP
.B
int qfull(Queue *q);
.SH DESCRIPTION
This suite of functions provides serial data buffering for device drivers.
Data is stored in a
.B Queue
structure as a sequence of variable-sized
.BR Blocks ;
see
.IR allocb (10.2).
.PP
.I Qopen
initialises and returns a pointer to a new
.BR Queue ,
configuring it according to the following parameters:
.TF limit
.PD
.TP
.I limit
Set the queue limit (high water mark) in bytes.
.TP
.I msg
Set message mode if non-zero; otherwise, stream mode (discussed below).
.TP
.I kick
Optional flow-control function called by
.I qbread
to restart writers, and by
.I qbwrite
(also
.IR qiwrite )
to restart readers.
.TP
.I arg
Argument to pass to
.I kick
.PP
.I Qhangup
marks
.I q
as `hung up'
for the given
.IR reason
.RB ( Ehungup
by default).
Subsequent attempts to write to the queue raise an
.IR error (10.2).
.I Qhangup
does not flush the queue: subsequent read requests are
handled normally until the queue empties.
.I Qread
and the other functions then return their conventional values
for a hungup stream: 0, -1 or a null pointer, depending on the function.
After a few such attempts by any process, an
.IR error (10.2)
is raised (typically
.BR Ehungup )
on each subsequent read.
.PP
If queued data is left unread, and not flushed by
.I qflush
or
.IR qclose ,
the data will again be readable following a subsequent
.IR qreopen .
.PP
.I Qclose
also marks a given
.I q
as `hung up',
but removes and frees any queued data Blocks.
.I Qclose
ignores calls when
.I q
is null.
.PP
.I Qreopen
makes a closed or hung up queue available for use again.
The queue's data limit is reset to the
.I limit
value given when the queue was first created by
.IR qopen ,
cancelling the effect of any previous call to
.IR qsetlimit .
.PP
.I Qfree
closes
.I q
with
.I qclose
and frees it.
The caller must ensure that no references remain;
these functions do not keep a reference count.
.SS "Flow control"
The queue I/O routines provide a flow control mechanism to coordinate producers and consumers.
Each queue has a limit on the number of bytes queued, its `high water mark',
initially set when the queue is created, but adjustable by
.IR qsetlimit ,
below.
The low water mark is not set explicitly:
it is always half the current queue limit.
When the high water mark is exceeded, writes normally block until a reader drains the
queue below its low water mark; the writer is then allowed to proceed.
Conversely, readers normally block when the queue is empty, until a writer
arrives with data, or the queue is closed.
.PP
A queue can be given a
.I kick
function when the queue is created by
.IR qopen .
The function is invoked by
.IR qread
and
.IR qbread ,
to prod an output routine when the queue falls below the low-water mark, and by
.IR qwrite ,
.IR qbwrite
and
.IR qiwrite ,
to notify a reader that a queue is no longer empty.
Because
.I kick
is called from the reading (or writing) process, or an interrupt handler, it
must not block.
.PP
Interrupt handlers must not
.IR sleep (10.2),
and are therefore restricted to using only the non-blocking functions described below.
.SS "Stream mode and message mode"
In stream mode,
no read will return more than one
block
of data, but
a read can split a block that contains more data than requested, leaving the remainder
in a new block at the front of the Queue.
Writes of more than the maximum
.B Block
size (currently 128k bytes)
are split into as many Blocks as required, each written separately to the queue,
in order, but with possible flow-control between them.
The queue is locked meanwhile, however, so that data from other writers is not intermingled.
.PP
In message mode, by contrast, a read will return at most
one block's worth of data, but the remainder of a partially-read block will be discarded,
not returned to the queue.
If a write count exceeds the maximum
.B Block
size, the excess data is discarded:
at most a single block can be queued.
.PP
The mode of the queue should be taken into account in the descriptions below
of the following functions:
.IR qwrite ,
.IR qiwrite ,
.IR qbread
and
.IR qconsume .
No other functions are aware of the distinction.
.SS "Write operations (flow controlled)"
.I Qwrite
copies
.I len
bytes of data from
.I buf
into one or more
.B Blocks
which it places on the
.IR q .
.I Qwrite
always returns
.IR len .
It can implement message mode.
.PP
.I Qbwrite
places the single Block
.I b
on the tail of
.IR q ,
waking any sleeping reader.
If the queue is full, the
writing process blocks until a reader
has reduced the queued data to
the low-water mark;
if the queue is non-blocking
(see
.I qnoblock
below),
the data is discarded without notice.
.I Qbwrite
normally returns
.IR len ,
but raises an
.IR error (10.2)
if the queue is closed (see
.I qhangup
and
.IR qclose ).
The block
.I b
is always freed.
Note that
.I b
can be empty (zero-length), to punctuate the data in a queue.
.I Qbwrite
cannot handle a list of Blocks;
.I qpass
must be used instead.
.SS Non-blocking writes
.PP
.I Qproduce
returns -1immediately if
.I q
is full.
Otherwise, it queues
.I len
bytes of data from
.I buf
in a single
.B Block
on
.I q
and returns the number of bytes written.
.PP
.I Qpass
attempts to place the list of Blocks headed by
.I b
on
.IR q ,
returning the number of bytes written if successful.
If
.I q
was full, it
frees the Block list
.I b
and returns -1.
.PP
.I Qpassnolim
puts the Block list
.I b
on
.I q
regardless of flow control; it returns the number of bytes in the list
.IR b .
.PP
.I Qiwrite
is a variant of
.I qwrite
used exclusively by the kernel print function,
to allow printing by interrupt handlers;
.I qiwrite
could be used with care by other routines, but
.IR qproduce
is preferable.
.I Qiwrite
writes the
.I len
bytes of data at
.I buf
into the
.I q
without regard to flow control;
the writer never blocks.
The queue is assumed to be open.
.I Qiwrite
always returns
.IR len .
It can implement message mode.
.SS "Read operations (flow controlled)"
.I Qbread
blocks until data arrives on
.IR q ,
then
returns the first
.BR Block ;
it limits the data returned
to
.I len
bytes (in the manner depending on the mode of
.IR q ).
It returns a null pointer if the queue has hung up.
.PP
.I Qread
reads a Block of up to
.I len
bytes from
.I q
using
.IR qbread ,
and copies the data in the Block into
.IR buf ,
then frees the Block and returns
the number of bytes read.
.I Qread
returns 0 on end of file or error (hangup).
It can implement message mode.
.PP
.I Qcopy
returns a Block with a copy of data from the queue (the data remains on the queue).
The copy begins
.I offset
bytes into the queue's data and proceeds until
.I len
bytes have been copied or no more data remains.
The Block's read and write pointers delimit the data copied into it.
.I Qcopy
can be used by a reliable transport protocol to copy a packet for transmission,
leaving the data queued for possible retransmission, if unacknowledged.
.SS Non-blocking reads
.PP
.I Qconsume
returns -1 immediately if
.I q
is empty.
Otherwise, it
copies up to
.I len
bytes from the first
.B Block
on the queue into
.IR buf ,
returning the number of bytes copied.
It can implement message mode.
.PP
.I Qget
returns a null pointer immediately if
.I q
is empty or closed.
Otherwise, it
returns the first
.B Block
on the queue.
.SS "Discard and flush"
.I Qdiscard
removes the first
.I len
data bytes from
.IR q ;
it returns the number of bytes actually discarded, in case
the queue is shorter than
.IR len .
If the queue drains below the low-water mark,
.I qdiscard
wakes any sleeping writers.
Since it does not block,
.I qdiscard
can safely be called from interrupt handlers.
It is useful in transport protocol drivers to remove data from the queue
once acknowledged.
.PP
.I Qflush
discards all data waiting on
.IR q ,
waking any waiting writer.
.SS "Queue status"
The following functions return a Queue's status.
Note that between a call to one of these functions and another operation,
the state can change if a driver allows concurrent access by
either another process or an interrupt handler.
.PP
.I Qlen
returns the number of bytes queued on
.IR q .
.PP
.I Qwindow
returns the number of bytes that can be written before reaching the queue's high-water mark.
A return of 0 means that a write operation will certainly block;
a non-zero return gives no guarantees (see
.IR qfull ,
below).
.PP
.I Qcanread
returns 1 if any data queued is queued. A subsequent read operation will not block.
.PP
.I Qfull
returns non-zero if
.I q
is flow-controlled and a write would block or a non-blocking write would return an error.
(Note that the implementation allows
.I qwindow
to return non-zero yet
.I qfull
to return true.)
.SS "Queue control"
.I Qsetlimit
sets the high water mark for the queue to
.IR limit .
Note that
.I qopen
saves the initial queue limit.
If the queue is closed and reopened (by
.IR qreopen )
that initial limit is restored.
.PP
.I Qnoblock
sets or resets non-blocking mode.
If
.I nonblock
is non-zero,
the queue becomes non-blocking, and
data written to a queue beyond its high water mark is discarded
by calls that would otherwise block.
.SH SOURCE
.B /os/port/qio.c
.br
.B /emu/port/qio.c
.SH SEE ALSO
.IR allocb (10.2),
.IR ref (10.2)
|