From 303909439d0487f53dd038a671fcacf8b4e7e9d6 Mon Sep 17 00:00:00 2001 From: Alexander Lochmann Date: Tue, 30 Apr 2024 01:29:43 +0200 Subject: [PATCH] Basic version of SST example module --- drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/sst/Kconfig | 30 +++++ drivers/sst/Makefile | 6 + drivers/sst/boundedbuffer.h | 49 ++++++++ drivers/sst/sst_chrdev.c | 222 ++++++++++++++++++++++++++++++++++++ 6 files changed, 310 insertions(+) create mode 100644 drivers/sst/Kconfig create mode 100644 drivers/sst/Makefile create mode 100644 drivers/sst/boundedbuffer.h create mode 100644 drivers/sst/sst_chrdev.c diff --git a/drivers/Kconfig b/drivers/Kconfig index 19ee995bd0ae..6f6e61444f05 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -239,4 +239,6 @@ source "drivers/peci/Kconfig" source "drivers/hte/Kconfig" +source "drivers/sst/Kconfig" + endmenu diff --git a/drivers/Makefile b/drivers/Makefile index bdf1c66141c9..388e3878a98c 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -189,3 +189,4 @@ obj-$(CONFIG_COUNTER) += counter/ obj-$(CONFIG_MOST) += most/ obj-$(CONFIG_PECI) += peci/ obj-$(CONFIG_HTE) += hte/ +obj-$(CONFIG_SST) += sst/ diff --git a/drivers/sst/Kconfig b/drivers/sst/Kconfig new file mode 100644 index 000000000000..21876629986c --- /dev/null +++ b/drivers/sst/Kconfig @@ -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 diff --git a/drivers/sst/Makefile b/drivers/sst/Makefile new file mode 100644 index 000000000000..c54ff443d0db --- /dev/null +++ b/drivers/sst/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for the SST's faulty drivers +# + +obj-$(CONFIG_SST) += sst_chrdev.o diff --git a/drivers/sst/boundedbuffer.h b/drivers/sst/boundedbuffer.h new file mode 100644 index 000000000000..25ecc91eecf7 --- /dev/null +++ b/drivers/sst/boundedbuffer.h @@ -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__ diff --git a/drivers/sst/sst_chrdev.c b/drivers/sst/sst_chrdev.c new file mode 100644 index 000000000000..33c57e6ec7b5 --- /dev/null +++ b/drivers/sst/sst_chrdev.c @@ -0,0 +1,222 @@ +#define pr_fmt(fmt) KBUILD_MODNAME " " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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");