linux-support 0.0.25

Comprehensive Linux support for namespaces, cgroups, processes, scheduling, parsing /proc, parsing /sys, signals, hyper threads, CPUS, NUMA nodes, unusual file descriptors, PCI devices and much, much more
// This file is part of linux-support. It is subject to the license terms in the COPYRIGHT file found in the top-level directory of this distribution and at https://raw.githubusercontent.com/lemonrock/linux-support/master/COPYRIGHT. No part of linux-support, including this file, may be copied, modified, propagated, or distributed except according to the terms contained in the COPYRIGHT file.
// Copyright © 2020 The developers of linux-support. See the COPYRIGHT file in the top-level directory of this distribution and at https://raw.githubusercontent.com/lemonrock/linux-support/master/COPYRIGHT.


/// Instance.
#[derive(Debug)]
pub struct ExpressDataPathInstance<ROTOB: ReceiveOrTransmitOrBoth, FFQ: FreeFrameQueue>
{
	user_memory: ManuallyDrop<UserMemory<FFQ>>,
	
	redirect_map_and_attached_program: ManuallyDrop<RedirectMapAndAttachedProgram>,
	
	network_interface_index: NetworkInterfaceIndex,
	
	force_copy: bool,
	
	force_zero_copy: bool,

	marker: PhantomData<ROTOB>,
}

impl<ROTOB: ReceiveOrTransmitOrBoth, FFQ: FreeFrameQueue> ExpressDataPathInstance<ROTOB, FFQ>
{
	/// Converts to single-threaded owned instance.
	pub fn owned<RingQueueDepths: CreateReceiveOrTransmitOrBoth<ReceiveOrTransmitOrBoth=ROTOB>>(self, receive_or_transmit_or_both_ring_queue_depths: RingQueueDepths, queue_identifier: QueueIdentifier, arguments: RingQueueDepths::Arguments) -> Result<OwnedExpressDataPathSocket<ROTOB, FFQ>, ExpressDataPathSocketCreationError>
	{
		Ok
		(
			OwnedExpressDataPathSocket
			{
				common:
				{
					let user_memory_socket_file_descriptor = self.user_memory.user_memory_socket_file_descriptor();
					let express_data_path_socket_file_descriptor = user_memory_socket_file_descriptor;
				
					CommonExpressDataPathSocket::new(express_data_path_socket_file_descriptor, self.network_interface_index, receive_or_transmit_or_both_ring_queue_depths, XdpSocketAddressFlags::empty(), self.force_copy, self.force_zero_copy, user_memory_socket_file_descriptor, queue_identifier, &self.redirect_map_and_attached_program, arguments)?
				},
				
				instance: ManuallyDrop::new(self),
			}
		)
	}
	
	/// Converts to instance that can then create per-thread instances (sic).
	///
	/// If using a shared instance, then each shared instance should:-
	///
	/// * Have a transmit queue depth equal to the completion queue depth;
	/// * Have a receive queue depth equal to the fill queue depth;
	///
	/// Additionally, the `number_of_chunks` should be equal to the total of all shared instances transmit queue depths + receive queue depths, unless only forwarding immediately, in which case it can be half this value.
	#[inline(always)]
	pub fn shareable(self, number_of_threads_guess: NonZeroUsize) -> ShareableExpressDataPathInstance<ROTOB, FFQ>
	{
		ShareableExpressDataPathInstance(Arc::new((self, BestForCompilationTargetSpinLock::default(), BestForCompilationTargetSpinLock::default(), Mutex::new(HashSet::with_capacity(number_of_threads_guess.get())))))
	}
	
	#[inline(always)]
	fn shared<RingQueueDepths: CreateReceiveOrTransmitOrBoth<ReceiveOrTransmitOrBoth=ROTOB>>(&self, receive_or_transmit_or_both_ring_queue_depths: RingQueueDepths, queue_identifier: QueueIdentifier, arguments: RingQueueDepths::Arguments, instance: &ShareableExpressDataPathInstance<RingQueueDepths::ReceiveOrTransmitOrBoth, FFQ>, express_data_path_socket_file_descriptor: ExpressDataPathSocketFileDescriptor) -> Result<SharedExpressDataPathSocket<ROTOB, FFQ>, ExpressDataPathSocketCreationError>
	{
		use self::ExpressDataPathSocketCreationError::*;
		
		let redirect_map_and_attached_program = &self.redirect_map_and_attached_program;
		
		if redirect_map_and_attached_program.is_our_owned_program_and_thus_can_not_be_shared()
		{
			return Err(AttachedExpressDataPathProgramNotSuitableForSharing)
		}
		
		Ok
		(
			SharedExpressDataPathSocket
			{
				common:
				{
					let user_memory_socket_file_descriptor = self.user_memory.user_memory_socket_file_descriptor();
					
					CommonExpressDataPathSocket::new(&express_data_path_socket_file_descriptor, self.network_interface_index, receive_or_transmit_or_both_ring_queue_depths, XdpSocketAddressFlags::SharedUserMemory, self.force_copy, self.force_zero_copy, user_memory_socket_file_descriptor, queue_identifier, redirect_map_and_attached_program, arguments)?
				},
				
				instance: instance.clone(),
				
				express_data_path_socket_file_descriptor: ManuallyDrop::new(express_data_path_socket_file_descriptor),
				
				queue_identifier,
			}
		)
	}
}

