summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Hellstrom <thomas-at-tungstengraphics-dot-com>2007-02-14 10:49:37 +0100
committerThomas Hellstrom <thomas-at-tungstengraphics-dot-com>2007-02-14 10:49:37 +0100
commit7bcb62b45d18ab7b48ad3cb5d13aec3bc577678e (patch)
treef6734c0e18f32a9f42957c371c03da2b1bcd3735
parent9efdae317ce01cea95f75855b175243ae858fde4 (diff)
Rework buffer object vm code to use nopfn() for kernels >= 2.6.19.
-rw-r--r--linux-core/drm_compat.c125
-rw-r--r--linux-core/drm_compat.h32
-rw-r--r--linux-core/drm_vm.c55
3 files changed, 145 insertions, 67 deletions
diff --git a/linux-core/drm_compat.c b/linux-core/drm_compat.c
index 8dd15ded..eeda4e4a 100644
--- a/linux-core/drm_compat.c
+++ b/linux-core/drm_compat.c
@@ -79,54 +79,14 @@ pgprot_t vm_get_page_prot(unsigned long vm_flags)
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15))
/*
- * vm code for kernels below 2,6,15 in which version a major vm write
+ * vm code for kernels below 2.6.15 in which version a major vm write
* occured. This implement a simple straightforward
* version similar to what's going to be
- * in kernel 2.6.20+?
+ * in kernel 2.6.19+
+ * Kernels below 2.6.15 use nopage whereas 2.6.19 and upwards use
+ * nopfn.
*/
-static int drm_pte_is_clear(struct vm_area_struct *vma,
- unsigned long addr)
-{
- struct mm_struct *mm = vma->vm_mm;
- int ret = 1;
- pte_t *pte;
- pmd_t *pmd;
- pud_t *pud;
- pgd_t *pgd;
-
-
- spin_lock(&mm->page_table_lock);
- pgd = pgd_offset(mm, addr);
- if (pgd_none(*pgd))
- goto unlock;
- pud = pud_offset(pgd, addr);
- if (pud_none(*pud))
- goto unlock;
- pmd = pmd_offset(pud, addr);
- if (pmd_none(*pmd))
- goto unlock;
- pte = pte_offset_map(pmd, addr);
- if (!pte)
- goto unlock;
- ret = pte_none(*pte);
- pte_unmap(pte);
- unlock:
- spin_unlock(&mm->page_table_lock);
- return ret;
-}
-
-int vm_insert_pfn(struct vm_area_struct *vma, unsigned long addr,
- unsigned long pfn, pgprot_t pgprot)
-{
- int ret;
- if (!drm_pte_is_clear(vma, addr))
- return -EBUSY;
-
- ret = io_remap_pfn_range(vma, addr, pfn, PAGE_SIZE, pgprot);
- return ret;
-}
-
static struct {
spinlock_t lock;
struct page *dummy_page;
@@ -186,10 +146,85 @@ struct page *drm_bo_vm_nopage(struct vm_area_struct *vma,
#endif
+#if !defined(DRM_FULL_MM_COMPAT) && \
+ ((LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15)) || \
+ (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19)))
+
+static int drm_pte_is_clear(struct vm_area_struct *vma,
+ unsigned long addr)
+{
+ struct mm_struct *mm = vma->vm_mm;
+ int ret = 1;
+ pte_t *pte;
+ pmd_t *pmd;
+ pud_t *pud;
+ pgd_t *pgd;
+
+ spin_lock(&mm->page_table_lock);
+ pgd = pgd_offset(mm, addr);
+ if (pgd_none(*pgd))
+ goto unlock;
+ pud = pud_offset(pgd, addr);
+ if (pud_none(*pud))
+ goto unlock;
+ pmd = pmd_offset(pud, addr);
+ if (pmd_none(*pmd))
+ goto unlock;
+ pte = pte_offset_map(pmd, addr);
+ if (!pte)
+ goto unlock;
+ ret = pte_none(*pte);
+ pte_unmap(pte);
+ unlock:
+ spin_unlock(&mm->page_table_lock);
+ return ret;
+}
+
+int vm_insert_pfn(struct vm_area_struct *vma, unsigned long addr,
+ unsigned long pfn)
+{
+ int ret;
+ if (!drm_pte_is_clear(vma, addr))
+ return -EBUSY;
+
+ ret = io_remap_pfn_range(vma, addr, pfn, PAGE_SIZE, vma->vm_page_prot);
+ return ret;
+}
+#endif
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19) && !defined(DRM_FULL_MM_COMPAT))
+
+/**
+ * While waiting for the fault() handler to appear in
+ * we accomplish approximately
+ * the same wrapping it with nopfn.
+ */
+
+unsigned long drm_bo_vm_nopfn(struct vm_area_struct * vma,
+ unsigned long address)
+{
+ struct fault_data data;
+ data.address = address;
+
+ (void) drm_bo_vm_fault(vma, &data);
+ if (data.type == VM_FAULT_OOM)
+ return NOPFN_OOM;
+ else if (data.type == VM_FAULT_SIGBUS)
+ return NOPFN_SIGBUS;
+
+ /*
+ * pfn already set.
+ */
+
+ return 0;
+}
+#endif
+
+
#ifdef DRM_ODD_MM_COMPAT
/*
- * VM compatibility code for 2.6.15-2.6.19(?). This code implements a complicated
+ * VM compatibility code for 2.6.15-2.6.18. This code implements a complicated
* workaround for a single BUG statement in do_no_page in these versions. The
* tricky thing is that we need to take the mmap_sem in exclusive mode for _all_
* vmas mapping the ttm, before dev->struct_mutex is taken. The way we do this is to
diff --git a/linux-core/drm_compat.h b/linux-core/drm_compat.h
index 313aab85..0dee3564 100644
--- a/linux-core/drm_compat.h
+++ b/linux-core/drm_compat.h
@@ -158,11 +158,14 @@ static __inline__ void *kcalloc(size_t nmemb, size_t size, int flags)
#include <linux/mm.h>
#include <asm/page.h>
-#if ((LINUX_VERSION_CODE < KERNEL_VERSION(2,6,21)) && \
- (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15)))
+#if ((LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)) && \
+ (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15)))
#define DRM_ODD_MM_COMPAT
#endif
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21))
+#define DRM_FULL_MM_COMPAT
+#endif
/*
@@ -200,18 +203,23 @@ extern int drm_map_page_into_agp(struct page *page);
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15))
extern struct page *get_nopage_retry(void);
extern void free_nopage_retry(void);
-struct fault_data;
-extern struct page *drm_bo_vm_fault(struct vm_area_struct *vma,
- struct fault_data *data);
#define NOPAGE_REFAULT get_nopage_retry()
#endif
+#if !defined(DRM_FULL_MM_COMPAT) && \
+ ((LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15)) || \
+ (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19)))
+
+struct fault_data;
+extern struct page *drm_bo_vm_fault(struct vm_area_struct *vma,
+ struct fault_data *data);
-#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,21))
+#endif
+#ifndef DRM_FULL_MM_COMPAT
/*
- * Hopefully, real NOPAGE_RETRY functionality will be in 2.6.19.
+ * Hopefully, real NOPAGE_RETRY functionality will be in 2.6.19.
* For now, just return a dummy page that we've allocated out of
* static space. The page will be put by do_nopage() since we've already
* filled out the pte.
@@ -228,13 +236,17 @@ struct fault_data {
extern int vm_insert_pfn(struct vm_area_struct *vma, unsigned long addr,
- unsigned long pfn, pgprot_t pgprot);
+ unsigned long pfn);
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19))
extern struct page *drm_bo_vm_nopage(struct vm_area_struct *vma,
unsigned long address,
int *type);
-
-#endif
+#else
+extern unsigned long drm_bo_vm_nopfn(struct vm_area_struct *vma,
+ unsigned long address);
+#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)) */
+#endif /* ndef DRM_FULL_MM_COMPAT */
#ifdef DRM_ODD_MM_COMPAT
diff --git a/linux-core/drm_vm.c b/linux-core/drm_vm.c
index 17778c26..4a340b57 100644
--- a/linux-core/drm_vm.c
+++ b/linux-core/drm_vm.c
@@ -720,11 +720,20 @@ EXPORT_SYMBOL(drm_mmap);
* \param vma Virtual memory area.
* \param data Fault data on failure or refault.
* \return Always NULL as we insert pfns directly.
+ *
+ * It's important that pfns are inserted while holding the bo->mutex lock.
+ * otherwise we might race with unmap_mapping_range() which is always
+ * called with the bo->mutex lock held.
+ *
+ * It's not pretty to modify the vma->vm_page_prot variable while not
+ * holding the mm semaphore in write mode. However, we have it i read mode,
+ * so we won't be racing with any other writers, and we only actually modify
+ * it when no ptes are present so it shouldn't be a big deal.
*/
-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21) || \
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19) || \
LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15))
-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21))
+#ifdef DRM_FULL_MM_COMPAT
static
#endif
struct page *drm_bo_vm_fault(struct vm_area_struct *vma,
@@ -738,7 +747,6 @@ struct page *drm_bo_vm_fault(struct vm_area_struct *vma,
drm_device_t *dev;
unsigned long pfn;
int err;
- pgprot_t pgprot;
unsigned long bus_base;
unsigned long bus_offset;
unsigned long bus_size;
@@ -759,14 +767,12 @@ struct page *drm_bo_vm_fault(struct vm_area_struct *vma,
* move it to a mappable.
*/
+#ifdef DRM_BO_FULL_COMPAT
if (!(bo->mem.flags & DRM_BO_FLAG_MAPPABLE)) {
- uint32_t mask_save = bo->mem.mask;
uint32_t new_mask = bo->mem.mask |
DRM_BO_FLAG_MAPPABLE |
DRM_BO_FLAG_FORCE_MAPPABLE;
-
err = drm_bo_move_buffer(bo, new_mask, 0, 0);
- bo->mem.mask = mask_save;
if (err) {
data->type = (err == -EAGAIN) ?
@@ -774,6 +780,24 @@ struct page *drm_bo_vm_fault(struct vm_area_struct *vma,
goto out_unlock;
}
}
+#else
+ if (!(bo->mem.flags & DRM_BO_FLAG_MAPPABLE)) {
+ unsigned long _end = jiffies + 3*DRM_HZ;
+ uint32_t new_mask = bo->mem.mask |
+ DRM_BO_FLAG_MAPPABLE |
+ DRM_BO_FLAG_FORCE_MAPPABLE;
+
+ do {
+ err = drm_bo_move_buffer(bo, new_mask, 0, 0);
+ } while((err == -EAGAIN) && !time_after_eq(jiffies, _end));
+
+ if (err) {
+ DRM_ERROR("Timeout moving buffer to mappable location.\n");
+ data->type = VM_FAULT_SIGBUS;
+ goto out_unlock;
+ }
+ }
+#endif
if (address > vma->vm_end) {
data->type = VM_FAULT_SIGBUS;
@@ -793,7 +817,7 @@ struct page *drm_bo_vm_fault(struct vm_area_struct *vma,
if (bus_size) {
pfn = ((bus_base + bus_offset) >> PAGE_SHIFT) + page_offset;
- pgprot = drm_io_prot(_DRM_AGP, vma);
+ vma->vm_page_prot = drm_io_prot(_DRM_AGP, vma);
} else {
ttm = bo->ttm;
@@ -804,10 +828,10 @@ struct page *drm_bo_vm_fault(struct vm_area_struct *vma,
goto out_unlock;
}
pfn = page_to_pfn(page);
- pgprot = vma->vm_page_prot;
+ vma->vm_page_prot = vm_get_page_prot(vma->vm_flags);
}
- err = vm_insert_pfn(vma, address, pfn, pgprot);
+ err = vm_insert_pfn(vma, address, pfn);
if (!err || err == -EBUSY)
data->type = VM_FAULT_MINOR;
@@ -870,10 +894,14 @@ static void drm_bo_vm_close(struct vm_area_struct *vma)
}
static struct vm_operations_struct drm_bo_vm_ops = {
-#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,21))
- .nopage = drm_bo_vm_nopage,
-#else
+#ifdef DRM_FULL_MM_COMPAT
.fault = drm_bo_vm_fault,
+#else
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19))
+ .nopfn = drm_bo_vm_nopfn,
+#else
+ .nopage = drm_bo_vm_nopage,
+#endif
#endif
.open = drm_bo_vm_open,
.close = drm_bo_vm_close,
@@ -896,6 +924,9 @@ int drm_bo_mmap_locked(struct vm_area_struct *vma,
vma->vm_private_data = map->handle;
vma->vm_file = filp;
vma->vm_flags |= VM_RESERVED | VM_IO;
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19))
+ vma->vm_flags |= VM_PFNMAP;
+#endif
drm_bo_vm_open_locked(vma);
#ifdef DRM_ODD_MM_COMPAT
drm_bo_map_bound(vma);