|
@@ -2872,6 +2872,31 @@ int may_expand_vm(struct mm_struct *mm, unsigned long npages)
|
|
return 1;
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static int special_mapping_fault(struct vm_area_struct *vma,
|
|
|
|
+ struct vm_fault *vmf);
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * Having a close hook prevents vma merging regardless of flags.
|
|
|
|
+ */
|
|
|
|
+static void special_mapping_close(struct vm_area_struct *vma)
|
|
|
|
+{
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static const char *special_mapping_name(struct vm_area_struct *vma)
|
|
|
|
+{
|
|
|
|
+ return ((struct vm_special_mapping *)vma->vm_private_data)->name;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static const struct vm_operations_struct special_mapping_vmops = {
|
|
|
|
+ .close = special_mapping_close,
|
|
|
|
+ .fault = special_mapping_fault,
|
|
|
|
+ .name = special_mapping_name,
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static const struct vm_operations_struct legacy_special_mapping_vmops = {
|
|
|
|
+ .close = special_mapping_close,
|
|
|
|
+ .fault = special_mapping_fault,
|
|
|
|
+};
|
|
|
|
|
|
static int special_mapping_fault(struct vm_area_struct *vma,
|
|
static int special_mapping_fault(struct vm_area_struct *vma,
|
|
struct vm_fault *vmf)
|
|
struct vm_fault *vmf)
|
|
@@ -2887,7 +2912,13 @@ static int special_mapping_fault(struct vm_area_struct *vma,
|
|
*/
|
|
*/
|
|
pgoff = vmf->pgoff - vma->vm_pgoff;
|
|
pgoff = vmf->pgoff - vma->vm_pgoff;
|
|
|
|
|
|
- for (pages = vma->vm_private_data; pgoff && *pages; ++pages)
|
|
|
|
|
|
+ if (vma->vm_ops == &legacy_special_mapping_vmops)
|
|
|
|
+ pages = vma->vm_private_data;
|
|
|
|
+ else
|
|
|
|
+ pages = ((struct vm_special_mapping *)vma->vm_private_data)->
|
|
|
|
+ pages;
|
|
|
|
+
|
|
|
|
+ for (; pgoff && *pages; ++pages)
|
|
pgoff--;
|
|
pgoff--;
|
|
|
|
|
|
if (*pages) {
|
|
if (*pages) {
|
|
@@ -2900,30 +2931,11 @@ static int special_mapping_fault(struct vm_area_struct *vma,
|
|
return VM_FAULT_SIGBUS;
|
|
return VM_FAULT_SIGBUS;
|
|
}
|
|
}
|
|
|
|
|
|
-/*
|
|
|
|
- * Having a close hook prevents vma merging regardless of flags.
|
|
|
|
- */
|
|
|
|
-static void special_mapping_close(struct vm_area_struct *vma)
|
|
|
|
-{
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-static const struct vm_operations_struct special_mapping_vmops = {
|
|
|
|
- .close = special_mapping_close,
|
|
|
|
- .fault = special_mapping_fault,
|
|
|
|
-};
|
|
|
|
-
|
|
|
|
-/*
|
|
|
|
- * Called with mm->mmap_sem held for writing.
|
|
|
|
- * Insert a new vma covering the given region, with the given flags.
|
|
|
|
- * Its pages are supplied by the given array of struct page *.
|
|
|
|
- * The array can be shorter than len >> PAGE_SHIFT if it's null-terminated.
|
|
|
|
- * The region past the last page supplied will always produce SIGBUS.
|
|
|
|
- * The array pointer and the pages it points to are assumed to stay alive
|
|
|
|
- * for as long as this mapping might exist.
|
|
|
|
- */
|
|
|
|
-struct vm_area_struct *_install_special_mapping(struct mm_struct *mm,
|
|
|
|
- unsigned long addr, unsigned long len,
|
|
|
|
- unsigned long vm_flags, struct page **pages)
|
|
|
|
|
|
+static struct vm_area_struct *__install_special_mapping(
|
|
|
|
+ struct mm_struct *mm,
|
|
|
|
+ unsigned long addr, unsigned long len,
|
|
|
|
+ unsigned long vm_flags, const struct vm_operations_struct *ops,
|
|
|
|
+ void *priv)
|
|
{
|
|
{
|
|
int ret;
|
|
int ret;
|
|
struct vm_area_struct *vma;
|
|
struct vm_area_struct *vma;
|
|
@@ -2940,8 +2952,8 @@ struct vm_area_struct *_install_special_mapping(struct mm_struct *mm,
|
|
vma->vm_flags = vm_flags | mm->def_flags | VM_DONTEXPAND | VM_SOFTDIRTY;
|
|
vma->vm_flags = vm_flags | mm->def_flags | VM_DONTEXPAND | VM_SOFTDIRTY;
|
|
vma->vm_page_prot = vm_get_page_prot(vma->vm_flags);
|
|
vma->vm_page_prot = vm_get_page_prot(vma->vm_flags);
|
|
|
|
|
|
- vma->vm_ops = &special_mapping_vmops;
|
|
|
|
- vma->vm_private_data = pages;
|
|
|
|
|
|
+ vma->vm_ops = ops;
|
|
|
|
+ vma->vm_private_data = priv;
|
|
|
|
|
|
ret = insert_vm_struct(mm, vma);
|
|
ret = insert_vm_struct(mm, vma);
|
|
if (ret)
|
|
if (ret)
|
|
@@ -2958,12 +2970,31 @@ out:
|
|
return ERR_PTR(ret);
|
|
return ERR_PTR(ret);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/*
|
|
|
|
+ * Called with mm->mmap_sem held for writing.
|
|
|
|
+ * Insert a new vma covering the given region, with the given flags.
|
|
|
|
+ * Its pages are supplied by the given array of struct page *.
|
|
|
|
+ * The array can be shorter than len >> PAGE_SHIFT if it's null-terminated.
|
|
|
|
+ * The region past the last page supplied will always produce SIGBUS.
|
|
|
|
+ * The array pointer and the pages it points to are assumed to stay alive
|
|
|
|
+ * for as long as this mapping might exist.
|
|
|
|
+ */
|
|
|
|
+struct vm_area_struct *_install_special_mapping(
|
|
|
|
+ struct mm_struct *mm,
|
|
|
|
+ unsigned long addr, unsigned long len,
|
|
|
|
+ unsigned long vm_flags, const struct vm_special_mapping *spec)
|
|
|
|
+{
|
|
|
|
+ return __install_special_mapping(mm, addr, len, vm_flags,
|
|
|
|
+ &special_mapping_vmops, (void *)spec);
|
|
|
|
+}
|
|
|
|
+
|
|
int install_special_mapping(struct mm_struct *mm,
|
|
int install_special_mapping(struct mm_struct *mm,
|
|
unsigned long addr, unsigned long len,
|
|
unsigned long addr, unsigned long len,
|
|
unsigned long vm_flags, struct page **pages)
|
|
unsigned long vm_flags, struct page **pages)
|
|
{
|
|
{
|
|
- struct vm_area_struct *vma = _install_special_mapping(mm,
|
|
|
|
- addr, len, vm_flags, pages);
|
|
|
|
|
|
+ struct vm_area_struct *vma = __install_special_mapping(
|
|
|
|
+ mm, addr, len, vm_flags, &legacy_special_mapping_vmops,
|
|
|
|
+ (void *)pages);
|
|
|
|
|
|
if (IS_ERR(vma))
|
|
if (IS_ERR(vma))
|
|
return PTR_ERR(vma);
|
|
return PTR_ERR(vma);
|