sixlowpan_benchmark.rs 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. //! 6lowpan benchmark example
  2. //!
  3. //! This example runs a simple TCP throughput benchmark using the 6lowpan implementation in smoltcp
  4. //! It is designed to run using the Linux ieee802154/6lowpan support,
  5. //! using mac802154_hwsim.
  6. //!
  7. //! mac802154_hwsim allows you to create multiple "virtual" radios and specify
  8. //! which is in range with which. This is very useful for testing without
  9. //! needing real hardware. By default it creates two interfaces `wpan0` and
  10. //! `wpan1` that are in range with each other. You can customize this with
  11. //! the `wpan-hwsim` tool.
  12. //!
  13. //! We'll configure Linux to speak 6lowpan on `wpan0`, and leave `wpan1`
  14. //! unconfigured so smoltcp can use it with a raw socket.
  15. //!
  16. //!
  17. //!
  18. //!
  19. //!
  20. //! # Setup
  21. //!
  22. //! modprobe mac802154_hwsim
  23. //!
  24. //! ip link set wpan0 down
  25. //! ip link set wpan1 down
  26. //! iwpan dev wpan0 set pan_id 0xbeef
  27. //! iwpan dev wpan1 set pan_id 0xbeef
  28. //! ip link add link wpan0 name lowpan0 type lowpan
  29. //! ip link set wpan0 up
  30. //! ip link set wpan1 up
  31. //! ip link set lowpan0 up
  32. //!
  33. //!
  34. //! # Running
  35. //!
  36. //! Compile with `cargo build --release --example sixlowpan_benchmark`
  37. //! Run it with `sudo ./target/release/examples/sixlowpan_benchmark [reader|writer]`.
  38. //!
  39. //! # Teardown
  40. //!
  41. //! rmmod mac802154_hwsim
  42. //!
  43. mod utils;
  44. use std::os::unix::io::AsRawFd;
  45. use std::str;
  46. use smoltcp::iface::{Config, Interface, SocketSet};
  47. use smoltcp::phy::{wait as phy_wait, Device, Medium, RawSocket};
  48. use smoltcp::socket::tcp;
  49. use smoltcp::wire::{EthernetAddress, Ieee802154Address, Ieee802154Pan, IpAddress, IpCidr};
  50. //For benchmark
  51. use smoltcp::time::{Duration, Instant};
  52. use std::cmp;
  53. use std::io::{Read, Write};
  54. use std::net::SocketAddrV6;
  55. use std::net::TcpStream;
  56. use std::sync::atomic::{AtomicBool, Ordering};
  57. use std::thread;
  58. use std::fs;
  59. fn if_nametoindex(ifname: &str) -> u32 {
  60. let contents = fs::read_to_string(format!("/sys/devices/virtual/net/{ifname}/ifindex"))
  61. .expect("couldn't read interface from \"/sys/devices/virtual/net\"")
  62. .replace('\n', "");
  63. contents.parse::<u32>().unwrap()
  64. }
  65. const AMOUNT: usize = 100_000_000;
  66. enum Client {
  67. Reader,
  68. Writer,
  69. }
  70. fn client(kind: Client) {
  71. let port: u16 = match kind {
  72. Client::Reader => 1234,
  73. Client::Writer => 1235,
  74. };
  75. let scope_id = if_nametoindex("lowpan0");
  76. let socket_addr = SocketAddrV6::new(
  77. "fe80:0:0:0:180b:4242:4242:4242".parse().unwrap(),
  78. port,
  79. 0,
  80. scope_id,
  81. );
  82. let mut stream = TcpStream::connect(socket_addr).expect("failed to connect TLKAGMKA");
  83. let mut buffer = vec![0; 1_000_000];
  84. let start = Instant::now();
  85. let mut processed = 0;
  86. while processed < AMOUNT {
  87. let length = cmp::min(buffer.len(), AMOUNT - processed);
  88. let result = match kind {
  89. Client::Reader => stream.read(&mut buffer[..length]),
  90. Client::Writer => stream.write(&buffer[..length]),
  91. };
  92. match result {
  93. Ok(0) => break,
  94. Ok(result) => {
  95. // print!("(P:{})", result);
  96. processed += result
  97. }
  98. Err(err) => panic!("cannot process: {err}"),
  99. }
  100. }
  101. let end = Instant::now();
  102. let elapsed = (end - start).total_millis() as f64 / 1000.0;
  103. println!("throughput: {:.3} Gbps", AMOUNT as f64 / elapsed / 0.125e9);
  104. CLIENT_DONE.store(true, Ordering::SeqCst);
  105. }
  106. static CLIENT_DONE: AtomicBool = AtomicBool::new(false);
  107. fn main() {
  108. #[cfg(feature = "log")]
  109. utils::setup_logging("info");
  110. let (mut opts, mut free) = utils::create_options();
  111. utils::add_middleware_options(&mut opts, &mut free);
  112. free.push("MODE");
  113. let mut matches = utils::parse_options(&opts, free);
  114. let device = RawSocket::new("wpan1", Medium::Ieee802154).unwrap();
  115. let fd = device.as_raw_fd();
  116. let mut device =
  117. utils::parse_middleware_options(&mut matches, device, /*loopback=*/ false);
  118. let mode = match matches.free[0].as_ref() {
  119. "reader" => Client::Reader,
  120. "writer" => Client::Writer,
  121. _ => panic!("invalid mode"),
  122. };
  123. // Create interface
  124. let mut config = match device.capabilities().medium {
  125. Medium::Ethernet => {
  126. Config::new(EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]).into())
  127. }
  128. Medium::Ip => Config::new(smoltcp::wire::HardwareAddress::Ip),
  129. Medium::Ieee802154 => Config::new(
  130. Ieee802154Address::Extended([0x1a, 0x0b, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42]).into(),
  131. ),
  132. };
  133. config.random_seed = rand::random();
  134. config.pan_id = Some(Ieee802154Pan(0xbeef));
  135. let mut iface = Interface::new(config, &mut device, Instant::now());
  136. iface.update_ip_addrs(|ip_addrs| {
  137. ip_addrs
  138. .push(IpCidr::new(
  139. IpAddress::v6(0xfe80, 0, 0, 0, 0x180b, 0x4242, 0x4242, 0x4242),
  140. 64,
  141. ))
  142. .unwrap();
  143. });
  144. let tcp1_rx_buffer = tcp::SocketBuffer::new(vec![0; 4096]);
  145. let tcp1_tx_buffer = tcp::SocketBuffer::new(vec![0; 4096]);
  146. let tcp1_socket = tcp::Socket::new(tcp1_rx_buffer, tcp1_tx_buffer);
  147. let tcp2_rx_buffer = tcp::SocketBuffer::new(vec![0; 4096]);
  148. let tcp2_tx_buffer = tcp::SocketBuffer::new(vec![0; 4096]);
  149. let tcp2_socket = tcp::Socket::new(tcp2_rx_buffer, tcp2_tx_buffer);
  150. let mut sockets = SocketSet::new(vec![]);
  151. let tcp1_handle = sockets.add(tcp1_socket);
  152. let tcp2_handle = sockets.add(tcp2_socket);
  153. let default_timeout = Some(Duration::from_millis(1000));
  154. thread::spawn(move || client(mode));
  155. let mut processed = 0;
  156. while !CLIENT_DONE.load(Ordering::SeqCst) {
  157. let timestamp = Instant::now();
  158. iface.poll(timestamp, &mut device, &mut sockets);
  159. // tcp:1234: emit data
  160. let socket = sockets.get_mut::<tcp::Socket>(tcp1_handle);
  161. if !socket.is_open() {
  162. socket.listen(1234).unwrap();
  163. }
  164. if socket.can_send() && processed < AMOUNT {
  165. let length = socket
  166. .send(|buffer| {
  167. let length = cmp::min(buffer.len(), AMOUNT - processed);
  168. (length, length)
  169. })
  170. .unwrap();
  171. processed += length;
  172. }
  173. // tcp:1235: sink data
  174. let socket = sockets.get_mut::<tcp::Socket>(tcp2_handle);
  175. if !socket.is_open() {
  176. socket.listen(1235).unwrap();
  177. }
  178. if socket.can_recv() && processed < AMOUNT {
  179. let length = socket
  180. .recv(|buffer| {
  181. let length = cmp::min(buffer.len(), AMOUNT - processed);
  182. (length, length)
  183. })
  184. .unwrap();
  185. processed += length;
  186. }
  187. match iface.poll_at(timestamp, &sockets) {
  188. Some(poll_at) if timestamp < poll_at => {
  189. phy_wait(fd, Some(poll_at - timestamp)).expect("wait error");
  190. }
  191. Some(_) => (),
  192. None => {
  193. phy_wait(fd, default_timeout).expect("wait error");
  194. }
  195. }
  196. }
  197. }