pub struct StreamingProcessSlice<'a> { /* private fields */ }
Expand description
A wrapper around a WriteableProcessSlice
for streaming data from the
kernel to a userspace process.
Applications like ADC sampling or network stacks require the kernel to
provide a process with a continuous, lossless stream of data from a source
that is not rate-controlled by the process. This wrapper implements the
kernel-side of a simple protocol to achieve this goal, without requiring
kernel-side buffering and by utilizing the atomic swap semantics of Tock’s
allow
system call. The protocol is versioned; the semantics for version 0
are as follows:
-
To receive a data stream from the kernel, a userspace process allocates two buffers.
-
The first buffer is prepared according to the format below. The
flags
field’s version bits are set to0
. The process clears theexceeded
flag. It may set or clear thehalt
flag. All reserved flags must be set to0
. Finally, theoffset
bytes (interpreted as a u32 value in native endianness) are set to0
. -
The process
allow
s this buffer to a kernel driver. -
The kernel driver writes incoming data starting at the
data
field +offset
bytes. After each write, the kernel incrementsoffset
by the number of bytes written.For each chunk written to the buffer (where a chunk is an application-defined construct, such as a network packet), the kernel only increments
offset
if the full chunk was successfully written into the buffer. The kernel may or may not modify any data after the currentoffset
value, regardless of whether any header fields were updated. The kernel never modifies any data in the region of[data.start; data.start + offset)
.Should the write of a chunk fail because the buffer has insufficient space left, the kernel will set the
exceeded
flag bit (index 0).The
halt
flag bit as set by the process governs the kernel’s behavior once theexceeded
flag is set: ifhalt
is cleared, the kernel will attempt to write future, smaller chunks to the buffer (and thus implicitly discarding some packets). Ifhalt
andexceeded
are both set, the kernel will stop writing any data into the buffer. -
The kernel will schedule an upcall to the process, indicating that a write to the buffer (or setting the
exceeded
) flag occurred. The kernel may schedule only one upcall for the first chunk written to the buffer, or multiple upcalls (e.g., one upcall per chunk written). A process must not rely on the number of upcalls received and instead rely on the buffer header (offset
and theflags
bits) to determine the amount of data written to the buffer. -
The process prepares its second buffer, following step 2. The process then issues an
allow
operation that atomically swaps the current allowed buffer by its second buffer. -
The process can now process the received chunks contained in the initial buffer, while the kernel receives new chunks in the other, newly allowed buffer.
As the kernel cannot track if an allow
ed buffer for a particular
[SyscallDriver
] implementation is intended to be a
StreamingProcessSlice
, the kernel must use the header in the buffer as
provided by the process. The implementation of StreamingProcessSlice
ensures that an incorrect header will not cause a panic, but incoming
packets could be dropped. A process using a syscall API that uses a
StreamingProcessSlice
must ensure it has properly initialized the header
before allow
ing the buffer.
The version 0 buffer format is specified as follows:
0 2 4 6 8
+-----------+-----------+-----------------------+----------...
| version | flags | write offset (32 bit) | data
+-----------+-----------+-----------------------+----------...
| 000...000 | x{14},H,E | <native endian u32> |
+-----------+-----------+-----------------------+----------...
The version
field is a u16 integer stored in the target’s native
endianness. The flags
field is a bitfield laid out as shown in the
diagram above (laid out in big endian, with E
being the least significant
bit at byte 3). The offset
field is a u32 integer stored in the target’s
native endianness.
The kernel does not impose any alignment restrictions on
StreamingProcessSlice
s of version 0.
The flags field is structured as follows:
V
: version bits. This kernel only supports version0
.H
:halt
flag. If this flag is set and theexceeded
flag is set, the kernel will not write any further data to this buffer.E
:exceeded
flag. The kernel sets this flag when the remaining buffer capacity is insufficient to append the current chunk.x{14}
: reserved flag bits. Unless specified otherwise, processes must clear these flags prior toallow
ing a buffer to the kernel. A kernel that does not know of a reserved flag must refuse to operate on a buffer that has such a flag set.
Implementations§
source§impl<'a> StreamingProcessSlice<'a>
impl<'a> StreamingProcessSlice<'a>
pub fn new(slice: &'a WriteableProcessSlice) -> StreamingProcessSlice<'_>
sourcepub fn append_chunk(&self, chunk: &[u8]) -> Result<(bool, u32), ErrorCode>
pub fn append_chunk(&self, chunk: &[u8]) -> Result<(bool, u32), ErrorCode>
Append a chunk of data to the slice.
If the underlying slice has a correct flags
and offset
value, is not
halted, and has sufficient space for this data
chunk, this function
returns the updated buffer offset (set to one past the last written
byte).
This function returns whether this chunk was the first non-zero-length
chunk
appended to the slice, and the offset after the append operation
(where the next chunk would be written in the data section).
This function fails with:
INVAL
: if the version is not0
, or the reserved flags are not cleared.BUSY
: if both thehalt
andexceeded
flags are set. In this case, the slice will not be modified.SIZE
: if the underlying slice is not large enough to fit the flags field and the offset field. In this case, theexceeded
flag will be set and the slice will not be modified.FAIL
: would need to increment offset beyond 2**32 - 1. Neither the payload slice nor any header fields will be modified.
Appending a zero-length chunk
will be treated as every other chunk,
but appending it will not set the exceeded flag, even if offset
is at
the maximum position for this buffer. A zero-length append operation can
still fail due to the buffer being halted, having an improper header,
etc. A zero-length chunk
will never be treated as the first chunk
appended to a buffer.
sourcepub fn append_chunk_from_iter<I: IntoIterator<Item = u8>>(
&self,
src: I,
) -> Result<(bool, u32), ErrorCode>
pub fn append_chunk_from_iter<I: IntoIterator<Item = u8>>( &self, src: I, ) -> Result<(bool, u32), ErrorCode>
Append a chunk of data from an iterator.
If the underlying slice has a correct flags
and offset
value, is not
halted, and has sufficient space for this data
chunk, this function
returns the updated buffer offset (set to one past the last written
byte).
This function returns whether this chunk was the first non-zero-length
chunk
appended to the slice, and the offset after the append operation
(where the next chunk would be written in the data section).
If the buffer does not have enough space, this function will still
partially copy this chunk and modify the slice payload data after
offset
. It will not update the offset
header field though, and
instead set the exceeded
flag.
This function fails with:
INVAL
: if the version is not0
, or the reserved flags are not cleared.BUSY
: if both thehalt
andexceeded
flags are set. In this case, the slice will not be modified.SIZE
: if the underlying slice is not large enough to fit the flags field and the offset field. In this case, theexceeded
flag will be set and the slice will not be modified.FAIL
: would need to increment offset beyond 2**32 - 1. Neither the payload slice nor any header fields will be modified.
Appending a zero-length chunk
will be treated as every other chunk,
but appending it will not set the exceeded flag, even if offset
is at
the maximum position for this buffer. A zero-length append operation can
still fail due to the buffer being halted, having an improper header,
etc. A zero-length chunk
will never be treated as the first chunk
appended to a buffer.