Starting from:
$30

$24

CSC Assignment 5 Solution


Homework Requirements


Environment

WARNING!!! Before starting on this assignment, make sure you have set up your VM properly. We would test all

students' homework using the following environment. You can type the following command in terminal on your

VM to see if your configuration matches the test environment. If not, you are still good to go, but please try to

test your program with the following environment for at least once. Because you may be able to run your

program on your environment, but not on TAs' environment, causng inconvenience or even grade deduction.

If you follow the tutorials then your VM setting should be fine, though verify your environment again is

recommended.


Linux Version

main@ubuntu:/$ cat /etc/issue

Ubuntu 16.04.5 LTS \n \l



GCC Version

main@ubuntu:/$ gcc --version

gcc (Ubuntu 5.4.0-6ubuntu1~16.04.10) 5.4.0 20160609

Copyright (C) 2015 Free Software Foundation, Inc.

This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.




Submission

Due on 23  59, 8 Dec, 2021

Please note that, teaching assistants may ask you to explain the meaning of your program, to ensure that the

codes are indeed written by yourself. Please also note that we would check whether your program is too similar

to your fellow students’ code using plagiarism detectors.

Violation against the format requirements will lead to grade deduction.

tree
Here is the format guide. The project structure is illustrated as below. You can also use    command to check if

your structure is fine. Structure mismatch would cause grade deduction.

Note that NO EXTRA BONUS FILE/FOLDER is needed for this assignment. Please mark whether you have finished

bonus in your report.

main@ubuntu:~/Desktop/Assignment_5_<student_id>$ tree

.

├── report.pdf

└── source

├── ioc_hw5.h

├── main.c

├── Makefile

├── mkdev.sh

├── rmdev.sh
└── test.c


1 directory, 7 files



Please compress all files in the file structure root folder into a single zip file and name it using your student id as

the code showing below and above, for example, Assignment_5_119010001.zip. The report should be submitted in the format of pdf, together with your source code. Format mismatch would cause grade deduction. Here is the sample step for compress your code.


main@ubuntu:~/Desktop$ zip -q -r Assignment_5_<student_id>.zip Assignment_5_<student_id> main@ubuntu:~/Desktop$ ls

Assignment_5_<student_id>    Assignment_5_<student_id>.zip






Task Description



In Assignment 5, you are required to make a prime device in Linux, and implement file operations in kernel module

to control this device.


Outline:


We will make a device under /dev by mknod command.


This device can find n-th prime number.


You will implement file operations in a kernel module to control this device.


And implement ioctl function to change the device configuration.


Simulate registers on device by allocating a memory region.


Global View:





























Specification:


Register character device and make it live:


You can use alloc_chrdev_region() to allocate a range of char device numbers.


And get available number by MAJOR() and MINOR() macro.
In your kernel module, you could allocate a cdev structure by cdev_alloc() and initialize it by cdev_init() to

bind cdev file_operations.


Add a device by cdev_add() to make it live.


Test program and printk:


Before write module, we need to know what this module do. So we provide a test program to test this

device.


You can modify test program and test your module.


We will check with our test cases.


In kernel module, you need to write printk to help debug and use dmesg command to show message.


To help demo program, your printk must be started with “OS_AS5: function_name: message”.











File operations:


You should write a struct file_operations to map the operations to functions in this module.


And use cdev_init() at module init to bind cdev and file_ operations.


static struct file_operations fops = {

owner: THIS_MODULE,

read: drv_read,

write: drv_write,

unlocked_ioctl: drv_ioctl,

open: drv_open,

release: drv_release,

};




ioctl:


ioctl

In Linux, device provide user mode program    function to change the device configuration.


ioctl

define many types of operation with switch case to do coordinated work.


And

ioctl

use mask to get value from these operation label.

Here we provide

ioc_hw5.h

 to define 6 works.

a.  (HW5_IOC_SETSTUID) Set student ID: printk your student ID

b.  (HW5_IOCSETRWOK) Set if RW OK: printk OK if you complete R/W function

