Skip to content

Commit 8b31e12

Browse files
committed
Add xoroshiro128+ based PRNG module
1 parent 60a047d commit 8b31e12

File tree

8 files changed

+334
-10
lines changed

8 files changed

+334
-10
lines changed

99-xoro.rules

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
KERNEL=="xoro", SUBSYSTEM=="xoro", MODE="0444"

Makefile

+19-8
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
1-
MODULENAME := ksort
2-
obj-m += $(MODULENAME).o
3-
$(MODULENAME)-y += main.o sort.o
1+
obj-m += ksort.o
2+
ksort-objs := \
3+
sort_mod.o \
4+
sort.o
5+
6+
obj-m += xoro.o
7+
xoro-objs := \
8+
xoro_mod.o
49

510
GIT_HOOKS := .git/hooks/applied
611

@@ -17,17 +22,23 @@ $(GIT_HOOKS):
1722
user: user.c
1823
$(CC) -o $@ $^
1924

20-
insmod: all
21-
sudo insmod $(MODULENAME).ko
25+
test_xoro: test_xoro.c
26+
$(CC) -o $@ $^
27+
28+
insmod: all rmmod
29+
sudo insmod ksort.ko
30+
sudo insmod xoro.ko
2231

2332
rmmod:
24-
sudo rmmod $(MODULENAME)
33+
@sudo rmmod ksort 2>/dev/null || echo
34+
@sudo rmmod xoro 2>/dev/null || echo
2535

26-
check:
36+
check: user test_xoro
2737
$(MAKE) insmod
2838
sudo ./user
39+
sudo ./test_xoro
2940
$(MAKE) rmmod
3041

3142
clean:
3243
$(MAKE) -C $(KDIR) M=$(PWD) clean
33-
$(RM) user
44+
$(RM) user test_xoro

README.md

+23
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,29 @@
22

33
A Linux kernel module that creates the device `/dev/ksort`, capable of performing concurrent sorts.
44

