From eb5ee5edc96be502e5fa32df101d88c11e6fe536 Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Sun, 23 Oct 2016 15:26:36 -0600 Subject: [PATCH] Event based ethernetd --- drivers/e1000d/src/device.rs | 22 +++- drivers/e1000d/src/main.rs | 5 + programs/netutils | 2 +- schemes/ethernetd/Cargo.toml | 2 +- schemes/ethernetd/src/main.rs | 84 ++++++++++++--- schemes/ethernetd/src/resource.rs | 96 ----------------- schemes/ethernetd/src/scheme.rs | 173 ++++++++++++++++++++++++++---- 7 files changed, 255 insertions(+), 129 deletions(-) delete mode 100644 schemes/ethernetd/src/resource.rs diff --git a/drivers/e1000d/src/device.rs b/drivers/e1000d/src/device.rs index 8604728..edadeb1 100644 --- a/drivers/e1000d/src/device.rs +++ b/drivers/e1000d/src/device.rs @@ -140,7 +140,8 @@ impl Scheme for Intel8254x { } } - Err(Error::new(EWOULDBLOCK)) + //Err(Error::new(EWOULDBLOCK)) + Ok(0) } fn write(&self, _id: usize, buf: &[u8]) -> Result { @@ -225,6 +226,25 @@ impl Intel8254x { icr != 0 } + pub fn next_read(&self) -> usize { + let head = unsafe { self.read(RDH) }; + let mut tail = unsafe { self.read(RDT) }; + + tail += 1; + if tail >= self.receive_ring.len() as u32 { + tail = 0; + } + + if tail != head { + let rd = unsafe { &* (self.receive_ring.as_ptr().offset(tail as isize) as *const Rd) }; + if rd.status & RD_DD == RD_DD { + return rd.length as usize; + } + } + + 0 + } + pub unsafe fn read(&self, register: u32) -> u32 { ptr::read_volatile((self.base + register as usize) as *mut u32) } diff --git a/drivers/e1000d/src/main.rs b/drivers/e1000d/src/main.rs index a80303b..c6a98f2 100644 --- a/drivers/e1000d/src/main.rs +++ b/drivers/e1000d/src/main.rs @@ -69,6 +69,11 @@ fn main() { todo.remove(i); } } + + let next_read = device_irq.next_read(); + if next_read > 0 { + return Ok(Some(next_read)); + } } Ok(None) }).expect("e1000d: failed to catch events on IRQ file"); diff --git a/programs/netutils b/programs/netutils index a45673a..3eb504d 160000 --- a/programs/netutils +++ b/programs/netutils @@ -1 +1 @@ -Subproject commit a45673aa948fe6d172a0e2b42ade86e0567c3afd +Subproject commit 3eb504dfe131c9fcba3e8ee824f382af9c6032cd diff --git a/schemes/ethernetd/Cargo.toml b/schemes/ethernetd/Cargo.toml index e7f1e03..8861e41 100644 --- a/schemes/ethernetd/Cargo.toml +++ b/schemes/ethernetd/Cargo.toml @@ -3,6 +3,6 @@ name = "ethernetd" version = "0.1.0" [dependencies] +event = { path = "../../crates/event/" } netutils = { path = "../../programs/netutils/" } -resource_scheme = { path = "../../crates/resource_scheme/" } syscall = { path = "../../syscall/" } diff --git a/schemes/ethernetd/src/main.rs b/schemes/ethernetd/src/main.rs index 861264c..13903f5 100644 --- a/schemes/ethernetd/src/main.rs +++ b/schemes/ethernetd/src/main.rs @@ -1,28 +1,88 @@ +extern crate event; extern crate netutils; -extern crate resource_scheme; extern crate syscall; +use event::EventQueue; +use std::cell::RefCell; use std::fs::File; -use std::io::{Read, Write}; +use std::io::{Result, Read, Write}; +use std::os::unix::io::FromRawFd; +use std::rc::Rc; use std::thread; -use resource_scheme::ResourceScheme; -use syscall::Packet; +use syscall::{Packet, SchemeMut, EWOULDBLOCK}; use scheme::EthernetScheme; -mod resource; mod scheme; fn main() { thread::spawn(move || { - let mut socket = File::create(":ethernet").expect("ethernetd: failed to create ethernet scheme"); - let scheme = EthernetScheme; - loop { + let network_fd = syscall::open("network:", syscall::O_RDWR | syscall::O_NONBLOCK).expect("ethernetd: failed to open network"); + let network = unsafe { File::from_raw_fd(network_fd) }; + + let socket_fd = syscall::open(":ethernet", syscall::O_RDWR | syscall::O_CREAT | syscall::O_NONBLOCK).expect("ethernetd: failed to create ethernet scheme"); + let socket = Rc::new(RefCell::new(unsafe { File::from_raw_fd(socket_fd) })); + + let scheme = Rc::new(RefCell::new(EthernetScheme::new(network))); + + let todo = Rc::new(RefCell::new(Vec::::new())); + + let mut event_queue = EventQueue::<()>::new().expect("ethernetd: failed to create event queue"); + + let socket_net = socket.clone(); + let scheme_net = scheme.clone(); + let todo_net = todo.clone(); + event_queue.add(network_fd, move |_count: usize| -> Result> { + if scheme_net.borrow_mut().input()? > 0 { + let mut todo = todo_net.borrow_mut(); + let mut i = 0; + while i < todo.len() { + let a = todo[i].a; + scheme_net.borrow_mut().handle(&mut todo[i]); + if todo[i].a == (-EWOULDBLOCK) as usize { + todo[i].a = a; + i += 1; + } else { + socket_net.borrow_mut().write(&mut todo[i])?; + todo.remove(i); + } + } + + for (id, handle) in scheme_net.borrow_mut().handles.iter() { + if let Some(frame) = handle.frames.get(0) { + socket_net.borrow_mut().write(&Packet { + id: 0, + pid: 0, + uid: 0, + gid: 0, + a: syscall::number::SYS_FEVENT, + b: *id, + c: syscall::flag::EVENT_READ, + d: frame.data.len() + })?; + } + } + } + Ok(None) + }).expect("ethernetd: failed to listen for network events"); + + event_queue.add(socket_fd, move |_count: usize| -> Result> { let mut packet = Packet::default(); - socket.read(&mut packet).expect("ethernetd: failed to read events from ethernet scheme"); - scheme.handle(&mut packet); - socket.write(&packet).expect("ethernetd: failed to write responses to ethernet scheme"); - } + socket.borrow_mut().read(&mut packet)?; + + let a = packet.a; + scheme.borrow_mut().handle(&mut packet); + if packet.a == (-EWOULDBLOCK) as usize { + packet.a = a; + todo.borrow_mut().push(packet); + } else { + socket.borrow_mut().write(&mut packet)?; + } + + Ok(None) + }).expect("ethernetd: failed to listen for scheme events"); + + event_queue.run().expect("ethernetd: failed to run event loop"); }); } diff --git a/schemes/ethernetd/src/resource.rs b/schemes/ethernetd/src/resource.rs deleted file mode 100644 index 6f9b18f..0000000 --- a/schemes/ethernetd/src/resource.rs +++ /dev/null @@ -1,96 +0,0 @@ -use std::{cmp, mem}; - -use netutils::{n16, MacAddr, EthernetIIHeader, EthernetII}; -use resource_scheme::Resource; -use syscall; -use syscall::error::*; - -/// A ethernet resource -pub struct EthernetResource { - /// The network - pub network: usize, - /// The data - pub data: Vec, - /// The MAC addresss - pub peer_addr: MacAddr, - /// The ethernet type - pub ethertype: u16, -} - -impl Resource for EthernetResource { - fn dup(&self) -> Result> { - let network = try!(syscall::dup(self.network)); - Ok(Box::new(EthernetResource { - network: network, - data: self.data.clone(), - peer_addr: self.peer_addr, - ethertype: self.ethertype, - })) - } - - fn path(&self, buf: &mut [u8]) -> Result { - let path_string = format!("ethernet:{}/{:X}", self.peer_addr.to_string(), self.ethertype); - let path = path_string.as_bytes(); - - for (b, p) in buf.iter_mut().zip(path.iter()) { - *b = *p; - } - - Ok(cmp::min(buf.len(), path.len())) - } - - fn read(&mut self, buf: &mut [u8]) -> Result { - if !self.data.is_empty() { - let mut data: Vec = Vec::new(); - mem::swap(&mut self.data, &mut data); - - for (b, d) in buf.iter_mut().zip(data.iter()) { - *b = *d; - } - - return Ok(cmp::min(buf.len(), data.len())); - } - - let mut bytes = [0; 65536]; - let count = try!(syscall::read(self.network, &mut bytes)); - - if let Some(frame) = EthernetII::from_bytes(&bytes[..count]) { - if frame.header.ethertype.get() == self.ethertype { - for (b, d) in buf.iter_mut().zip(frame.data.iter()) { - *b = *d; - } - - return Ok(cmp::min(buf.len(), frame.data.len())); - } - } - - Ok(0) - } - - fn write(&mut self, buf: &[u8]) -> Result { - let data = Vec::from(buf); - - match syscall::write(self.network, &EthernetII { - header: EthernetIIHeader { - src: MacAddr { bytes: [0x50, 0x51, 0x52, 0x53, 0x54, 0x55] }, - dst: self.peer_addr, - ethertype: n16::new(self.ethertype), - }, - data: data, - } - .to_bytes()) { - Ok(_) => Ok(buf.len()), - Err(err) => Err(err), - } - } - - fn sync(&mut self) -> Result { - syscall::fsync(self.network) - } -} - -impl Drop for EthernetResource { - fn drop(&mut self) { - let _ = syscall::close(self.network); - } -} diff --git a/schemes/ethernetd/src/scheme.rs b/schemes/ethernetd/src/scheme.rs index a56924f..4eef6f4 100644 --- a/schemes/ethernetd/src/scheme.rs +++ b/schemes/ethernetd/src/scheme.rs @@ -1,18 +1,64 @@ -use std::{str, u16}; +use std::collections::{BTreeMap, VecDeque}; +use std::fs::File; +use std::io::{self, Read}; +use std::os::unix::io::AsRawFd; +use std::{cmp, str, u16}; -use netutils::{MacAddr, EthernetII}; -use resource_scheme::ResourceScheme; +use netutils::{getcfg, n16, MacAddr, EthernetII, EthernetIIHeader}; use syscall; -use syscall::error::{Error, Result, EACCES, ENOENT, EINVAL}; +use syscall::error::{Error, Result, EACCES, EBADF, ENOENT, EINVAL, EWOULDBLOCK}; use syscall::flag::O_RDWR; +use syscall::scheme::SchemeMut; -use resource::EthernetResource; +#[derive(Clone)] +pub struct Handle { + /// The Host's MAC address + pub host_addr: MacAddr, + /// The Peer's MAC address + pub peer_addr: MacAddr, + /// The ethernet type + pub ethertype: u16, + /// The data + pub frames: VecDeque, +} -pub struct EthernetScheme; +pub struct EthernetScheme { + network: File, + next_id: usize, + pub handles: BTreeMap +} -impl ResourceScheme for EthernetScheme { - fn open_resource(&self, url: &[u8], _flags: usize, uid: u32, _gid: u32) -> Result> { +impl EthernetScheme { + pub fn new(network: File) -> EthernetScheme { + EthernetScheme { + network: network, + next_id: 1, + handles: BTreeMap::new(), + } + } + + //TODO: Minimize allocation + //TODO: Reduce iteration cost (use BTreeMap of ethertype to handle?) + pub fn input(&mut self) -> io::Result { + let mut bytes = [0; 65536]; + let count = self.network.read(&mut bytes)?; + if let Some(frame) = EthernetII::from_bytes(&bytes[.. count]) { + for (_id, handle) in self.handles.iter_mut() { + if frame.header.ethertype.get() == handle.ethertype { + handle.frames.push_back(frame.clone()); + } + } + Ok(count) + } else { + Ok(0) + } + } +} + +impl SchemeMut for EthernetScheme { + fn open(&mut self, url: &[u8], _flags: usize, uid: u32, _gid: u32) -> Result { if uid == 0 { + let mac_addr = MacAddr::from_str(&getcfg("mac").map_err(|err| err.into_sys())?); let path = try!(str::from_utf8(url).or(Err(Error::new(EINVAL)))); let mut parts = path.split("/"); if let Some(host_string) = parts.next() { @@ -20,13 +66,18 @@ impl ResourceScheme for EthernetScheme { if let Ok(network) = syscall::open("network:", O_RDWR) { let ethertype = u16::from_str_radix(ethertype_string, 16).unwrap_or(0) as u16; - if !host_string.is_empty() { - return Ok(Box::new(EthernetResource { - network: network, - data: Vec::new(), + if ! host_string.is_empty() { + let next_id = self.next_id; + self.next_id += 1; + + self.handles.insert(next_id, Handle { + host_addr: mac_addr, peer_addr: MacAddr::from_str(host_string), ethertype: ethertype, - })); + frames: VecDeque::new() + }); + + return Ok(next_id); } else { loop { let mut bytes = [0; 65536]; @@ -34,12 +85,22 @@ impl ResourceScheme for EthernetScheme { Ok(count) => { if let Some(frame) = EthernetII::from_bytes(&bytes[..count]) { if frame.header.ethertype.get() == ethertype { - return Ok(Box::new(EthernetResource { - network: network, - data: frame.data, - peer_addr: frame.header.src, + let next_id = self.next_id; + self.next_id += 1; + + let peer_addr = frame.header.src; + + let mut frames = VecDeque::new(); + frames.push_back(frame); + + self.handles.insert(next_id, Handle { + host_addr: mac_addr, + peer_addr: peer_addr, ethertype: ethertype, - })); + frames: frames + }); + + return Ok(next_id); } } } @@ -62,4 +123,80 @@ impl ResourceScheme for EthernetScheme { Err(Error::new(EACCES)) } } + + fn dup(&mut self, id: usize) -> Result { + let next_id = self.next_id; + self.next_id += 1; + + let handle = { + let handle = self.handles.get(&id).ok_or(Error::new(EBADF))?; + handle.clone() + }; + + self.handles.insert(next_id, handle); + + Ok(next_id) + } + + fn read(&mut self, id: usize, buf: &mut [u8]) -> Result { + let handle = self.handles.get_mut(&id).ok_or(Error::new(EBADF))?; + + if let Some(frame) = handle.frames.pop_front() { + for (b, d) in buf.iter_mut().zip(frame.data.iter()) { + *b = *d; + } + + Ok(cmp::min(buf.len(), frame.data.len())) + } else { + Err(Error::new(EWOULDBLOCK)) + } + } + + fn write(&mut self, id: usize, buf: &[u8]) -> Result { + let handle = self.handles.get(&id).ok_or(Error::new(EBADF))?; + + match syscall::write(self.network.as_raw_fd(), &EthernetII { + header: EthernetIIHeader { + src: handle.host_addr, + dst: handle.peer_addr, + ethertype: n16::new(handle.ethertype), + }, + data: Vec::from(buf), + } + .to_bytes()) { + Ok(_) => Ok(buf.len()), + Err(err) => Err(err), + } + } + + fn fevent(&mut self, id: usize, _flags: usize) -> Result { + let _handle = self.handles.get(&id).ok_or(Error::new(EBADF))?; + + Ok(id) + } + + fn fpath(&mut self, id: usize, buf: &mut [u8]) -> Result { + let handle = self.handles.get(&id).ok_or(Error::new(EBADF))?; + + let path_string = format!("ethernet:{}/{:X}", handle.peer_addr.to_string(), handle.ethertype); + let path = path_string.as_bytes(); + + for (b, p) in buf.iter_mut().zip(path.iter()) { + *b = *p; + } + + Ok(cmp::min(buf.len(), path.len())) + } + + fn fsync(&mut self, id: usize) -> Result { + let _handle = self.handles.get(&id).ok_or(Error::new(EBADF))?; + + syscall::fsync(self.network.as_raw_fd()) + } + + fn close(&mut self, id: usize) -> Result { + let handle = self.handles.remove(&id).ok_or(Error::new(EBADF))?; + drop(handle); + Ok(0) + } }