237 lines
12 KiB
TeX
237 lines
12 KiB
TeX
\label{ioman}
|
|
The IOManager that is the second lowest layer of the embedded filesystems library is
|
|
responsible for coordinating disk input and output, as well as managing a caching
|
|
system. This documentation describes the second implementation of IOMan, which includes
|
|
features such as :
|
|
\begin{itemize}
|
|
\item Delayed write
|
|
\item Buffer reference statistics
|
|
\item Buffer exportable to users
|
|
\item Support for cached direct I/O as well as indirect I/O
|
|
\item Can allocate memory itself (on the stack), or you can do it yourself (heap)
|
|
\end{itemize}
|
|
|
|
\subsubsection{General operation}
|
|
Because of the limited memory nature of most embedded devices for which this library is
|
|
intended several design decisions were made to minimize memory usage. Some of these required
|
|
that some concessions be made. One of them is that there is no memory protection, since
|
|
most devices don't have the memory to support this, or lack the ability to protect memory.
|
|
|
|
When IOMan receives a request for a sector, it will make sure it has the sector in it's
|
|
own memory cache and then give the caller a \code{euint8*} pointer to that cache. The
|
|
user is then free to do operations on that memory, and when it is done it should tell
|
|
IOMan so. Several things can go wrong with this: you can request a sector for reading,
|
|
and then write in the cache, thereby corrupting it. Or you can request a sector, but never
|
|
release it (sort of a memory leak), which may result in very bad performance, and a deadlocked
|
|
I/O manager.
|
|
|
|
But, taking into account that very little memory is required for operation, if you follow the I/O man rules, you will get a pretty clever caching object that will make writing new filesystems
|
|
a simple job.
|
|
|
|
\subsubsection{Cache decisions}
|
|
Whenever ioman receives a request to fetch a sector, be it read or write, it will have to make sure
|
|
it has, or can get the sector you want. It follows a certain path to do this.\label{cachemethod}
|
|
\begin{enumerate}
|
|
\item First of all it will scan it's cache range to see if it already has the sector.
|
|
If it is found, and it was a write request, the cache is marked writable. Usage and
|
|
reference get incremented and a pointer is then returned to the requester. If the
|
|
buffer cannot be found, ioman proceeds to step 2.
|
|
\item When an item is not in cache, it has to be fetched from the disc, the best place to
|
|
store it is in memory that does not contain anything useful yet. Ioman will search for
|
|
a place that is currently not occupied by anything. If it is found, the sector will be
|
|
placed on that spot and a pointer returned. Else, ioman proceeds to step 3.
|
|
\item Since there is no other choice than to overwrite an already existing cache, ioman will
|
|
try to find one that is the least interesting. First it will search for caches that
|
|
are marked not writable, and have no users. Ioman will then select the one that has the
|
|
least references. If there are none, it will search for caches that don't have users and
|
|
are writable. Once again the one with the least references is returned. Since it is
|
|
writable ioman will flush it to disc first. After that the requested sector is put there
|
|
and a pointer returned. If it cannot find any caches that have no users it will go to
|
|
step 4.
|
|
\item Since every cache spot is in use ioman will have to select one for overallocation.
|
|
Since this selection depends on many factors and is rather complex, a points
|
|
system is used. The algorithm considers every cache place and allocated a certain number
|
|
of points to it, lower means that it is a better candidate for overallocation. Fifty
|
|
percent of the points goes to the cache being marked writable, since having to write
|
|
a sector is expensive. Another 35 percent goes to how many overallocations have
|
|
already been done on that spot. It doesn't make sense to always overalloc the same buffer,
|
|
it is better to spread this. The remaining 15 percent is determined by the number of
|
|
references to the sector.
|
|
|
|
After a function has selected the best candidate, ioman will overwrite that spot with
|
|
the new sector. It will also push the status and sectornumber onto that cache's
|
|
retrieval stack, so that when the sector is released, the older sector can be retrieved.
|
|
If this fails go to step 5.
|
|
\item When ioman gets here it will return a (nil) pointer and flag an error.
|
|
\end{enumerate}
|
|
|
|
\subsubsection{Functions}
|
|
|
|
\begin{longtable}{|p{0.35\textwidth}|p{0.65\textwidth}|}
|
|
|
|
\hline
|
|
\multicolumn{2}{|c|}{
|
|
\textbf{I/O Manager Functions}
|
|
} \\
|
|
\multicolumn{2}{|c|}{} \\
|
|
\hline
|
|
\hline
|
|
\endfirsthead
|
|
|
|
\hline
|
|
\multicolumn{2}{|c|}{\textbf{I/O Manager Functions (continued)}} \\
|
|
\hline
|
|
\endhead
|
|
|
|
\hline
|
|
\endfoot
|
|
|
|
\hline
|
|
\endlastfoot
|
|
|
|
\code{ioman\_init} & \code{esint8 (IOManager *ioman, hwInterface *iface, euint8* bufferarea)} \\
|
|
\hline
|
|
\multicolumn{2}{|p{\textwidth}|}{
|
|
This function is called to initialize the internal state of the I/O manager. It should be the
|
|
first function you call on an ioman object. Failure to do so will result in undefined behavior.
|
|
The function clears all internal variables to a default safe state, and sets up it's memory region.
|
|
|
|
There are two possibilities, if you supply a 0 pointer then a function will be called that contains
|
|
a static variable with a size of 512 * \code{IOMAN\_NUMBUFFERS}, else, it will be assumed that
|
|
you allocated that memory yourself and the pointer you provided will be used.
|
|
}\\
|
|
\hline
|
|
|
|
\code{\external{ioman\_reset}} & \code{void (IOManager *ioman)} \\
|
|
\hline
|
|
\multicolumn{2}{|p{\textwidth}|}{
|
|
This function is called from the initialization function, it does the actual reset of all variables.
|
|
}\\
|
|
\hline
|
|
|
|
\code{ioman\_pop} & \code{esint8 (IOManager *ioman,euint16 bufplace)} \\
|
|
\hline
|
|
\multicolumn{2}{|p{\textwidth}|}{
|
|
This function fetches settings (sector number, usage and status register) from stack \code{bufplace}
|
|
and puts it back on the main registers. It will return 0 on successful pop, and -1 on error, or when
|
|
there are no elements to pop.
|
|
}\\
|
|
\hline
|
|
|
|
\code{ioman\_push} & \code{esint8 (IOManager *ioman,euint16 bufplace)} \\
|
|
\hline
|
|
\multicolumn{2}{|p{\textwidth}|}{
|
|
This function pushes the settings of cache \code{bufplace} onto that cache's stack. It does not
|
|
destroy the data in the main registers. It will return 0 for a successful push, and -1 on error, or
|
|
when there is no more space to push a new element.
|
|
}\\
|
|
\hline
|
|
|
|
\code{ioman\_readSector} & \code{esint8 (IOManager *ioman,euint32 address,euint8* buf)} \\
|
|
\hline
|
|
\multicolumn{2}{|p{\textwidth}|}{
|
|
This function does the actual reading from the hardware, it is the one and only function that
|
|
calls \code{if\_readBuf()}, here a retry on failure policy could be implemented. This function
|
|
will correctly stream errors upwards. All calls made to this function in the iomanager are checked
|
|
for their return value, so errors propagate correctly upwards.
|
|
|
|
The address it receives is relative to the beginning of the disc, no assumptions about \code{buf}
|
|
may be made, it can be withing ioman's cache memory range, but it could also be a buffer from userspace.
|
|
|
|
The function will return 0 on success and -1 on failure.
|
|
}\\
|
|
\hline
|
|
|
|
\code{ioman\_writeSector} & \code{esint8 (IOManager *ioman, euint32 address, euint8* buf)} \\
|
|
\hline
|
|
\multicolumn{2}{|p{\textwidth}|}{
|
|
This function does the actual writing to the hardware, it is the one and only function that
|
|
calls \code{if\_writeBuf()}, here a retry on failure policy could be implemented. This function
|
|
will correctly stream errors upwards. All calls made to this function in the iomanager are checked
|
|
for their return value, so errors propagate correctly upwards.
|
|
|
|
The address it receives is relative to the beginning of the disc, no assumptions about \code{buf}
|
|
may be made, it can be withing ioman's cache memory range, but it could also be a buffer from userspace.
|
|
|
|
The function will return 0 on success and -1 on failure.
|
|
}\\
|
|
\hline
|
|
|
|
\code{\external{ioman\_getSector}} & \code{euint8* (IOManager *ioman,euint32 address, euint8 mode)} \\
|
|
\hline
|
|
\multicolumn{2}{|p{\textwidth}|}{
|
|
This function is the one that is called most from the higher library routines. It is the function
|
|
that will present you with a pointer to memory containing sector number \code{address}. There are
|
|
several modes that you can select or combine.\newline
|
|
\begin{tabular}{|l|p{.6\textwidth}|}
|
|
\hline
|
|
\code{IOM\_MODE\_READONLY} & This attribute says to ioman that it needs a buffer only for reading.
|
|
This does not mean that you are allowed to write to it, doing so results in undefined behavior.
|
|
You cannot combine this option with the \code{IOM\_MODE\_READWRITE} option.\\
|
|
\code{IOM\_MODE\_READWRITE} & This attribute says to ioman that it would like not only to read from
|
|
but also to write to that buffer. When you release the sector your changes will be written to disc.
|
|
This may not happen immediately though, if you want to force it take a look at the
|
|
\code{ioman\_flushRange()} function. This option cannot be combined with the
|
|
\code{IOM\_MODE\_READONLY} option.\\
|
|
\code{IOM\_MODE\_EXP\_REQ} & This option tell the iomanager that the request is exceptional, for
|
|
example that the request is unlikely to happen again. The library adds this flags to the options
|
|
when requesting the bootrecord, to prevent it from getting a high rating, which should prevent it
|
|
from being removed from the cache.\\
|
|
\hline
|
|
\end{tabular}\newline
|
|
These options can be combined by ORing them together.
|
|
}\\
|
|
\hline
|
|
|
|
\code{ioman\_releaseSector} & \code{esint8 (IOManager *ioman,euint8* buf)} \\
|
|
\hline
|
|
\multicolumn{2}{|p{\textwidth}|}{
|
|
This function tells ioman that you are done with one of the cache elements and that it can do
|
|
it's bidding with it. Forgetting to call this function may result in deadlocked iomanagers.
|
|
}\\
|
|
\hline
|
|
|
|
\code{ioman\_directSectorRead} & \code{esint8 (IOManager *ioman,euint32 address, euint8* buf)} \\
|
|
\hline
|
|
\multicolumn{2}{|p{\textwidth}|}{
|
|
This is a variant of the normal getsector. Sometimes you need a sector from the disc, but all
|
|
you want to do with it is export it directly to userbuffers. It would be foolish to force a
|
|
caching of that sector if there is external space available for it.
|
|
|
|
This function will fetch sector \code{address} from disc and place it in the memory pointed to
|
|
by \code{buf}. Should there be a free spot available the sector will be cached there, so that
|
|
it may be used in the future. If the sector was available from cache in the first place, it
|
|
will simply be \code{memCpy()}'d from the cache to the userspace buffer.
|
|
}\\
|
|
\hline
|
|
|
|
\code{ioman\_directSectorWrite} & \code{esint8 (IOManager *ioman,euint32 address, euint8* buf)} \\
|
|
\hline
|
|
\multicolumn{2}{|p{\textwidth}|}{
|
|
This function is based on the same philosophy as \code{ioman\_directSectorRead()}, however,
|
|
contrary to what the name may lead to believe it also passes through a caching layer. If
|
|
there is an unused spot (or the sector is in cache), the userbuffer will be copied to that
|
|
spot and will remain there until the space is needed or a flush is forced.
|
|
}\\
|
|
\hline
|
|
|
|
\code{ioman\_flushRange} & \code{esint8 (IOManager *ioman,euint32 address\_low, euint32 address\_high)} \\
|
|
\hline
|
|
\multicolumn{2}{|p{\textwidth}|}{
|
|
This function is used to ask ioman to flush all sectors to disc that are in a specific
|
|
range. For example you might want to flush a specific range of your filesystem without
|
|
needlessly disturb other parts. The range is \code{address\_low <= n => address\_high}.
|
|
Off course only sectors that are marked as writable are flushed to disc.
|
|
}\\
|
|
\hline
|
|
|
|
\code{ioman\_flushAll} & \code{esint8 (IOManager *ioman)} \\
|
|
\hline
|
|
\multicolumn{2}{|p{\textwidth}|}{
|
|
This function will cause ioman to flush out all cache units that are marked writable. If
|
|
they do not have any users, they will lose their writable mark.
|
|
}\\
|
|
\hline
|
|
\end{longtable}
|
|
|