% to get hardcopy of this lecture, you need the following files (plus the
% normal latex and tex base directories):
%     lecture.tex:     latex source file
%     macros.tex:      figure macros
%     psfig.tex:       postscript figure (psfig) macro definition
%     extent.idraw:	figure, created with idraw
%     pocs-header.sty: Principles of Computer Systems lecture latex header
%
% run the following programs:
% latex lecture
%   -> Creates lecture.dvi, plus latex intermediate files
% latex lecture
%   -> Run a second time to get cross-references right
% dvi2ps lecture |  egrep -v '^showpage' > lecture.ps
%   -> Merges the figures with the text, can also pipe to lpr
%
\documentstyle[12pt,pocs-header]{article}
\Scribe{Robert Grace}
\Lecturer{Butler Lampson}
\LectureNumber{3}
\LectureDate{September 18, 1991}
\include{macros} % used to include figures in this document
\begin{document}
\MakeScribeTop

\section{Handouts}

Handout 10, ``Simple File System Implementation,'' accompany the lecture.
Lecture 1 notes were distributed as well.

\section{Overview}

This lecture dealt with the specification and
partial implementation of a ``simple'' file system, and the issues
surrounding the modelling of such a system.  In our discussion, concurrency
was abstracted out and only straightforward data structures were
assumed.  Topics discussed were:

\begin{itemize}
\item {\bf Physical device characteristics} -- How the underlying
technology required for a file system affects the approach to
forming a file system abstraction.  Performance and cost of a
typical hard disk and cache were examined.

\item {\bf Specification issues} -- Some good rules to keep in mind,
especially when designing a file system.

\item {\bf System crashes} -- How to preserve
stability in our storage across unexpected crashes by using {\em atomic
operations}, in which a disk write either completes or doesn't change
the disk state at all.  

\item {\bf Caching} -- Basic principles of cache use in a file system,
and ways of specifying its operation.

\item {\bf File representation} -- Choosing a rep that will balance
good performance and the functionality desired by the client.
\end{itemize}

\section{File System Model}

In our approach to creating a file system specification, we must find
an abstraction that will fulfill the client's desires while giving the
implementor ``room to breathe.''  This implies that we look at the
client's needs for functionality and performance, and the
tools and technology the implementor is given to work with.  The
agreement reached from both sides will determine the system {\em interface}
used between the two.

\subsection{Functionality}
While suggesting the functionality of the file system, we wish to keep in mind
the level of detail at which the specification should be written.
Similarly, we should be aware of how important certain aspects of
functionality are to the client.  Our file system, for example, must
preserve stability in the storage across crashes, but we haven't yet
investigated crash {\em recovery}, which may be something the client
will require.  Other assumptions about functionality are:
\begin{itemize}
\item The disk hardware is organized by blocks.  The disk controller
ensures that all block writes are atomic through crashes or power
failures.  This is done by error-correcting codes used in the disk's
storage of data; either a block is written, not written, or the block
is garbled (when viewed by the disk's error checker) so that it can be discarded after system recovery.

\item The disk hardware will not fail.  Although this happens in real
life, the requirements that safety from disk failure would put on ensuring
stability would make our model much more complex.

\item The processor {\em will} fail at some unspecified time, but
single instructions are ensured to run to completion once they are
started.
\end{itemize}

For this exercise, we will also assume that the file system operates
on a single directory, and that no concurrency exists (apart from
crashes).  Also, our use of a {\em write-back} cache means a disk
write operation from the client's perspective might seem to go through
before a crash, but in reality may still be in the cache at the time
the crash occurs, awaiting the write-behind.  We can only ensure that the order of disk
writes remains correct.  This would be insufficient for most
applications, but it can still serve as a solid basis for a reliable log-based
recovery system.

The specifications for the file and disk abstractions appear in handout 10.  In the $File$ module, note the specification for
the write operation.  Two important specification decisions are
reflected here.  First, it is seen that the procedure is not atomic,
contrary to what one might expect.  This is because a requirement for
atomicity here would require the implementation to block while writing
arbitrarily long data segments, a situation that is undesirable from
the client's point of view and costly for the implementor.
Second, we see that the spec handles non-trivial cases (where the data
extends past the bounds of the existing file being operated on) instead of raising an exception.
The significance of both of these decisions shows that the level of
detail in a spec is often well-determined.  The functionality
requirements of the client and the ``facts of life'' that the
implementor deals with guide these decisions.  

The module $BufferedDisk$ offers an implementation of $Dsk$ that
uses the write-behind cache and a queue to preserve the order of
cached block writes.  The queue supplements the cache by maintaining
an ordered array of disk addresses whose corresponding data are in the cache, so
that during the write-behind, the cache can look to the queue to
determine the order in which to transfer entries to the disk.

The abstraction function in $BufferedDisk$ is straightforward -- each ``disk'' address accessed in the $Dsk$ spec
maps to either 1) a cache entry (if the address is defined), or 2) an
actual disk entry (if the cache does not find the address requested).
The representation invariants here naturally reflect the space
limitations of the hardware used.

