162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* Copyright(c) 2016-2018 Intel Corporation. All rights reserved. */ 362306a36Sopenharmony_ci#include <linux/memremap.h> 462306a36Sopenharmony_ci#include <linux/pagemap.h> 562306a36Sopenharmony_ci#include <linux/module.h> 662306a36Sopenharmony_ci#include <linux/device.h> 762306a36Sopenharmony_ci#include <linux/pfn_t.h> 862306a36Sopenharmony_ci#include <linux/cdev.h> 962306a36Sopenharmony_ci#include <linux/slab.h> 1062306a36Sopenharmony_ci#include <linux/dax.h> 1162306a36Sopenharmony_ci#include <linux/fs.h> 1262306a36Sopenharmony_ci#include <linux/mm.h> 1362306a36Sopenharmony_ci#include <linux/mman.h> 1462306a36Sopenharmony_ci#include "dax-private.h" 1562306a36Sopenharmony_ci#include "bus.h" 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_cistatic int check_vma(struct dev_dax *dev_dax, struct vm_area_struct *vma, 1862306a36Sopenharmony_ci const char *func) 1962306a36Sopenharmony_ci{ 2062306a36Sopenharmony_ci struct device *dev = &dev_dax->dev; 2162306a36Sopenharmony_ci unsigned long mask; 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci if (!dax_alive(dev_dax->dax_dev)) 2462306a36Sopenharmony_ci return -ENXIO; 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci /* prevent private mappings from being established */ 2762306a36Sopenharmony_ci if ((vma->vm_flags & VM_MAYSHARE) != VM_MAYSHARE) { 2862306a36Sopenharmony_ci dev_info_ratelimited(dev, 2962306a36Sopenharmony_ci "%s: %s: fail, attempted private mapping\n", 3062306a36Sopenharmony_ci current->comm, func); 3162306a36Sopenharmony_ci return -EINVAL; 3262306a36Sopenharmony_ci } 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci mask = dev_dax->align - 1; 3562306a36Sopenharmony_ci if (vma->vm_start & mask || vma->vm_end & mask) { 3662306a36Sopenharmony_ci dev_info_ratelimited(dev, 3762306a36Sopenharmony_ci "%s: %s: fail, unaligned vma (%#lx - %#lx, %#lx)\n", 3862306a36Sopenharmony_ci current->comm, func, vma->vm_start, vma->vm_end, 3962306a36Sopenharmony_ci mask); 4062306a36Sopenharmony_ci return -EINVAL; 4162306a36Sopenharmony_ci } 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci if (!vma_is_dax(vma)) { 4462306a36Sopenharmony_ci dev_info_ratelimited(dev, 4562306a36Sopenharmony_ci "%s: %s: fail, vma is not DAX capable\n", 4662306a36Sopenharmony_ci current->comm, func); 4762306a36Sopenharmony_ci return -EINVAL; 4862306a36Sopenharmony_ci } 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci return 0; 5162306a36Sopenharmony_ci} 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci/* see "strong" declaration in tools/testing/nvdimm/dax-dev.c */ 5462306a36Sopenharmony_ci__weak phys_addr_t dax_pgoff_to_phys(struct dev_dax *dev_dax, pgoff_t pgoff, 5562306a36Sopenharmony_ci unsigned long size) 5662306a36Sopenharmony_ci{ 5762306a36Sopenharmony_ci int i; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci for (i = 0; i < dev_dax->nr_range; i++) { 6062306a36Sopenharmony_ci struct dev_dax_range *dax_range = &dev_dax->ranges[i]; 6162306a36Sopenharmony_ci struct range *range = &dax_range->range; 6262306a36Sopenharmony_ci unsigned long long pgoff_end; 6362306a36Sopenharmony_ci phys_addr_t phys; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci pgoff_end = dax_range->pgoff + PHYS_PFN(range_len(range)) - 1; 6662306a36Sopenharmony_ci if (pgoff < dax_range->pgoff || pgoff > pgoff_end) 6762306a36Sopenharmony_ci continue; 6862306a36Sopenharmony_ci phys = PFN_PHYS(pgoff - dax_range->pgoff) + range->start; 6962306a36Sopenharmony_ci if (phys + size - 1 <= range->end) 7062306a36Sopenharmony_ci return phys; 7162306a36Sopenharmony_ci break; 7262306a36Sopenharmony_ci } 7362306a36Sopenharmony_ci return -1; 7462306a36Sopenharmony_ci} 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistatic void dax_set_mapping(struct vm_fault *vmf, pfn_t pfn, 7762306a36Sopenharmony_ci unsigned long fault_size) 7862306a36Sopenharmony_ci{ 7962306a36Sopenharmony_ci unsigned long i, nr_pages = fault_size / PAGE_SIZE; 8062306a36Sopenharmony_ci struct file *filp = vmf->vma->vm_file; 8162306a36Sopenharmony_ci struct dev_dax *dev_dax = filp->private_data; 8262306a36Sopenharmony_ci pgoff_t pgoff; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci /* mapping is only set on the head */ 8562306a36Sopenharmony_ci if (dev_dax->pgmap->vmemmap_shift) 8662306a36Sopenharmony_ci nr_pages = 1; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci pgoff = linear_page_index(vmf->vma, 8962306a36Sopenharmony_ci ALIGN(vmf->address, fault_size)); 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci for (i = 0; i < nr_pages; i++) { 9262306a36Sopenharmony_ci struct page *page = pfn_to_page(pfn_t_to_pfn(pfn) + i); 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci page = compound_head(page); 9562306a36Sopenharmony_ci if (page->mapping) 9662306a36Sopenharmony_ci continue; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci page->mapping = filp->f_mapping; 9962306a36Sopenharmony_ci page->index = pgoff + i; 10062306a36Sopenharmony_ci } 10162306a36Sopenharmony_ci} 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_cistatic vm_fault_t __dev_dax_pte_fault(struct dev_dax *dev_dax, 10462306a36Sopenharmony_ci struct vm_fault *vmf) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci struct device *dev = &dev_dax->dev; 10762306a36Sopenharmony_ci phys_addr_t phys; 10862306a36Sopenharmony_ci pfn_t pfn; 10962306a36Sopenharmony_ci unsigned int fault_size = PAGE_SIZE; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci if (check_vma(dev_dax, vmf->vma, __func__)) 11262306a36Sopenharmony_ci return VM_FAULT_SIGBUS; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci if (dev_dax->align > PAGE_SIZE) { 11562306a36Sopenharmony_ci dev_dbg(dev, "alignment (%#x) > fault size (%#x)\n", 11662306a36Sopenharmony_ci dev_dax->align, fault_size); 11762306a36Sopenharmony_ci return VM_FAULT_SIGBUS; 11862306a36Sopenharmony_ci } 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci if (fault_size != dev_dax->align) 12162306a36Sopenharmony_ci return VM_FAULT_SIGBUS; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci phys = dax_pgoff_to_phys(dev_dax, vmf->pgoff, PAGE_SIZE); 12462306a36Sopenharmony_ci if (phys == -1) { 12562306a36Sopenharmony_ci dev_dbg(dev, "pgoff_to_phys(%#lx) failed\n", vmf->pgoff); 12662306a36Sopenharmony_ci return VM_FAULT_SIGBUS; 12762306a36Sopenharmony_ci } 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci pfn = phys_to_pfn_t(phys, PFN_DEV|PFN_MAP); 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci dax_set_mapping(vmf, pfn, fault_size); 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci return vmf_insert_mixed(vmf->vma, vmf->address, pfn); 13462306a36Sopenharmony_ci} 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_cistatic vm_fault_t __dev_dax_pmd_fault(struct dev_dax *dev_dax, 13762306a36Sopenharmony_ci struct vm_fault *vmf) 13862306a36Sopenharmony_ci{ 13962306a36Sopenharmony_ci unsigned long pmd_addr = vmf->address & PMD_MASK; 14062306a36Sopenharmony_ci struct device *dev = &dev_dax->dev; 14162306a36Sopenharmony_ci phys_addr_t phys; 14262306a36Sopenharmony_ci pgoff_t pgoff; 14362306a36Sopenharmony_ci pfn_t pfn; 14462306a36Sopenharmony_ci unsigned int fault_size = PMD_SIZE; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci if (check_vma(dev_dax, vmf->vma, __func__)) 14762306a36Sopenharmony_ci return VM_FAULT_SIGBUS; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci if (dev_dax->align > PMD_SIZE) { 15062306a36Sopenharmony_ci dev_dbg(dev, "alignment (%#x) > fault size (%#x)\n", 15162306a36Sopenharmony_ci dev_dax->align, fault_size); 15262306a36Sopenharmony_ci return VM_FAULT_SIGBUS; 15362306a36Sopenharmony_ci } 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci if (fault_size < dev_dax->align) 15662306a36Sopenharmony_ci return VM_FAULT_SIGBUS; 15762306a36Sopenharmony_ci else if (fault_size > dev_dax->align) 15862306a36Sopenharmony_ci return VM_FAULT_FALLBACK; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci /* if we are outside of the VMA */ 16162306a36Sopenharmony_ci if (pmd_addr < vmf->vma->vm_start || 16262306a36Sopenharmony_ci (pmd_addr + PMD_SIZE) > vmf->vma->vm_end) 16362306a36Sopenharmony_ci return VM_FAULT_SIGBUS; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci pgoff = linear_page_index(vmf->vma, pmd_addr); 16662306a36Sopenharmony_ci phys = dax_pgoff_to_phys(dev_dax, pgoff, PMD_SIZE); 16762306a36Sopenharmony_ci if (phys == -1) { 16862306a36Sopenharmony_ci dev_dbg(dev, "pgoff_to_phys(%#lx) failed\n", pgoff); 16962306a36Sopenharmony_ci return VM_FAULT_SIGBUS; 17062306a36Sopenharmony_ci } 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci pfn = phys_to_pfn_t(phys, PFN_DEV|PFN_MAP); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci dax_set_mapping(vmf, pfn, fault_size); 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci return vmf_insert_pfn_pmd(vmf, pfn, vmf->flags & FAULT_FLAG_WRITE); 17762306a36Sopenharmony_ci} 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci#ifdef CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD 18062306a36Sopenharmony_cistatic vm_fault_t __dev_dax_pud_fault(struct dev_dax *dev_dax, 18162306a36Sopenharmony_ci struct vm_fault *vmf) 18262306a36Sopenharmony_ci{ 18362306a36Sopenharmony_ci unsigned long pud_addr = vmf->address & PUD_MASK; 18462306a36Sopenharmony_ci struct device *dev = &dev_dax->dev; 18562306a36Sopenharmony_ci phys_addr_t phys; 18662306a36Sopenharmony_ci pgoff_t pgoff; 18762306a36Sopenharmony_ci pfn_t pfn; 18862306a36Sopenharmony_ci unsigned int fault_size = PUD_SIZE; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci if (check_vma(dev_dax, vmf->vma, __func__)) 19262306a36Sopenharmony_ci return VM_FAULT_SIGBUS; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci if (dev_dax->align > PUD_SIZE) { 19562306a36Sopenharmony_ci dev_dbg(dev, "alignment (%#x) > fault size (%#x)\n", 19662306a36Sopenharmony_ci dev_dax->align, fault_size); 19762306a36Sopenharmony_ci return VM_FAULT_SIGBUS; 19862306a36Sopenharmony_ci } 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci if (fault_size < dev_dax->align) 20162306a36Sopenharmony_ci return VM_FAULT_SIGBUS; 20262306a36Sopenharmony_ci else if (fault_size > dev_dax->align) 20362306a36Sopenharmony_ci return VM_FAULT_FALLBACK; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci /* if we are outside of the VMA */ 20662306a36Sopenharmony_ci if (pud_addr < vmf->vma->vm_start || 20762306a36Sopenharmony_ci (pud_addr + PUD_SIZE) > vmf->vma->vm_end) 20862306a36Sopenharmony_ci return VM_FAULT_SIGBUS; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci pgoff = linear_page_index(vmf->vma, pud_addr); 21162306a36Sopenharmony_ci phys = dax_pgoff_to_phys(dev_dax, pgoff, PUD_SIZE); 21262306a36Sopenharmony_ci if (phys == -1) { 21362306a36Sopenharmony_ci dev_dbg(dev, "pgoff_to_phys(%#lx) failed\n", pgoff); 21462306a36Sopenharmony_ci return VM_FAULT_SIGBUS; 21562306a36Sopenharmony_ci } 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci pfn = phys_to_pfn_t(phys, PFN_DEV|PFN_MAP); 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci dax_set_mapping(vmf, pfn, fault_size); 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci return vmf_insert_pfn_pud(vmf, pfn, vmf->flags & FAULT_FLAG_WRITE); 22262306a36Sopenharmony_ci} 22362306a36Sopenharmony_ci#else 22462306a36Sopenharmony_cistatic vm_fault_t __dev_dax_pud_fault(struct dev_dax *dev_dax, 22562306a36Sopenharmony_ci struct vm_fault *vmf) 22662306a36Sopenharmony_ci{ 22762306a36Sopenharmony_ci return VM_FAULT_FALLBACK; 22862306a36Sopenharmony_ci} 22962306a36Sopenharmony_ci#endif /* !CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD */ 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_cistatic vm_fault_t dev_dax_huge_fault(struct vm_fault *vmf, unsigned int order) 23262306a36Sopenharmony_ci{ 23362306a36Sopenharmony_ci struct file *filp = vmf->vma->vm_file; 23462306a36Sopenharmony_ci vm_fault_t rc = VM_FAULT_SIGBUS; 23562306a36Sopenharmony_ci int id; 23662306a36Sopenharmony_ci struct dev_dax *dev_dax = filp->private_data; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci dev_dbg(&dev_dax->dev, "%s: %s (%#lx - %#lx) order:%d\n", current->comm, 23962306a36Sopenharmony_ci (vmf->flags & FAULT_FLAG_WRITE) ? "write" : "read", 24062306a36Sopenharmony_ci vmf->vma->vm_start, vmf->vma->vm_end, order); 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci id = dax_read_lock(); 24362306a36Sopenharmony_ci if (order == 0) 24462306a36Sopenharmony_ci rc = __dev_dax_pte_fault(dev_dax, vmf); 24562306a36Sopenharmony_ci else if (order == PMD_ORDER) 24662306a36Sopenharmony_ci rc = __dev_dax_pmd_fault(dev_dax, vmf); 24762306a36Sopenharmony_ci else if (order == PUD_ORDER) 24862306a36Sopenharmony_ci rc = __dev_dax_pud_fault(dev_dax, vmf); 24962306a36Sopenharmony_ci else 25062306a36Sopenharmony_ci rc = VM_FAULT_SIGBUS; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci dax_read_unlock(id); 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci return rc; 25562306a36Sopenharmony_ci} 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_cistatic vm_fault_t dev_dax_fault(struct vm_fault *vmf) 25862306a36Sopenharmony_ci{ 25962306a36Sopenharmony_ci return dev_dax_huge_fault(vmf, 0); 26062306a36Sopenharmony_ci} 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_cistatic int dev_dax_may_split(struct vm_area_struct *vma, unsigned long addr) 26362306a36Sopenharmony_ci{ 26462306a36Sopenharmony_ci struct file *filp = vma->vm_file; 26562306a36Sopenharmony_ci struct dev_dax *dev_dax = filp->private_data; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci if (!IS_ALIGNED(addr, dev_dax->align)) 26862306a36Sopenharmony_ci return -EINVAL; 26962306a36Sopenharmony_ci return 0; 27062306a36Sopenharmony_ci} 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_cistatic unsigned long dev_dax_pagesize(struct vm_area_struct *vma) 27362306a36Sopenharmony_ci{ 27462306a36Sopenharmony_ci struct file *filp = vma->vm_file; 27562306a36Sopenharmony_ci struct dev_dax *dev_dax = filp->private_data; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci return dev_dax->align; 27862306a36Sopenharmony_ci} 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_cistatic const struct vm_operations_struct dax_vm_ops = { 28162306a36Sopenharmony_ci .fault = dev_dax_fault, 28262306a36Sopenharmony_ci .huge_fault = dev_dax_huge_fault, 28362306a36Sopenharmony_ci .may_split = dev_dax_may_split, 28462306a36Sopenharmony_ci .pagesize = dev_dax_pagesize, 28562306a36Sopenharmony_ci}; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_cistatic int dax_mmap(struct file *filp, struct vm_area_struct *vma) 28862306a36Sopenharmony_ci{ 28962306a36Sopenharmony_ci struct dev_dax *dev_dax = filp->private_data; 29062306a36Sopenharmony_ci int rc, id; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci dev_dbg(&dev_dax->dev, "trace\n"); 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci /* 29562306a36Sopenharmony_ci * We lock to check dax_dev liveness and will re-check at 29662306a36Sopenharmony_ci * fault time. 29762306a36Sopenharmony_ci */ 29862306a36Sopenharmony_ci id = dax_read_lock(); 29962306a36Sopenharmony_ci rc = check_vma(dev_dax, vma, __func__); 30062306a36Sopenharmony_ci dax_read_unlock(id); 30162306a36Sopenharmony_ci if (rc) 30262306a36Sopenharmony_ci return rc; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci vma->vm_ops = &dax_vm_ops; 30562306a36Sopenharmony_ci vm_flags_set(vma, VM_HUGEPAGE); 30662306a36Sopenharmony_ci return 0; 30762306a36Sopenharmony_ci} 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci/* return an unmapped area aligned to the dax region specified alignment */ 31062306a36Sopenharmony_cistatic unsigned long dax_get_unmapped_area(struct file *filp, 31162306a36Sopenharmony_ci unsigned long addr, unsigned long len, unsigned long pgoff, 31262306a36Sopenharmony_ci unsigned long flags) 31362306a36Sopenharmony_ci{ 31462306a36Sopenharmony_ci unsigned long off, off_end, off_align, len_align, addr_align, align; 31562306a36Sopenharmony_ci struct dev_dax *dev_dax = filp ? filp->private_data : NULL; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci if (!dev_dax || addr) 31862306a36Sopenharmony_ci goto out; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci align = dev_dax->align; 32162306a36Sopenharmony_ci off = pgoff << PAGE_SHIFT; 32262306a36Sopenharmony_ci off_end = off + len; 32362306a36Sopenharmony_ci off_align = round_up(off, align); 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci if ((off_end <= off_align) || ((off_end - off_align) < align)) 32662306a36Sopenharmony_ci goto out; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci len_align = len + align; 32962306a36Sopenharmony_ci if ((off + len_align) < off) 33062306a36Sopenharmony_ci goto out; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci addr_align = current->mm->get_unmapped_area(filp, addr, len_align, 33362306a36Sopenharmony_ci pgoff, flags); 33462306a36Sopenharmony_ci if (!IS_ERR_VALUE(addr_align)) { 33562306a36Sopenharmony_ci addr_align += (off - addr_align) & (align - 1); 33662306a36Sopenharmony_ci return addr_align; 33762306a36Sopenharmony_ci } 33862306a36Sopenharmony_ci out: 33962306a36Sopenharmony_ci return current->mm->get_unmapped_area(filp, addr, len, pgoff, flags); 34062306a36Sopenharmony_ci} 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_cistatic const struct address_space_operations dev_dax_aops = { 34362306a36Sopenharmony_ci .dirty_folio = noop_dirty_folio, 34462306a36Sopenharmony_ci}; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_cistatic int dax_open(struct inode *inode, struct file *filp) 34762306a36Sopenharmony_ci{ 34862306a36Sopenharmony_ci struct dax_device *dax_dev = inode_dax(inode); 34962306a36Sopenharmony_ci struct inode *__dax_inode = dax_inode(dax_dev); 35062306a36Sopenharmony_ci struct dev_dax *dev_dax = dax_get_private(dax_dev); 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci dev_dbg(&dev_dax->dev, "trace\n"); 35362306a36Sopenharmony_ci inode->i_mapping = __dax_inode->i_mapping; 35462306a36Sopenharmony_ci inode->i_mapping->host = __dax_inode; 35562306a36Sopenharmony_ci inode->i_mapping->a_ops = &dev_dax_aops; 35662306a36Sopenharmony_ci filp->f_mapping = inode->i_mapping; 35762306a36Sopenharmony_ci filp->f_wb_err = filemap_sample_wb_err(filp->f_mapping); 35862306a36Sopenharmony_ci filp->f_sb_err = file_sample_sb_err(filp); 35962306a36Sopenharmony_ci filp->private_data = dev_dax; 36062306a36Sopenharmony_ci inode->i_flags = S_DAX; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci return 0; 36362306a36Sopenharmony_ci} 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_cistatic int dax_release(struct inode *inode, struct file *filp) 36662306a36Sopenharmony_ci{ 36762306a36Sopenharmony_ci struct dev_dax *dev_dax = filp->private_data; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci dev_dbg(&dev_dax->dev, "trace\n"); 37062306a36Sopenharmony_ci return 0; 37162306a36Sopenharmony_ci} 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_cistatic const struct file_operations dax_fops = { 37462306a36Sopenharmony_ci .llseek = noop_llseek, 37562306a36Sopenharmony_ci .owner = THIS_MODULE, 37662306a36Sopenharmony_ci .open = dax_open, 37762306a36Sopenharmony_ci .release = dax_release, 37862306a36Sopenharmony_ci .get_unmapped_area = dax_get_unmapped_area, 37962306a36Sopenharmony_ci .mmap = dax_mmap, 38062306a36Sopenharmony_ci .mmap_supported_flags = MAP_SYNC, 38162306a36Sopenharmony_ci}; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_cistatic void dev_dax_cdev_del(void *cdev) 38462306a36Sopenharmony_ci{ 38562306a36Sopenharmony_ci cdev_del(cdev); 38662306a36Sopenharmony_ci} 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_cistatic void dev_dax_kill(void *dev_dax) 38962306a36Sopenharmony_ci{ 39062306a36Sopenharmony_ci kill_dev_dax(dev_dax); 39162306a36Sopenharmony_ci} 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_cistatic int dev_dax_probe(struct dev_dax *dev_dax) 39462306a36Sopenharmony_ci{ 39562306a36Sopenharmony_ci struct dax_device *dax_dev = dev_dax->dax_dev; 39662306a36Sopenharmony_ci struct device *dev = &dev_dax->dev; 39762306a36Sopenharmony_ci struct dev_pagemap *pgmap; 39862306a36Sopenharmony_ci struct inode *inode; 39962306a36Sopenharmony_ci struct cdev *cdev; 40062306a36Sopenharmony_ci void *addr; 40162306a36Sopenharmony_ci int rc, i; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci if (static_dev_dax(dev_dax)) { 40462306a36Sopenharmony_ci if (dev_dax->nr_range > 1) { 40562306a36Sopenharmony_ci dev_warn(dev, 40662306a36Sopenharmony_ci "static pgmap / multi-range device conflict\n"); 40762306a36Sopenharmony_ci return -EINVAL; 40862306a36Sopenharmony_ci } 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci pgmap = dev_dax->pgmap; 41162306a36Sopenharmony_ci } else { 41262306a36Sopenharmony_ci if (dev_dax->pgmap) { 41362306a36Sopenharmony_ci dev_warn(dev, 41462306a36Sopenharmony_ci "dynamic-dax with pre-populated page map\n"); 41562306a36Sopenharmony_ci return -EINVAL; 41662306a36Sopenharmony_ci } 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci pgmap = devm_kzalloc(dev, 41962306a36Sopenharmony_ci struct_size(pgmap, ranges, dev_dax->nr_range - 1), 42062306a36Sopenharmony_ci GFP_KERNEL); 42162306a36Sopenharmony_ci if (!pgmap) 42262306a36Sopenharmony_ci return -ENOMEM; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci pgmap->nr_range = dev_dax->nr_range; 42562306a36Sopenharmony_ci dev_dax->pgmap = pgmap; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci for (i = 0; i < dev_dax->nr_range; i++) { 42862306a36Sopenharmony_ci struct range *range = &dev_dax->ranges[i].range; 42962306a36Sopenharmony_ci pgmap->ranges[i] = *range; 43062306a36Sopenharmony_ci } 43162306a36Sopenharmony_ci } 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci for (i = 0; i < dev_dax->nr_range; i++) { 43462306a36Sopenharmony_ci struct range *range = &dev_dax->ranges[i].range; 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci if (!devm_request_mem_region(dev, range->start, 43762306a36Sopenharmony_ci range_len(range), dev_name(dev))) { 43862306a36Sopenharmony_ci dev_warn(dev, "mapping%d: %#llx-%#llx could not reserve range\n", 43962306a36Sopenharmony_ci i, range->start, range->end); 44062306a36Sopenharmony_ci return -EBUSY; 44162306a36Sopenharmony_ci } 44262306a36Sopenharmony_ci } 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci pgmap->type = MEMORY_DEVICE_GENERIC; 44562306a36Sopenharmony_ci if (dev_dax->align > PAGE_SIZE) 44662306a36Sopenharmony_ci pgmap->vmemmap_shift = 44762306a36Sopenharmony_ci order_base_2(dev_dax->align >> PAGE_SHIFT); 44862306a36Sopenharmony_ci addr = devm_memremap_pages(dev, pgmap); 44962306a36Sopenharmony_ci if (IS_ERR(addr)) 45062306a36Sopenharmony_ci return PTR_ERR(addr); 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci inode = dax_inode(dax_dev); 45362306a36Sopenharmony_ci cdev = inode->i_cdev; 45462306a36Sopenharmony_ci cdev_init(cdev, &dax_fops); 45562306a36Sopenharmony_ci cdev->owner = dev->driver->owner; 45662306a36Sopenharmony_ci cdev_set_parent(cdev, &dev->kobj); 45762306a36Sopenharmony_ci rc = cdev_add(cdev, dev->devt, 1); 45862306a36Sopenharmony_ci if (rc) 45962306a36Sopenharmony_ci return rc; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci rc = devm_add_action_or_reset(dev, dev_dax_cdev_del, cdev); 46262306a36Sopenharmony_ci if (rc) 46362306a36Sopenharmony_ci return rc; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci run_dax(dax_dev); 46662306a36Sopenharmony_ci return devm_add_action_or_reset(dev, dev_dax_kill, dev_dax); 46762306a36Sopenharmony_ci} 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_cistatic struct dax_device_driver device_dax_driver = { 47062306a36Sopenharmony_ci .probe = dev_dax_probe, 47162306a36Sopenharmony_ci .type = DAXDRV_DEVICE_TYPE, 47262306a36Sopenharmony_ci}; 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_cistatic int __init dax_init(void) 47562306a36Sopenharmony_ci{ 47662306a36Sopenharmony_ci return dax_driver_register(&device_dax_driver); 47762306a36Sopenharmony_ci} 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_cistatic void __exit dax_exit(void) 48062306a36Sopenharmony_ci{ 48162306a36Sopenharmony_ci dax_driver_unregister(&device_dax_driver); 48262306a36Sopenharmony_ci} 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ciMODULE_AUTHOR("Intel Corporation"); 48562306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 48662306a36Sopenharmony_cimodule_init(dax_init); 48762306a36Sopenharmony_cimodule_exit(dax_exit); 48862306a36Sopenharmony_ciMODULE_ALIAS_DAX_DEVICE(0); 489