simplify eventfd handling a bit

This commit is contained in:
Ralf Jung 2024-09-22 16:49:21 +02:00
parent ed24426824
commit d70fd882ef

View File

@ -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;
@ -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
@ -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.
@ -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)
}
}
@ -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(());
}
}
}