\subsection{Hardware Characteristics}
In addition to functionality, an important issue to address when
developing the model's specification is this:  is performance more important to the client than the cost of
the different types of hardware avaliable to the implementor, and is
the performance requirement possible to attain with such hardware?
For our file system, we desire to use a cache in addition to the disk
for better performance.  

\subsubsection{Performance}
We are given some sample performance figures to help us reason about
our file system model.  These will suggest two performance features
available to us to use:
\begin{itemize}
\item {\bf Sequential ordering} -- To initiate a disk access operation,
the disk head must move to the beginning of the first block in the
segment, an operation that takes about $10^{5}$ times as long as a
random access to RAM by the CPU.  However, rate of access of sequential bytes
on a disk is within an order of magnitude of a typical sequential RAM
access rate.  This suggests that ordering the data written to stable
storage in groups of $10^{5}$ bytes or more will give comparable performance.

\item {\bf Caching} As we saw earlier in the memory module,
supplementing data storage with a write-behind cache improves
performance given the cache is sufficiently large.  If we take a 1 MB
cache with a miss rate of $10^{-5}$, a cache access time given by
$t_{c}$, and the access time of our disk as $t_{m}$, the time for a
typical byte read should be \newline 
\begin{center}
$(t_{c} + r * t_{m}) = 2 * t_{c}$
\end{center}
\end{itemize}

For our example, both sequential ordering and the write-back
cache were utilized to enhance performance.  

\subsubsection{Cost}
The cost of implementing a file system is based on various factors.
Most obvious is the potential cost of the hardware available for our
system.  Current ``retail'' prices for disk- and memory-based storage
weigh in favor of disk storage:  hard disk storage typically costs 
\$2 -- 2.5/MB, where RAM memory (for our cache) runs about
\$40/MB.    

\section{File Representation}
The representation of files on the disk is a significant
factor in implementing a file system, since performance of the file system,
efficient use of disk space, and disk space allocation all depend on
the representation used.  What is required is a mapping of a file
identifier (usually a character string) to disk storage units (disk
blocks in this example).  

The most straightforward representation to consider is the mapping of
the filename to a sequence of disk blocks, $SEQ[DB]$.  This would
certainly help us achieve sequentiality.  However, with many such
files on a disk, as the files may grow in size they would need
constant reorganization on the disk to avoid overlaps.  

A solution to this is to partition the file into segments of disk
blocks, each at different disk addresses.  On page 7 of handout 10,
the concept of using {\em extents} is described.  An extent is a record pointing
to a segment of disk space.  It is a record whose first entry is a
pointer to either 1) the starting address of a disk segment, or 2) the
address of a sequence of extents.  The second entry specifies the
extent's size, which is often a constant for all extents in a
filesystem (4 kbytes in our discussion).

For small files that span only a few extents, a simple sequence of
extents is used to represent the file (refer to Fig. 1).  Quick access
is facilitated by representing files with a single sequence of extents
(cells 0 -- 11 in the figure).  For a disk access, the implementation
simply accesses each extent by skipping through in increments of the
extent size (4k here).  If an extent sequence is initially allocated
at least $n$ (11 in the figure) adjacent positions, the file may grow up to $n$ times the
extent size and still require only the basic extents.

For files requiring more space, new extent sequences must be
allocated.  Our representation may apply a tree-based structure such
as that seen in the directory inode representation used in UNIX. In this representation, the address field
of the extent in cell 12 of the figure would point to the beginning
address of another sequence of 13 extents, whose last cell would point to another sequence, and so
on.  For even larger files, {\em each} of the 13 extents could point
to another 13-cell extent sequence; any size file could be represented
by such a tree structure.

Although the access of a random part of larger files gets increasingly
slower (this is inevitable), we can see that a tree structure based on
cells of extent allows a search in log time, whereas the original
basic sequence representation would require linear time.  For larger
files, the performance improvement in our design choice is obvious.

\begin{figure}
\centerline{\psfig{width=8in,figure=extent.idraw}}
\caption{A tree of extents.}
\label{extent}
\end{figure}

\end{document}
