This project is due on Thursday, May 14, at 11:59:59 PM (Eastern daylight time). You must use the submit to turn in your homework like so: submit cs421_jtang proj2 babbler.c
Your driver code must be named babbler.c. It will be compiled against a 3.14 Linux kernel source tree, via the Kbuild supplied below. It must not have any compilation warnings; warnings will result in grading penalties.
In addition, your 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:
/usr/src/linux/scripts/Lindent babbler.c
In this project, you are a software developer for a social networking startup company, Babbler. When a Babbler user sends a babble (a short message consisting of up to 140 characters), the babble.ko kernel driver will forward that message to all Babbler listerners. Your driver will create a miscellaneous device /dev/babbler. When a process reads from this device from user space, the driver will return the most recent babble. Subsequent reads from the same process or any additional processes will block until a new babble is received. Babbles are sent by writing to /dev/babbler or via the automated BabbleBot service.
To begin, you will need to update your kernel to the latest version and apply some custom code to the kernel.
cd /usr/src/linux git fetch git reset ——hard origin/linux-3.14.y(Note the above is dash, dash, "hard".)
git am $HOME/hw1/customize-uname.patch
trigger_irq()
that
allows a driver to simulate a hardware interrupt by calling
the INT opcode associated with an IRQ. In this way,
drivers can be tested within a virtual environment.
git am 000*patch
make oldconfigIf Kconfig asks if you want to build the BabbleBot driver, be sure to select Y.
make sudo make modules_install install sudo reboot(Be sure you select your new kernel from the GRUB menu.)
Your next task is to create the initial babble driver. Begin by creating a new directory for your project within your home directory. Next, download the following files into that directory via wget:
First, examine the header file include/linux/babblebot.h in
the kernel source tree. This file was added specifically for this
project. As in true Linux kernel style, the documentation for those
functions are in the source
file drivers/platform/x86/babblebot.c. In your
driver babbler.c, add the line #include
<linux/babblebot.h>
to the top.
As with Project 1, your code will create a miscellaneous device. This time call it /dev/babbler instead of /dev/rot13; make the necessary changes to your code to create this device. Now run make to compile both the babbler.ko driver as well as babble-reader program. Ensure that you can load and unload the driver and that it creates the proper device node.
As with the first project, the babbler_open() and babbler_release() callbacks do nothing.
Change the babbler_read() and babbler_write() callbacks to behave as follows:
/** * babbler_read() - callback invoked when a process reads from this * character device * @filp: process's file object that is reading this device * @ubuf: location to write incoming babble * @count: number of bytes requested by process * @ppos: file offset (ignored) * * Write to @ubuf the lesser of @count and size of the incoming * babble. * * While there are no babbles, if the requesting process is performing * a non-blocking read (see @filp), then return -EAGAIN. Otherwise * block this process (as interruptible) until a babble is * received. See wait_for_completion_interruptible(). * * The first time any process successfully reads the babble, clear the * babble. Subsequent reads from the same process or from any process * blocks until a new babble arrives. * * Return: number of bytes written to @ubuf, or negative on error */ static ssize_t babbler_read(struct file *filp, char __user * ubuf, size_t count, loff_t * ppos); /** * babbler_write() - callback invoked when a process writes to this * character device * @filp: process's file object that is writing this device (ignored) * @ubuf: source buffer of bytes to copy into internal buffer * @count: number of bytes in @ubuf * @ppos: file offset (ignored) * * Read from @ubuf up to the lesser of @count bytes or BABBLE_MAX_SIZE * bytes. Then wake up any readers (via complete() or complete_all()) * that were blocked. * * Return: number of bytes read from @ubuf, or negative on error */ static ssize_t babbler_write(struct file *filp, const char __user * ubuf, size_t count, loff_t * ppos);
When a process writes to /dev/babbler, copy the user buffer
into an internal buffer of size BABBLE_MAX_SIZE
. Any
writes to /dev/babbler replaces the internal
buffer. Reading from /dev/babbler returns the most recent
babble and clears the internal buffer.
Test this by running the following:
echo -n 'I <3 #CS421' > /dev/babbler
cat /dev/babbler (returns "I <3 #CS421")
Your next task is to allow for multiple readers of /dev/babbler. The first time any process reads from /dev/babbler, return the most recent babble. All subsequent reads, including those from the same process, block until a new babble is received. First add a completion variable to your driver.
Next, in babbler_read()
, if no babble is available the
code needs to do one of two things. If
the caller is
non-blocking (see the file object filp
's
field f_flags
; if the bit O_NONBLOCK
is
set it is non-blocking), then simply return the
value -EAGAIN
.
Otherwise enter into a while
loop until a babble is
received. Use a spinlock to guard the while loop, as if you were
using a Pthread
condition variable.
Call wait_for_completion_interruptible()
against your
completion variable. Prior to the call, release the spinlock; when
the call returns, be sure to reaquire the spinlock and
then reinitialize
the completion variable.
In babbler_write()
, call complete()
or complete_all()
after it has updated the internal
buffer so as to awake all waiting read processes. Be
sure babbler_write()
uses the same spinlock to protect
all shared resources.
Test these change by opening several terminals. In each terminal, run the program babble-reader. In your original terminal, run the previous echo command. You should see your babble appear in one of the terminals.
Do the following to test your driver's non-blocking behavior. First, unload and then reload the driver. Next, run babble-reader with the ——nb flag. Assuming that babblebot has not generated any babbles yet, babble-reader should immediately quit, with the error message Resource temporarily unavialable. Third, echo a string to /dev/babbler. Run babble-reader ——nb again. This time, babble-reader should display the echoed string and again display the same Resource temporarily unavailable error message.
The final task is to add interrupt handling. Read the code drivers/platform/x86/babblebot.c. The BabbleBot, when enabled, waits randomly before generating interrupts. It is your driver's responsibility to catch those interrupts and copy the babble that was generated by the bot. Those babbles are then sent to all readers, just as if a user had written to /dev/babble.
In babbler_init()
, install a threaded interrupt handler
against BABBLE_IRQ
. Remove that handler in
babbler_exit()
.
The top-half of your interrupt handler does two things:
calls babblebot_disable()
and then returns the value
indicating that the bottom-half should run.
The bottom-half copies the babble from BabbleBot into the
internal buffer, via a combination of babblebot_read()
and babblebot_size()
calls. Just
like babbler_write()
, call complete_all()
to awaken all readers. Finally, it reenables the bot by
calling babblebot_enable()
. Again, this bottom half
must correctly use the spinlock to guard shared resources.
Once you are confident your ISR works,
add babblebot_enable()
to babbler_init()
(and likewise disable the bot
in babbler_exit()
). Install your module. Run the
following command in a terminal to show all interrupts:
while /bin/true; do grep 6: /proc/interrupts; sleep 1; doneThe interrupt count should increment within 10 seconds. Run multiple copies of babble-reader; those readers should now display babbles from BabbleBot.
struct
miscdevice
's .mode
field.
babbler_read()
, be sure that to release the
spinlock while waiting for the completion variable.
You may earn an additional 20% credit for this assignment by augmenting your babble handling. Whenever a babble is received, either via babbler_write() or from BabbleBot, record the current hour, minute, and second. Modify babbler_read() to return the timestamp, expressed as colon separated two digits, in addition to the babble. For example, this would result in the following babbles:
20:19:07 To be or not to be -- that is the #question: 20:19:12 The #slingsandarrows of #outrageousfortune 20:19:20 And by opposing end them.