Skip to content

Instantly share code, notes, and snippets.

@joelagnel
Last active March 17, 2023 14:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save joelagnel/458dad7e82160eb6265835dd0dacd70e to your computer and use it in GitHub Desktop.
Save joelagnel/458dad7e82160eb6265835dd0dacd70e to your computer and use it in GitHub Desktop.
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/kthread.h>
#include <linux/delay.h>
#include <linux/moduleparam.h>
#include <linux/list.h>
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("A kernel module to test mutex first-come, first-served behavior");
static int num_threads = 3;
static int num_iterations = 5;
static int thread_delay_ms = 10;
module_param(num_threads, int, 0);
MODULE_PARM_DESC(num_threads, "Number of threads to create (default: 3)");
module_param(num_iterations, int, 0);
MODULE_PARM_DESC(num_iterations, "Number of iterations per thread (default: 5)");
module_param(thread_delay_ms, int, 0);
MODULE_PARM_DESC(thread_delay_ms, "Delay between thread iterations in milliseconds (default: 10)");
static struct task_struct **threads;
static DEFINE_MUTEX(shared_resource_mutex);
static int shared_resource_counter = 0;
struct mutex_queue_node {
int id;
struct list_head list;
};
static LIST_HEAD(mutex_queue);
static int thread_fn(void *data)
{
int id = *(int *)data;
int i;
struct mutex_queue_node *node = NULL;
printk(KERN_INFO "Thread %d starting...\n", id);
for (i = 0; i < num_iterations; i++) {
mutex_lock(&shared_resource_mutex);
node = kmalloc(sizeof(*node), GFP_KERNEL);
if (!node) {
mutex_unlock(&shared_resource_mutex);
printk(KERN_ERR "Failed to allocate memory for mutex queue node\n");
return -ENOMEM;
}
node->id = id;
printk(KERN_INFO "Thread %d acquired the mutex\n", id);
if (!list_empty(&mutex_queue)) {
struct mutex_queue_node *last_node = list_entry(mutex_queue.prev, struct mutex_queue_node, list);
if (last_node->id != id) {
printk(KERN_ERR "Thread %d acquired mutex out of order\n", id);
BUG_ON(1);
}
}
/* Add node to the end of the queue */
list_add_tail(&node->list, &mutex_queue);
/* Access the shared resource */
shared_resource_counter++;
printk(KERN_INFO "Thread %d incremented the shared resource counter to %d\n",
id, shared_resource_counter);
/* Remove node from the front of the queue */
node = list_entry(mutex_queue.next, struct mutex_queue_node, list);
list_del(&node->list);
kfree(node);
printk(KERN_INFO "Thread %d released the mutex\n", id);
mutex_unlock(&shared_resource_mutex);
msleep(thread_delay_ms);
}
printk(KERN_INFO "Thread %d exiting...\n", id);
return 0;
}
int *thread_ids;
static int __init mutex_test_init(void)
{
int i;
printk(KERN_INFO "Mutex test module loaded\n");
if (num_threads < 1 || num_threads > 1000) {
printk(KERN_ERR "Invalid number of threads: %d (should be between 1 and 1000)\n", num_threads);
return -EINVAL;
}
thread_ids = kmalloc(num_threads * sizeof(int), GFP_KERNEL);
if (!thread_ids) {
printk(KERN_ERR "Failed to allocate memory for thread IDs\n");
return -ENOMEM;
}
threads = kmalloc(num_threads * sizeof(struct task_struct *), GFP_KERNEL);
if (!threads) {
printk(KERN_ERR "Failed to allocate memory for threads\n");
kfree(thread_ids);
return -ENOMEM;
}
for (i = 0; i < num_threads; i++) {
thread_ids[i] = i;
threads[i] = kthread_run(thread_fn, &thread_ids[i], "mutex-test-%d", i);
if (IS_ERR(threads[i])) {
printk(KERN_ERR "Failed to create thread %d\n", i);
kfree(thread_ids);
kfree(threads);
return PTR_ERR(threads[i]);
}
}
return 0;
}
static void __exit mutex_test_exit(void)
{
int i;
printk(KERN_INFO "Mutex test module unloaded\n");
for (i = 0; i < num_threads; i++) {
if (threads[i]) {
kthread_stop(threads[i]);
}
}
if (threads) {
kfree(threads);
}
if (thread_ids) {
kfree(thread_ids);
}
}
module_init(mutex_test_init);
module_exit(mutex_test_exit);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment