diff --git a/tools/labs/skels/deferred_work/1-2-timer/Kbuild b/tools/labs/skels/deferred_work/1-2-timer/Kbuild new file mode 100644 index 00000000000000..fa3cd3e263f84b --- /dev/null +++ b/tools/labs/skels/deferred_work/1-2-timer/Kbuild @@ -0,0 +1,3 @@ +ccflags-y = -Wno-unused-function -Wno-unused-label -Wno-unused-variable + +obj-m = timer.o diff --git a/tools/labs/skels/deferred_work/1-2-timer/timer.c b/tools/labs/skels/deferred_work/1-2-timer/timer.c new file mode 100644 index 00000000000000..7d386cb2698327 --- /dev/null +++ b/tools/labs/skels/deferred_work/1-2-timer/timer.c @@ -0,0 +1,50 @@ +/* + * Deferred Work + * + * Exercise #1, #2: simple timer + */ + +#include +#include +#include +#include + +MODULE_DESCRIPTION("Simple kernel timer"); +MODULE_AUTHOR("SO2"); +MODULE_LICENSE("GPL"); + +#define TIMER_TIMEOUT 1 + +static struct timer_list timer; + +static void timer_handler(struct timer_list *tl) +{ + /* TODO 1: print a message */ + pr_info("Hello timer!\n"); + + /* TODO 2: rechedule timer */ + mod_timer(&timer, jiffies + TIMER_TIMEOUT * HZ); +} + +static int __init timer_init(void) +{ + pr_info("[timer_init] Init module\n"); + + /* TODO 1: initialize timer */ + timer_setup(&timer, timer_handler, 0); + + /* TODO 1: schedule timer for the first time */ + mod_timer(&timer, jiffies + TIMER_TIMEOUT * HZ); + return 0; +} + +static void __exit timer_exit(void) +{ + pr_info("[timer_exit] Exit module\n"); + + /* TODO 1: cleanup; make sure the timer is not running after we exit */ + del_timer_sync(&timer); +} + +module_init(timer_init); +module_exit(timer_exit); diff --git a/tools/labs/skels/deferred_work/3-4-5-deferred/include/deferred.h b/tools/labs/skels/deferred_work/3-4-5-deferred/include/deferred.h new file mode 100644 index 00000000000000..f9408c704401ad --- /dev/null +++ b/tools/labs/skels/deferred_work/3-4-5-deferred/include/deferred.h @@ -0,0 +1,35 @@ +/* + * SO2 - Lab 6 - Deferred Work + * + * Exercises #3, #4, #5: deferred work + * + * Header file. + */ + +#ifndef __DEFERRED_H__ +#define __DEFERRED_H__ + +#include + +#define MY_IOCTL_TIMER_SET _IOW('k', 1, unsigned long) +#define MY_IOCTL_TIMER_CANCEL _IO ('k', 2) +#define MY_IOCTL_TIMER_ALLOC _IOW('k', 3, unsigned long) +#define MY_IOCTL_TIMER_MON _IO ('k', 4) + +/* converts ioctl command code to message */ +inline static char *ioctl_command_to_string(int cmd) +{ + switch(cmd) { + case MY_IOCTL_TIMER_SET: + return "Set timer"; + case MY_IOCTL_TIMER_CANCEL: + return "Cancel timer"; + case MY_IOCTL_TIMER_ALLOC: + return "Allocate memory"; + case MY_IOCTL_TIMER_MON: + return "Monitor pid"; + } + return "Unknown command"; +} + +#endif /* __DEFERRED_H__ */ diff --git a/tools/labs/skels/deferred_work/3-4-5-deferred/kernel/Kbuild b/tools/labs/skels/deferred_work/3-4-5-deferred/kernel/Kbuild new file mode 100644 index 00000000000000..fa3f727c8a53d2 --- /dev/null +++ b/tools/labs/skels/deferred_work/3-4-5-deferred/kernel/Kbuild @@ -0,0 +1,3 @@ +ccflags-y = -Wno-unused-function -Wno-unused-label -Wno-unused-variable + +obj-m = deferred.o diff --git a/tools/labs/skels/deferred_work/3-4-5-deferred/kernel/deferred.c b/tools/labs/skels/deferred_work/3-4-5-deferred/kernel/deferred.c new file mode 100644 index 00000000000000..b7844c6690aea1 --- /dev/null +++ b/tools/labs/skels/deferred_work/3-4-5-deferred/kernel/deferred.c @@ -0,0 +1,258 @@ +/* + * SO2 - Lab 6 - Deferred Work + * + * Exercises #3, #4, #5: deferred work + * + * Code skeleton. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../include/deferred.h" + +#define MY_MAJOR 42 +#define MY_MINOR 0 +#define MODULE_NAME "deferred" + +#define TIMER_TYPE_NONE -1 +#define TIMER_TYPE_SET 0 +#define TIMER_TYPE_ALLOC 1 +#define TIMER_TYPE_MON 2 + +MODULE_DESCRIPTION("Deferred work character device"); +MODULE_AUTHOR("SO2"); +MODULE_LICENSE("GPL"); + +struct mon_proc { + struct task_struct *task; + struct list_head list; +}; + +static struct my_device_data { + struct cdev cdev; + /* TODO 1: add timer */ + struct timer_list timer; + bool already_deleted_timer; + /* TODO 2: add flag */ + bool use_blocking_op; + /* TODO 3: add work */ + struct work_struct my_work; + /* TODO 4: add list for monitored processes */ + struct list_head list; + /* TODO 4: add spinlock to protect list */ + spinlock_t lock; + bool flag; +} dev; + +static void alloc_io(void) +{ + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(5 * HZ); + pr_info("Yawn! I've been sleeping for 5 seconds.\n"); +} + +static struct mon_proc *get_proc(pid_t pid) +{ + struct task_struct *task; + struct mon_proc *p; + + rcu_read_lock(); + task = pid_task(find_vpid(pid), PIDTYPE_PID); + rcu_read_unlock(); + if (!task) + return ERR_PTR(-ESRCH); + + p = kmalloc(sizeof(*p), GFP_ATOMIC); + if (!p) + return ERR_PTR(-ENOMEM); + + get_task_struct(task); + p->task = task; + + return p; +} + + +/* TODO 3: define work handler */ +static void my_work_handler(struct work_struct * work) +{ + alloc_io(); +} + +#define ALLOC_IO_DIRECT +/* TODO 3: undef ALLOC_IO_DIRECT*/ +#undef ALLOC_IO_DIRECT + +static void timer_handler(struct timer_list *tl) +{ + /* TODO 1: implement timer handler */ + if (!dev.flag) { + /* TODO 2: check flags: TIMER_TYPE_SET or TIMER_TYPE_ALLOC */ + if (!dev.use_blocking_op) { + struct task_struct *p = current; + pr_info("Comm: %s\n", p->comm); + pr_info("PID: %d\n", p->pid); + } else { + /* TODO 3: schedule work */ + schedule_work(&dev.my_work); + } + } else { + /* TODO 4: iterate the list and check the proccess state */ + struct mon_proc *p, *n; + spin_lock(&dev.lock); + list_for_each_entry_safe(p, n, &dev.list, list) { + // /* TODO 4: if task is dead print info ... */ + // /* TODO 4: ... decrement task usage counter ... */ + // /* TODO 4: ... remove it from the list ... */ + // /* TODO 4: ... free the struct mon_proc */ + if (p->task->state == TASK_DEAD) { + pr_info("Delete proc from list %d\n", p->task->pid); + put_task_struct(p->task); + list_del(&p->list); + kfree(p); + } + } + spin_unlock(&dev.lock); + mod_timer(&dev.timer, jiffies + HZ); + } +} + +static int deferred_open(struct inode *inode, struct file *file) +{ + struct my_device_data *my_data = + container_of(inode->i_cdev, struct my_device_data, cdev); + file->private_data = my_data; + pr_info("[deferred_open] Device opened\n"); + return 0; +} + +static int deferred_release(struct inode *inode, struct file *file) +{ + pr_info("[deferred_release] Device released\n"); + return 0; +} + +static long deferred_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct my_device_data *my_data = (struct my_device_data*) file->private_data; + + pr_info("[deferred_ioctl] Command: %s\n", ioctl_command_to_string(cmd)); + + switch (cmd) { + case MY_IOCTL_TIMER_SET: + /* TODO 2: set flag */ + my_data->use_blocking_op = false; + /* TODO 1: schedule timer */ + mod_timer(&my_data->timer, jiffies + arg * HZ); + my_data->already_deleted_timer = false; + break; + case MY_IOCTL_TIMER_CANCEL: + /* TODO 1: cancel timer */ + del_timer_sync(&my_data->timer); + my_data->already_deleted_timer = true; + break; + case MY_IOCTL_TIMER_ALLOC: + /* TODO 2: set flag and schedule timer */ + my_data->use_blocking_op = true; + pr_info("ioctl: %d\n", my_data->use_blocking_op); + mod_timer(&my_data->timer, jiffies + arg * HZ); + break; + case MY_IOCTL_TIMER_MON: + { + /* TODO 4: use get_proc() and add task to list */ + struct mon_proc *p = get_proc(arg); + + /* TODO 4: protect access to list */ + spin_lock_bh(&my_data->lock); + if (p) { + list_add(&p->list, &my_data->list); + } + spin_unlock_bh(&my_data->lock); + + /* TODO 4: set flag and schedule timer */ + my_data->flag = true; + mod_timer(&my_data->timer, jiffies + HZ); + break; + } + default: + return -ENOTTY; + } + return 0; +} + +struct file_operations my_fops = { + .owner = THIS_MODULE, + .open = deferred_open, + .release = deferred_release, + .unlocked_ioctl = deferred_ioctl, +}; + +static int deferred_init(void) +{ + int err; + + pr_info("[deferred_init] Init module\n"); + err = register_chrdev_region(MKDEV(MY_MAJOR, MY_MINOR), 1, MODULE_NAME); + if (err) { + pr_info("[deffered_init] register_chrdev_region: %d\n", err); + return err; + } + + /* TODO 2: Initialize flag. */ + dev.use_blocking_op = false; + dev.flag = false; + /* TODO 3: Initialize work. */ + INIT_WORK(&dev.my_work, my_work_handler); + + /* TODO 4: Initialize lock and list. */ + spin_lock_init(&dev.lock); + INIT_LIST_HEAD(&dev.list); + + cdev_init(&dev.cdev, &my_fops); + cdev_add(&dev.cdev, MKDEV(MY_MAJOR, MY_MINOR), 1); + + /* TODO 1: Initialize timer. */ + timer_setup(&dev.timer, timer_handler, 0); + dev.already_deleted_timer = false; + return 0; +} + +static void deferred_exit(void) +{ + struct mon_proc *p, *n; + + pr_info("[deferred_exit] Exit module\n" ); + + cdev_del(&dev.cdev); + unregister_chrdev_region(MKDEV(MY_MAJOR, MY_MINOR), 1); + + /* TODO 1: Cleanup: make sure the timer is not running after exiting. */ + if (!dev.already_deleted_timer) { + del_timer_sync(&dev.timer); + dev.already_deleted_timer = true; + } + + /* TODO 3: Cleanup: make sure the work handler is not scheduled. */ + flush_scheduled_work(); + /* TODO 4: Cleanup the monitered process list */ + spin_lock(&dev.lock); + list_for_each_entry_safe(p, n, &dev.list, list) { + /* TODO 4: ... decrement task usage counter ... */ + /* TODO 4: ... remove it from the list ... */ + /* TODO 4: ... free the struct mon_proc */ + put_task_struct(p->task); + list_del(&p->list); + kfree(p); + } + spin_unlock(&dev.lock); +} + +module_init(deferred_init); +module_exit(deferred_exit); diff --git a/tools/labs/skels/deferred_work/3-4-5-deferred/kernel/makenode b/tools/labs/skels/deferred_work/3-4-5-deferred/kernel/makenode new file mode 100644 index 00000000000000..1e46669d4a1c98 --- /dev/null +++ b/tools/labs/skels/deferred_work/3-4-5-deferred/kernel/makenode @@ -0,0 +1,9 @@ +#!/bin/sh + +device=deferred +type=c +major=42 +minor=0 + +rm -f /dev/${device} +mknod /dev/${device} $type $major $minor && ls -al /dev/${device} diff --git a/tools/labs/skels/deferred_work/3-4-5-deferred/user/.gitignore b/tools/labs/skels/deferred_work/3-4-5-deferred/user/.gitignore new file mode 100644 index 00000000000000..ee4c92682341e4 --- /dev/null +++ b/tools/labs/skels/deferred_work/3-4-5-deferred/user/.gitignore @@ -0,0 +1 @@ +/test diff --git a/tools/labs/skels/deferred_work/3-4-5-deferred/user/Makefile b/tools/labs/skels/deferred_work/3-4-5-deferred/user/Makefile new file mode 100644 index 00000000000000..62768622c42fee --- /dev/null +++ b/tools/labs/skels/deferred_work/3-4-5-deferred/user/Makefile @@ -0,0 +1,9 @@ +CFLAGS=-Wall -m32 +LDFLAGS=-static -m32 + +test: test.o + +.PHONY: clean + +clean: + -rm -f *~ *.o test diff --git a/tools/labs/skels/deferred_work/3-4-5-deferred/user/test.c b/tools/labs/skels/deferred_work/3-4-5-deferred/user/test.c new file mode 100644 index 00000000000000..3cef70f86e2b75 --- /dev/null +++ b/tools/labs/skels/deferred_work/3-4-5-deferred/user/test.c @@ -0,0 +1,93 @@ +/* + * SO2 - Lab 6 - Deferred Work + * + * Exercises #3, #4, #5: deferred work + * + * User-mode test program. + */ + +#include +#include +#include +#include +#include +#include +#include "../include/deferred.h" + +#define DEVICE_PATH "/dev/deferred" + +/* prints error message and exits */ +void error(char *message) +{ + perror(message); + exit(EXIT_FAILURE); +} + +/* prints usage message and exits */ +void usage() +{ + printf("Usage: test \n options:\n" + "\ts - set timer to run after seconds\n" + "\tc - cancel timer\n" + "\ta - allocate memory after seconds\n" + "\tp - monitor pid\n" + "\n"); + exit(1); +} + +#define BUFFER_LEN 128 + +int main(int argc, char **argv) +{ + int fd; + unsigned long seconds, pid; + + if (argc < 2) + usage(); + + fd = open(DEVICE_PATH, O_RDONLY); + if (fd < 0) + error(DEVICE_PATH); + + switch (argv[1][0]) { + case 's': + /* Set timer. */ + if (argc < 3) + usage(); + seconds = atoi(argv[2]); + printf("Set timer to %ld seconds\n", seconds); + if (ioctl(fd, MY_IOCTL_TIMER_SET, seconds) < 0) + error("ioctl set timer error"); + break; + case 'c': + /* Cancel timer. */ + printf("Cancel timer\n"); + if (ioctl(fd, MY_IOCTL_TIMER_CANCEL) < 0) + error("ioctl cancel timer error"); + break; + case 'a': + /* Allocate memory. */ + if (argc < 3) + usage(); + seconds = atoi(argv[2]); + printf("Allocate memory after %ld seconds\n",seconds); + if (ioctl(fd, MY_IOCTL_TIMER_ALLOC, seconds) < 0) + error("ioctl allocate memory error"); + break; + case 'p': + /* Monitor pid. */ + if (argc < 3) + usage(); + pid = atoi(argv[2]); + printf("Monitor PID %lu.\n", pid); + if (ioctl(fd, MY_IOCTL_TIMER_MON, pid) < 0) + error("ioctl monitor pid error"); + break; + default: + error("Wrong parameter"); + } + + close(fd); + + return 0; +} diff --git a/tools/labs/skels/deferred_work/6-kthread/Kbuild b/tools/labs/skels/deferred_work/6-kthread/Kbuild new file mode 100644 index 00000000000000..028c060071dd8c --- /dev/null +++ b/tools/labs/skels/deferred_work/6-kthread/Kbuild @@ -0,0 +1,3 @@ +ccflags-y = -Wno-unused-function -Wno-unused-label -Wno-unused-variable + +obj-m = kthread.o diff --git a/tools/labs/skels/deferred_work/6-kthread/kthread.c b/tools/labs/skels/deferred_work/6-kthread/kthread.c new file mode 100644 index 00000000000000..eaea6ccb2ecbe0 --- /dev/null +++ b/tools/labs/skels/deferred_work/6-kthread/kthread.c @@ -0,0 +1,61 @@ +/* + * SO2 - Lab 6 - Deferred Work + * + * Exercise #6: kernel thread + */ + +#include +#include +#include +#include +#include +#include + +MODULE_DESCRIPTION("Simple kernel thread"); +MODULE_AUTHOR("SO2"); +MODULE_LICENSE("GPL"); + +wait_queue_head_t wq_stop_thread; +atomic_t flag_stop_thread; +wait_queue_head_t wq_thread_terminated; +atomic_t flag_thread_terminated; + + +int my_thread_f(void *data) +{ + pr_info("[my_thread_f] Current process id is %d (%s)\n", + current->pid, current->comm); + /* TODO: Wait for command to remove module on wq_stop_thread queue. */ + wait_event_interruptible(wq_stop_thread, atomic_read(&flag_stop_thread) != 0); + + /* TODO: set flag to mark kernel thread termination */ + atomic_set(&flag_thread_terminated, 1); + /* TODO: notify the unload process that we have exited */ + wake_up_interruptible(&wq_thread_terminated); + + pr_info("[my_thread_f] Exiting\n"); + do_exit(0); +} + +static int __init kthread_init(void) +{ + pr_info("[kthread_init] Init module\n"); + + /* TODO: init the waitqueues and flags */ + /* TODO: create and start the kernel thread */ + struct task_struct *p = current; + + kthread_create(my_thread_f, NULL, "%skthread%d", "my", 0); + int wake_up_process(p); + return 0; +} + +static void __exit kthread_exit(void) +{ + /* TODO: notify the kernel thread that its time to exit */ + /* TODO: wait for the kernel thread to exit */ + pr_info("[kthread_exit] Exit module\n"); +} + +module_init(kthread_init); +module_exit(kthread_exit);