This project is due on Sunday, December 18, at 11:59:59 PM (Eastern standard time). You must use submit to turn in your project like so: submit cs421_jtang proj2 minesweeper2.c minesweeper2-test.c
Your module code must be named minesweeper2.c, and it will be compiled against a 4.4 Linux kernel source tree, via the Kbuild supplied below. It must not have any compilation warnings; warnings will result in grading penalties. This module code must be properly indented and have a file header comment, as described on the coding conventions page. Prior to submission, use the kernel's indentation script to reformat your code, like so:
~/linux/scripts/Lindent minesweeper2.c
In addition, you will write a unit test program, minesweeper2-test.c, and it will be compiled on Ubuntu 14.04 as follow:
gcc ‐‐std=c99 ‐Wall ‐O2 ‐pthread ‐o minesweeper2-test minesweeper2-test.c cs421net.c ‐lmThere must not be any compilation warnings in your submission; warnings will result in grading penalties. In addition, this code must also have a file header comment and be properly indented. You will submit this test code along with your module code.
In this project, you will write a Linux driver that extends the Minesweeper game introduced in the first project. This improved version of the game adds networking support, cheat mode, and statistics tracking.
All instructions hereforth assume you successfully completed the first project. If you have not done so, go back and finish the assignment before proceeding. You have been warned.
To begin, create a directory for your project and download the following files into that directory via wget:
After downloading all of these files into a new directory, copy your files from Project 1 as follows:
#include "xt_cs421net.h"
.
#include "cs421net.h"
. At the top
of main()
, add a function call to
cs421net_init()
.
Now run make to compile everything. Upon success, you should now have the kernel driver minesweeper2.ko. Run proj2_start.sh; it may ask for your password, as that it runs some sudo commands. This will set up your VM for this project. Rerun this script if you ever need to reboot your VM.
The first change to your code is to add networking support. Read the code in xt_cs421net.c. When this Netfilter module is installed, via the proj2_start.sh script, it will raise an interrupt every time your computer receives network packets on TCP port 4210. It is your driver's responsibility to handle those interrupts and treat the packets as if a user were writing to /dev/ms_ctl.
First add the line #include <linux/interrupt.h>
to the top of minesweeper2.c. Then update
minesweeper_init()
to install
a threaded interrupt
handler for interrupt number CS421NET_IRQ
. Remove
that handler in
minesweeper_exit()
. Implement your interrupt handler as
follows:
/** * cs421net_top() - top-half of network ISR * @irq: IRQ that was invoked * @cookie: Pointer to data that was passed into * request_threaded_irq() (ignored) * * If @irq is %CS421NET_IRQ, then wake up the bottom-half. Otherwise, * return %IRQ_NONE. */ static irqreturn_t cs421net_top(int irq, void *cookie); /** * cs421net_bottom() - bottom-half to network ISR * @irq: IRQ that was invoked (ignored) * @cookie: Pointer that was passed into request_threaded_irq() * (ignored) * * Fetch the incoming packet, via cs421net_get_data(). Treat the data * as if it were user input, as per minesweeper_ctl_write(). Remember * to add appropriate spin lock calls in this function. * * Note that the incoming payload is NOT a string; you can NOT use * strcpy() or strlen() on it. * * Return: always %IRQ_HANDLED */ static irqreturn_t cs421net_bottom(int irq, void *cookie);
As that cs421net_bottom()
will be manipulating your
game state, be sure to guard the code with a spin lock. Because the
bottom
half is running in process context, not interrupt context, you
do not need to use spin_lock_irqsave()
.
Once you are confident your ISR works,
call cs421net_enable()
in minesweeper_init()
, and likewise disable network
integration with cs421net_disable()
in minesweeper_exit()
. Install your module, and
check /proc/interrupts to ensure your ISR was registered.
To test this enhancement, ensure you earlier ran proj2_start.sh. In a second terminal, run this command: telnet localhost 4210. Use this second terminal to send messages to your driver, just as if you were writing game commands to /dev/ms_ctl. Close the telnet session by pressing control-] (right bracket) and then entering close.
You can also test this by connecting to other students' virtual machines. Assuming that both yours and your friend's VMs are on the same network, enable port forwarding as necessary. Then telnet to the other VM; you can view IP addresses via the ifconfig command.
The next new feature is to add a cheat mode to the
game. In minesweeper2.c, add the line #include
<linux/sched.h>
. In ms_read()
, if the UID
of the requesting task is zero (i.e., the root user) then do not
copy user_view
to the caller. Instead, return a
representation of the board with the mines marked. Test this by
running either sudo cat /dev/ms or sudo
./play.sh.
Then add two new commands to ms_ctl_write()
:
mines_marked
as necessary.
mines_marked
as necessary. Do
not allow the user to remove the last mine in the game; return
an error in this case.
-EPERM
. If these inputs
came via CS421Net, ignore them.
Be sure to update the game status with the new number of mines in the game. Upon a new game, reset the number of mines in the game to 10.
Test these new commands from the command-line like so:
$ sudo su -c 'echo a42 > /dev/ms_ctl' $ sudo su -c 'echo d10 > /dev/ms_ctl'
You can retrieve your own UID via the shell command id or
programmatically via getuid()
. Within the kernel, be
aware
of various blog
posts describing the current_uid()
call. Kernel
UIDs have changed, and thus older blog posts are inaccurate. In
modern kernels, you have
to use
the val
field to get the numeric identifier; also,
see
the
kernel code itself.
The last new game feature is to record game statistics for each game played. Whenever a game ends, either because the user revealed a mine or because all mines were marked correctly, record these three values:
In minesweeper2.c, add the lines #include
<linux/gfp.h>
, #include
<linux/list.h>
, and #include
<linux/slab.h>
. Declare
a kernel linked
list to hold the above three values. Then declare a global
variable to hold a pointer to the beginning of the linked list.
Use kzalloc()
to dynamically create each list
node. Append nodes to the linked list with the
macro list_add_tail()
. Because this list can be
accessed by multiple threads, guard accesses to the list using a
spin lock.
Next, create a global character pointer; name
it stats_view
. In minesweeper_init()
,
use vzalloc()
to dynamically
allocate PAGE_SIZE
bytes for the pointer and set the
buffer to zero. Duplicate the existing
function ms_mmap()
as ms_stats_mmap()
; in
this copy change the parameter in vmalloc_to_pfn()
to stats_view
. See
the original Project 1
minesweeper.c for examples of how to use these functions.
Then, create a third miscellaneous
device, /dev/ms_stats. Register this device during module
initialization. Set its mmap
callback
to ms_stats_mmap()
. This device has no read nor write
callbacks. Set the
default file
mode to 0444.
Every time your linked list changes, regenerate the contents
of stats_view
. For each node on the list, write the
node's values, space-separated, to stats_view
, using
newlines to separate each node. Assume that all statistics can fit
within a PAGE_SIZE
.
In minesweeper_exit()
, free all memory that was
allocated for the list. Release the memory associated
with stats_view
with vfree()
. Finally,
deregister the new miscellaneous device.
As an example, the memory-mapped contents of /dev/ms_stats could contain the following:
10 5 1 (Five mines were correctly marked and 1 incorrectly mark.) 12 0 0 (No mines were marked, correctly nor incorrectly. Also, the root user added two mines to the game.) 10 10 0 (All 10 mines were correctly marked, and thus the game was won.)
Now that you have (in theory) a working driver, you must then write
your own unit tests. Read the networking code
in cs421net.h. You can use cs421net_send()
to
send arbitrary data to the server. Modify
your minesweeper2-test.c to exercise all functionality new
to this assignment. This includes a mix of inputs when writing to
the device nodes and network socket, and confirming the memory map
contents of /dev/ms_stats.
As a hint, your minesweeper2-test should have two modes: when it is running as the root user and when not. If its current UID is 0, then it should be able to add and delete mines; otherwise adding and deleting mines should not be permitted. The grader will run minesweeper2-test using sudo and without.
The unit tests must have comments that explain what things are being tested. As before, your goal is to test boundary conditions of your driver's interfaces. You will be graded based upon the thoroughness of the tests.
sprintf()
/ snprintf()
/ scnprintf()
to easily add strings
to stats_view
.
You may earn an additional 5% credit for this assignment by recording time spent playing each game. Store the current timestamp when a game begins. When the game ends, record in the statistics list the game duration, in seconds (rounded down). Prepend each line read from /dev/ms_stats with the time. For example, a read from /dev/ms_stats could return the following:
30 10 5 1 (This game lasted 30 seconds, before the player lost.) 1 12 0 0 (This player lost almost immediately.) 600 10 10 0 (The player won the game after playing for 10 minutes.)
You may earn another 5% extra credit by augmenting the output from /dev/ms_stats. Change that memory map to return a list sorted by time, from shortest duration to longest. You may choose the sorting behavior in cases of ties.
Afterwards, update minesweeper2-test.c to test this new functionality.
If you choose to perform this extra credit, put a comment at the top of your file, alerting the grader.