aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHans Petter Selasky <hselasky@FreeBSD.org>2017-07-07 13:44:14 +0000
committerHans Petter Selasky <hselasky@FreeBSD.org>2017-07-07 13:44:14 +0000
commit611572285ab90cfeb9a849e08712836bae139e6c (patch)
tree00bcd1ee6a6a7477ecbab9f578b3ac775fb23723
parentea165254130ea22f0530743bbe8e6083eaff4552 (diff)
downloadsrc-611572285ab90cfeb9a849e08712836bae139e6c.tar.gz
src-611572285ab90cfeb9a849e08712836bae139e6c.zip
Complete r320189 which allows a NULL VM fault handler in the LinuxKPI.
Instead of mapping a dummy page upon a page fault, map the page pointed to by the physical address given by IDX_TO_OFF(vmap->vm_pfn). To simplify the implementation use OBJT_DEVICE to implement our own linux_cdev_pager_fault() instead of using the existing linux_cdev_pager_populate(). Some minor code factoring while at it. Reviewed by: markj @ MFC after: 1 week Sponsored by: Mellanox Technologies
Notes
Notes: svn path=/head/; revision=320775
-rw-r--r--sys/compat/linuxkpi/common/src/linux_compat.c89
1 files changed, 77 insertions, 12 deletions
diff --git a/sys/compat/linuxkpi/common/src/linux_compat.c b/sys/compat/linuxkpi/common/src/linux_compat.c
index a802ef999472..e0b7cb931fd7 100644
--- a/sys/compat/linuxkpi/common/src/linux_compat.c
+++ b/sys/compat/linuxkpi/common/src/linux_compat.c
@@ -474,11 +474,57 @@ linux_file_free(struct linux_file *filp)
}
static int
+linux_cdev_pager_fault(vm_object_t vm_obj, vm_ooffset_t offset, int prot,
+ vm_page_t *mres)
+{
+ struct vm_area_struct *vmap;
+
+ vmap = linux_cdev_handle_find(vm_obj->handle);
+
+ MPASS(vmap != NULL);
+ MPASS(vmap->vm_private_data == vm_obj->handle);
+
+ if (likely(vmap->vm_ops != NULL && offset < vmap->vm_len)) {
+ vm_paddr_t paddr = IDX_TO_OFF(vmap->vm_pfn) + offset;
+ vm_page_t page;
+
+ if (((*mres)->flags & PG_FICTITIOUS) != 0) {
+ /*
+ * If the passed in result page is a fake
+ * page, update it with the new physical
+ * address.
+ */
+ page = *mres;
+ vm_page_updatefake(page, paddr, vm_obj->memattr);
+ } else {
+ /*
+ * Replace the passed in "mres" page with our
+ * own fake page and free up the all of the
+ * original pages.
+ */
+ VM_OBJECT_WUNLOCK(vm_obj);
+ page = vm_page_getfake(paddr, vm_obj->memattr);
+ VM_OBJECT_WLOCK(vm_obj);
+
+ vm_page_replace_checked(page, vm_obj,
+ (*mres)->pindex, *mres);
+
+ vm_page_lock(*mres);
+ vm_page_free(*mres);
+ vm_page_unlock(*mres);
+ *mres = page;
+ }
+ page->valid = VM_PAGE_BITS_ALL;
+ return (VM_PAGER_OK);
+ }
+ return (VM_PAGER_FAIL);
+}
+
+static int
linux_cdev_pager_populate(vm_object_t vm_obj, vm_pindex_t pidx, int fault_type,
vm_prot_t max_prot, vm_pindex_t *first, vm_pindex_t *last)
{
struct vm_area_struct *vmap;
- struct vm_fault vmf;
int err;
linux_set_current(curthread);
@@ -488,18 +534,20 @@ linux_cdev_pager_populate(vm_object_t vm_obj, vm_pindex_t pidx, int fault_type,
MPASS(vmap != NULL);
MPASS(vmap->vm_private_data == vm_obj->handle);
- /* fill out VM fault structure */
- vmf.virtual_address = (void *)((uintptr_t)pidx << PAGE_SHIFT);
- vmf.flags = (fault_type & VM_PROT_WRITE) ? FAULT_FLAG_WRITE : 0;
- vmf.pgoff = 0;
- vmf.page = NULL;
-
VM_OBJECT_WUNLOCK(vm_obj);
down_write(&vmap->vm_mm->mmap_sem);
- if (unlikely(vmap->vm_ops == NULL || vmap->vm_ops->fault == NULL)) {
+ if (unlikely(vmap->vm_ops == NULL)) {
err = VM_FAULT_SIGBUS;
} else {
+ struct vm_fault vmf;
+
+ /* fill out VM fault structure */
+ vmf.virtual_address = (void *)((uintptr_t)pidx << PAGE_SHIFT);
+ vmf.flags = (fault_type & VM_PROT_WRITE) ? FAULT_FLAG_WRITE : 0;
+ vmf.pgoff = 0;
+ vmf.page = NULL;
+
vmap->vm_pfn_count = 0;
vmap->vm_pfn_pcount = &vmap->vm_pfn_count;
vmap->vm_obj = vm_obj;
@@ -631,10 +679,19 @@ linux_cdev_pager_dtor(void *handle)
linux_cdev_handle_free(vmap);
}
-static struct cdev_pager_ops linux_cdev_pager_ops = {
+static struct cdev_pager_ops linux_cdev_pager_ops[2] = {
+ {
+ /* OBJT_MGTDEVICE */
.cdev_pg_populate = linux_cdev_pager_populate,
.cdev_pg_ctor = linux_cdev_pager_ctor,
.cdev_pg_dtor = linux_cdev_pager_dtor
+ },
+ {
+ /* OBJT_DEVICE */
+ .cdev_pg_fault = linux_cdev_pager_fault,
+ .cdev_pg_ctor = linux_cdev_pager_ctor,
+ .cdev_pg_dtor = linux_cdev_pager_dtor
+ },
};
static int
@@ -1184,8 +1241,15 @@ linux_dev_mmap_single(struct cdev *dev, vm_ooffset_t *offset,
vmap = linux_cdev_handle_insert(vm_private_data, vmap);
- *object = cdev_pager_allocate(vm_private_data, OBJT_MGTDEVICE,
- &linux_cdev_pager_ops, size, nprot, *offset, curthread->td_ucred);
+ if (vmap->vm_ops->fault == NULL) {
+ *object = cdev_pager_allocate(vm_private_data, OBJT_DEVICE,
+ &linux_cdev_pager_ops[1], size, nprot, *offset,
+ curthread->td_ucred);
+ } else {
+ *object = cdev_pager_allocate(vm_private_data, OBJT_MGTDEVICE,
+ &linux_cdev_pager_ops[0], size, nprot, *offset,
+ curthread->td_ucred);
+ }
if (*object == NULL) {
linux_cdev_handle_remove(vmap);
@@ -1196,7 +1260,8 @@ linux_dev_mmap_single(struct cdev *dev, vm_ooffset_t *offset,
struct sglist *sg;
sg = sglist_alloc(1, M_WAITOK);
- sglist_append_phys(sg, (vm_paddr_t)vmap->vm_pfn << PAGE_SHIFT, vmap->vm_len);
+ sglist_append_phys(sg,
+ (vm_paddr_t)vmap->vm_pfn << PAGE_SHIFT, vmap->vm_len);
*object = vm_pager_allocate(OBJT_SG, sg, vmap->vm_len,
nprot, 0, curthread->td_ucred);