In a file mapped area, I can use a function like vma_address
to get the virtual address of a page in the page cache.
static inline unsigned long
vma_address(struct page *page, struct vm_area_struct *vma)
{
pgoff_t pgoff;
unsigned long address;
VM_BUG_ON_PAGE(PageKsm(page), page); /* KSM page->index unusable */
pgoff = page_to_pgoff(page);
if (pgoff >= vma->vm_pgoff) {
address = vma->vm_start +
((pgoff - vma->vm_pgoff) << PAGE_SHIFT);
/* Check for address beyond vma (or wrapped through 0?) */
if (address < vma->vm_start || address >= vma->vm_end)
address = -EFAULT;
} else if (PageHead(page) &&
pgoff + compound_nr(page) - 1 >= vma->vm_pgoff) {
/* Test above avoids possibility of wrap to 0 on 32-bit */
address = vma->vm_start;
} else {
address = -EFAULT;
}
return address;
}
But I find out that when opening a file (open
instead of mmap
), there is no vma in address_space at all. Here is my code:
// code in driver, called by file_operations.write
// fd: the fd of a regular file opened by user program
void printFile(int fd) {
struct fdtable *fdt;
struct file *file;
struct address_space *mapping;
struct vm_area_struct *vma;
struct page *page;
int i;
fdt = current->files->fdt;
file = fdt->fd[fd];
if (file == NULL) {
DEBUG_PRINT(DEVICE_NAME ": &file is NULLn");
return;
}
mapping = file->f_mapping;
if (mapping == NULL) {
DEBUG_PRINT(DEVICE_NAME ": &address_space is NULLn");
return;
}
DEBUG_PRINT(DEVICE_NAME ": &file: %p, &address_space: %p, nrpages: %ldn",
file, mapping, mapping->nrpages);
// just print root vma in address_space
i_mmap_lock_read(mapping);
if (mapping->i_mmap.rb_root.rb_node != NULL) {
vma = container_of(mapping->i_mmap.rb_root.rb_node, struct vm_area_struct,
shared.rb);
printOneVMA(vma);
} else {
DEBUG_PRINT(DEVICE_NAME ": no vma in address_spacen");
}
i_mmap_unlock_read(mapping);
// print pages in address_space
if (mapping_empty(mapping)) {
DEBUG_PRINT(DEVICE_NAME ": i_pages in address_space is emptyn");
} else {
for (i = 0; i < mapping->nrpages; ++i) {
page = xa_load(&mapping->i_pages, i);
if (page != NULL) {
DEBUG_PRINT(DEVICE_NAME
": &page(%d): %p, &page.mapping: %p, page.index: %ldn",
i, page, page->mapping, page->index);
}
}
}
}
// user program
int main() {
int fd;
fd = open(DEVICE_PATH, O_RDWR);
if (fd < 0) {
perror("Failed to open the device");
exit(EXIT_FAILURE);
}
int test_fd = open("./text.txt", O_RDWR);
if (test_fd < 0) {
perror("Failed to open the file");
close(fd);
exit(EXIT_FAILURE);
}
write(test_fd, "Hello, world!", 13);
write(fd, NULL, test_fd); // will call printFile in kernel mode
getchar();
close(fd);
close(test_fd);
return 0;
}
Here is the output of dmesg:
printVMADriver: &file: 000000003f849862, &address_space: 0000000055fd35f1, nrpages: 1
printVMADriver: no vma in address_space
printVMADriver: &page(0): 00000000746bbc62, &page.mapping: 0000000055fd35f1, page.index: 0
I believe the page’s virtual address is in kernel space, but I don’t know how to verify it, because there is no vma. How can I get the virtual address of this page? Can anyone explain the difference between this kind of page and pages in a file mapped area?