Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support BufferSegments in linux kernel #422

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions capnp/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@
#[cfg(feature = "alloc")]
#[macro_use]
extern crate alloc;
#[cfg(feature = "kernel")]
extern crate alloc;

/// Code generated from
/// [schema.capnp](https://github.com/capnproto/capnproto/blob/master/c%2B%2B/src/capnp/schema.capnp).
Expand Down Expand Up @@ -358,6 +360,9 @@ pub enum ErrorKind {
/// Only one of the section pointers is pointing to ourself
OnlyOneOfTheSectionPointersIsPointingToOurself,

/// Out of memory
OutOfMemory,

/// Packed input did not end cleanly on a segment boundary.
PackedInputDidNotEndCleanlyOnASegmentBoundary,

Expand Down Expand Up @@ -489,6 +494,13 @@ impl core::convert::From<NotInSchema> for Error {
}
}

#[cfg(any(feature = "alloc", feature = "kernel"))]
impl core::convert::From<alloc::collections::TryReserveError> for Error {
fn from(_err: alloc::collections::TryReserveError) -> Self {
Self::from_kind(ErrorKind::OutOfMemory)
}
}

impl core::fmt::Display for ErrorKind {
fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::result::Result<(), core::fmt::Error> {
match self {
Expand Down Expand Up @@ -548,6 +560,7 @@ impl core::fmt::Display for ErrorKind {
Self::NestingLimitExceeded => write!(fmt, "nesting limit exceeded"),
Self::NotAStruct => write!(fmt, "not a struct"),
Self::OnlyOneOfTheSectionPointersIsPointingToOurself => write!(fmt, "Only one of the section pointers is pointing to ourself"),
Self::OutOfMemory => write!(fmt, "Out of memory"),
Self::PackedInputDidNotEndCleanlyOnASegmentBoundary => write!(fmt, "Packed input did not end cleanly on a segment boundary."),
Self::PrematureEndOfFile => write!(fmt, "Premature end of file"),
Self::PrematureEndOfPackedInput => write!(fmt, "Premature end of packed input."),
Expand Down
65 changes: 54 additions & 11 deletions capnp/src/serialize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,20 +26,22 @@
mod no_alloc_slice_segments;
pub use no_alloc_slice_segments::NoAllocSliceSegments;

#[cfg(any(feature = "alloc", feature = "kernel"))]
use crate::io::Read;
#[cfg(feature = "alloc")]
use crate::io::{Read, Write};
#[cfg(feature = "alloc")]
use crate::io::Write;
#[cfg(any(feature = "alloc", feature = "kernel"))]
use alloc::vec::Vec;
#[cfg(feature = "alloc")]
use core::convert::TryInto;
#[cfg(feature = "alloc")]
#[cfg(any(feature = "alloc", feature = "kernel"))]
use core::ops::Deref;

use crate::message;
#[cfg(feature = "alloc")]
#[cfg(any(feature = "alloc", feature = "kernel"))]
use crate::private::units::BYTES_PER_WORD;
use crate::Result;
#[cfg(feature = "alloc")]
#[cfg(any(feature = "alloc", feature = "kernel"))]
use crate::{Error, ErrorKind};

pub const SEGMENTS_COUNT_LIMIT: usize = 512;
Expand Down Expand Up @@ -124,7 +126,7 @@ pub fn read_message_from_flat_slice_no_alloc<'a>(

/// Segments read from a buffer, useful for when you have the message in a buffer and don't want the extra
/// copy of `read_message`.
#[cfg(feature = "alloc")]
#[cfg(any(feature = "alloc", feature = "kernel"))]
pub struct BufferSegments<T> {
buffer: T,

Expand All @@ -136,7 +138,7 @@ pub struct BufferSegments<T> {
segment_indices: Vec<(usize, usize)>,
}

#[cfg(feature = "alloc")]
#[cfg(any(feature = "alloc", feature = "kernel"))]
impl<T: Deref<Target = [u8]>> BufferSegments<T> {
/// Reads a serialized message (including a segment table) from a buffer and takes ownership, without copying.
/// The buffer is allowed to be longer than the message. Provide this to `Reader::new` with options that make
Expand Down Expand Up @@ -166,7 +168,7 @@ impl<T: Deref<Target = [u8]>> BufferSegments<T> {
}
}

#[cfg(feature = "alloc")]
#[cfg(any(feature = "alloc", feature = "kernel"))]
impl<T: Deref<Target = [u8]>> message::ReaderSegments for BufferSegments<T> {
fn get_segment(&self, id: u32) -> Option<&[u8]> {
if id < self.segment_indices.len() as u32 {
Expand Down Expand Up @@ -227,35 +229,57 @@ impl crate::message::ReaderSegments for OwnedSegments {
}
}

#[cfg(feature = "alloc")]
#[cfg(any(feature = "alloc", feature = "kernel"))]
/// Helper object for constructing an `OwnedSegments` or a `SliceSegments`.
pub struct SegmentLengthsBuilder {
segment_indices: Vec<(usize, usize)>,
total_words: usize,
}

#[cfg(feature = "alloc")]
#[cfg(any(feature = "alloc", feature = "kernel"))]
impl SegmentLengthsBuilder {
/// Creates a new `SegmentsLengthsBuilder`, initializing the segment_indices vector with
/// `Vec::with_capacitiy(capacity)`. `capacity` should equal the number of times that `push_segment()`
/// is expected to be called.
#[cfg(feature = "alloc")]
pub fn with_capacity(capacity: usize) -> Self {
Self {
segment_indices: Vec::with_capacity(capacity),
total_words: 0,
}
}
/// Creates a new `SegmentsLengthsBuilder`, initializing the segment_indices vector with
/// `Vec::with_capacitiy(capacity)`. `capacity` should equal the number of times that `push_segment()`
/// is expected to be called.
#[cfg(feature = "kernel")]
pub fn with_capacity(capacity: usize) -> Result<Self> {
Ok(Self {
segment_indices: Vec::try_with_capacity(capacity)?,
total_words: 0,
})
}

#[cfg(feature = "alloc")]
/// Pushes a new segment length. The `n`th time (starting at 0) this is called specifies the length of
/// the segment with ID `n`.
pub fn push_segment(&mut self, length_in_words: usize) {
self.segment_indices
.push((self.total_words, self.total_words + length_in_words));
self.total_words += length_in_words;
}
#[cfg(feature = "kernel")]
/// Pushes a new segment length. The `n`th time (starting at 0) this is called specifies the length of
/// the segment with ID `n`.
pub fn push_segment(&mut self, length_in_words: usize) -> Result<()> {
self.segment_indices
.try_push((self.total_words, self.total_words + length_in_words))?;
self.total_words += length_in_words;
Ok(())
}

/// Constructs an `OwnedSegments`, allocating a single buffer of 8-byte aligned memory to hold
/// all segments.
#[cfg(feature = "alloc")]
pub fn into_owned_segments(self) -> OwnedSegments {
let owned_space = crate::Word::allocate_zeroed_vec(self.total_words);
OwnedSegments {
Expand All @@ -265,6 +289,7 @@ impl SegmentLengthsBuilder {
}

/// Constructs a `SliceSegments`, where the passed-in slice is assumed to contain the segments.
#[cfg(feature = "alloc")]
pub fn into_slice_segments(self, slice: &[u8]) -> SliceSegments {
assert!(self.total_words * BYTES_PER_WORD <= slice.len());
SliceSegments {
Expand Down Expand Up @@ -332,7 +357,7 @@ where
///
/// The segment table format for streams is defined in the Cap'n Proto
/// [encoding spec](https://capnproto.org/encoding.html)
#[cfg(feature = "alloc")]
#[cfg(any(feature = "alloc", feature = "kernel"))]
fn read_segment_table<R>(
read: &mut R,
options: message::ReaderOptions,
Expand Down Expand Up @@ -364,25 +389,43 @@ where
)));
}

#[cfg(feature = "alloc")]
let mut segment_lengths_builder = SegmentLengthsBuilder::with_capacity(segment_count);
#[cfg(feature = "kernel")]
let mut segment_lengths_builder = SegmentLengthsBuilder::with_capacity(segment_count)?;
#[cfg(feature = "alloc")]
segment_lengths_builder
.push_segment(u32::from_le_bytes(buf[4..8].try_into().unwrap()) as usize);
#[cfg(feature = "kernel")]
segment_lengths_builder
.push_segment(u32::from_le_bytes(buf[4..8].try_into().unwrap()) as usize)?;
if segment_count > 1 {
if segment_count < 4 {
read.read_exact(&mut buf)?;
for idx in 0..(segment_count - 1) {
let segment_len =
u32::from_le_bytes(buf[(idx * 4)..(idx + 1) * 4].try_into().unwrap()) as usize;
#[cfg(feature = "alloc")]
segment_lengths_builder.push_segment(segment_len);
#[cfg(feature = "kernel")]
segment_lengths_builder.push_segment(segment_len)?;
}
} else {
#[cfg(feature = "alloc")]
let mut segment_sizes = vec![0u8; (segment_count & !1) * 4];
#[cfg(feature = "kernel")]
let mut segment_sizes = Vec::try_with_capacity((segment_count & !1) * 4)?;
#[cfg(feature = "kernel")]
segment_sizes.try_resize((segment_count & !1) * 4, 0)?;
read.read_exact(&mut segment_sizes[..])?;
for idx in 0..(segment_count - 1) {
let segment_len =
u32::from_le_bytes(segment_sizes[(idx * 4)..(idx + 1) * 4].try_into().unwrap())
as usize;
#[cfg(feature = "alloc")]
segment_lengths_builder.push_segment(segment_len);
#[cfg(feature = "kernel")]
segment_lengths_builder.push_segment(segment_len)?;
}
}
}
Expand Down
4 changes: 4 additions & 0 deletions capnp/src/stringify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,12 @@ pub(crate) fn print(
dynamic_value::Reader::UInt16(x) => formatter.write_fmt(format_args!("{x}")),
dynamic_value::Reader::UInt32(x) => formatter.write_fmt(format_args!("{x}")),
dynamic_value::Reader::UInt64(x) => formatter.write_fmt(format_args!("{x}")),
#[cfg(not(feature = "kernel"))]
dynamic_value::Reader::Float32(x) => formatter.write_fmt(format_args!("{x}")),
#[cfg(not(feature = "kernel"))]
dynamic_value::Reader::Float64(x) => formatter.write_fmt(format_args!("{x}")),
#[cfg(feature = "kernel")]
dynamic_value::Reader::Float32(_) | dynamic_value::Reader::Float64(_) => Err(fmt::Error),
Copy link
Member

@dwrensha dwrensha Jul 4, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So when compiling for the Linux kernel, format_args!("{x}") is a compile-time error when x is a floating point number? Is that true for format_args!("{x:?}") as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For your first question, yes:

error[E0277]: `f32` doesn't implement `Display`
  --> rust/capnp/stringify.rs:74:81
   |
74 |         dynamic_value::Reader::Float32(x) => formatter.write_fmt(format_args!("{x}")),
   |                                                                                 ^ `f32` cannot be formatted with the default formatter
   |
   = help: the trait `Display` is not implemented for `f32`
   = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
   = help: the following other types implement trait `Display`:
             i128
             i16
             i32
             i64
             i8
             isize
             u128
             u16
           and 4 others
   = note: this error originates in the macro `format_args` (in Nightly builds, run with -Z macro-backtrace for more info)

error: aborting due to previous error

However, I was surprised to see it is not a compile time error for format_args!("{x:?}"). I will need to test what happens in that case.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Trying to display a float using the debug formatting leads to a kernel panic:

        let x:f32 = 4.2;
        pr_info!("trying to display float");
        pr_info!("{}", format_args!("{x:?}"));

~ # mount -t puzzlefs none /mnt
[   10.747124] puzzlefs: trying to display float
[   10.747132] rust_kernel: panicked at 'floating point support is turned off', /home/amiculas/.rustup/toolchains/1.68.2-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/fmt/1
[   10.750707] ------------[ cut here ]------------
[   10.751557] kernel BUG at rust/helpers.c:34!
[   10.752269] invalid opcode: 0000 [#1] SMP
[   10.752892] CPU: 0 PID: 49 Comm: mount Not tainted 6.4.0-rc4+ #330
[   10.753791] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Arch Linux 1.16.2-1-1 04/01/2014
[   10.755058] RIP: 0010:rust_helper_BUG+0x8/0x10
[   10.755683] Code: 00 00 48 8d 7d d0 48 c7 c6 a0 da 82 81 e8 a0 20 2d 00 0f 0b cc cc cc cc cc cc cc cc cc cc cc cc cc cc f3 0f 1e fa 55 48 89 e5 <0f> 0b 66 0f 1f 44 00 00 f3 0f 1e fa 55 d
[   10.758422] RSP: 0018:ffffc90000107620 EFLAGS: 00010092
[   10.759250] RAX: 00000000000000bc RBX: ffffc90000107d30 RCX: ffffffff81a35ff0
[   10.760201] RDX: 0000000000000002 RSI: c0000000ffffefff RDI: 0000000000002ffd
[   10.760929] RBP: ffffc90000107620 R08: 0000000000000000 R09: ffffffff81a4e280
[   10.761654] R10: 00000000ffffefff R11: 0000000000000000 R12: 0000000000000000
[   10.762390] R13: 0000000000000010 R14: ffffffff8185ed30 R15: 0000000000000000
[   10.763128] FS:  0000000000f353c0(0000) GS:ffff88813bc00000(0000) knlGS:0000000000000000
[   10.763979] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[   10.764589] CR2: 0000000000641fc0 CR3: 0000000100e8c002 CR4: 0000000000770eb0
[   10.765158] PKRU: 55555554
[   10.765341] Call Trace:
[   10.765504]  <TASK>
[   10.765645]  ? __die_body+0x62/0xb0
[   10.765874]  ? die+0x8c/0xb0
[   10.766065]  ? do_trap+0x87/0x150
[   10.766282]  ? rust_helper_BUG+0x8/0x10
[   10.766530]  ? handle_invalid_op+0x68/0x80
[   10.766794]  ? rust_helper_BUG+0x8/0x10
[   10.767041]  ? exc_invalid_op+0x36/0x50
[   10.767288]  ? asm_exc_invalid_op+0x1f/0x30
[   10.767559]  ? rust_helper_BUG+0x8/0x10
[   10.767806]  rust_begin_unwind+0x62/0x80
[   10.768059]  ? mas_replace+0x2b4/0x350
[   10.768302]  ? _RNvXsX_NtCsjQkEVdlX3YU_4core3fmtRNtNtCscRhiiH9rU7O_6kernel3str4CStrNtB5_7Display3fmtBz_+0xc0/0xc0
[   10.768981]  _RNvNtCsjQkEVdlX3YU_4core9panicking9panic_fmt+0x2d/0x30
[   10.769490]  _RNvXNtNtCsjQkEVdlX3YU_4core3fmt7nofloatfNtB4_5Debug3fmt+0x40/0x50
[   10.769981]  ? call_rcu+0xd4/0x220
[   10.770165]  _RNvNtCsjQkEVdlX3YU_4core3fmt5write+0x1ba/0x220
[   10.770389]  _RNvXs5_NtCsjQkEVdlX3YU_4core3fmtNtB5_9ArgumentsNtB5_7Display3fmt+0x4a/0x50
[   10.770703]  ? _RINvNtCsjQkEVdlX3YU_4core3ptr13drop_in_placeINtNtNtCscRhiiH9rU7O_6kernel2fs5param15ConcreteHandleruNCINvNtBJ_3s327handleruE0EECshR6aa2uJhw8_8puzzlefs+0x10/0x10
[   10.771303]  _RNvNtCsjQkEVdlX3YU_4core3fmt5write+0x1ba/0x220
[   10.771527]  rust_fmt_argument+0x5f/0x70
[   10.771683]  ? _RINvNtCsjQkEVdlX3YU_4core3ptr13drop_in_placeINtNtNtCscRhiiH9rU7O_6kernel2fs5param15ConcreteHandleruNCINvNtBJ_3s327handleruE0EECshR6aa2uJhw8_8puzzlefs+0x10/0x10
[   10.772286]  pointer+0x4c6/0x6a0
[   10.772418]  vsnprintf+0x493/0x730
[   10.772557]  vprintk_store+0x194/0x5a0
[   10.772708]  ? console_unlock+0xe1/0x100
[   10.772865]  vprintk_emit+0x65/0x1c0
[   10.773009]  vprintk_default+0x1c/0x20
[   10.773159]  vprintk+0x4d/0x60
[   10.773284]  _printk+0x4a/0x50
[   10.773408]  _RNvXCshR6aa2uJhw8_8puzzlefsNtB2_8PuzzleFsNtNtCscRhiiH9rU7O_6kernel2fs4Type10fill_super+0x50b/0x520
[   10.773801]  ? _RINvNtCsjQkEVdlX3YU_4core3ptr13drop_in_placeINtNtNtCscRhiiH9rU7O_6kernel2fs5param15ConcreteHandleruNCINvNtBJ_3s327handleruE0EECshR6aa2uJhw8_8puzzlefs+0x10/0x10
[   10.774399]  ? _RINvNtCsjQkEVdlX3YU_4core3ptr13drop_in_placeINtNtNtCscRhiiH9rU7O_6kernel2fs5param15ConcreteHandleruNCINvNtBJ_3s327handleruE0EECshR6aa2uJhw8_8puzzlefs+0x10/0x10
[   10.774997]  ? _RINvNtCsjQkEVdlX3YU_4core3ptr13drop_in_placeINtNtNtCscRhiiH9rU7O_6kernel2fs5param15ConcreteHandleruNCINvNtBJ_3s327handleruE0EECshR6aa2uJhw8_8puzzlefs+0x10/0x10
[   10.775598]  ? _RINvNtCsjQkEVdlX3YU_4core3ptr13drop_in_placeINtNtNtCscRhiiH9rU7O_6kernel2fs5param15ConcreteHandleruNCINvNtBJ_3s327handleruE0EECshR6aa2uJhw8_8puzzlefs+0x10/0x10
[   10.776195]  ? _RNvXs4_NtCsjQkEVdlX3YU_4core3fmtNtB5_9ArgumentsNtB5_5Debug3fmt+0x50/0x50
[   10.776508]  ? _RNvXs13_NtNtCsjQkEVdlX3YU_4core4sync6atomicNtB6_11AtomicUsizeNtNtBa_3fmt5Debug3fmt+0x20/0x20
[   10.776888]  ? _RNvMNtCscRhiiH9rU7O_6kernel2fsINtB2_6TablesNtCshR6aa2uJhw8_8puzzlefs8PuzzleFsE17get_tree_callbackBH_+0x20/0x20
[   10.777325]  _RNvMNtCscRhiiH9rU7O_6kernel2fsINtB2_6TablesNtCshR6aa2uJhw8_8puzzlefs8PuzzleFsE19fill_super_callbackBH_+0x29/0x50
[   10.777763]  ? _RNvMNtCscRhiiH9rU7O_6kernel2fsINtB2_6TablesNtCshR6aa2uJhw8_8puzzlefs8PuzzleFsE17get_tree_callbackBH_+0x20/0x20
[   10.778199]  vfs_get_super+0x8c/0x120
[   10.778346]  get_tree_nodev+0x14/0x20
[   10.778492]  vfs_get_tree+0x20/0x90
[   10.778635]  do_new_mount+0x140/0x340
[   10.778784]  path_mount+0x384/0x620
[   10.778924]  __x64_sys_mount+0x142/0x1a0
[   10.779081]  do_syscall_64+0x48/0x90
[   10.779224]  entry_SYSCALL_64_after_hwframe+0x46/0xb0
[   10.779423] RIP: 0033:0x4958ce
[   10.779547] Code: 48 c7 c1 e0 ff ff ff f7 d8 64 89 01 48 83 c8 ff c3 66 2e 0f 1f 84 00 00 00 00 00 90 f3 0f 1e fa 49 89 ca b8 a5 00 00 00 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 e0 8
[   10.780314] RSP: 002b:00007ffcabbf9228 EFLAGS: 00000246 ORIG_RAX: 00000000000000a5
[   10.780608] RAX: ffffffffffffffda RBX: 00007ffcabbf93f8 RCX: 00000000004958ce
[   10.780886] RDX: 00007ffcabbf9fba RSI: 00007ffcabbf9fc8 RDI: 00007ffcabbf9fc3
[   10.781163] RBP: 00007ffcabbf9fc3 R08: 0000000000000000 R09: 0000000000000000
[   10.781440] R10: 0000000000008000 R11: 0000000000000246 R12: 00007ffcabbf9fc8
[   10.781717] R13: 00007ffcabbf9fba R14: 0000000000008000 R15: 00007ffcabbf9720
[   10.782007]  </TASK>
[   10.782097] Modules linked in:
[   10.782222] ---[ end trace 0000000000000000 ]---
[   10.782404] RIP: 0010:rust_helper_BUG+0x8/0x10
[   10.782581] Code: 00 00 48 8d 7d d0 48 c7 c6 a0 da 82 81 e8 a0 20 2d 00 0f 0b cc cc cc cc cc cc cc cc cc cc cc cc cc cc f3 0f 1e fa 55 48 89 e5 <0f> 0b 66 0f 1f 44 00 00 f3 0f 1e fa 55 d
[   10.783295] RSP: 0018:ffffc90000107620 EFLAGS: 00010092
[   10.783499] RAX: 00000000000000bc RBX: ffffc90000107d30 RCX: ffffffff81a35ff0
[   10.783775] RDX: 0000000000000002 RSI: c0000000ffffefff RDI: 0000000000002ffd
[   10.784053] RBP: ffffc90000107620 R08: 0000000000000000 R09: ffffffff81a4e280
[   10.784331] R10: 00000000ffffefff R11: 0000000000000000 R12: 0000000000000000
[   10.784609] R13: 0000000000000010 R14: ffffffff8185ed30 R15: 0000000000000000
[   10.784886] FS:  0000000000f353c0(0000) GS:ffff88813bc00000(0000) knlGS:0000000000000000
[   10.785198] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[   10.785425] CR2: 0000000000641fc0 CR3: 0000000100e8c002 CR4: 0000000000770eb0
[   10.785703] PKRU: 55555554
[   10.785812] note: mount[49] exited with irqs disabled
Segmentation fault

dynamic_value::Reader::Enum(e) => match cvt(e.get_enumerant())? {
Some(enumerant) => formatter.write_str(cvt(enumerant.get_proto().get_name())?),
None => formatter.write_fmt(format_args!("{}", e.get_value())),
Expand Down
Loading