5+
## User access to the character device
6+
7+
In order to access the character device as a user you need to add a rule to
8+
udev with the included rules file.
9+
```
10+
$ sudo cp 99-xoro.rules /etc/udev/rules.d/
11+
$ sudo udevadm control --reload
12+
```
13+
14+
## Build and Test
15+
16+
Build from source:
17+
```shell
18+
$ make
19+
```
20+
21+
Test:
22+
```shell
23+
$ make check
24+
```
25+
26+
You should see also more messages in the kernel log.
27+
528
## References
629
* [The Linux Kernel Module Programming Guide](https://sysprog21.github.io/lkmpg/)
730
* [Writing a simple device driver](https://www.apriorit.com/dev-blog/195-simple-driver-for-linux-os)

scripts/aspell-pws

+4
Original file line numberDiff line numberDiff line change
@@ -167,3 +167,7 @@ tasklet
167167
kmalloc
168168
kzalloc
169169
kfree
170+
xoroshiro
171+
xoro
172+
RNG
173+
PRNG

main.c renamed to sort_mod.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ MODULE_AUTHOR("National Cheng Kung University, Taiwan");
1111
MODULE_DESCRIPTION("Concurrent sorting driver");
1212
MODULE_VERSION("0.1");
1313

14-
#define DEVICE_NAME "ksort"
14+
#define DEVICE_NAME "sort"
1515

1616
static dev_t dev = -1;
1717
static struct cdev cdev;

test_xoro.c

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
#include <errno.h>
2+
#include <fcntl.h>
3+
#include <stdint.h>
4+
#include <stdio.h>
5+
#include <stdlib.h>
6+
#include <unistd.h>
7+
8+
#define MAX_BYTES_PER_READ 8
9+
static unsigned char rx[MAX_BYTES_PER_READ]; // Receive buffer from the LKM.
10+
11+
void zero_rx(void)
12+
{
13+
for (int b_idx = 0; b_idx < MAX_BYTES_PER_READ; b_idx++) {
14+
rx[b_idx] = 0;
15+
}
16+
}
17+
18+
int main(int argc, char *argv[])
19+
{
20+
int fd = open("/dev/xoro", O_RDONLY);
21+
if (0 > fd) {
22+
perror("Failed to open the device.");
23+
return errno;
24+
}
25+
26+
// Test reading different numbers of bytes.
27+
for (int n_bytes = 0; n_bytes < 10; n_bytes++) {
28+
zero_rx(); // Clear/zero the buffer before copying in read data.
29+
30+
ssize_t n_bytes_read =
31+
read(fd, rx, n_bytes); // Read the response from the LKM
32+
33+
if (0 > n_bytes_read) {
34+
perror("Failed to read all bytes.");
35+
36+
return errno;
37+
}
38+
39+
uint64_t value_ = 0;
40+
for (int b_idx = 0; b_idx < n_bytes_read; b_idx++) {
41+
unsigned char b = rx[b_idx];
42+
value_ |= ((uint64_t) b << (8 * b_idx));
43+
}
44+
printf("n_bytes=%d n_bytes_read=%ld value=%016lx\n", n_bytes,
45+
n_bytes_read, value_);
46+
}
47+
fflush(stdout);
48+
49+
return 0;
50+
}

user.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
#include <string.h>
66
#include <unistd.h>
77

8-
#define KSORT_DEV "/dev/ksort"
8+
#define KSORT_DEV "/dev/sort"
99

1010
int main()
1111
{

xoro_mod.c

+235
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
/* Acharacter device which returns bytes from a PRNG in the xorshiro family. */
2+
3+
#include <linux/device.h>
4+
#include <linux/fs.h>
5+
#include <linux/init.h>
6+
#include <linux/kernel.h>
7+
#include <linux/module.h>
8+
#include <linux/mutex.h>
9+
#include <linux/uaccess.h>
10+
11+
#define DEVICE_NAME "xoro"
12+
#define CLASS_NAME "xoro"
13+
14+
MODULE_LICENSE("Dual MIT/GPL");
15+
MODULE_AUTHOR("National Cheng Kung University, Taiwan");
16+
MODULE_DESCRIPTION("Xoroshiro128p PRNG");
17+
MODULE_VERSION("0.1");
18+
19+
static int major_number;
20+
static struct class *dev_class = NULL;
21+
static struct device *dev_device = NULL;
22+
23+
static int n_opens = 0; /* Count the number of times device is opened. */
24+
25+
/* Mutex to allow only one userspace program to read at once. */
26+
static DEFINE_MUTEX(xoro_mutex);
27+
28+
/* This is xoroshiro128+ 1.0, written by David Blackman and Sebastiano Vigna
29+
* ([email protected]). It passes all tests we are aware of except for the four
30+
* lower bits, which might fail linearity tests (and just those), so if
31+
* low linear complexity is not considered an issue (as it is usually the
32+
* case) it can be used to generate 64-bit outputs, too; moreover, this
33+
* generator has a very mild Hamming-weight dependency making our test
34+
* (https://xoshiro.di.unimi.it/hwd.php) fail after 5 TB of output; we believe
35+
* this slight bias cannot affect any application. If you are concerned,
36+
* use xoroshiro128++, xoroshiro128** or xoshiro256+.
37+
*
38+
* We suggest to use a sign test to extract a random Boolean value, and
39+
* right shifts to extract subsets of bits.
40+
*
41+
* The state must be seeded so that it is not everywhere zero. If you have
42+
* a 64-bit seed, we suggest to seed a splitmix64 generator and use its
43+
* output to fill s.
44+
*
45+
* NOTE: the parameters (a=24, b=16, b=37) of this version give slightly
46+
* better results in our test than the 2016 version (a=55, b=14, c=36).
47+
*/
48+
49+
static inline uint64_t rotl(const uint64_t x, int k)
50+
{
51+
return (x << k) | (x >> (64 - k));
52+
}
53+
54+
static uint64_t s[2];
55+
56+
static void seed(uint64_t s0, uint64_t s1)
57+
{
58+
s[0] = s0;
59+
s[1] = s1;
60+
return;
61+
}
62+
63+
static uint64_t next(void)
64+
{
65+
const uint64_t s0 = s[0];
66+
uint64_t s1 = s[1];
67+
const uint64_t result = s0 + s1;
68+
69+
s1 ^= s0;
70+
s[0] = rotl(s0, 24) ^ s1 ^ (s1 << 16); /* a, b */
71+
s[1] = rotl(s1, 37); /* c */
72+
73+
return result;
74+
}
75+
76+
/* This is the jump function for the generator. It is equivalent to 2^64 calls
77+
* to next(); it can be used to generate 2^64 non-overlapping subsequences for
78+
* parallel computations.
79+
*/
80+
static void jump(void)
81+
{
82+
static const uint64_t JUMP[] = {0xdf900294d8f554a5, 0x170865df4b3201fc};
83+
84+
uint64_t s0 = 0;
85+
uint64_t s1 = 0;
86+
int i, b;
87+
for (i = 0; i < sizeof JUMP / sizeof *JUMP; i++)
88+
for (b = 0; b < 64; b++) {
89+
if (JUMP[i] & (uint64_t) (1) << b) {
90+
s0 ^= s[0];
91+
s1 ^= s[1];
92+
}
93+
next();
94+
}
95+
96+
s[0] = s0;
97+
s[1] = s1;
98+
}
99+
100+
static int dev_open(struct inode *, struct file *);
101+
static int dev_release(struct inode *, struct file *);
102+
static ssize_t dev_read(struct file *, char *, size_t, loff_t *);
103+
static struct file_operations fops = {
104+
.open = dev_open,
105+
.read = dev_read,
106+
.release = dev_release,
107+
};
108+
109+
/**
110+
* Initialize /dev/xoro.
111+
* Returns 0 if successful.
112+
*/
113+
static int __init xoro_init(void)
114+
{
115+
printk(KERN_INFO "XORO: Initializing...\n");
116+
117+
major_number = register_chrdev(0, DEVICE_NAME, &fops);
118+
if (0 > major_number) {
119+
printk(KERN_ALERT "XORO: Failed to register major_number\n");
120+
return major_number;
121+
}
122+
printk(KERN_INFO "XORO: major_number=%d\n", major_number);
123+
124+
dev_class = class_create(THIS_MODULE, CLASS_NAME);
125+
if (IS_ERR(dev_class)) {
126+
unregister_chrdev(major_number, DEVICE_NAME); // backout
127+
printk(KERN_ALERT "XORO: Failed to create dev_class\n");
128+
return PTR_ERR(dev_class);
129+
}
130+
printk(KERN_INFO "XORO: dev_class[name]=%s\n", CLASS_NAME);
131+
132+
// Register the device driver
133+
dev_device = device_create(dev_class, NULL, MKDEV(major_number, 0), NULL,
134+
DEVICE_NAME);
135+
if (IS_ERR(dev_device)) {
136+
class_destroy(dev_class); // backout
137+
unregister_chrdev(major_number, DEVICE_NAME); // backout
138+
printk(KERN_ALERT "XORO: Failed to create dev_device\n");
139+
return PTR_ERR(dev_device);
140+
}
141+
printk(KERN_INFO "XORO: dev_device[name]=%s\n", DEVICE_NAME);
142+
143+
mutex_init(&xoro_mutex);
144+
145+
seed(314159265, 1618033989); // Initialize PRNG with pi and phi.
146+
147+
printk(KERN_INFO "XORO: Initialized\n");
148+
return 0;
149+
}
150+
151+
/**
152+
* Free all module resources.
153+
* Not used if part of a built-in driver rather than a LKM.
154+
*/
155+
static void __exit xoro_exit(void)
156+
{
157+
mutex_destroy(&xoro_mutex);
158+
159+
device_destroy(dev_class, MKDEV(major_number, 0));
160+
161+
class_unregister(dev_class);
162+
class_destroy(dev_class);
163+
164+
unregister_chrdev(major_number, DEVICE_NAME);
165+
166+
printk(KERN_INFO "XORO: Exit\n");
167+
}
168+
169+
/**
170+
* open() syscall.
171+
* Increment counter, perform another jump to effectively give each
172+
* reader a separate PRNG.
173+
* @inodep Pointer to an inode object (defined in linux/fs.h)
174+
* @filep Pointer to a file object (defined in linux/fs.h)
175+
*/
176+
static int dev_open(struct inode *inodep, struct file *filep)
177+
{
178+
/* Try to acquire the mutex (returns 0 on fail) */
179+
if (!mutex_trylock(&xoro_mutex)) {
180+
printk(KERN_INFO "XORO: %s busy\n", DEVICE_NAME);
181+
return -EBUSY;
182+
}
183+
184+
jump(); // xorolus.c
185+
186+
printk(KERN_INFO "XORO: %s opened. n_opens=%d\n", DEVICE_NAME, n_opens++);
187+
188+
return 0;
189+
}
190+
191+
/**
192+
* Called whenever device is read from user space.
193+
* @filep Pointer to a file object (defined in linux/fs.h).
194+
* @buffer Pointer to the buffer to which this function may write data.
195+
* @len Number of bytes requested.
196+
* @offset Unused.
197+
* Returns number of bytes successfully read. Negative on error.
198+
*/
199+
static ssize_t dev_read(struct file *filep,
200+
char *buffer,
201+
size_t len,
202+
loff_t *offset)
203+
{
204+
// Give at most 8 bytes per read.
205+
size_t len_ = (len > 8) ? 8 : len;
206+
207+
uint64_t value = next(); // xorolus.c
208+
209+
// copy_to_user has the format ( * to, *from, size) and returns 0 on success
210+
int n_notcopied = copy_to_user(buffer, (char *) (&value), len_);
211+
212+
if (0 != n_notcopied) {
213+
printk(KERN_ALERT "XORO: Failed to read %d/%ld bytes\n", n_notcopied,
214+
len_);
215+
return -EFAULT;
216+
} else {
217+
printk(KERN_INFO "XORO: read %ld bytes\n", len_);
218+
return len_;
219+
}
220+
}
221+
222+
/**
223+
* Called when the userspace program calls close().
224+
* @inodep A pointer to an inode object (defined in linux/fs.h)
225+
* @filep A pointer to a file object (defined in linux/fs.h)
226+
*/
227+
static int dev_release(struct inode *inodep, struct file *filep)
228+
{
229+
mutex_unlock(&xoro_mutex);
230+
printk(KERN_INFO "XORO: %s closed\n", DEVICE_NAME);
231+
return 0;
232+
}
233+
234+
module_init(xoro_init);
235+
module_exit(xoro_exit);

0 commit comments

Comments
 (0)