Basic version of SST example module

This commit is contained in:
Alexander Lochmann 2024-04-30 01:29:43 +02:00
parent d23900f974
commit 303909439d
6 changed files with 310 additions and 0 deletions

View File

@ -239,4 +239,6 @@ source "drivers/peci/Kconfig"
source "drivers/hte/Kconfig"
source "drivers/sst/Kconfig"
endmenu

View File

@ -189,3 +189,4 @@ obj-$(CONFIG_COUNTER) += counter/
obj-$(CONFIG_MOST) += most/
obj-$(CONFIG_PECI) += peci/
obj-$(CONFIG_HTE) += hte/
obj-$(CONFIG_SST) += sst/

30
drivers/sst/Kconfig Normal file
View File

@ -0,0 +1,30 @@
# SPDX-License-Identifier: GPL-2.0
#
# Character device configuration
#
menuconfig SST
bool "Modules for system software techniques"
default y
help
TODO
if SST
config SST_MEMLEAK
tristate "Memleak errors for exercise a1.2"
default y
help
TODO
config SST_BOUNDS
tristate "Out-of-bounds accesses for exercise a1.3"
default n
help
TODO
config SST_LOCKING
tristate "Fauulty locking for exercise a1.4"
default n
help
TODO
endif

6
drivers/sst/Makefile Normal file
View File

@ -0,0 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
#
# Makefile for the SST's faulty drivers
#
obj-$(CONFIG_SST) += sst_chrdev.o

View File

@ -0,0 +1,49 @@
#ifndef __BOUNDEDBUFFER_H__
#define __BOUNDEDBUFFER_H__
/*
* For some reasons, our ring buffer (aka BSB ring buffer)
* can only hold size - 1 elements.
* If we want to store DEFAULT_ITERATIONS elements, as desired,
* the buffer must be one element larger.
* Hence, RING_BUFFER_SIZE_REAL is used for allocating the actual buffer
* and used for the size member.
* In contrast, RING_BUFFER_SIZE_VIRT is used when asigning a new value
* for iterations in procfile_iter_write.
*/
#ifndef BOUNDEDBUFFER_SIZE
#error Buffer size not defined!
#endif
#define RING_BUFFER_SIZE_REAL (BOUNDEDBUFFER_SIZE + 1)
#define RING_BUFFER_SIZE_VIRT (RING_BUFFER_SIZE_REAL - 1)
#define RING_BUFFER_STORAGE_TYPE char*
struct boundedbuffer {
int next_in;
int next_out;
int size;
RING_BUFFER_STORAGE_TYPE data[RING_BUFFER_SIZE_REAL];
};
static noinline int is_full(volatile struct boundedbuffer *buffer) { return (buffer->next_in + 1) % buffer->size == buffer->next_out; }
static noinline int is_empty(volatile struct boundedbuffer *buffer) { return buffer->next_out == buffer->next_in; }
static noinline int produce(volatile struct boundedbuffer *buffer, RING_BUFFER_STORAGE_TYPE data) {
if (is_full(buffer)) {
return -1;
}
buffer->data[buffer->next_in] = data;
buffer->next_in = (buffer->next_in + 1) % buffer->size;
return 0;
}
static noinline int consume(volatile struct boundedbuffer *buffer, RING_BUFFER_STORAGE_TYPE *ret) {
if (is_empty(buffer)) {
return -1;
}
*ret = buffer->data[buffer->next_out];
buffer->next_out = (buffer->next_out + 1) % buffer->size;
return 0;
}
#endif // __BOUNDEDBUFFER_H__

222
drivers/sst/sst_chrdev.c Normal file
View File