c.  (HW5_IOCSETIOCOK) Set if ioctl OK: printk OK if you complete ioctl function

d.  (HW5_IOCSETIRQOK) Set if IRQ OK: printk OK if you complete bonus

e.  (HW5_IOCSETBLOCK) Set blocking or non-blocking: set write function mode

f. (HW5_IOCWAITREADABLE) Wait if readable now (synchronize function): used before read to confirm it can read answer now when use non-blocking write mode.


ioc_hw5.h

ioctl lables defined in    .
_IOW(type, nr, size )    ioctl

is used for an    to write data to the driver. It is to generate command

numbers.


#ifndef IOC_HW5_H

#define IOC_HW5_H

#define HW5_IOC_MAGIC    'k'

#define HW5_IOCSETSTUID    _IOW(HW5_IOC_MAGIC, 1, int)

#define HW5_IOCSETRWOK    _IOW(HW5_IOC_MAGIC, 2, int)

#define HW5_IOCSETIOCOK    _IOW(HW5_IOC_MAGIC, 3, int)

#define HW5_IOCSETIRQOK    _IOW(HW5_IOC_MAGIC, 4, int)

#define HW5_IOCSETBLOCK    _IOW(HW5_IOC_MAGIC, 5, int)

#define HW5_IOCWAITREADABLE    _IOR(HW5_IOC_MAGIC, 6, int)

#define HW5_IOC_MAXNR    6

#endif



ioctl(fd, HW5_IOCSETBLOCK, &ret)

Demo for ioctl call in user mode:


fd

    • an open file descriptor


HW5_IOCSETBLOCK

    • device-dependent request code.


&ret

    • an untyped pointer to memory


write:


Define a data struct that is passed in write function.


struct dataIn {

char a;

int b;

short c;

};



a

is operator: '+', '-', '***', '/', or '**p' (‘p’ means find prime number)


b

is operand 1


c

is operand 2


Use INIT_WORK() and schedule_work() to queue work to system queue.


Find Prime operation:


It finds c-th prime number bigger than b.

(e.g, “1 p 3” means to find 3rd prime number which is bigger than 1, then it should be 5.)


And you will feel the I/O latency when execute test program for “100 p 10000” and “100 p 20000”.


We will check your blocking and non-blocking IO by observing the delay of the message printed by test program.



R/W function packaged in arithmetic function in user mode program.


arithmetic(fd, 'p', 100, 10000);


fd

    • an open file descriptor


p

    • operator


100

    • operand1


10000

    • operand2


Work Routine:
The work you enqueued should be written in a work routine function in module.


These work will be processed by another kernel thread.


computation is written in a work routine in module


// Arithmetic function

static void drv_arithmetic_routine(struct work_struct* ws);




Blocking and Non-Blocking IO:


The test program can use ioctl to set blocking or non-blocking.


Your write function in module can be blocking or non-blocking.


Blocking write need to wait computation completed.


Non-blocking write just return after queuing work.


Read function only has blocking, because not queuing work.


Blocking Write:


In test program, we just need a write function.


Do not need another synchronize function.


But block when writing.


Blocking write in test program:


/******************Blocking IO******************/ printf("Blocking IO\n");

ret = 1;

if (ioctl(fd, HW5_IOCSETBLOCK, &ret) < 0) {

printf("set blocking failed\n");

return -1;

}

write(fd, &data, sizeof(data));

//Do not need to synchronize

//But need to wait computation completed

read(fd, &ret, sizeof(int));

printf("ans=%d ret=%d\n\n", ans, ret); /***********************************************/



Non- Blocking Write:


In test program, we can do something after write function.


Write function return after queueing work, it is non-blocking.


But need another synchronize function to wait work completed.


Non-blocking write in test program:


/****************Non-Blocking IO****************/ printf("Non-Blocking IO\n");

ret = 0;

if (ioctl(fd, HW5_IOCSETBLOCK, &ret) < 0) {

printf("set non-blocking failed\n");
return -1;


}

