Skip to content

jiacelyne/rustqueue-project

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

rustqueue — a bounded FIFO message queue as a Linux kernel module

rustqueue is a Linux kernel module that exposes a bounded FIFO message queue at /dev/rustqueue, written in safe Rust. It demonstrates how Rust's ownership and locking discipline make a small in-kernel IPC primitive easy to write and structurally free of buffer-handling bugs.


Demo

Basic enqueue and dequeue:

ubuntu@ln24:~/rustqueue-project$ echo "first message"  | sudo tee /dev/rustqueue > /dev/null
ubuntu@ln24:~/rustqueue-project$ echo "second message" | sudo tee /dev/rustqueue > /dev/null
ubuntu@ln24:~/rustqueue-project$ echo "third message"  | sudo tee /dev/rustqueue > /dev/null
ubuntu@ln24:~/rustqueue-project$ sudo cat /dev/rustqueue
first message
ubuntu@ln24:~/rustqueue-project$ sudo cat /dev/rustqueue
second message
ubuntu@ln24:~/rustqueue-project$ sudo cat /dev/rustqueue
third message

Kernel log output:

ubuntu@ln24:~/rustqueue-project$ sudo dmesg | tail -10
[  539.891725] rustqueue: loading out-of-tree module taints kernel.
[  539.891748] rustqueue: module verification failed: signature and/or required key missing - tainting kernel
[  539.892234] rustqueue: module loaded (capacity 16 messages)
[  616.489881] rustqueue: enqueued 14 bytes (1 in queue)
[  620.920965] rustqueue: enqueued 15 bytes (2 in queue)
[  628.330564] rustqueue: enqueued 14 bytes (3 in queue)
[  631.878653] rustqueue: dequeued (2 remaining)
[  635.882471] rustqueue: dequeued (1 remaining)
[  639.089140] rustqueue: dequeued (0 remaining)

Full-queue rejection (capacity is 16 messages):

ubuntu@ln24:~/rustqueue-project$ for i in {1..20}; do echo "message $i" | sudo tee /dev/rustqueue > /dev/null || echo "write $i FAILED"; done
/dev/rustqueue: No space left on device (os error 28)
write 17 FAILED
/dev/rustqueue: No space left on device (os error 28)
write 18 FAILED
/dev/rustqueue: No space left on device (os error 28)
write 19 FAILED
/dev/rustqueue: No space left on device (os error 28)
write 20 FAILED

What this is

rustqueue implements a simple in-kernel FIFO message queue exposed as a character device. Writing to /dev/rustqueue enqueues a message; reading from it dequeues and returns the oldest message (one message per cat invocation). The queue holds up to 16 messages of up to 4096 bytes each.

The module exists to demonstrate that Rust's ownership model and type-level locking discipline apply cleanly to kernel programming — the mutex protecting the queue can never be forgotten, double-locked, or used after the lock guard is dropped. The compiler enforces it.

Requires a Rust-enabled kernel (CONFIG_RUST=y). Ubuntu 26.04 LTS ships one out of the box. See Rust in the Linux kernel for background.


Build & run

Prerequisites:

sudo apt install -y build-essential linux-headers-$(uname -r) kmod
sudo apt install -y rustc-1.93 rust-1.93-src bindgen
sudo update-alternatives --install /usr/bin/rustc rustc /usr/bin/rustc-1.93 100

Build:

git clone https://github.com/jiacelyne/rustqueue-project.git
cd rustqueue-project
make

Load and test:

sudo insmod rustqueue.ko
ls -la /dev/rustqueue

echo "hello" | sudo tee /dev/rustqueue > /dev/null
sudo cat /dev/rustqueue   # → "hello"

sudo rmmod rustqueue

Note: /dev/rustqueue is created as root:root 0600. All reads and writes require sudo. Use sudo tee ... > /dev/null instead of sudo echo ... > — the redirect is evaluated before sudo runs.


Code tour

Start reading in this order:

  1. module! macro (line 13) — registers the module with the kernel, declares its name, description, and license.
  2. InPlaceModule::init (line 34) — module entry point. Initializes the global queue and registers the /dev/rustqueue misc device. Uses try_pin_init! because MiscDeviceRegistration must be pinned in memory.
  3. MiscDevice::open (line 55) — called on every open() syscall. Allocates a fresh RustQueueDevice with an empty pending slot for this file descriptor.
  4. write_iter (line 67) — enqueue path. Acquires the global QUEUE mutex, checks capacity, copies the user's bytes into a KVec<u8>, and pushes it onto the queue.
  5. read_iter (line 89) — dequeue path. On the first read() of a given open, pulls one message off the global queue into the per-open pending slot. Subsequent reads on the same descriptor stream bytes out of pending until EOF.

Design notes

Why one message per cat invocation?
The read_iter design lazily dequeues exactly one message on the first read() of each open file descriptor, then streams it out across however many read() syscalls cat issues until it sees EOF. This maps naturally to the cat mental model and avoids the complexity of partial-message buffering.


Future work

  • Partial reads. When the user's buffer is smaller than the next message, leave the unread tail in the queue instead of discarding it.
  • Per-process queues. Use the open() handler to give each process its own queue, or each open file descriptor its own session.
  • Configurable capacity. Expose MAX_MESSAGES as a module parameter (module_param!) so users can tune it without recompiling.
  • Non-blocking semantics. Implement poll() so user-space select/epoll can wait for queue activity efficiently.
  • Persistence. Serialize queue contents to a file on module unload and restore them on load.

License

GPL-2.0 — required to match the Linux kernel. See LICENSE.

About

No description, website, or topics provided.

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors