diff options
| -rw-r--r-- | linux-core/drm_compat.c | 125 | ||||
| -rw-r--r-- | linux-core/drm_compat.h | 32 | ||||
| -rw-r--r-- | linux-core/drm_vm.c | 55 | 
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); | 
