From 5498ee2e92b363a93a21021b8c3082e5d6bcdced Mon Sep 17 00:00:00 2001 From: Frans Kaashoek Date: Mon, 12 Aug 2019 10:25:55 -0400 Subject: Draft mount/umount lab as an alternative lab for fs.html fs.html should perhaps be split in small homeworks as in previous years in preparation for lectures and/or as demos during lecture. --- labs/fs1.html | 198 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 198 insertions(+) create mode 100644 labs/fs1.html (limited to 'labs') diff --git a/labs/fs1.html b/labs/fs1.html new file mode 100644 index 0000000..cf5bc8e --- /dev/null +++ b/labs/fs1.html @@ -0,0 +1,198 @@ + + +Lab: mount/umount + + + + +

Lab: mount/umount

+ +

In this lab you will add support for mounting/unmounting of file +systems to xv6. This lab will expose you to many parts of the xv6 file +system, including pathname lookup, inodes, logging, disk driver, +concurrency, etc. + +

Your job is modify xv6 so that your modified kernel passes the + tests in mounttest. You will have to implement two system + calls: mount(char *source, char *target) + and umount(char *target). Mount attaches the device + referenced by source (e.g., /disk1) at the + location specified by target. For + example, mount("/disk1", "/m") will attach disk1 + at the directory /m. After this mount call, users can use + pathnames such as /m/README to read the + file README stored in the root directory + on disk1. Umount removes the attachment. For + example, umount("/m") unmounts disk1 from /m. + +

There are several major challenges in implementing the mount system +calls: + +

+ +

The rest of this assignment provides some hints how you might go +about the above challenges. + +

Adding system calls

+ +

Add the stubs for the two systems calls to xv6 so that you can +compile mounttest and add two empty functions for the two system calls +to sysfile.c. Run mounttest and it will fail on the first call +to mount. + + +

Adding a second disk

+ +

To be able to mount another disk, you need to extend xv6 to support +at least two disks. Modify virtio_disk.c to support an array of two +disks instead of a single disk. The address of the second disk +is 0x10002000; modify the macro R to take a disk +number (0, 1,..) and read/write to the memory address for that disk. + +

All functions in virtio_disk.c need to take the disk +number as an argument to update the state of the disk that is +read/written to or to receive an interrupt from the disk. +Modify virtio_disk_init to take a disk number as an argument +and update is to that it initializes that disk. Similar, go through +the other functions; make these changes should be most mechanical +(i.e., text substitutions). + +

The second disk interrupts at IRQ 2; modify trap.c to receive that +interrupt and virtio_disk_intr with the number of the disk +that generated the interrupt. + +

Modify the file Makefile to tell qemu to provide a second +disk. Define the variable QEMUEXTRA = -drive +file=fs1.img,if=none,format=raw,id=x1 -device +virtio-blk-device,drive=x1,bus=virtio-mmio-bus.1 and +add $(QEMUEXTRA) to the end of QEMUOPTS. + +

Create a second disk image fs1.img. Easiest thing to do + is just copy the file fs.img. You might want to add rules + to the Makefile to make this image and remove it on make + clean. + +

Add to the user program init a call to create a device for the new + disk. For example, add the line mknod("disk1", DISK, 1); to + init.c. This will create an inode of type device in the root + directory with major number DISK and minor number 1. + +

The first argument of the mount system call ("disk1") will + refer to the device you created using mknod above. In your + implementation of the mount system call, + call virtio_disk_init with the minor number as the argument + to initialize the second disk. (We reserve minor number 0 for the + first disk.) + +

Boot xv6, run mounttest, and make sure virtio_disk_init + gets called (e.g., add print statement). You won't know if your + changes are correct, but your code should compile and invoke the + driver for the second disk. + +

Modify the logging system

+ +

After calling virtio_disk_init, you need to also + call loginit to initialize the logging system for the + second disk (and restore the second disk if a power failure happened + while modifying the second disk). Generalize the logging system to + support to two logs, one on disk 0 and one disk 1. These changes + are mostly mechanical (e.g., log. changes + to log[n].), similar to generalizing the disk driver to + support two disks. + +

To make xv6 compile, you need to provide a disk number + to begin_op and end_op. It will be a challenge to + figure out what the right value is; for now just specify the first + disk (i.e., 0). This isn't correct, since modifications to the + second disk should be logged on the second disk, but we have no way + yet to read/write the second disk. Come back to this later when you + have a better idea how things will fit together, but make sure that + xv6 compiles and still runs. + +

Pathname lookup

+ +

Modify namex to traverse mount points: when namex + sees an inode to which a file system is attached, it should traverse + to the root inode of that file system. Hint: modify the in-memory + inode in file.h to keep some additional state, and initialize that + state in the mount system call. Note that the inode already has a + field for disk number (i.e., dev), which is initialized and + passed to reads and writes to the driver. dev corresponds + to the minor number for disk devices. + +

Your modified xv6 should be able to pass the first tests in + mounttest (i.e., stat). This is likely to be challenging, + however, because now your kernel will be reading from the second + disk for the first time, and you may run into many issues. + +

Even though stat may return correctly, your code is likely + to be incorrect, because in namex + because iunlockput may modify the second disk (e.g., if + another process removes the file or directory) and those + modifications must be written to the second disk. Your job is to + fix the calls to begin_op and end_op to take the + right device. One challenge is that begin_op is called at + the beginning of a system call but then you don't know the device + that will be involved; you will have to postpone this call until you + know which inode is involved (which tells you will which device is + involved). Another challenge is that you cannot postpone + calling begin_op passed ilock because that + violates lock ordering in xv6; you should not be + calling begin_op while holding locks on inodes. (The log + system allows a few systems calls to run; if a system call that + holds an inode lock isn't admitted and one of the admitted system + calls needs that inode to complete, then xv6 will deadlock.) + +

Once you have implemented a plan for begin_op + and end_op, see if your kernel can pass test0. It + is likely that you will have to modify your implementation of the + mount system call to handle several corner cases. See the tests + in test0. + +

Run usertests to see if you didn't break anything else. Since you + modified namex and begin/end_op, which are at the + core of the xv6 file system, you might have introduced bugs, perhaps + including deadlocks. Deadlocks manifest themselves as no output + being produced because all processes are sleeping (hit ctrl-p a few + times). Your kernel might also suffer kernel panics, because your + changes violate invariants. You may have to iterate a few times to + get a good design and implementation. + +

umount

+ +

Once your kernel passes usertests and test0 of mounttest, implement + umount. Make sure your kernel can pass test1 of mounttest. + +

Test2 of mounttest stresses more; if you have done + everything right above, your kernel may be able to pass it. + + + + +

Optional challenges

+ +

Modify xv6 so that init mounts the first disk on the root inode. + This will allow you to remove some code specific for the first disk + from the kernel. + +

Support mounts on top of mounts. -- cgit v1.2.3