All remaining assignments for this class are based on the creation and extension of a
user-space device driver for a in memory filesystem that is built on top of a block
storage device. At the highest level, you will translate file system commands into
memory frame level memory operations (see memory interface specification below).
The file system commands include open, read, write, seek and close for files that are
written to your file system driver. These operations perform the same as the normal
UNIX I/O operations, with the caveat that they direct file contents to the block storage
device instead of the host filesystem. The arrangement of software is as follows:
System and Project Overview
In the previous assignment, you wrote a basic device driver that sits between a virtual
application and virtualized hardware. The application makes use of the abstraction you
will provide called Block Memory System (BLOCK). As a reminder, the design of the
system is shown in the figure to the right:
In this assignment, you will have to implement a new functionality for the driver you
previoulsy wrote: persistence across sequential runs. What this means in practice is
that your code should now be able to "remember" the state of the BLOCK device after
it was powered off and on again. The next section will go into more details on how to
implement this.
All the code you need for this assignement is already in your possession. The only new
file you will need is a new workload that is provided below. This workload whould be
run after the first workload has been successfully run. If both workloads pass
successfully, you most likey implemented the persistence correctly.
How to implement the persistence
In the code that was given to you in the previous assignment, the BLOCK device is
already configured to write its contents to a file (called block_memsys.bck) when it is
powered off (i.e. it receives the BLOCK_OP_POWOFF opcode), and read it back when
powered on (i.e. when it receives the BLOCK_OP_INITMS opcode. This means that when
you initialize the device, its memory state (the contents of its frames) is the same as
when you last powered it off.
The steps you then have to take to implement persistence are:
• Disable the zero-ing of the memory in block_poweron().
• Determine how you want to store the files metadata in the device, and make sure
the frames you want to use for this purpose cannot be used to store file data by
you other functions.
• In your block_poweroff() function, add a step that will write the metadata of your
files to these frames on the device.
• Add an extra step in your block_poweron() function that will read the frames that
contain the files metadata you have stored, and use it to populate your data
structures you use to keep track of your files. Keep in mind that all files start closed
(i.e. you should not have to create any file handle at this point).
The key to this assignment is figuring out how you want to store your metadata to the
device, in a way that can be reliably read later on. How you do this is up to you, but
think carefull about it before beginning to code. Keep in mind that the next assignment
will once again use your code.
The Block Memory System (reminder)
You will implement your driver on top of the BLOCK memory system (which is referred
to throughout simply as the BLOCK). The BLOCK consists of one continuous disk that
contains many frames. Each frame is a fixed byte sized memory block. Some key facts
of the system include (see block_controller.h for definitions):
• The disk contains BLOCK_BLOCK_SIZE frames, each of which is numbered
from 0 to BLOCK_BLOCK_SIZE-1.
• A frame is BLOCK_FRAME_SIZE bytes.
• The connection to the BLOCK routinely corrupts frames, and thus a checksum field
is used to ensure frame integrity.
You communicate with the memory system by sending code through a set of packed
registers. These registers are set within a 64-bit value to encode the opcode and
arguments to the hardware device. The opcode is laid out as follows:
Bits Register (note that top is bit 0)
------ -----------------------------------
0-7 - KY1 (Key Register 1)
7-23 - FM1 (Key Register 2)
24-55 - CS1 (Checksum register 1)
56-63 - RT1 (Return code register 1)
The following opcodes define how you interact with the controller. Note that
the UNUSED parts of the opcode should always be zero, as should any register not
explicitly listed in the specifications below. If the frame argument is not specified, then
it should be passed as NULL.
Register Request Value Response Value
BLOCK_OP_INITMS - Initialize the memory system
Register: KY1 BLOCK_OP_INITMS BLOCK_OP_INITMS
Register: RT N/A 0 if successful, -1 if failure
BLOCK_OP_BZERO - zero the entire block system
Register: KY1 BLOCK_OP_BZERO BLOCK_OP_BZERO
Register: RT N/A 0 if successful, -1 if failure
BLOCK_OP_RDFRME - read a frame from the block system
Register: KY1 BLOCK_OP_RDFRME BLOCK_OP_RDFRME
Register: RT N/A 0 if successful, -1 if failure
Register: FM1 frame number to read from N/A
BLOCK_OP_WRFRME - write a frame to the block device
Register: KY1 BLOCK_OP_WRFRME BLOCK_OP_WRFRME
Register: RT N/A 0 if successful, -1 if failure
Register: FM1 frame number to write to N/A
BLOCK_OP_POWOFF - power off the memory system
Register: KY1 BLOCK_OP_POWOFF BLOCK_OP_POWOFF
Register: RT N/A 0 if successful, -1 if failure
To execute an opcode, create a 64 bit value (uint64_t) and pass it any needed buffers
to the bus function defined in block_controller.h:
BlockXferRegister block_io_bus(BlockXferRegister regstate, void *buf)
The function returns packed register values with as listed in the "Response Value"
above.