printf("Queueing work\n");

write(fd, &data, sizeof(data));

//Can do something here

//But cannot confirm computation completed

printf("Waiting\n");

//synchronize function

while (1) {

if (ioctl(fd, HW5_IOCWAITREADABLE, &readable), readable)

break;

sleep(1);

}

printf("Can read now.\n");

read(fd, &ret, sizeof(int));

printf("ans=%d ret=%d\n\n", ans, ret);

/***********************************************/




Interrupt driven IO:


When implementing blocking write and synchronize function, they use a while loop busy waiting the

interrupt.


You can use a variable to simulate the interrupt.


At the final of the work routine function, change this variable as triggering the interrupt.


And then, blocking write and synchronize function can exit the while loop.


DMA Buffer:


To simulate register and memory on device, you need to kmalloc a dma buffer.


This buffer is as I/O port mapping in main memory.


What device do is written in work routine function. This function get data from this buffer.


Defined value written into dma buffer:


// DMA

#define DMA_BUFSIZE 64

#define DMA_STUID_ADDR 0x0    // Student ID

#define DMA_RWOK_ADDR 0x4    // RW function complete

#define DMA_IOCOK_ADDR 0x8    // ioctl function complete

#define DMA_IRQOK_ADDR 0xc    // ISR function complete

#define DMA_COUNT_ADDR 0x10    // interrupt count function complete

#define DMA_ANS_ADDR 0x14    // Computation answer

#define DMA_READABLE_ADDR 0x18    // READABLE variable for synchronize

#define DMA_BLOCK_ADDR 0x1c    // Blocking or non-blocking IO

#define DMA_OPCODE_ADDR 0x20    // data.a opcode

#define DMA_OPERAND_B_ADDR 0x21    // data.b operand1

#define DMA_OPERAND_C_ADDR 0x25    // data.c operand2

static void *dma_buf;




In and out functions:


You need to implement in & out function to access dma buffer just like physical device.


Out function is used to output data to dma buffer.
On function is used to input data from dma buffer.


The 6 in & out functions are definded in module to operate

dma_buf

:

c    s
(    ,

and

i
maps with data type

char
,

short

and

int

)

// in and out function

static void myoutc(unsigned char data,unsigned short int port); static void myouts(unsigned short data,unsigned short int port); static void myouti(unsigned int data,unsigned short int port); static unsigned char myinc(unsigned short int port);

static unsigned short myins(unsigned short int port); static unsigned int myini(unsigned short int port);



Demo usage of in and out functions:


myouti(value, DMAIOCOKADDR)


value    dma_buffer

    • data you want to write into


DMAIOCOKADDR    dma_buffer

    • port in


Data transfer between kernel and user space:


get_user(x, ptr)


Get a simple variable from user space.


x

    • Variable to store result.


ptr

: Source address, in user space.


put_user(x, ptr)


Write a simple value into user space.


x

    • Value to copy to user space.


ptr

    • Destination address, in user space.


Template structure:


Makefile is provided:


make

Command:

(It will firstly build your main.c as kernel module “mydev.ko”, insert “mydev.ko”, and then build “test.c” as executable file “test”.)



mname := mydev

$(mname)-objs := main.o

obj-m := $(mname).o

KERNELDIR := /lib/modules/`uname -r`/build

all:

$(MAKE) -C $(KERNELDIR) M=`pwd` modules

sudo insmod mydev.ko

gcc -o test test.c



make clean

Command:

(It will remove “mydev.ko” and use “dmesg” to list kernel logs that includes keyword “OS_AS5”)


clean:

$(MAKE) -C $(KERNELDIR) M=`pwd` clean
sudo rmmod mydev


rm test

dmesg | grep OS_AS5



mknod

script is provided:


#!/bin/bash

mknod /dev/mydev c $1 $2

chmod 666 /dev/mydev

ls -l /dev/mydev




In mknod command: c means character device. Followed two number are Major and Minor number to specify device.