@ -0,0 +1,222 @@
#define pr_fmt(fmt) KBUILD_MODNAME " " fmt
#include <linux/module.h>
#include <linux/printk.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/kthread.h>
#include <linux/cdev.h>
#include <linux/delay.h>
#include <linux/random.h>
#define BOUNDEDBUFFER_SIZE 20
#include "boundedbuffer.h"
#define MOD_NAME "SST memleak"
#define SST_MEMLEAK_CHRDEV "the-universe"
#define sst_debug(format, ...) \
pr_debug("(fn:%s,line:%d,pid:%d): " format, \
__func__, __LINE__, \
current->pid, ##__VA_ARGS__)
typedef struct {
struct boundedbuffer questions;
struct semaphore answers_rdy;
struct boundedbuffer answers;
struct task_struct *thread;
wait_queue_head_t wait;
spinlock_t lock;
} sst_info_t;
static int sst_chrdev_major = 0;
static struct device *my_universe = NULL;
static sst_info_t *sst_info = NULL;
static char the_answer[] = "The answer is 42.\n";
static char an_answer[] = "You shall ask a question!\n";
static int control_thread_work(void *data) {
sst_info_t *sst_data = (sst_info_t*)data;
int err, sleep = 0;
size_t len_answer = 0, len_question = 0;
char *question = NULL, *answer = NULL;
sst_debug("Using data at %lx\n", (uintptr_t)sst_info);
while(1) {
wait_event_interruptible(sst_data->wait, !is_empty(&sst_info->questions));
if (kthread_should_stop()) {
break;
}
spin_lock(&sst_data->lock);
err = consume(&sst_data->questions, &question);
spin_unlock(&sst_data->lock);
if (err) {
pr_err("Questions was empty!\n");
continue;
} else {
sst_debug("Received msg '%s'\n", question);
} len_answer = max(sizeof(the_answer),sizeof(an_answer)) + 1;
answer = kmalloc(len_answer, GFP_KERNEL);
if (!answer) {
pr_err("Cannot allocate memory for answer!\n");
kfree(question);
continue;
}
len_question = strlen(question);
if (question[len_question - 1] == '?') {
snprintf(answer, len_answer, "%s", the_answer);
} else {
snprintf(answer, len_answer, "%s", an_answer);
}
if (produce(&sst_data->answers, answer)) {
pr_err("Answers is full!\n");
kfree(answer);
} else {
sst_debug("The universe has an answer for you!\n");
up(&sst_data->answers_rdy);
}
sleep = get_random_u8() % 90;
sst_debug("Randomly sleeping for %d secs. ZzzzZZzz\n", sleep);
ssleep(sleep);
kfree(question);
}
return 0;
}
static int universe_open(struct inode *inode, struct file *file) {
file->private_data = sst_info;
return 0;
}
static int universe_release(struct inode *inode, struct file *file) {
return 0;
}
static ssize_t universe_read(struct file *file, char __user *buf, size_t count,
loff_t *ppos) {
sst_info_t *sst_info = (sst_info_t*)file->private_data;
char *answer = NULL;
int remaining = 0;
size_t len = 0;
if (down_trylock(&sst_info->answers_rdy)) {
sst_debug("Sry. No answer for you!\n");
return 0;
}
spin_lock(&sst_info->lock);
if (consume(&sst_info->answers, &answer)) {
pr_err("Cannot read from answers!\n");
}
spin_unlock(&sst_info->lock);
len = strlen(answer);
remaining = copy_to_user(buf, answer, count);
if (remaining) {
pr_err("Sorry, your buffer is %d bytes too small.\n", remaining);
return -EFAULT;
}
sst_debug("Copied %lu bytes of your answer to the userspace: %s\n", len, answer);
*ppos += len;
return len;
}
static ssize_t universe_write(struct file *file, const char __user *buf, size_t count,
loff_t *ppos) {
sst_info_t *sst_info = (sst_info_t*)file->private_data;
char *buf_copy;
int err;
buf_copy = memdup_user_nul(buf, count);
if (IS_ERR(buf_copy)) {
spin_unlock(&sst_info->lock);
return PTR_ERR(buf_copy);
}
spin_lock(&sst_info->lock);
err = produce(&sst_info->questions, buf_copy);
spin_unlock(&sst_info->lock);
if (err) {
pr_err("Weird! The universe is full.\n");
return -ENOMEM;
}
sst_debug("Asked the universe a question...\n");
wake_up_interruptible(&sst_info->wait);
return count;
}
static const struct file_operations universe_fops = {
.owner = THIS_MODULE,
.read = universe_read,
.write = universe_write,
.open = universe_open,
.release = universe_release,
};
static struct class universe_class = {
.name = SST_MEMLEAK_CHRDEV,
};
static int __init sst_memleak_init(void) {
int err;
sst_chrdev_major = err = register_chrdev(0, SST_MEMLEAK_CHRDEV, &universe_fops);
if (err < 0) {
pr_err("Cannot register chrdev: %d\n", err);
goto out;
}
err = class_register(&universe_class);
if (err) {
pr_err("Cannot register universe class: %d\n", err);
goto out_chrdev;
}
my_universe = device_create(&universe_class, NULL, MKDEV(sst_chrdev_major, 0), NULL, SST_MEMLEAK_CHRDEV);
if (IS_ERR(my_universe)) {
err = PTR_ERR(my_universe);
pr_err("Cannot create device: %d\n", err);
goto out_class;
}
sst_info = kmalloc(sizeof(*sst_info), GFP_KERNEL);
if (!sst_info) {
return -ENOMEM;
}
spin_lock_init(&sst_info->lock);
sema_init(&sst_info->answers_rdy, 0);
init_waitqueue_head(&sst_info->wait);
sst_info->questions.size = RING_BUFFER_SIZE_REAL;
sst_info->answers.size = RING_BUFFER_SIZE_REAL;
sst_debug("Allocated private data for the universe at %lx\n", (uintptr_t)sst_info);
sst_info->thread = kthread_create(control_thread_work, sst_info, "sst-memleak-%d", 1);
if (IS_ERR(sst_info->thread)) {
kfree(sst_info); //TODO
return -EFAULT;
}
wake_up_process(sst_info->thread);
sst_debug("Created and started control thread (%d)\n", sst_info->thread->pid);
pr_notice("Loaded module %s\n", MOD_NAME);
return 0;
out_class:
class_unregister(&universe_class);
out_chrdev:
unregister_chrdev(sst_chrdev_major, SST_MEMLEAK_CHRDEV);
out:
return err;
}
static void __exit sst_memleak_exit(void) {
sst_debug("Waiting for control thread (%d) to terminate...\n", sst_info->thread->pid);
kthread_stop(sst_info->thread);
sst_debug("Control thread (%d) to terminated.\n", sst_info->thread->pid);
kfree(sst_info);
sst_debug("Freed private data for the universe at %lx\n", (uintptr_t)sst_info);
device_destroy(&universe_class, MKDEV(sst_chrdev_major, 0));
class_unregister(&universe_class);
unregister_chrdev(sst_chrdev_major, SST_MEMLEAK_CHRDEV);
pr_notice("Unloaded module %s\n", MOD_NAME);
}
module_init(sst_memleak_init);
module_exit(sst_memleak_exit);
MODULE_LICENSE("LGPL");