CMSC 421: Principles of Operating Systems

Project 2: Babbler 2: Babble Harder

This project is due on Thursday, May 12, at 11:59:59 PM (Eastern daylight time). You must use the submit to turn in your assignment like so: submit cs421_jtang proj2 babbler2.c babbler2-test.c

Your driver code must be named babbler2.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 babbler2.c
  

In addition, you will write a unit test program, babbler2-test.c, and it will be compiled on Ubuntu 14.04 as follow:

  gcc --std=c99 -Wall -O2 -pthread -o babbler2-test babbler2-test.c
There 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 driver code.

In this project, you will write a Linux driver that extends the Babbler system introduced in the first project, by adding support support for networking, multitopics, and privacy.

Part 1: Compiling All Modules

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:

http://www.csee.umbc.edu/~jtang/cs421.s16/homework/proj2/Kbuild
Read by Linux kernel's build system, defines what is being built.
http://www.csee.umbc.edu/~jtang/cs421.s16/homework/proj2/Makefile
Builds the kernel module and unit test program, by simply running make. Also included is a clean target to remove all built objects.
http://www.csee.umbc.edu/~jtang/cs421.s16/homework/proj2/xt_babblenet.c
A Netfilter module that simulates a network device. You do not need to modify this file.
http://www.csee.umbc.edu/~jtang/cs421.s16/homework/proj2/0001-x86-irq-Update-software-interrupts-to-use-irq-descri.patch
A patch to the Linux kernel to let xt_babblenet run correctly.
http://www.csee.umbc.edu/~jtang/cs421.s16/homework/proj2/proj2_start.sh
Shell script necessary to set up the Babbler2 networking system. After downloading this file, mark the script executable (chmod u+x proj2_start.sh).
http://www.csee.umbc.edu/~jtang/cs421.s16/homework/proj2/proj2_stop.sh
Shell script to disable the Babbler2 networking system. After downloading this file, mark the script executable (chmod u+x proj2_stop.sh).
(Optional) http://www.csee.umbc.edu/~jtang/cs421.s16/homework/proj2/babbler2.c
Skeleton code for your babbler2 kernel driver. If you successfully finished the first project, copy your babbler.c to this directory and rename that copy babbler2.c. Then use a tool like meld to merge the supplied babbler2.c with your babbler2.c.
(Optional) http://www.csee.umbc.edu/~jtang/cs421.s16/homework/proj2/babbler2-test.c
Skeleton code for your unit test code. If you successfully finished the first project, copy your babbler-test.c to this directory and rename that copy babbler2-test.c. Then use a tool like meld to merge the supplied babbler2-test.c with your babbler2-test.c.

After downloading all of these files, you need to then recompile your kernel:

  1. Change directory to your kernel source code.
  2. Use git am to apply the patch 0001-x86-irq-Update-software-interrupts-to-use-irq-descri.patch
  3. Recompile and reinstall the kernel, as per the final part in Homework 1.
  4. Reboot your VM, making sure you select the new kernel from the grub menu.
  5. As necessary, reinstall VirtualBox Guest Additions.

Now run make to compile everything. Upon success, you should now have the kernel driver babbler2.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.

Part 2: Add Interrupt Handling

In this part, you will add networking support to your Babbler system. Read the code xt_babblenet.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/babbler.

In babbler_init(), install a threaded interrupt handler for interrupt number BABBLENET_IRQ. Remove that handler in babbler_exit(). Implement your interrupt handler as follow:

/**
 * babblenet_top() - top-half of BabbleNet ISR
 * @irq: IRQ that was invoked (ignored)
 * @cookie: Pointer to data that was passed into
 * request_threaded_irq() (ignored)
 *
 * If @irq is BABBLENET_IRQ, then wake up the bottom-half. Otherwise,
 * return IRQ_NONE.
 */
static irqreturn_t babblenet_top(int irq, void *cookie);