impl<ROTOB: ReceiveOrTransmitOrBoth, FFQ: FreeFrameQueue> ExpressDataPathInstance<ROTOB, FFQ>
{
	/// For interest.
	#[inline(always)]
	pub fn calculate_maximum_transmission_unit_payload_size(chunk_size: FFQ::CS, frame_headroom: FrameHeadroom) -> Result<MaximumTransmissionUnitPayloadSize, ParseNumberError>
	{
		chunk_size.calculate_maximum_transmission_unit_payload_size(frame_headroom)
	}
	
	/// New instance.
	#[inline(always)]
	pub fn new<RingQueueDepths: FillOrCompletionOrBothRingQueueDepths + CreateReceiveOrTransmitOrBoth<ReceiveOrTransmitOrBoth=ROTOB>>(number_of_chunks: NonZeroU32, frame_headroom: FrameHeadroom, chunk_size: FFQ::CS, fill_or_completion_or_both_ring_queue_depths: RingQueueDepths, user_memory_area_page_size_or_huge_page_size_settings: &PageSizeOrHugePageSizeSettings,  redirect_map_and_attached_program: Either<RedirectMapAndAttachedProgramSettings, RedirectMapAndAttachedProgram>, network_interface_name: NetworkInterfaceName, force_copy: bool, force_zero_copy: bool) -> Result<Self, ExpressDataPathSocketCreationError>
	{
		let (network_interface_index, maximum_transmission_unit_payload_size) = Self::network_interface_index_and_maximum_transmission_unit_payload_size(network_interface_name)?;
		
		let user_memory = ManuallyDrop::new(UserMemory::new(number_of_chunks, chunk_size, frame_headroom, maximum_transmission_unit_payload_size, fill_or_completion_or_both_ring_queue_depths, user_memory_area_page_size_or_huge_page_size_settings)?);
		
		Ok
		(
			Self
			{
				redirect_map_and_attached_program: ManuallyDrop::new
				(
					match redirect_map_and_attached_program
					{
						Left(settings) => RedirectMapAndAttachedProgram::new_suitable_for_owned_or_reuse_already_attached(network_interface_index, settings)?,
						
						Right(redirect_map_and_attached_program) => redirect_map_and_attached_program,
					}
				),
				
				network_interface_index,
				
				force_copy,
				
				force_zero_copy,
				
				marker: PhantomData,
				
				user_memory,
			}
		)
	}
	
	#[inline(always)]
	fn network_interface_index_and_maximum_transmission_unit_payload_size(network_interface_name: NetworkInterfaceName) -> Result<(NetworkInterfaceIndex, MaximumTransmissionUnitPayloadSize), ExpressDataPathSocketCreationError>
	{
		use self::ExpressDataPathSocketCreationError::*;
		
		Ok
		(
			(
				{
					let network_device_control = NetworkDeviceInputOutputControl::new(Cow::Borrowed(&network_interface_name)).map_err(CouldNotCreateNetworkDeviceControlSocket)?;
					network_device_control.network_interface_name_to_network_interface_index().map_err(CouldNotGetValidNetworkInterfaceName)?.ok_or(NoSuchNetworkInterfaceName)?
				},
				{
					let mut netlink_socket_file_descriptor = NetlinkSocketFileDescriptor::open().map_err(CouldNotCreateNetlinkSocket)?;
					let get_link_message_data = RouteNetlinkProtocol::get_link(&mut netlink_socket_file_descriptor, |get_link_message_data| get_link_message_data.network_interface_name == network_interface_name).map_err(NetlinkGetLinksFailed )?.ok_or(NoSuchNetworkInterfaceName)?;
					get_link_message_data.maximum_transmission_unit
				}
			)
		)
	}
}