Skip to content

Commit

Permalink
simplify eventfd handling a bit
Browse files Browse the repository at this point in the history
  • Loading branch information
RalfJung committed Sep 22, 2024
1 parent c6607e2 commit d7741a9
Showing 1 changed file with 30 additions and 49 deletions.
79 changes: 30 additions & 49 deletions src/shims/unix/linux/eventfd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,12 @@
use std::cell::{Cell, RefCell};
use std::io;
use std::io::{Error, ErrorKind};
use std::mem;

use crate::shims::unix::fd::FileDescriptionRef;
use crate::shims::unix::linux::epoll::{EpollReadyEvents, EvalContextExt as _};
use crate::shims::unix::*;
use crate::{concurrency::VClock, *};

// We'll only do reads and writes in chunks of size u64.
const U64_ARRAY_SIZE: usize = mem::size_of::<u64>();

/// Maximum value that the eventfd counter can hold.
const MAX_COUNTER: u64 = u64::MAX - 1;

Expand Down Expand Up @@ -65,35 +61,45 @@ impl FileDescription for Event {
dest: &MPlaceTy<'tcx>,
ecx: &mut MiriInterpCx<'tcx>,
) -> InterpResult<'tcx> {
// eventfd read at the size of u64.
let buf_place = ecx.ptr_to_mplace_unaligned(ptr, ecx.machine.layouts.u64);
// We're treating the buffer as a `u64`.
let ty = ecx.machine.layouts.u64;
// Check the size of slice, and return error only if the size of the slice < 8.
if len < U64_ARRAY_SIZE {
let result = Err(Error::from(ErrorKind::InvalidInput));
return return_read_bytes_and_count_ev(&buf_place, None, result, dest, ecx);
if len < ty.size.bytes_usize() {
ecx.set_last_error_from_io_error(Error::from(ErrorKind::InvalidInput))?;
ecx.write_int(-1, dest)?;
return Ok(());
}

// eventfd read at the size of u64.
let buf_place = ecx.ptr_to_mplace_unaligned(ptr, ty);

// Block when counter == 0.
let counter = self.counter.get();
if counter == 0 {
if self.is_nonblock {
let result = Err(Error::from(ErrorKind::WouldBlock));
return return_read_bytes_and_count_ev(&buf_place, None, result, dest, ecx);
} else {
//FIXME: blocking is not supported
throw_unsup_format!("eventfd: blocking is unsupported");
ecx.set_last_error_from_io_error(Error::from(ErrorKind::WouldBlock))?;
ecx.write_int(-1, dest)?;
return Ok(());
}

throw_unsup_format!("eventfd: blocking is unsupported");
} else {
// Synchronize with all prior `write` calls to this FD.
ecx.acquire_clock(&self.clock.borrow());
let result = Ok(U64_ARRAY_SIZE);

// Give old counter value to userspace, and set counter value to 0.
ecx.write_int(counter, &buf_place)?;
self.counter.set(0);

// When any of the event happened, we check and update the status of all supported event
// types for current file description.
ecx.check_and_update_readiness(self_ref)?;

return_read_bytes_and_count_ev(&buf_place, Some(counter), result, dest, ecx)
// Tell userspace how many bytes we wrote.
ecx.write_int(buf_place.layout.size.bytes(), dest)?;
}

Ok(())
}

/// A write call adds the 8-byte integer value supplied in
Expand All @@ -117,14 +123,16 @@ impl FileDescription for Event {
dest: &MPlaceTy<'tcx>,
ecx: &mut MiriInterpCx<'tcx>,
) -> InterpResult<'tcx> {
// We're treating the buffer as a `u64`.
let ty = ecx.machine.layouts.u64;
// Check the size of slice, and return error only if the size of the slice < 8.
if len < U64_ARRAY_SIZE {
if len < ty.layout.size.bytes_usize() {
let result = Err(Error::from(ErrorKind::InvalidInput));
return ecx.return_written_byte_count_or_error(result, dest);
}

// Read the user supplied value from the pointer.
let buf_place = ecx.ptr_to_mplace_unaligned(ptr, ecx.machine.layouts.u64);
let buf_place = ecx.ptr_to_mplace_unaligned(ptr, ty);
let num = ecx.read_scalar(&buf_place)?.to_u64()?;

// u64::MAX as input is invalid because the maximum value of counter is u64::MAX - 1.
Expand All @@ -142,22 +150,20 @@ impl FileDescription for Event {
}
self.counter.set(new_count);
}
None | Some(u64::MAX) => {
None | Some(u64::MAX) =>
if self.is_nonblock {
let result = Err(Error::from(ErrorKind::WouldBlock));
return ecx.return_written_byte_count_or_error(result, dest);
} else {
//FIXME: blocking is not supported
throw_unsup_format!("eventfd: blocking is unsupported");
}
}
},
};
// When any of the event happened, we check and update the status of all supported event
// types for current file description.
ecx.check_and_update_readiness(self_ref)?;

let result = Ok(U64_ARRAY_SIZE);
ecx.return_written_byte_count_or_error(result, dest)
// Return how many bytes we read.
ecx.write_int(buf_place.layout.size.bytes(), dest)
}
}

Expand Down Expand Up @@ -222,28 +228,3 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
Ok(Scalar::from_i32(fd_value))
}
}

/// This function either writes to the user supplied buffer and to dest place, or sets the
/// last libc error and writes -1 to dest. This is only used by eventfd.
fn return_read_bytes_and_count_ev<'tcx>(
buf_place: &MPlaceTy<'tcx>,
read_val: Option<u64>,
result: io::Result<usize>,
dest: &MPlaceTy<'tcx>,
ecx: &mut MiriInterpCx<'tcx>,
) -> InterpResult<'tcx> {
match result.map(|c| i64::try_from(c).unwrap()) {
Ok(read_bytes) => {
// Write to the user supplied buffer.
ecx.write_int(read_val.unwrap(), buf_place)?;
// Write to the function return value place.
ecx.write_int(read_bytes, dest)?;
return Ok(());
}
Err(e) => {
ecx.set_last_error_from_io_error(e)?;
ecx.write_int(-1, dest)?;
return Ok(());
}
}
}

0 comments on commit d7741a9

Please sign in to comment.