Basic version of SST example module
This commit is contained in:
parent
d23900f974
commit
303909439d
@ -239,4 +239,6 @@ source "drivers/peci/Kconfig"
|
|||||||
|
|
||||||
source "drivers/hte/Kconfig"
|
source "drivers/hte/Kconfig"
|
||||||
|
|
||||||
|
source "drivers/sst/Kconfig"
|
||||||
|
|
||||||
endmenu
|
endmenu
|
||||||
|
@ -189,3 +189,4 @@ obj-$(CONFIG_COUNTER) += counter/
|
|||||||
obj-$(CONFIG_MOST) += most/
|
obj-$(CONFIG_MOST) += most/
|
||||||
obj-$(CONFIG_PECI) += peci/
|
obj-$(CONFIG_PECI) += peci/
|
||||||
obj-$(CONFIG_HTE) += hte/
|
obj-$(CONFIG_HTE) += hte/
|
||||||
|
obj-$(CONFIG_SST) += sst/
|
||||||
|
30
drivers/sst/Kconfig
Normal file
30
drivers/sst/Kconfig
Normal 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
6
drivers/sst/Makefile
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
# SPDX-License-Identifier: GPL-2.0
|
||||||
|
#
|
||||||
|
# Makefile for the SST's faulty drivers
|
||||||
|
#
|
||||||
|
|
||||||
|
obj-$(CONFIG_SST) += sst_chrdev.o
|
49
drivers/sst/boundedbuffer.h
Normal file
49
drivers/sst/boundedbuffer.h
Normal 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
222
drivers/sst/sst_chrdev.c
Normal 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");
|
Loading…
Reference in New Issue
Block a user