xref: /openbmc/linux/drivers/dax/device.c (revision 1d024e7a)
151cf784cSDan Williams // SPDX-License-Identifier: GPL-2.0
251cf784cSDan Williams /* Copyright(c) 2016-2018 Intel Corporation. All rights reserved. */
389ec9f2cSDan Williams #include <linux/memremap.h>
47b6be844SDan Williams #include <linux/pagemap.h>
57b6be844SDan Williams #include <linux/module.h>
67b6be844SDan Williams #include <linux/device.h>
77b6be844SDan Williams #include <linux/pfn_t.h>
87b6be844SDan Williams #include <linux/cdev.h>
97b6be844SDan Williams #include <linux/slab.h>
107b6be844SDan Williams #include <linux/dax.h>
117b6be844SDan Williams #include <linux/fs.h>
127b6be844SDan Williams #include <linux/mm.h>
13ef842302SDave Jiang #include <linux/mman.h>
1473616367SDan Williams #include "dax-private.h"
1551cf784cSDan Williams #include "bus.h"
167b6be844SDan Williams 
check_vma(struct dev_dax * dev_dax,struct vm_area_struct * vma,const char * func)177b6be844SDan Williams static int check_vma(struct dev_dax *dev_dax, struct vm_area_struct *vma,
187b6be844SDan Williams 		const char *func)
197b6be844SDan Williams {
207b6be844SDan Williams 	struct device *dev = &dev_dax->dev;
217b6be844SDan Williams 	unsigned long mask;
227b6be844SDan Williams 
237b6be844SDan Williams 	if (!dax_alive(dev_dax->dax_dev))
247b6be844SDan Williams 		return -ENXIO;
257b6be844SDan Williams 
267b6be844SDan Williams 	/* prevent private mappings from being established */
277b6be844SDan Williams 	if ((vma->vm_flags & VM_MAYSHARE) != VM_MAYSHARE) {
285a14e91dSJeff Moyer 		dev_info_ratelimited(dev,
295a14e91dSJeff Moyer 				"%s: %s: fail, attempted private mapping\n",
307b6be844SDan Williams 				current->comm, func);
317b6be844SDan Williams 		return -EINVAL;
327b6be844SDan Williams 	}
337b6be844SDan Williams 
3433cf94d7SJoao Martins 	mask = dev_dax->align - 1;
357b6be844SDan Williams 	if (vma->vm_start & mask || vma->vm_end & mask) {
365a14e91dSJeff Moyer 		dev_info_ratelimited(dev,
375a14e91dSJeff Moyer 				"%s: %s: fail, unaligned vma (%#lx - %#lx, %#lx)\n",
387b6be844SDan Williams 				current->comm, func, vma->vm_start, vma->vm_end,
397b6be844SDan Williams 				mask);
407b6be844SDan Williams 		return -EINVAL;
417b6be844SDan Williams 	}
427b6be844SDan Williams 
437b6be844SDan Williams 	if (!vma_is_dax(vma)) {
445a14e91dSJeff Moyer 		dev_info_ratelimited(dev,
455a14e91dSJeff Moyer 				"%s: %s: fail, vma is not DAX capable\n",
467b6be844SDan Williams 				current->comm, func);
477b6be844SDan Williams 		return -EINVAL;
487b6be844SDan Williams 	}
497b6be844SDan Williams 
507b6be844SDan Williams 	return 0;
517b6be844SDan Williams }
527b6be844SDan Williams 
5373616367SDan Williams /* see "strong" declaration in tools/testing/nvdimm/dax-dev.c */
dax_pgoff_to_phys(struct dev_dax * dev_dax,pgoff_t pgoff,unsigned long size)5473616367SDan Williams __weak phys_addr_t dax_pgoff_to_phys(struct dev_dax *dev_dax, pgoff_t pgoff,
557b6be844SDan Williams 		unsigned long size)
567b6be844SDan Williams {
5760e93dc0SDan Williams 	int i;
5860e93dc0SDan Williams 
5960e93dc0SDan Williams 	for (i = 0; i < dev_dax->nr_range; i++) {
6060e93dc0SDan Williams 		struct dev_dax_range *dax_range = &dev_dax->ranges[i];
6160e93dc0SDan Williams 		struct range *range = &dax_range->range;
6260e93dc0SDan Williams 		unsigned long long pgoff_end;
63753a0850SDan Williams 		phys_addr_t phys;
647b6be844SDan Williams 
6560e93dc0SDan Williams 		pgoff_end = dax_range->pgoff + PHYS_PFN(range_len(range)) - 1;
6660e93dc0SDan Williams 		if (pgoff < dax_range->pgoff || pgoff > pgoff_end)
6760e93dc0SDan Williams 			continue;
6860e93dc0SDan Williams 		phys = PFN_PHYS(pgoff - dax_range->pgoff) + range->start;
69f5516ec5SDan Williams 		if (phys + size - 1 <= range->end)
707b6be844SDan Williams 			return phys;
7160e93dc0SDan Williams 		break;
727b6be844SDan Williams 	}
737b6be844SDan Williams 	return -1;
747b6be844SDan Williams }
757b6be844SDan Williams 
dax_set_mapping(struct vm_fault * vmf,pfn_t pfn,unsigned long fault_size)76a0fb038eSJoao Martins static void dax_set_mapping(struct vm_fault *vmf, pfn_t pfn,
77a0fb038eSJoao Martins 			      unsigned long fault_size)
78a0fb038eSJoao Martins {
79a0fb038eSJoao Martins 	unsigned long i, nr_pages = fault_size / PAGE_SIZE;
80a0fb038eSJoao Martins 	struct file *filp = vmf->vma->vm_file;
8114606001SJoao Martins 	struct dev_dax *dev_dax = filp->private_data;
82a0fb038eSJoao Martins 	pgoff_t pgoff;
83a0fb038eSJoao Martins 
8414606001SJoao Martins 	/* mapping is only set on the head */
8514606001SJoao Martins 	if (dev_dax->pgmap->vmemmap_shift)
8614606001SJoao Martins 		nr_pages = 1;
8714606001SJoao Martins 
88a0fb038eSJoao Martins 	pgoff = linear_page_index(vmf->vma,
89a0fb038eSJoao Martins 			ALIGN(vmf->address, fault_size));
90a0fb038eSJoao Martins 
91a0fb038eSJoao Martins 	for (i = 0; i < nr_pages; i++) {
92a0fb038eSJoao Martins 		struct page *page = pfn_to_page(pfn_t_to_pfn(pfn) + i);
93a0fb038eSJoao Martins 
9414606001SJoao Martins 		page = compound_head(page);
95a0fb038eSJoao Martins 		if (page->mapping)
96a0fb038eSJoao Martins 			continue;
97a0fb038eSJoao Martins 
98a0fb038eSJoao Martins 		page->mapping = filp->f_mapping;
99a0fb038eSJoao Martins 		page->index = pgoff + i;
100a0fb038eSJoao Martins 	}
101a0fb038eSJoao Martins }
102a0fb038eSJoao Martins 
__dev_dax_pte_fault(struct dev_dax * dev_dax,struct vm_fault * vmf)103226ab561SDan Williams static vm_fault_t __dev_dax_pte_fault(struct dev_dax *dev_dax,
1046ec228b6SJoao Martins 				struct vm_fault *vmf)
1057b6be844SDan Williams {
1067b6be844SDan Williams 	struct device *dev = &dev_dax->dev;
1077b6be844SDan Williams 	phys_addr_t phys;
1086ec228b6SJoao Martins 	pfn_t pfn;
1097b6be844SDan Williams 	unsigned int fault_size = PAGE_SIZE;
1107b6be844SDan Williams 
1117b6be844SDan Williams 	if (check_vma(dev_dax, vmf->vma, __func__))
1127b6be844SDan Williams 		return VM_FAULT_SIGBUS;
1137b6be844SDan Williams 
11433cf94d7SJoao Martins 	if (dev_dax->align > PAGE_SIZE) {
1156daaca52SDan Williams 		dev_dbg(dev, "alignment (%#x) > fault size (%#x)\n",
11633cf94d7SJoao Martins 			dev_dax->align, fault_size);
1177b6be844SDan Williams 		return VM_FAULT_SIGBUS;
1187b6be844SDan Williams 	}
1197b6be844SDan Williams 
12033cf94d7SJoao Martins 	if (fault_size != dev_dax->align)
1217b6be844SDan Williams 		return VM_FAULT_SIGBUS;
1227b6be844SDan Williams 
12373616367SDan Williams 	phys = dax_pgoff_to_phys(dev_dax, vmf->pgoff, PAGE_SIZE);
1247b6be844SDan Williams 	if (phys == -1) {
1256daaca52SDan Williams 		dev_dbg(dev, "pgoff_to_phys(%#lx) failed\n", vmf->pgoff);
1267b6be844SDan Williams 		return VM_FAULT_SIGBUS;
1277b6be844SDan Williams 	}
1287b6be844SDan Williams 
1296ec228b6SJoao Martins 	pfn = phys_to_pfn_t(phys, PFN_DEV|PFN_MAP);
1307b6be844SDan Williams 
1316ec228b6SJoao Martins 	dax_set_mapping(vmf, pfn, fault_size);
1320e7325f0SJoao Martins 
1336ec228b6SJoao Martins 	return vmf_insert_mixed(vmf->vma, vmf->address, pfn);
1347b6be844SDan Williams }
1357b6be844SDan Williams 
__dev_dax_pmd_fault(struct dev_dax * dev_dax,struct vm_fault * vmf)136226ab561SDan Williams static vm_fault_t __dev_dax_pmd_fault(struct dev_dax *dev_dax,
1376ec228b6SJoao Martins 				struct vm_fault *vmf)
1387b6be844SDan Williams {
1397b6be844SDan Williams 	unsigned long pmd_addr = vmf->address & PMD_MASK;
1407b6be844SDan Williams 	struct device *dev = &dev_dax->dev;
1417b6be844SDan Williams 	phys_addr_t phys;
1427b6be844SDan Williams 	pgoff_t pgoff;
1436ec228b6SJoao Martins 	pfn_t pfn;
1447b6be844SDan Williams 	unsigned int fault_size = PMD_SIZE;
1457b6be844SDan Williams 
1467b6be844SDan Williams 	if (check_vma(dev_dax, vmf->vma, __func__))
1477b6be844SDan Williams 		return VM_FAULT_SIGBUS;
1487b6be844SDan Williams 
14933cf94d7SJoao Martins 	if (dev_dax->align > PMD_SIZE) {
1506daaca52SDan Williams 		dev_dbg(dev, "alignment (%#x) > fault size (%#x)\n",
15133cf94d7SJoao Martins 			dev_dax->align, fault_size);
1527b6be844SDan Williams 		return VM_FAULT_SIGBUS;
1537b6be844SDan Williams 	}
1547b6be844SDan Williams 
15533cf94d7SJoao Martins 	if (fault_size < dev_dax->align)
1567b6be844SDan Williams 		return VM_FAULT_SIGBUS;
15733cf94d7SJoao Martins 	else if (fault_size > dev_dax->align)
1587b6be844SDan Williams 		return VM_FAULT_FALLBACK;
1597b6be844SDan Williams 
1607b6be844SDan Williams 	/* if we are outside of the VMA */
1617b6be844SDan Williams 	if (pmd_addr < vmf->vma->vm_start ||
1627b6be844SDan Williams 			(pmd_addr + PMD_SIZE) > vmf->vma->vm_end)
1637b6be844SDan Williams 		return VM_FAULT_SIGBUS;
1647b6be844SDan Williams 
1657b6be844SDan Williams 	pgoff = linear_page_index(vmf->vma, pmd_addr);
16673616367SDan Williams 	phys = dax_pgoff_to_phys(dev_dax, pgoff, PMD_SIZE);
1677b6be844SDan Williams 	if (phys == -1) {
1686daaca52SDan Williams 		dev_dbg(dev, "pgoff_to_phys(%#lx) failed\n", pgoff);
1697b6be844SDan Williams 		return VM_FAULT_SIGBUS;
1707b6be844SDan Williams 	}
1717b6be844SDan Williams 
1726ec228b6SJoao Martins 	pfn = phys_to_pfn_t(phys, PFN_DEV|PFN_MAP);
1737b6be844SDan Williams 
1746ec228b6SJoao Martins 	dax_set_mapping(vmf, pfn, fault_size);
1750e7325f0SJoao Martins 
1766ec228b6SJoao Martins 	return vmf_insert_pfn_pmd(vmf, pfn, vmf->flags & FAULT_FLAG_WRITE);
1777b6be844SDan Williams }
1787b6be844SDan Williams 
1797b6be844SDan Williams #ifdef CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD
__dev_dax_pud_fault(struct dev_dax * dev_dax,struct vm_fault * vmf)180226ab561SDan Williams static vm_fault_t __dev_dax_pud_fault(struct dev_dax *dev_dax,
1816ec228b6SJoao Martins 				struct vm_fault *vmf)
1827b6be844SDan Williams {
1837b6be844SDan Williams 	unsigned long pud_addr = vmf->address & PUD_MASK;
1847b6be844SDan Williams 	struct device *dev = &dev_dax->dev;
1857b6be844SDan Williams 	phys_addr_t phys;
1867b6be844SDan Williams 	pgoff_t pgoff;
1876ec228b6SJoao Martins 	pfn_t pfn;
1887b6be844SDan Williams 	unsigned int fault_size = PUD_SIZE;
1897b6be844SDan Williams 
1907b6be844SDan Williams 
1917b6be844SDan Williams 	if (check_vma(dev_dax, vmf->vma, __func__))
1927b6be844SDan Williams 		return VM_FAULT_SIGBUS;
1937b6be844SDan Williams 
19433cf94d7SJoao Martins 	if (dev_dax->align > PUD_SIZE) {
1956daaca52SDan Williams 		dev_dbg(dev, "alignment (%#x) > fault size (%#x)\n",
19633cf94d7SJoao Martins 			dev_dax->align, fault_size);
1977b6be844SDan Williams 		return VM_FAULT_SIGBUS;
1987b6be844SDan Williams 	}
1997b6be844SDan Williams 
20033cf94d7SJoao Martins 	if (fault_size < dev_dax->align)
2017b6be844SDan Williams 		return VM_FAULT_SIGBUS;
20233cf94d7SJoao Martins 	else if (fault_size > dev_dax->align)
2037b6be844SDan Williams 		return VM_FAULT_FALLBACK;
2047b6be844SDan Williams 
2057b6be844SDan Williams 	/* if we are outside of the VMA */
2067b6be844SDan Williams 	if (pud_addr < vmf->vma->vm_start ||
2077b6be844SDan Williams 			(pud_addr + PUD_SIZE) > vmf->vma->vm_end)
2087b6be844SDan Williams 		return VM_FAULT_SIGBUS;
2097b6be844SDan Williams 
2107b6be844SDan Williams 	pgoff = linear_page_index(vmf->vma, pud_addr);
21173616367SDan Williams 	phys = dax_pgoff_to_phys(dev_dax, pgoff, PUD_SIZE);
2127b6be844SDan Williams 	if (phys == -1) {
2136daaca52SDan Williams 		dev_dbg(dev, "pgoff_to_phys(%#lx) failed\n", pgoff);
2147b6be844SDan Williams 		return VM_FAULT_SIGBUS;
2157b6be844SDan Williams 	}
2167b6be844SDan Williams 
2176ec228b6SJoao Martins 	pfn = phys_to_pfn_t(phys, PFN_DEV|PFN_MAP);
2187b6be844SDan Williams 
2196ec228b6SJoao Martins 	dax_set_mapping(vmf, pfn, fault_size);
2200e7325f0SJoao Martins 
2216ec228b6SJoao Martins 	return vmf_insert_pfn_pud(vmf, pfn, vmf->flags & FAULT_FLAG_WRITE);
2227b6be844SDan Williams }
2237b6be844SDan Williams #else
__dev_dax_pud_fault(struct dev_dax * dev_dax,struct vm_fault * vmf)224226ab561SDan Williams static vm_fault_t __dev_dax_pud_fault(struct dev_dax *dev_dax,
2256ec228b6SJoao Martins 				struct vm_fault *vmf)
2267b6be844SDan Williams {
2277b6be844SDan Williams 	return VM_FAULT_FALLBACK;
2287b6be844SDan Williams }
2297b6be844SDan Williams #endif /* !CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD */
2307b6be844SDan Williams 
dev_dax_huge_fault(struct vm_fault * vmf,unsigned int order)231*1d024e7aSMatthew Wilcox (Oracle) static vm_fault_t dev_dax_huge_fault(struct vm_fault *vmf, unsigned int order)
2327b6be844SDan Williams {
2337b6be844SDan Williams 	struct file *filp = vmf->vma->vm_file;
23436bdac1eSSouptick Joarder 	vm_fault_t rc = VM_FAULT_SIGBUS;
23536bdac1eSSouptick Joarder 	int id;
2367b6be844SDan Williams 	struct dev_dax *dev_dax = filp->private_data;
2377b6be844SDan Williams 
238*1d024e7aSMatthew Wilcox (Oracle) 	dev_dbg(&dev_dax->dev, "%s: %s (%#lx - %#lx) order:%d\n", current->comm,
2396daaca52SDan Williams 			(vmf->flags & FAULT_FLAG_WRITE) ? "write" : "read",
240*1d024e7aSMatthew Wilcox (Oracle) 			vmf->vma->vm_start, vmf->vma->vm_end, order);
2417b6be844SDan Williams 
2427b6be844SDan Williams 	id = dax_read_lock();
243*1d024e7aSMatthew Wilcox (Oracle) 	if (order == 0)
2446ec228b6SJoao Martins 		rc = __dev_dax_pte_fault(dev_dax, vmf);
245*1d024e7aSMatthew Wilcox (Oracle) 	else if (order == PMD_ORDER)
2466ec228b6SJoao Martins 		rc = __dev_dax_pmd_fault(dev_dax, vmf);
247*1d024e7aSMatthew Wilcox (Oracle) 	else if (order == PUD_ORDER)
2486ec228b6SJoao Martins 		rc = __dev_dax_pud_fault(dev_dax, vmf);
249*1d024e7aSMatthew Wilcox (Oracle) 	else
2507b6be844SDan Williams 		rc = VM_FAULT_SIGBUS;
2512232c638SDan Williams 
2527b6be844SDan Williams 	dax_read_unlock(id);
2537b6be844SDan Williams 
2547b6be844SDan Williams 	return rc;
2557b6be844SDan Williams }
2567b6be844SDan Williams 
dev_dax_fault(struct vm_fault * vmf)257226ab561SDan Williams static vm_fault_t dev_dax_fault(struct vm_fault *vmf)
2587b6be844SDan Williams {
259*1d024e7aSMatthew Wilcox (Oracle) 	return dev_dax_huge_fault(vmf, 0);
2607b6be844SDan Williams }
2617b6be844SDan Williams 
dev_dax_may_split(struct vm_area_struct * vma,unsigned long addr)262dd3b614fSDmitry Safonov static int dev_dax_may_split(struct vm_area_struct *vma, unsigned long addr)
2639702cffdSDan Williams {
2649702cffdSDan Williams 	struct file *filp = vma->vm_file;
2659702cffdSDan Williams 	struct dev_dax *dev_dax = filp->private_data;
2669702cffdSDan Williams 
26733cf94d7SJoao Martins 	if (!IS_ALIGNED(addr, dev_dax->align))
2689702cffdSDan Williams 		return -EINVAL;
2699702cffdSDan Williams 	return 0;
2709702cffdSDan Williams }
2719702cffdSDan Williams 
dev_dax_pagesize(struct vm_area_struct * vma)272c1d53b92SDan Williams static unsigned long dev_dax_pagesize(struct vm_area_struct *vma)
273c1d53b92SDan Williams {
274c1d53b92SDan Williams 	struct file *filp = vma->vm_file;
275c1d53b92SDan Williams 	struct dev_dax *dev_dax = filp->private_data;
276c1d53b92SDan Williams 
27733cf94d7SJoao Martins 	return dev_dax->align;
278c1d53b92SDan Williams }
279c1d53b92SDan Williams 
2807b6be844SDan Williams static const struct vm_operations_struct dax_vm_ops = {
2817b6be844SDan Williams 	.fault = dev_dax_fault,
2827b6be844SDan Williams 	.huge_fault = dev_dax_huge_fault,
283dd3b614fSDmitry Safonov 	.may_split = dev_dax_may_split,
284c1d53b92SDan Williams 	.pagesize = dev_dax_pagesize,
2857b6be844SDan Williams };
2867b6be844SDan Williams 
dax_mmap(struct file * filp,struct vm_area_struct * vma)2877b6be844SDan Williams static int dax_mmap(struct file *filp, struct vm_area_struct *vma)
2887b6be844SDan Williams {
2897b6be844SDan Williams 	struct dev_dax *dev_dax = filp->private_data;
2907b6be844SDan Williams 	int rc, id;
2917b6be844SDan Williams 
2926daaca52SDan Williams 	dev_dbg(&dev_dax->dev, "trace\n");
2937b6be844SDan Williams 
2947b6be844SDan Williams 	/*
2957b6be844SDan Williams 	 * We lock to check dax_dev liveness and will re-check at
2967b6be844SDan Williams 	 * fault time.
2977b6be844SDan Williams 	 */
2987b6be844SDan Williams 	id = dax_read_lock();
2997b6be844SDan Williams 	rc = check_vma(dev_dax, vma, __func__);
3007b6be844SDan Williams 	dax_read_unlock(id);
3017b6be844SDan Williams 	if (rc)
3027b6be844SDan Williams 		return rc;
3037b6be844SDan Williams 
3047b6be844SDan Williams 	vma->vm_ops = &dax_vm_ops;
3051c71222eSSuren Baghdasaryan 	vm_flags_set(vma, VM_HUGEPAGE);
3067b6be844SDan Williams 	return 0;
3077b6be844SDan Williams }
3087b6be844SDan Williams 
3097b6be844SDan Williams /* return an unmapped area aligned to the dax region specified alignment */
dax_get_unmapped_area(struct file * filp,unsigned long addr,unsigned long len,unsigned long pgoff,unsigned long flags)3107b6be844SDan Williams static unsigned long dax_get_unmapped_area(struct file *filp,
3117b6be844SDan Williams 		unsigned long addr, unsigned long len, unsigned long pgoff,
3127b6be844SDan Williams 		unsigned long flags)
3137b6be844SDan Williams {
3147b6be844SDan Williams 	unsigned long off, off_end, off_align, len_align, addr_align, align;
3157b6be844SDan Williams 	struct dev_dax *dev_dax = filp ? filp->private_data : NULL;
3167b6be844SDan Williams 
3177b6be844SDan Williams 	if (!dev_dax || addr)
3187b6be844SDan Williams 		goto out;
3197b6be844SDan Williams 
32033cf94d7SJoao Martins 	align = dev_dax->align;
3217b6be844SDan Williams 	off = pgoff << PAGE_SHIFT;
3227b6be844SDan Williams 	off_end = off + len;
3237b6be844SDan Williams 	off_align = round_up(off, align);
3247b6be844SDan Williams 
3257b6be844SDan Williams 	if ((off_end <= off_align) || ((off_end - off_align) < align))
3267b6be844SDan Williams 		goto out;
3277b6be844SDan Williams 
3287b6be844SDan Williams 	len_align = len + align;
3297b6be844SDan Williams 	if ((off + len_align) < off)
3307b6be844SDan Williams 		goto out;
3317b6be844SDan Williams 
3327b6be844SDan Williams 	addr_align = current->mm->get_unmapped_area(filp, addr, len_align,
3337b6be844SDan Williams 			pgoff, flags);
3347b6be844SDan Williams 	if (!IS_ERR_VALUE(addr_align)) {
3357b6be844SDan Williams 		addr_align += (off - addr_align) & (align - 1);
3367b6be844SDan Williams 		return addr_align;
3377b6be844SDan Williams 	}
3387b6be844SDan Williams  out:
3397b6be844SDan Williams 	return current->mm->get_unmapped_area(filp, addr, len, pgoff, flags);
3407b6be844SDan Williams }
3417b6be844SDan Williams 
34241c9b1beSDave Jiang static const struct address_space_operations dev_dax_aops = {
34346de8b97SMatthew Wilcox (Oracle) 	.dirty_folio	= noop_dirty_folio,
34441c9b1beSDave Jiang };
34541c9b1beSDave Jiang 
dax_open(struct inode * inode,struct file * filp)3467b6be844SDan Williams static int dax_open(struct inode *inode, struct file *filp)
3477b6be844SDan Williams {
3487b6be844SDan Williams 	struct dax_device *dax_dev = inode_dax(inode);
3497b6be844SDan Williams 	struct inode *__dax_inode = dax_inode(dax_dev);
3507b6be844SDan Williams 	struct dev_dax *dev_dax = dax_get_private(dax_dev);
3517b6be844SDan Williams 
3526daaca52SDan Williams 	dev_dbg(&dev_dax->dev, "trace\n");
3537b6be844SDan Williams 	inode->i_mapping = __dax_inode->i_mapping;
3547b6be844SDan Williams 	inode->i_mapping->host = __dax_inode;
35541c9b1beSDave Jiang 	inode->i_mapping->a_ops = &dev_dax_aops;
3567b6be844SDan Williams 	filp->f_mapping = inode->i_mapping;
3575660e13dSJeff Layton 	filp->f_wb_err = filemap_sample_wb_err(filp->f_mapping);
358735e4ae5SJeff Layton 	filp->f_sb_err = file_sample_sb_err(filp);
3597b6be844SDan Williams 	filp->private_data = dev_dax;
3607b6be844SDan Williams 	inode->i_flags = S_DAX;
3617b6be844SDan Williams 
3627b6be844SDan Williams 	return 0;
3637b6be844SDan Williams }
3647b6be844SDan Williams 
dax_release(struct inode * inode,struct file * filp)3657b6be844SDan Williams static int dax_release(struct inode *inode, struct file *filp)
3667b6be844SDan Williams {
3677b6be844SDan Williams 	struct dev_dax *dev_dax = filp->private_data;
3687b6be844SDan Williams 
3696daaca52SDan Williams 	dev_dbg(&dev_dax->dev, "trace\n");
3707b6be844SDan Williams 	return 0;
3717b6be844SDan Williams }
3727b6be844SDan Williams 
3737b6be844SDan Williams static const struct file_operations dax_fops = {
3747b6be844SDan Williams 	.llseek = noop_llseek,
3757b6be844SDan Williams 	.owner = THIS_MODULE,
3767b6be844SDan Williams 	.open = dax_open,
3777b6be844SDan Williams 	.release = dax_release,
3787b6be844SDan Williams 	.get_unmapped_area = dax_get_unmapped_area,
3797b6be844SDan Williams 	.mmap = dax_mmap,
380ef842302SDave Jiang 	.mmap_supported_flags = MAP_SYNC,
3817b6be844SDan Williams };
3827b6be844SDan Williams 
dev_dax_cdev_del(void * cdev)3839567da0bSDan Williams static void dev_dax_cdev_del(void *cdev)
3849567da0bSDan Williams {
3859567da0bSDan Williams 	cdev_del(cdev);
3869567da0bSDan Williams }
3879567da0bSDan Williams 
dev_dax_kill(void * dev_dax)3889567da0bSDan Williams static void dev_dax_kill(void *dev_dax)
3899567da0bSDan Williams {
3909567da0bSDan Williams 	kill_dev_dax(dev_dax);
3919567da0bSDan Williams }
3929567da0bSDan Williams 
dev_dax_probe(struct dev_dax * dev_dax)3932d515352SArnd Bergmann static int dev_dax_probe(struct dev_dax *dev_dax)
3947b6be844SDan Williams {
3957b6be844SDan Williams 	struct dax_device *dax_dev = dev_dax->dax_dev;
396f11cf813SDan Williams 	struct device *dev = &dev_dax->dev;
397f5516ec5SDan Williams 	struct dev_pagemap *pgmap;
3987b6be844SDan Williams 	struct inode *inode;
3997b6be844SDan Williams 	struct cdev *cdev;
40089ec9f2cSDan Williams 	void *addr;
40160e93dc0SDan Williams 	int rc, i;
40289ec9f2cSDan Williams 
403fc65c4ebSJoao Martins 	if (static_dev_dax(dev_dax))  {
404fc65c4ebSJoao Martins 		if (dev_dax->nr_range > 1) {
405fc65c4ebSJoao Martins 			dev_warn(dev,
406fc65c4ebSJoao Martins 				"static pgmap / multi-range device conflict\n");
40760e93dc0SDan Williams 			return -EINVAL;
408fc65c4ebSJoao Martins 		}
40960e93dc0SDan Williams 
410fc65c4ebSJoao Martins 		pgmap = dev_dax->pgmap;
411fc65c4ebSJoao Martins 	} else {
412fc65c4ebSJoao Martins 		if (dev_dax->pgmap) {
413fc65c4ebSJoao Martins 			dev_warn(dev,
414fc65c4ebSJoao Martins 				 "dynamic-dax with pre-populated page map\n");
415fc65c4ebSJoao Martins 			return -EINVAL;
416fc65c4ebSJoao Martins 		}
417fc65c4ebSJoao Martins 
41809b80137SJoao Martins 		pgmap = devm_kzalloc(dev,
41909b80137SJoao Martins                        struct_size(pgmap, ranges, dev_dax->nr_range - 1),
42009b80137SJoao Martins                        GFP_KERNEL);
421f5516ec5SDan Williams 		if (!pgmap)
422f5516ec5SDan Williams 			return -ENOMEM;
423fc65c4ebSJoao Martins 
42460e93dc0SDan Williams 		pgmap->nr_range = dev_dax->nr_range;
425fc65c4ebSJoao Martins 		dev_dax->pgmap = pgmap;
426fc65c4ebSJoao Martins 
427fc65c4ebSJoao Martins 		for (i = 0; i < dev_dax->nr_range; i++) {
428fc65c4ebSJoao Martins 			struct range *range = &dev_dax->ranges[i].range;
429fc65c4ebSJoao Martins 			pgmap->ranges[i] = *range;
430fc65c4ebSJoao Martins 		}
431f5516ec5SDan Williams 	}
43260e93dc0SDan Williams 
43360e93dc0SDan Williams 	for (i = 0; i < dev_dax->nr_range; i++) {
43460e93dc0SDan Williams 		struct range *range = &dev_dax->ranges[i].range;
43560e93dc0SDan Williams 
43660e93dc0SDan Williams 		if (!devm_request_mem_region(dev, range->start,
43760e93dc0SDan Williams 					range_len(range), dev_name(dev))) {
43860e93dc0SDan Williams 			dev_warn(dev, "mapping%d: %#llx-%#llx could not reserve range\n",
43960e93dc0SDan Williams 					i, range->start, range->end);
44060e93dc0SDan Williams 			return -EBUSY;
44160e93dc0SDan Williams 		}
44260e93dc0SDan Williams 	}
44360e93dc0SDan Williams 
444f5516ec5SDan Williams 	pgmap->type = MEMORY_DEVICE_GENERIC;
44514606001SJoao Martins 	if (dev_dax->align > PAGE_SIZE)
44614606001SJoao Martins 		pgmap->vmemmap_shift =
44714606001SJoao Martins 			order_base_2(dev_dax->align >> PAGE_SHIFT);
448f5516ec5SDan Williams 	addr = devm_memremap_pages(dev, pgmap);
44950f44ee7SDan Williams 	if (IS_ERR(addr))
45089ec9f2cSDan Williams 		return PTR_ERR(addr);
45189ec9f2cSDan Williams 
4527b6be844SDan Williams 	inode = dax_inode(dax_dev);
4537b6be844SDan Williams 	cdev = inode->i_cdev;
4547b6be844SDan Williams 	cdev_init(cdev, &dax_fops);
4559567da0bSDan Williams 	cdev->owner = dev->driver->owner;
4569567da0bSDan Williams 	cdev_set_parent(cdev, &dev->kobj);
4579567da0bSDan Williams 	rc = cdev_add(cdev, dev->devt, 1);
4587b6be844SDan Williams 	if (rc)
4599567da0bSDan Williams 		return rc;
4607b6be844SDan Williams 
4619567da0bSDan Williams 	rc = devm_add_action_or_reset(dev, dev_dax_cdev_del, cdev);
4629567da0bSDan Williams 	if (rc)
4639567da0bSDan Williams 		return rc;
4647b6be844SDan Williams 
4659567da0bSDan Williams 	run_dax(dax_dev);
4669567da0bSDan Williams 	return devm_add_action_or_reset(dev, dev_dax_kill, dev_dax);
4677b6be844SDan Williams }
4689567da0bSDan Williams 
469d200781eSDan Williams static struct dax_device_driver device_dax_driver = {
4709567da0bSDan Williams 	.probe = dev_dax_probe,
471e9ee9fe3SDan Williams 	.type = DAXDRV_DEVICE_TYPE,
4729567da0bSDan Williams };
4737b6be844SDan Williams 
dax_init(void)4747b6be844SDan Williams static int __init dax_init(void)
4757b6be844SDan Williams {
4769567da0bSDan Williams 	return dax_driver_register(&device_dax_driver);
4777b6be844SDan Williams }
4787b6be844SDan Williams 
dax_exit(void)4797b6be844SDan Williams static void __exit dax_exit(void)
4807b6be844SDan Williams {
481d200781eSDan Williams 	dax_driver_unregister(&device_dax_driver);
4827b6be844SDan Williams }
4837b6be844SDan Williams 
4847b6be844SDan Williams MODULE_AUTHOR("Intel Corporation");
4857b6be844SDan Williams MODULE_LICENSE("GPL v2");
4869567da0bSDan Williams module_init(dax_init);
4877b6be844SDan Williams module_exit(dax_exit);
4889567da0bSDan Williams MODULE_ALIAS_DAX_DEVICE(0);
489