-
Notifications
You must be signed in to change notification settings - Fork 5
Multipage Deallocator #19
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
fs/hayleyfs/balloc.rs
Outdated
|
|
||
| fn dealloc_dir_page_list(&self, pages: &DirPageListWrapper<Clean, Free>) -> Result<()> { | ||
| if let Some(allocator) = self { | ||
| let page_list = pages.get_page_list_cursor(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove this line, since we obtain the cursor for the list on the next line.
fs/hayleyfs/balloc.rs
Outdated
| } | ||
|
|
||
|
|
||
| fn dealloc_multiple_page(&self, mut page_list : Cursor<'_, Box<LinkedPage>>) -> Result<()> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be cleaner to move this code into dealloc_data_page_list so that we don't have to pass a Cursor type around.
fs/hayleyfs/balloc.rs
Outdated
|
|
||
|
|
||
| fn dealloc_multiple_page(&self, mut page_list : Cursor<'_, Box<LinkedPage>>) -> Result<()> { | ||
| // hash map to store free list lock for every cpu |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm a little confused by this comment -- it stores the free list for each CPU, not the lock, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're absolutely right. I made sure to change the comment.
fs/hayleyfs/balloc.rs
Outdated
| if let Some(page) = page { | ||
| pr_info!("Page: {}", page.get_page_no()); | ||
|
|
||
| let cpu : usize = ((page.get_page_no() - self.start) / self.pages_per_cpu).try_into()?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There might be a function defined elsewhere to do this calculation (if not, we should make one).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll go ahead and make a helper function to do this calculation.
fs/hayleyfs/balloc.rs
Outdated
|
|
||
| let cpu : usize = ((page.get_page_no() - self.start) / self.pages_per_cpu).try_into()?; | ||
|
|
||
| if matches!(cpu_free_list_map.get(&cpu), None) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since we are only matching against one pattern, and it's the Option type, it would be more idiomatic/cleaner to do if cpu_free_list_map.get(&cpu).is_none().
fs/hayleyfs/balloc.rs
Outdated
| } | ||
|
|
||
| // add cpu page to vector (vector is mutable) | ||
| let cpu_page_vec : &mut Vec<PageNum> = cpu_free_list_map.get_mut(&cpu).unwrap(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use ? rather than unwrap here -- even though this should always succeed, it's safer to avoid using unwrap wherever possible, since the kernel will panic and crash if we call unwrap on an error.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When I change it to ?, I get the following compiler error:
"cannot use the ? operator in a method that returns `core::result::Result<(), kernel::Err
Update: I think I made the unwrap safe by calling is_some() on the option returned by the get_mut() function
fs/hayleyfs/balloc.rs
Outdated
| } | ||
| }; | ||
|
|
||
| // check that the page was not already present in the tree. CAUSING ERRORS DUE TO TYPE ANNOTATION ISSUES |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What error is this running into?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was able to resolve that error but forgot to delete the comment. I'll make sure to delete it.
… in dealloc_data_page_list and deallocating in PerCpuPageAllocator
|
Applied changes. The deallocator has been tested with 1 CPU, but I am struggling to create a scenario where pages are allocated on multiple CPUs. |
|
Confirmed that pages removed from all cpus and removed prinfo statements. |
fs/hayleyfs/balloc.rs
Outdated
| if cpu_free_list_map.get(&cpu).is_none() { | ||
| cpu_free_list_map.try_insert(cpu, Vec::new())?; | ||
| } | ||
|
|
||
| // add cpu page to vector (vector is mutable) | ||
| let cpu_page_vec_option : Option<&mut Vec<PageNum>> = cpu_free_list_map.get_mut(&cpu); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be cleaner (and potentially more efficient) to take out the if statement that checks if cpu is in cpu_free_list_map; instead, you can obtain cpu_page_vec_option as you currently do, and then rather than returning an error if cpu_page_vec_option is None below, create a new vector with the page number and insert it into the map.
Also -- the convention here would be to just name the vector cpu_page_vec; it doesn't have to reflect the fact that it's an Option.
fs/hayleyfs/balloc.rs
Outdated
| if cpu_page_vec_option.is_some() { | ||
| let cpu_page_vec : &mut Vec<PageNum> = cpu_page_vec_option.unwrap(); // safe unwrap | ||
| cpu_page_vec.try_push(page.get_page_no())?; | ||
| } else { | ||
| pr_info!("CPU not added to RBTree\n"); | ||
| return Err(EINVAL); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another Rust idiom -- you can structure this code as follows:
if let Some(cpu_page_vec) = cpu_page_vec_option() {
cpu_page_vec.try_push(page.get_page_no())?;
} else {
. . .
}
It's a bit more concise. If you rename cpu_page_vec_option to cpu_page_vec, Rust will also let you write the if condition like this:
if let Some(cpu_page_vec) = cpu_page _vec
Within the if case, cpu_page_vec will refer to the vector itself; outside of that, the variable name will still refer to the Option<Vec<>>.
|
|
||
| fn dealloc_multiple_page(&self, cpu_free_list_map : RBTree<usize, Vec<PageNum>>) -> Result<()> { | ||
| // add pages to corresponding free list in ascending cpu # order | ||
| for (cpu, page_nos) in cpu_free_list_map.iter() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just to be sure -- have you confirmed/checked that this always iterates over the CPU keys in ascending order?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've confirmed through dmesg that this iterates over CPU keys in ascending order (I think the RB tree default is ascending), but I don't know how to formally test it, so I'll remove the comment.
| fn pageno2cpuid(&self, page_no : PageNum) -> Result<usize> { | ||
| let cpu: usize = ((page_no - self.start) / self.pages_per_cpu).try_into()?; | ||
| Ok(cpu) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there not already a function for this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I haven't found one that does this in balloc.rs. I noticed in dealloc_page the same formula is used to calculate the cpu.
reconfig.sh
Outdated
| #!/bin/bash | ||
| sudo umount /dev/pmem0 | ||
| sudo rmmod hayleyfs | ||
| make LLVM=-14 fs/hayleyfs/hayleyfs.ko | ||
| sudo insmod fs/hayleyfs/hayleyfs.ko | ||
| sudo mount -o init -t hayleyfs /dev/pmem0 /mnt/pmem | ||
| touch test.txt | ||
| echo "Hello World" > test.txt | ||
| sudo mv test.txt /mnt/pmem | ||
| rm test.txt | ||
| cd /mnt/pmem | ||
| pwd |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Have you written any tests beyond this? I think it would be good to add (in a tests/ directory at the top level) more cases, e.g. larger files, checking that everything works properly when there are multiple files in the system, etc.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added two tests - one testing a large file and one testing a series of small files.
hayley-leblanc
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The multi-page deallocate in SquirrelFS looks good! I've left a few suggestions/comments on the tests.
evaluation/tests/large_remove.c
Outdated
| long int enlarge_file(char *path, long int size) { | ||
| int fd = open(path, O_CREAT | O_RDWR); | ||
| assert (fd > -1); | ||
| lseek(fd, size, SEEK_SET); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this actually increase the file's size? According to lseek documentation (https://man7.org/linux/man-pages/man2/lseek.2.html),
lseek()allows the file offset to be set beyond the end of the file (but this does not change the size of the file).
Edit: I see, the increase comes from the fputc writing a character at offset size. I would still suggest switching to one of the following approaches.
A more standard way of increasing the file's size (and one that would be sure to exercise page allocation code paths) would be to use the write system call to write a specified amount of data to the file: https://man7.org/linux/man-pages/man2/write.2.html, or the truncate syscall to set its size to a specified value. It doesn't actually matter what is written, so you could allocate a buffer but not update/set any of its bytes and just write whatever is already in it to the file. You can also do this with a FILE* obtained from fopen/fdopen using a function like fprintf, but the write system call may be easier because I think fprintf requires you to provide bytes to write as a string.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great suggestion. I added a write call and it was much cleaner.
evaluation/tests/large_remove.c
Outdated
| fputc('\0', fp); | ||
| fclose(fp); | ||
| close(fd); | ||
| fd = open(path, O_CREAT | O_RDWR); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why close and reopen the file?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are write. I over complicated the code with lseek and changed to a write call.
evaluation/tests/large_remove.c
Outdated
| long int new_size = enlarge_file(path, multiple * PAGESZ); | ||
| used_all_pages = prev_size == new_size; | ||
| multiple = used_all_pages ? multiple : multiple + 1; | ||
| prev_size = new_size; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure I understand exactly what this loop is doing; why increase multiple in this way?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The loop was intended to extend the file to use the maximum amount of pages before we run out, but that was overcomplicated. I now borrow the code from large_write and allocate 200000 pages instead.
evaluation/tests/large_remove.c
Outdated
| prev_size = new_size; | ||
| } | ||
| // a new file with one less page should be able to be allocated after removal | ||
| assert(prev_size > 0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A stronger check would be to assert that the file you created is the expected size.
| for (int i = 0; i < num_files; i ++) { | ||
| sprintf(filename, "/mnt/pmem/%d", i); | ||
| int fd = open(filename, O_CREAT | O_RDWR); | ||
| lseek(fd, PAGESZ * 2, SEEK_SET); | ||
| FILE *fp = fdopen(fd, "w"); | ||
| assert(fp); | ||
| putc('\0', fp); | ||
| fclose(fp); | ||
| close(fd); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same comment as the other test w.r.t. increasing file size.
evaluation/tests/large_remove.c
Outdated
|
|
||
| assert(statvfs("/mnt/pmem", &stat) == 0); | ||
| unsigned long pages_end = stat.f_bfree; | ||
| assert(pages_start == pages_end); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another useful sanity check would be to ensure that between creating and deleting the big file, the number of pages in use has changed.
Changed data page deallocator to take a list of data pages and deallocate them without acquiring and releasing lock after each dealloc.