You can get available number by MAJOR() and MINOR() macro after alloc_chrdev_region() in module_init() function.



How to use mknod script is shown on Tutorial_11 Slide 3 to 6.


Steps you need to run the template:


Run
make



dmesg
Run
to check available device number

sudo ./mkdev.sh MAJOR MINOR
Run
to build file node (MAJOR and MINOR are the available device
number checked from previous step)

./test
Run
to start testing

make clean
Run
to remove the module and check the messages

sudo ./rmdev.sh
Run
to remove the file node


You should complete module init and exit functions:


static int __init init_modules(void) {

printk("%s:%s():...............Start...............\n", PREFIX_TITLE, __func__);

/* Register chrdev */

/* Init cdev and make it alive */

/* Allocate DMA buffer */

/* Allocate work routine */

return 0;

}

static void __exit exit_modules(void) {

/* Free DMA buffer when exit modules */

/* Delete character device */

/* Free work routine */





printk("%s:%s():..............End..............\n", PREFIX_TITLE, __func__);



}
- Implement read/write/ioctl operations and arithmetic routine:

```C

static ssize_t drv_read(struct file *filp, char __user *buffer, size_t ss, loff_t* lo) { /* Implement read operation for your device */

return 0;

}

static ssize_t drv_write(struct file *filp, const char __user *buffer, size_t ss, loff_t* lo) { /* Implement write operation for your device */

return 0;

}

static long drv_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { /* Implement ioctl setting for your device */
return 0;

}

static void drv_arithmetic_routine(struct work_struct* ws) { /* Implement arthemetic routine */

}



Function Requirements (90 points):


Register a character device when module initialized. (5 points)


Initialized a cdev and add it to make it alive. (5 points)


Allocate DMA buffer. (5 points)


Allocate work routine. (5 points)


Implement read operation for your device. (10 points)


Implement write operation for your device. (20 points)


Implement ioctl setting for your device. (15 points)


Implement arithmetic routine for your device. (10 points)


Complete module exit functions. (5 points)


Update your student ID in test case and make it be print in kernel ioctl. (5 points)


Run test cases to check write and read operations. (5 points)


Demo Output


Test case: (:

+    -    *    /
,    ,    ,

is for your testing, we will mainly test

p

operation)

arithmetic(fd, '+', 100, 10);

arithmetic(fd, '-', 100, 10);

arithmetic(fd, '*', 100, 10);

arithmetic(fd, '/', 100, 10);

arithmetic(fd, 'p', 100, 10000);

arithmetic(fd, 'p', 100, 20000);




User mode output:


...............Start...............

100 + 10 = 110

Blocking IO

ans=110 ret=110

Non-Blocking IO

Queueing work

Waiting
Can read now.


ans=110 ret=110

100-10=90

Blocking IO

ans=90 ret=90

Non-Blocking IO

Queueing work

Waiting

Can read now.

ans=90 ret=90

100 * 10 = 1000

Blocking IO

ans=1000 ret=1000

Non-Blocking IO

Queueing work

Waiting

Can read now.

ans=1000 ret=1000

100/10=10

Blocking IO

ans=10 ret=10

Non-Blocking IO

Queueing work

Waiting

Can read now.

ans=10 ret=10

100 p 10000 = 105019

Blocking IO

ans=105019 ret=105019

Non-Blocking IO

Queueing work

Waiting

Can read now.

ans=105019 ret=105019

100 p 20000 = 225077

Blocking IO

ans=225077 ret=225077

Non-Blocking IO

Queueing work

Waiting

Can read now.

ans=225077 ret=225077

...............End...............




Kernel mode output:


[35962.261080]
OS_AS5:init_modules():...............
Start...............
[35962.261081]
OS_AS5:init_modules(): register chrdev(244,0)
[35962.261087]
OS_AS5:init_modules(): allocate dma buffer

[35984.556209]
OS_AS5:drv_open(): device open
[35984.556212]
OS_AS5,drv_ioctl(): My STUID is = <student_id>
[35984.556213]
OS_AS5,drv_ioctl(): RM OK
[35984.556213]
OS_AS5,drv_ioctl(): IOC OK
[35990.337307]
OS_AS5,drv_ioctl(): Blocking IO
[35990.337310]
OS_AS5:drv_write(): queue work
[35990.337311]
OS_AS5:drv_write(): block
[35993.161033]
OS_AS5,drv_arithmetic_routine(): 100 p 20000 = 225077
[35993.161125]
OS_AS5:drv_read(): ans = 225077
[35993.161140]
OS_AS5,drv_ioctl(): Non-Blocking IO
[35993.161142]
OS_AS5:drv_write(): queue work
[35993.161145]
OS_AS5,drv_ioctl(): wait readable 1
[35996.012354]
OS_AS5,drv_arithmetic_routine(): 100 p 20000 = 225077
[35996.116977]
OS_AS5:drv_read(): ans = 225077
[35996.117106]
OS_AS5:drv_release(): device close
[36010.064424]
OS_AS5:exit_modules(): free dma buffer
[36010.064426]
OS_AS5:exit_modules(): unregister chrdev
[36010.064426]
OS_AS5:exit_modules():..............End..............






Bonus Task (10 pionts)



Global View (Bonus)






















Count the interrupt times of input device like keyboard.


Hint: watch -n 1 cat /proc/interrupts


Use request_irq() in module_init to add an ISR into an IRQ number's action list.


And free_irq() when module_ exit, otherwise kernel panic.


Please define IRQ_NUM at head of code.


Demo output:


[35962.261080]
OS_AS5:init_modules():...............Start...............
[35962.261081]
OS_AS5:init_modules(): register chrdev(244,0)
[35962.261087]
OS_AS5:init_modules(): request_irq 1 returns 0
[35962.261087]
OS_AS5:init_modules(): allocate dma buffer
[35984.556209]
OS_AS5:drv_open(): device open
[35984.556212]
OS_AS5,drv_ioctl(): My STUID is = <student_id>
[35984.556213]
OS_AS5,drv_ioctl(): RM OK
[35984.556213]
OS_AS5,drv_ioctl(): IOC OK
[35984.556214]
OS_AS5,drv_ioctl(): IRQ OK
[35990.337307]
OS_AS5,drv_ioctl(): Blocking IO
[35990.337310]
OS_AS5:drv_write(): queue work
[35990.337311]
OS_AS5:drv_write(): block
[35993.161033]
OS_AS5,drv_arithmetic_routine(): 100 p 20000 = 225077
[35993.161125]
OS_AS5:drv_read(): ans = 225077
[35993.161140]
OS_AS5,drv_ioctl(): Non-Blocking IO
[35993.161142]
OS_AS5:drv_write(): queue work
[35993.161145]
OS_AS5,drv_ioctl(): wait readable 1
[35996.012354]
OS_AS5,drv_arithmetic_routine(): 100 p 20000 = 225077
[35996.116977]
OS_AS5:drv_read(): ans = 225077
[35996.117106]
OS_AS5:drv_release(): device close
[36010.064423]
OS_AS5:exit_modules(): interrupt count = 90
[36010.064424]
OS_AS5:exit_modules(): free dma buffer
[36010.064426]
OS_AS5:exit_modules(): unregister chrdev
[36010.064426]
OS_AS5:exit_modules():..............End..............






Report (10 points)


Write a report for your assignment, which should include main information as below:

Your name and student id.

How did you design your program?

The environment of running your program. (E.g., version of OS and kernel)

The steps to execute your program.

Screenshot of your program output.

What did you learn from the tasks?




Grading rules


Here is a sample grading scheme. Different from the points specified above, this is the general guide when TA's

grading.

Completion
Marks


Bonus
10 points


Report
10 points



Completed with good quality
80
~ 90



Completed accurately
80
+



Fully Submitted (compile successfully)
60
+


Partial submitted
0~60



No submission
0



Late submission
Not allowed



More products