Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions tools/labs/skels/deferred_work/1-2-timer/Kbuild
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
ccflags-y = -Wno-unused-function -Wno-unused-label -Wno-unused-variable

obj-m = timer.o
50 changes: 50 additions & 0 deletions tools/labs/skels/deferred_work/1-2-timer/timer.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Deferred Work
*
* Exercise #1, #2: simple timer
*/

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/sched.h>

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);
35 changes: 35 additions & 0 deletions tools/labs/skels/deferred_work/3-4-5-deferred/include/deferred.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* SO2 - Lab 6 - Deferred Work
*
* Exercises #3, #4, #5: deferred work
*
* Header file.
*/

#ifndef __DEFERRED_H__
#define __DEFERRED_H__

#include <asm/ioctl.h>

#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__ */
3 changes: 3 additions & 0 deletions tools/labs/skels/deferred_work/3-4-5-deferred/kernel/Kbuild
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
ccflags-y = -Wno-unused-function -Wno-unused-label -Wno-unused-variable

obj-m = deferred.o
258 changes: 258 additions & 0 deletions tools/labs/skels/deferred_work/3-4-5-deferred/kernel/deferred.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,258 @@
/*
* SO2 - Lab 6 - Deferred Work
*
* Exercises #3, #4, #5: deferred work
*
* Code skeleton.
*/

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/sched/task.h>
#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);
9 changes: 9 additions & 0 deletions tools/labs/skels/deferred_work/3-4-5-deferred/kernel/makenode
Original file line number Diff line number Diff line change
@@ -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}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/test
9 changes: 9 additions & 0 deletions tools/labs/skels/deferred_work/3-4-5-deferred/user/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
CFLAGS=-Wall -m32
LDFLAGS=-static -m32

test: test.o

.PHONY: clean

clean:
-rm -f *~ *.o test
Loading