/**
 * babblenet_bottom() - bottom-half to BabbleNet ISR
 * @irq: IRQ that was invoked (ignore)
 * @cookie: Pointer that was passed into request_threaded_irq()
 * (ignored)
 *
 * Fetch the incoming packet, via babblenet_get_babble(), and
 * overwrite any previously stored babble. As per babbler_write(),
 * truncate the payload to the first 140 characters. 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 babblenet_bottom(int irq, void *cookie);

As that babblenet_bottom() will be manipulating your internal babbler buffer, be sure to guard the code with a spin lock. (Because the bottom half is not running in interrupt context, you do not need spin_lock_irqsave()).

Once you are confident your ISR works, call babblenet_enable() in babbler_init() (and likewise disable network integration via babblenet_disable() in babbler_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. For example, suppose that you had previously written the topic #cs421 to /dev/babbler_ctl. If you then send this input in the telnet session:

    I <3 networked #cs421!
  
Then reading from /dev/babbler should return that babble.

You can close the telnet session by pressing control-] (right bracket) and then entering close.

Part 3: Support Multiple Topics

The next task is to add support for multiple topics. In the first project, a write to /dev/babbler_ctl overwrite the previous topic. Now you must modify babbler_ctl_write() to append topics to a linked list.

At the top of babbler2.c, declare a a kernel linked list to hold the topics. Then declare a global variable to hold a pointer to the beginning of the linked list. Because this list can be accessed by multiple threads, guard accesses to the list using a spin lock.

Next, modify babbler_ctl_write() to allocate a new list entry for each incoming topic. Add that entry to the global topics list with the list_add_tail() macro. Assume the user will never add duplicate topics.

Then update the contents of topics_buffer. For each topic in the global topics list, write that topic to topics_buffer, using newlines to separate each topic. Do not put a newline after the last topic. Assume that the entire topic list can fit within a PAGE_SIZE.

Update babbler_read() to match against the topics list. If the babble contains any of the topics, then return it to the user.

In babbler_exit(), free all memory that was allocated for the list.

Part 4: Add Privacy Option

The final new feature for this project is to add a privacy option. A user can request privacy by prefixing the babble with the "@" symbol followed by digits and then a space, like so:

@1000 I >3 #cs421
The number (in this case, 1000) indicates the UID of the recipient. If the UID of the process reading from /dev/babbler does not match the babble's privacy UID, then no babble is returned, regardless if any topics matched or not. Given the above example, only user 1000 can retrieve the babble; all other users would get nothing.

Modify babbler_read() to support this privacy option. If the first character is not "@" proceed as normal. Otherwise, compare the babble's required UID against the calling process's UID. If the two match, then check against the topics list and otherwise execute as before. Otherwise, if the UIDs do not match, return 0 and do not clear the babble.

When a babble begins with "@", you may assume that there will be at least one digit following, there will be a space after the final digit, and that the entire number fits within an unsigned long. You may not assume how many digits will be in the UID.

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 recently, and thus older posts are inaccurate. In modern kernels, you have to use the val field to get the numeric identifier; also, see the kernel code itself.

Part 5: Testing and Documentation

Now that you have (in theory) a working driver, you must then write your own unit tests. Read the networking code in babbler2-test.c. You can use babblenet_send() to send arbitrary babbles to the BabbleNet server. Modify the rest of babbler2-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, confirming the memory map contents, and verifying that only matched topics and UIDs are returned.

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.

Other Hints and Notes

Extra Credit

You may earn an additional 20% credit for this assignment by augmenting your babble and topic handling. Whenever a babble is received, either via babbler_write() or from BabbleNet, record the current hour, minute, and second. Modify babbler_read() to return the timestamp, using the format HH:MM:SS, in addition to the babble. For example, a read from /dev/babbler could return the following:

20:19:07 #hamlet said: To be or not to be -- that is the #quest ion:
20:19:12 The #slings and arrows of outrageous #fortune
20:19:20 And by opposing end them. #hamlet #act3

In addition, keep track of the most recent appearance of each topic. For each processed babble, update the time of each matched topic. Report those times (as ASCII strings) while generating the contents of topics_buffer, by appending to each topic a colon and and then the time (also in HH:MM:SS format). If a topic has never been used, show its time as merely a dash. For example, suppose the user had registered these topics:

If no other babbles have been received other those listed above, then the contents of topics_buffer would be:
    #fortune: 20:19:12
    #hamlet: 20:19:20
    #macbeth: -
    #quest: 20:19:07
    #slings: 20:19:12
  
You will need to update topics_buffer not just in babbler_ctl_write(), but also in babbler_write() and babblenet_bottom().

Afterwards, update babbler2-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.