diff --git a/labs/fs.html b/labs/fs.html index 34f64e0..d478f96 100644 --- a/labs/fs.html +++ b/labs/fs.html @@ -1,4 +1,4 @@ -q + Lab: file system @@ -136,114 +136,220 @@ new clean file system image for you. bread().

You should allocate indirect blocks and doubly-indirect -blocks only as needed, like the original bmap(). + blocks only as needed, like the original bmap(). -

Memory-mapped files

+

Optional challenge: support triple-indirect blocks. -

In this assignment you will implement the core of the systems - calls mmap and munmap; see the man pages for an - explanation what they do (run man 2 mmap in your terminal). - The test program mmaptest tells you what should work. +

Writing with a Log

-

Here are some hints about how you might go about this assignment: +Insert a print statement in bwrite (in bio.c) so that you get a +print every time a block is written to disk: -

- -

Run usertests to make sure you didn't break anything. +

+
    +
  1. Annotate the bwrite lines with the kind of information that is +being written to the disk (e.g., "README's inode", "allocation +bitmap"). If the log is being written, note both that the log is being +written and also what kind of information is being written to the log. +
  2. Mark with an arrow the first point at which, if a +crash occured, README would be missing after a reboot +(after the call to recover_from_log()). +
+

+
-

Optional challenges: -

+

Creating a Problem

+ +

+The point of the xv6 log is to cause all the disk updates of a +filesystem operation to be atomic with respect to crashes. +For example, file creation involves both adding a new entry +to a directory and marking the new file's inode as in-use. +A crash that happened after one but before the other would +leave the file system in an incorrect state after a reboot, +if there were no log. + +

+The following steps will break the logging code in a way that +leaves a file partially created. + +

+First, replace commit() in log.c with +this code: +

+#include "kernel/proc.h"
+void
+commit(void)
+{
+  int pid = myproc()->pid;
+  if (log.lh.n > 0) {
+    write_log();
+    write_head();
+    if(pid > 1)            // AAA
+      log.lh.block[0] = 0; // BBB
+    install_trans();
+    if(pid > 1)            // AAA
+      panic("commit mimicking crash"); // CCC
+    log.lh.n = 0; 
+    write_head();
+  }
+}
+
+ +

+The BBB line causes the first block in the log to be written to +block zero, rather than wherever it should be written. During file +creation, the first block in the log is the new file's inode updated +to have non-zero type. +Line BBB causes the block +with the updated inode to be written to block 0 (whence +it will never be read), leaving the on-disk inode still marked +unallocated. The CCC line forces a crash. +The AAA lines suppress this buggy behavior for init, +which creates files before the shell starts. + +

+Second, replace recover_from_log() in log.c +with this code: +

+static void
+recover_from_log(void)
+{
+  read_head();      
+  printf("recovery: n=%d but ignoring\n", log.lh.n);
+  // install_trans();
+  log.lh.n = 0;
+  // write_head();
+}
+
+ +

+This modification suppresses log recovery (which would repair +the damage caused by your change to commit()). + +

+Finally, remove the -snapshot option from the definition +of QEMUEXTRA in your Makefile so that the disk image will see the +changes. + +

+Now remove fs.img and run xv6: +

+  % rm fs.img ; make qemu
+
+

+Tell the xv6 shell to create a file: +

+  $ echo hi > a
+
+ +

+You should see the panic from commit(). So far +it is as if a crash occurred in a non-logging system in the middle +of creating a file. + +

+Now re-start xv6, keeping the same fs.img: +

+  % make qemu
+
+ +

+And look at file a: +

+  $ cat a
+
+ +

+ You should see panic: ilock: no type. Make sure you understand what happened. +Which of the file creation's modifications were written to the disk +before the crash, and which were not? + +

Solving the Problem

+ +Now fix recover_from_log(): +
+static void
+recover_from_log(void)
+{
+  read_head();
+  cprintf("recovery: n=%d\n", log.lh.n);
+  install_trans();
+  log.lh.n = 0;
+  write_head();
+}
+
+ +

+Run xv6 (keeping the same fs.img) and read a again: +

+  $ cat a
+
+ +

+This time there should be no crash. Make sure you understand why +the file system now works. + +

+Why was the file empty, even though you created +it with echo hi > a? + +

+Now remove your modifications to commit() +(the if's and the AAA and BBB lines), so that logging works again, +and remove fs.img. + +

Streamlining Commit

+ +

+Suppose the file system code wants to update an inode in block 33. +The file system code will call bp=bread(block 33) and update the +buffer data. write_log() in commit() +will copy the data to a block in the log on disk, for example block 3. +A bit later in commit, install_trans() reads +block 3 from the log (containing block 33), copies its contents into the in-memory +buffer for block 33, and then writes that buffer to block 33 on the disk. + +

+However, in install_trans(), it turns out that the modified +block 33 is guaranteed to be still in the buffer cache, where the +file system code left it. Make sure you understand why it would be a +mistake for the buffer cache to evict block 33 from the buffer cache +before the commit. + +

+Since the modified block 33 is guaranteed to already be in the buffer +cache, there's no need for install_trans() to read block +33 from the log. Your job: modify log.c so that, when +install_trans() is called from commit(), +install_trans() does not perform the needless read from the log. + +

To test your changes, create a file in xv6, restart, and +make sure the file is still there.