[Tinyos-2-commits] CVS: tinyos-2.x/tos/chips/at45db LogStorageP.nc,
1.1.2.11, 1.1.2.12
David Gay
idgay at users.sourceforge.net
Thu Jun 1 15:09:28 PDT 2006
Update of /cvsroot/tinyos/tinyos-2.x/tos/chips/at45db
In directory sc8-pr-cvs10.sourceforge.net:/tmp/cvs-serv2144
Modified Files:
Tag: tinyos-2_0_devel-BRANCH
LogStorageP.nc
Log Message:
document design
support invalid read pointers in non-circled logs
Index: LogStorageP.nc
===================================================================
RCS file: /cvsroot/tinyos/tinyos-2.x/tos/chips/at45db/Attic/LogStorageP.nc,v
retrieving revision 1.1.2.11
retrieving revision 1.1.2.12
diff -C2 -d -r1.1.2.11 -r1.1.2.12
*** LogStorageP.nc 1 Jun 2006 16:53:13 -0000 1.1.2.11
--- LogStorageP.nc 1 Jun 2006 22:09:26 -0000 1.1.2.12
***************
*** 52,55 ****
--- 52,160 ----
implementation
{
+ /* Some design notes.
+
+ - The logId's in the LogRead and LogWrites are shifted left by 1 bit.
+ The low-order bit is 1 for circular logs, 0 for linear ones
+ (see newRequest and endRequest, and the LogStorageC configuration)
+
+ - Data is written sequentially to the pages of a log volume. Each page
+ ends with a footer (nx_struct pageinfo) recording metadata on the
+ current page:
+ o a cookie
+ o the "position" of the current page in the log (see below)
+ o the offset of the last record on this page (i.e., the offset
+ at which the last append ended) - only valid if flags & F_LASTVALID
+ o flags:
+ x F_SYNC page was synchronised - data after lastRecordOffset
+ is not log data; implies F_LASTVALID
+ x F_CIRCLED this page is not from the first run through the log's
+ pages (never set in linear logs)
+ x F_LASTVALID not set if no record ended on this page
+ o a CRC
+
+ - "Positions" are stored in the metadata, used as cookies by
+ currentOffset and seek, and stored in the wpos and rpos fields of the
+ volume state structure. They represent the number of bytes that
+ writing has advanced in the log since the log was erased, with
+ PAGE_SIZE added. Note that this is basically the number of bytes
+ written, except that when a page is synchronised unused bytes in the
+ page count towards increasing the position.
+
+ As a result, on page p, the following equation holds:
+ (metadata(p).pos - PAGE_SIZE) % volume-size == p * PAGE_SIZE
+ (this also means that the "position" metadata field could be replaced
+ by a count of the number of times writing has cycled through the log,
+ reducing the metadata size)
+
+ The PAGE_SIZE offset on positions is caused by Invariant 2 below: to
+ ensure that Invariant 2 is respected, at flash erase time, we write a
+ valid page with position 0 to the last block of the flash. As a result,
+ the first writes to the flash, in page 0, are at "position" PAGE_SIZE.
+
+ - This code is designed to deal with "one-at-a-time" failures (i.e.,
+ the system will not modify any blocks after a previous failed
+ write). This should allow recovery from:
+ o arbitrary reboots
+ o write failure (the underlying PageEEPROM shuts down after any
+ write fails; all pages are flushed before moving on to the next
+ page)
+ It will not recover from arbitrary data corruption
+
+ - When sync is called, the current write page is written to flash with an
+ F_SYNC flag and writing continues on the next page (wasting on average
+ half a flasg page)
+
+ - We maintain the following invariants on log volumes, even in the face
+ of the "one-at-a-time" failures described above:
+ 1) at least one of the first and last blocks are valid
+ 2) the last block, if valid, has the F_SYNC flag
+
+ - Locating the log boundary page (the page with the greatest position):
+
+ Invariant 1, the one-at-a-time failure model and the metadata position
+ definition guarantees that the physical flash pages have the following
+ properties:
+ an initial set of V1 valid pages,
+ followed by a set of I invalid pages,
+ followed by a set of V2 valid pages
+ with V1+i+V2=total-number-of-pages, and V1, V2, I >= 0
+ Additionally, the position of all pages in V1 is greater than in V2,
+ and consecutive pages in V1 (respectively V2) have greater positions
+ than their predecessors.
+
+ From this, it's possible to locate the log boundary page (the page with
+ the greatest position) using the following algorithm:
+ o let basepos=metadata(lastpage).pos, or 0 if the last page is invalid
+ o locate (using a binary search) the page p with the largest position
+ greater than basepos
+ invalid pages can be assumed to have positions less than basepos
+ if there is no such page p, let p = lastpage
+
+ Once the log boundary page is known, we resume writing at the last
+ page before p with a record boundary (Invariant 2, combined with
+ limiting individual records to volumesize - PAGE_SIZE ensures there
+ will be such a page).
+
+ - The read pointer has a special "invalid" state which represents the
+ current beginning of the log. In that state, LogRead.currentOffset()
+ returns SEEK_BEGINNING rather than a regular position.
+
+ The read pointer is invalidated:
+ o at boot time
+ o after the volume is erased
+ o after the write position "catches up" with the read position
+ o after a failed seek
+
+ Reads from an invalid pointer:
+ o start reading from the beginning of the flash if we are on the
+ first run through the log volume
+ o start reading at the first valid page after the write page with
+ an F_LASTVALID flag; the read offset is set to the lastRecordOffset
+ value
+ if this page has the SYNC flag, we start at the beginning of the
+ next page
+ */
+
+
enum {
F_SYNC = 1,
***************
*** 140,152 ****
}
- void readFromBeginning() {
- /* Set position to page before first page and force advance to next
- page on next read */
- s[client].rpos = SEEK_BEGINNING;
- s[client].rpage = firstVolumePage() - 1;
- s[client].rend = s[client].roffset = 0;
- s[client].rvalid = TRUE;
- }
-
void invalidateReadPointer() {
s[client].rvalid = FALSE;
--- 245,248 ----
***************
*** 225,228 ****
--- 321,325 ----
}
+ /* Enqueue request and request the underlying flash */
error_t newRequest(uint8_t newRequest, logstorage_t id,
uint8_t *buf, storage_len_t length) {
***************
*** 294,303 ****
void eraseMetadataDone() {
! s[client].positionKnown = TRUE;
s[client].wpos = PAGE_SIZE; // last page has offset 0 and is before us
s[client].circled = FALSE;
setWritePage(firstVolumePage());
- readFromBeginning();
endRequest(SUCCESS);
}
--- 391,402 ----
void eraseMetadataDone() {
! /* Set write pointer to the beginning of the flash */
s[client].wpos = PAGE_SIZE; // last page has offset 0 and is before us
s[client].circled = FALSE;
setWritePage(firstVolumePage());
+ invalidateReadPointer();
+
+ s[client].positionKnown = TRUE;
endRequest(SUCCESS);
}
***************
*** 306,310 ****
if (firstPage == lastPage - 1)
{
! /* We create a valid, synced last page */
metadata.flags = F_SYNC | F_LASTVALID;
metadata.lastRecordOffset = 0;
--- 405,409 ----
if (firstPage == lastPage - 1)
{
! /* We create a valid, synced last page (see invariants) */
metadata.flags = F_SYNC | F_LASTVALID;
metadata.lastRecordOffset = 0;
***************
*** 339,342 ****
--- 438,442 ----
/* We've found the last valid page with a record-end. Set up
the read and write positions. */
+ invalidateReadPointer();
if (metadata.flags & F_SYNC) /* must start on next page */
***************
*** 352,372 ****
}
- /* If we're on the first pass (no F_CIRCLED flag), the read
- pointer starts at the beginning of the flash. Otherwise,
- we invalidate it, which will force read requests to find
- the first valid page after the current write pointer. */
s[client].circled = (metadata.flags & F_CIRCLED) != 0;
! if (s[client].circled)
{
! if (!s[client].circular) // oops
! {
! endRequest(FAIL);
! return;
! }
!
! invalidateReadPointer();
}
- else
- readFromBeginning();
/* And we can now proceed to the real request */
--- 452,461 ----
}
s[client].circled = (metadata.flags & F_CIRCLED) != 0;
! if (s[client].circled && !s[client].circular) // oops
{
! endRequest(FAIL);
! return;
}
/* And we can now proceed to the real request */
***************
*** 459,463 ****
}
! /* Locate log beginning and ending */
void locateStart() {
metaState = META_LOCATEFIRST;
--- 548,552 ----
}
! /* Locate log beginning and ending. See description at top of file. */
void locateStart() {
metaState = META_LOCATEFIRST;
***************
*** 483,486 ****
--- 572,576 ----
if (s[client].wpage == lastVolumePage())
{
+ /* We reached the end of a linear log */
endRequest(ESIZE);
return;
***************
*** 559,563 ****
void wmetadataStart() {
! /* The caller ensures that metadata is set correctly. */
metaState = META_WRITE;
firstPage = s[client].wpage; // remember page to commit
--- 649,654 ----
void wmetadataStart() {
! /* The caller ensures that metadata.flags (except F_CIRCLED) and
! metadata.lastRecordOffset are set correctly. */
metaState = META_WRITE;
firstPage = s[client].wpage; // remember page to commit
***************
*** 619,624 ****
if (!s[client].rvalid)
{
! /* Find a valid page after wpage */
! s[client].rpage = s[client].wpage;
rmetadataStart();
return;
--- 710,723 ----
if (!s[client].rvalid)
{
! if (s[client].circled)
! /* Find a valid page after wpage, skipping invalid pages */
! s[client].rpage = s[client].wpage;
! else
! {
! /* resume writing at the beginning of the first page */
! s[client].rvalid = TRUE;
! s[client].rpage = lastVolumePage() - 1;
! }
!
rmetadataStart();
return;
***************
*** 632,636 ****
if ((s[client].rpage + 1 == lastVolumePage() && !s[client].circular) ||
s[client].rpage == s[client].wpage)
! endRequest(SUCCESS);
else
rmetadataStart();
--- 731,735 ----
if ((s[client].rpage + 1 == lastVolumePage() && !s[client].circular) ||
s[client].rpage == s[client].wpage)
! endRequest(SUCCESS); // end of log
else
rmetadataStart();
***************
*** 660,663 ****
--- 759,764 ----
void continueReadAt(at45pageoffset_t roffset) {
+ /* Resume reading at firstPage whose metadata is currently available
+ in the metadata variable */
metaState = META_IDLE;
s[client].rpos = metadata.pos + roffset;
More information about the Tinyos-2-commits
mailing list