From 7ee240a384854068b898e08e7e3366226047bacf Mon Sep 17 00:00:00 2001
From: Wedson Almeida Filho <wedsonaf@google.com>
Date: Tue, 26 Apr 2022 14:32:57 +0000
Subject: [PATCH] samples/rust: add echo server sample

This example uses Rust async for the server and a workqueue-based
executor to run async tasks.

Signed-off-by: Wedson Almeida Filho <wedsonaf@google.com>
---
 samples/rust/Kconfig             | 10 ++++++
 samples/rust/Makefile            |  1 +
 samples/rust/rust_echo_server.rs | 60 ++++++++++++++++++++++++++++++++
 3 files changed, 71 insertions(+)
 create mode 100644 samples/rust/rust_echo_server.rs

diff --git a/samples/rust/Kconfig b/samples/rust/Kconfig
index 4f90f8d6935188..fe923ef2ab93ba 100644
--- a/samples/rust/Kconfig
+++ b/samples/rust/Kconfig
@@ -130,6 +130,16 @@ config SAMPLE_RUST_NETFILTER
 
 	  If unsure, say N.
 
+config SAMPLE_RUST_ECHO_SERVER
+	tristate "Echo server module"
+	help
+	  This option builds the Rust echo server  module sample.
+
+	  To compile this as a module, choose M here:
+	  the module will be called rust_echo_server.
+
+	  If unsure, say N.
+
 config SAMPLE_RUST_HOSTPROGS
 	bool "Host programs"
 	help
diff --git a/samples/rust/Makefile b/samples/rust/Makefile
index fb5a205ebb8cf8..636cf1e73d23cc 100644
--- a/samples/rust/Makefile
+++ b/samples/rust/Makefile
@@ -12,5 +12,6 @@ obj-$(CONFIG_SAMPLE_RUST_SEMAPHORE_C)		+= rust_semaphore_c.o
 obj-$(CONFIG_SAMPLE_RUST_RANDOM)		+= rust_random.o
 obj-$(CONFIG_SAMPLE_RUST_PLATFORM)		+= rust_platform.o
 obj-$(CONFIG_SAMPLE_RUST_NETFILTER)		+= rust_netfilter.o
+obj-$(CONFIG_SAMPLE_RUST_ECHO_SERVER)		+= rust_echo_server.o
 
 subdir-$(CONFIG_SAMPLE_RUST_HOSTPROGS)		+= hostprogs
diff --git a/samples/rust/rust_echo_server.rs b/samples/rust/rust_echo_server.rs
new file mode 100644
index 00000000000000..5fc802f4dc33c2
--- /dev/null
+++ b/samples/rust/rust_echo_server.rs
@@ -0,0 +1,60 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Rust echo server sample.
+
+use kernel::{
+    kasync::executor::{workqueue::Executor as WqExecutor, AutoStopHandle, Executor},
+    kasync::net::{TcpListener, TcpStream},
+    net::{self, Ipv4Addr, SocketAddr, SocketAddrV4},
+    prelude::*,
+    spawn_task,
+    sync::{Ref, RefBorrow},
+};
+
+async fn echo_server(stream: TcpStream) -> Result {
+    let mut buf = [0u8; 1024];
+    loop {
+        let n = stream.read(&mut buf).await?;
+        if n == 0 {
+            return Ok(());
+        }
+        stream.write_all(&buf[..n]).await?;
+    }
+}
+
+async fn accept_loop(listener: TcpListener, executor: Ref<impl Executor>) {
+    loop {
+        if let Ok(stream) = listener.accept().await {
+            let _ = spawn_task!(executor.as_ref_borrow(), echo_server(stream));
+        }
+    }
+}
+
+fn start_listener(ex: RefBorrow<'_, impl Executor + Send + Sync + 'static>) -> Result {
+    let addr = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::ANY, 8080));
+    let listener = TcpListener::try_new(net::init_ns(), &addr)?;
+    spawn_task!(ex, accept_loop(listener, ex.into()))?;
+    Ok(())
+}
+
+struct RustEchoServer {
+    _handle: AutoStopHandle<dyn Executor>,
+}
+
+impl kernel::Module for RustEchoServer {
+    fn init(_name: &'static CStr, _module: &'static ThisModule) -> Result<Self> {
+        let handle = WqExecutor::try_new(kernel::workqueue::system())?;
+        start_listener(handle.executor())?;
+        Ok(Self {
+            _handle: handle.into(),
+        })
+    }
+}
+
+module! {
+    type: RustEchoServer,
+    name: b"rust_echo_server",
+    author: b"Rust for Linux Contributors",
+    description: b"Rust tcp echo sample",
+    license: b"GPL v2",
+}