diff options
Diffstat (limited to 'drivers/dax/dax.c')
| -rw-r--r-- | drivers/dax/dax.c | 46 | 
1 files changed, 37 insertions, 9 deletions
diff --git a/drivers/dax/dax.c b/drivers/dax/dax.c index 8d9829ff2a78..806f180c80d8 100644 --- a/drivers/dax/dax.c +++ b/drivers/dax/dax.c @@ -25,6 +25,7 @@  #include "dax.h"  static dev_t dax_devt; +DEFINE_STATIC_SRCU(dax_srcu);  static struct class *dax_class;  static DEFINE_IDA(dax_minor_ida);  static int nr_dax = CONFIG_NR_DEV_DAX; @@ -60,7 +61,7 @@ struct dax_region {   * @region - parent region   * @dev - device backing the character device   * @cdev - core chardev data - * @alive - !alive + rcu grace period == no new mappings can be established + * @alive - !alive + srcu grace period == no new mappings can be established   * @id - child id in the region   * @num_resources - number of physical address extents in this device   * @res - array of physical address ranges @@ -427,6 +428,7 @@ static int __dax_dev_pte_fault(struct dax_dev *dax_dev, struct vm_fault *vmf)  	int rc = VM_FAULT_SIGBUS;  	phys_addr_t phys;  	pfn_t pfn; +	unsigned int fault_size = PAGE_SIZE;  	if (check_vma(dax_dev, vmf->vma, __func__))  		return VM_FAULT_SIGBUS; @@ -437,9 +439,12 @@ static int __dax_dev_pte_fault(struct dax_dev *dax_dev, struct vm_fault *vmf)  		return VM_FAULT_SIGBUS;  	} +	if (fault_size != dax_region->align) +		return VM_FAULT_SIGBUS; +  	phys = pgoff_to_phys(dax_dev, vmf->pgoff, PAGE_SIZE);  	if (phys == -1) { -		dev_dbg(dev, "%s: phys_to_pgoff(%#lx) failed\n", __func__, +		dev_dbg(dev, "%s: pgoff_to_phys(%#lx) failed\n", __func__,  				vmf->pgoff);  		return VM_FAULT_SIGBUS;  	} @@ -464,6 +469,7 @@ static int __dax_dev_pmd_fault(struct dax_dev *dax_dev, struct vm_fault *vmf)  	phys_addr_t phys;  	pgoff_t pgoff;  	pfn_t pfn; +	unsigned int fault_size = PMD_SIZE;  	if (check_vma(dax_dev, vmf->vma, __func__))  		return VM_FAULT_SIGBUS; @@ -480,10 +486,20 @@ static int __dax_dev_pmd_fault(struct dax_dev *dax_dev, struct vm_fault *vmf)  		return VM_FAULT_SIGBUS;  	} +	if (fault_size < dax_region->align) +		return VM_FAULT_SIGBUS; +	else if (fault_size > dax_region->align) +		return VM_FAULT_FALLBACK; + +	/* if we are outside of the VMA */ +	if (pmd_addr < vmf->vma->vm_start || +			(pmd_addr + PMD_SIZE) > vmf->vma->vm_end) +		return VM_FAULT_SIGBUS; +  	pgoff = linear_page_index(vmf->vma, pmd_addr);  	phys = pgoff_to_phys(dax_dev, pgoff, PMD_SIZE);  	if (phys == -1) { -		dev_dbg(dev, "%s: phys_to_pgoff(%#lx) failed\n", __func__, +		dev_dbg(dev, "%s: pgoff_to_phys(%#lx) failed\n", __func__,  				pgoff);  		return VM_FAULT_SIGBUS;  	} @@ -503,6 +519,8 @@ static int __dax_dev_pud_fault(struct dax_dev *dax_dev, struct vm_fault *vmf)  	phys_addr_t phys;  	pgoff_t pgoff;  	pfn_t pfn; +	unsigned int fault_size = PUD_SIZE; +  	if (check_vma(dax_dev, vmf->vma, __func__))  		return VM_FAULT_SIGBUS; @@ -519,10 +537,20 @@ static int __dax_dev_pud_fault(struct dax_dev *dax_dev, struct vm_fault *vmf)  		return VM_FAULT_SIGBUS;  	} +	if (fault_size < dax_region->align) +		return VM_FAULT_SIGBUS; +	else if (fault_size > dax_region->align) +		return VM_FAULT_FALLBACK; + +	/* if we are outside of the VMA */ +	if (pud_addr < vmf->vma->vm_start || +			(pud_addr + PUD_SIZE) > vmf->vma->vm_end) +		return VM_FAULT_SIGBUS; +  	pgoff = linear_page_index(vmf->vma, pud_addr);  	phys = pgoff_to_phys(dax_dev, pgoff, PUD_SIZE);  	if (phys == -1) { -		dev_dbg(dev, "%s: phys_to_pgoff(%#lx) failed\n", __func__, +		dev_dbg(dev, "%s: pgoff_to_phys(%#lx) failed\n", __func__,  				pgoff);  		return VM_FAULT_SIGBUS;  	} @@ -542,7 +570,7 @@ static int __dax_dev_pud_fault(struct dax_dev *dax_dev, struct vm_fault *vmf)  static int dax_dev_huge_fault(struct vm_fault *vmf,  		enum page_entry_size pe_size)  { -	int rc; +	int rc, id;  	struct file *filp = vmf->vma->vm_file;  	struct dax_dev *dax_dev = filp->private_data; @@ -551,7 +579,7 @@ static int dax_dev_huge_fault(struct vm_fault *vmf,  			? "write" : "read",  			vmf->vma->vm_start, vmf->vma->vm_end); -	rcu_read_lock(); +	id = srcu_read_lock(&dax_srcu);  	switch (pe_size) {  	case PE_SIZE_PTE:  		rc = __dax_dev_pte_fault(dax_dev, vmf); @@ -565,7 +593,7 @@ static int dax_dev_huge_fault(struct vm_fault *vmf,  	default:  		return VM_FAULT_FALLBACK;  	} -	rcu_read_unlock(); +	srcu_read_unlock(&dax_srcu, id);  	return rc;  } @@ -686,11 +714,11 @@ static void unregister_dax_dev(void *dev)  	 * Note, rcu is not protecting the liveness of dax_dev, rcu is  	 * ensuring that any fault handlers that might have seen  	 * dax_dev->alive == true, have completed.  Any fault handlers -	 * that start after synchronize_rcu() has started will abort +	 * that start after synchronize_srcu() has started will abort  	 * upon seeing dax_dev->alive == false.  	 */  	dax_dev->alive = false; -	synchronize_rcu(); +	synchronize_srcu(&dax_srcu);  	unmap_mapping_range(dax_dev->inode->i_mapping, 0, 0, 1);  	cdev_del(cdev);  	device_unregister(dev);  | 
