Support ext4 on NV-DIMMs
Subsystems (15)
v9 cover Support ext4 on NV-DIMMs
From: Matthew Wilcox <[email protected]>
One of the primary uses for NV-DIMMs is to expose them as a block device and use a filesystem to store files on the NV-DIMM. While that works, it currently wastes memory and CPU time buffering the files in the page cache. We have support in ext2 for bypassing the page cache, but it has some races which are unfixable in the current design. This series of patches rewrite the underlying support, and add support for direct access to ext4.
This iteration of the patchset rebases to 3.16-rc7 and makes substantial changes based on feedback from Jan Kara, Boaz Harrosh and Kirill Shutemov:
- Fixes a double-unlock on i_mmap_mutex
- Switch the order of calling delete_from_page_cache() and unmap_mapping_range() to match the truncate path
- Make dax_mkwrite a macro (Kirill)
- Drop vm_replace_mixed(); instead call unmap_mapping_range() before calling vm_insert_mixed() (Kirill)
- Avoid lock inversion between i_mmap_mutex and transaction start (Jan)
- Move alignment & length checks into bdev_direct_access() (Boaz)
- Fix bugs in COW code; unfortunately this means reintroducing the knowledge that the i_mmap_mutex protects PFNs to the core MM code.
Jan Kara (1):
ext4: Avoid lock inversion between i_mmap_mutex and transaction start
Matthew Wilcox (20):
axonram: Fix bug in direct_access
Change direct_access calling convention
Fix XIP fault vs truncate race
Allow page fault handlers to perform the COW
Introduce IS_DAX(inode)
Add copy_to_iter(), copy_from_iter() and iov_iter_zero() Replace XIP read and write with DAX I/O
Replace ext2_clear_xip_target with dax_clear_blocks Replace the XIP page fault handler with the DAX page fault handler Replace xip_truncate_page with dax_truncate_page
Replace XIP documentation with DAX documentation
Remove get_xip_mem
ext2: Remove ext2_xip_verify_sb()
ext2: Remove ext2_use_xip
ext2: Remove xip.c and xip.h
Remove CONFIG_EXT2_FS_XIP and rename CONFIG_FS_XIP to CONFIG_FS_DAX
ext2: Remove ext2_aops_xip
Get rid of most mentions of XIP in ext2
xip: Add xip_zero_page_range
brd: Rename XIP to DAX
Ross Zwisler (1):
ext4: Add DAX functionality
Documentation/filesystems/Locking | 3 - Documentation/filesystems/dax.txt | 91 +++++++ Documentation/filesystems/ext4.txt | 2 + Documentation/filesystems/xip.txt | 68 ------ arch/powerpc/sysdev/axonram.c | 19 +- drivers/block/Kconfig | 13 +- drivers/block/brd.c | 26 +- drivers/s390/block/dcssblk.c | 21 +- fs/Kconfig | 21 +- fs/Makefile | 1 + fs/block_dev.c | 34 +++ fs/dax.c | 476 ++++++++++++++++++++++++++++++++++++ fs/exofs/inode.c | 1 - fs/ext2/Kconfig | 11 - fs/ext2/Makefile | 1 - fs/ext2/ext2.h | 10 +- fs/ext2/file.c | 45 +++- fs/ext2/inode.c | 38 +-- fs/ext2/namei.c | 13 +- fs/ext2/super.c | 53 ++-- fs/ext2/xip.c | 91 ------- fs/ext2/xip.h | 26 -- fs/ext4/ext4.h | 6 + fs/ext4/file.c | 53 +++- fs/ext4/indirect.c | 18 +- fs/ext4/inode.c | 65 +++-- fs/ext4/namei.c | 10 +- fs/ext4/super.c | 39 ++- fs/open.c | 5 +- include/linux/blkdev.h | 6 +- include/linux/fs.h | 49 +++- include/linux/mm.h | 1 + include/linux/uio.h | 3 + mm/Makefile | 1 - mm/fadvise.c | 6 +- mm/filemap.c | 6 +- mm/filemap_xip.c | 483 ------------------------------------- mm/iov_iter.c | 237 ++++++++++++++++-- mm/madvise.c | 2 +- mm/memory.c | 33 ++- 40 files changed, 1206 insertions(+), 881 deletions(-) create mode 100644 Documentation/filesystems/dax.txt delete mode 100644 Documentation/filesystems/xip.txt create mode 100644 fs/dax.c delete mode 100644 fs/ext2/xip.c delete mode 100644 fs/ext2/xip.h delete mode 100644 mm/filemap_xip.c
Signature · 7 lines
2.0.1 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v9 1/22 axonram: Fix bug in direct_access
The 'pfn' returned by axonram was completely bogus, and has been since 2008.
--- arch/powerpc/sysdev/axonram.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
arch/powerpc/sysdev/axonram.c +1 -1
@@ -156,7 +156,7 @@ axon_ram_direct_access(struct block_device *device, sector_t sector, } *kaddr = (void *)(bank->ph_addr + offset); - *pfn =virt_to_phys (kaddr) >>PAGE_SHIFT ; + *pfn =virt_to_phys (*kaddr) >>PAGE_SHIFT ;return 0 ; }
Signature · 7 lines
2.0.1 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v9 2/22 Change direct_access calling convention
In reply to Matthew WilcoxIn order to support accesses to larger chunks of memory, pass in a 'size' parameter (counted in bytes), and return the amount available at that address.
Add a new helper function, bdev_direct_access(), to handle common functionality including partition handling, checking for the sector being page-aligned, and checking the length of the request does not pass the end of the partition.
--- Documentation/filesystems/xip.txt | 15 +++++++++------ arch/powerpc/sysdev/axonram.c | 17 ++++------------- drivers/block/brd.c | 12 +++++------- drivers/s390/block/dcssblk.c | 21 +++++++++------------ fs/block_dev.c | 34 ++++++++++++++++++++++++++++++++++ fs/ext2/xip.c | 31 +++++++++++++------------------ include/linux/blkdev.h | 6 ++++-- 7 files changed, 78 insertions(+), 58 deletions(-)
Documentation/filesystems/xip.txt +9 -6
@@ -28,12 +28,15 @@ Implementation Execute-in-place is implemented in three steps: block device operation, address space operation, and file operations. -A block device operation named direct_access is used to retrieve a -reference (pointer) to a block on-disk. The reference is supposed to be -cpu-addressable, physical address and remain valid until the release operation -is performed. A struct block_device reference is used to address the device, -and a sector_t argument is used to identify the individual block. As an -alternative, memory technology devices can be used for this. +A block device operation named direct_access is used to translate the +block device sector number to a page frame number (pfn) that identifies +the physical page for the memory. It also returns a kernel virtual +address that can be used to access the memory. + +The direct_access method takes a 'size' parameter that indicates the +number of bytes being requested. The function should return the number +of bytes that it can provide, although it must not exceed the number of +bytes requested. It may also return a negative errno if an error occurs. The block device operation is optional, these block devices support it as of today:
arch/powerpc/sysdev/axonram.c +4 -13
@@ -139,26 +139,17 @@ axon_ram_make_request(struct request_queue *queue, struct bio *bio)* axon_ram_direct_access - direct_access() method for block device * @device, @sector, @data: see block_device_operations method */ -static int +static long axon_ram_direct_access (struct block_device *device,sector_t sector, -void **kaddr,unsigned long *pfn) +void **kaddr,unsigned long *pfn,long size) {struct axon_ram_bank *bank = device->bd_disk->private_data; -loff_t offset; - - offset = sector; -if (device->bd_part !=NULL ) - offset += device->bd_part->start_sect; - offset <<=AXON_RAM_SECTOR_SHIFT ; -if (offset >= bank->size) { -dev_err (&bank->device->dev,"Access outside of address space\n" ); -return -ERANGE ; - } +loff_t offset = (loff_t )sector <<AXON_RAM_SECTOR_SHIFT ; *kaddr = (void *)(bank->ph_addr + offset); *pfn =virt_to_phys (*kaddr) >>PAGE_SHIFT ; -return 0 ; +return min_t (long, size, bank->size - offset); }static const struct block_device_operations axon_ram_devops = {
drivers/block/brd.c +5 -7
@@ -370,25 +370,23 @@ static int brd_rw_page(struct block_device *bdev, sector_t sector, }#ifdef CONFIG_BLK_DEV_XIP -static int brd_direct_access (struct block_device *bdev,sector_t sector, -void **kaddr,unsigned long *pfn) +static long brd_direct_access (struct block_device *bdev,sector_t sector, +void **kaddr,unsigned long *pfn,long size) {struct brd_device *brd = bdev->bd_disk->private_data;struct page *page;if (!brd)return -ENODEV ; -if (sector & (PAGE_SECTORS -1 )) -return -EINVAL ; -if (sector +PAGE_SECTORS >get_capacity (bdev->bd_disk)) -return -ERANGE ; page =brd_insert_page (brd, sector);if (!page)return -ENOSPC ; *kaddr =page_address (page); *pfn =page_to_pfn (page); -return 0 ; +/* Could optimistically check to see if the next page in the +* file is mapped to the next page of physical RAM */ +return min_t (long,PAGE_SIZE , size); }#endif
drivers/s390/block/dcssblk.c +9 -12
@@ -28,8 +28,8 @@static int dcssblk_open (struct block_device *bdev,fmode_t mode);static void dcssblk_release (struct gendisk *disk,fmode_t mode);static void dcssblk_make_request (struct request_queue *q,struct bio *bio); -static int dcssblk_direct_access (struct block_device *bdev,sector_t secnum, -void **kaddr,unsigned long *pfn); +static long dcssblk_direct_access (struct block_device *bdev,sector_t secnum, +void **kaddr,unsigned long *pfn,long size);static char dcssblk_segments[DCSSBLK_PARM_LEN ] ="\0" ;
@@ -866,25 +866,22 @@ fail:bio_io_error (bio); } -static int +static long dcssblk_direct_access (struct block_device *bdev,sector_t secnum, -void **kaddr,unsigned long *pfn) +void **kaddr,unsigned long *pfn,long size) {struct dcssblk_dev_info *dev_info; -unsigned long pgoff; +unsigned long offset, dev_sz; dev_info = bdev->bd_disk->private_data;if (!dev_info)return -ENODEV ; -if (secnum % (PAGE_SIZE /512 )) -return -EINVAL ; - pgoff = secnum / (PAGE_SIZE /512 ); -if ((pgoff+1 )*PAGE_SIZE -1 > dev_info->end - dev_info->start) -return -ERANGE ; - *kaddr = (void *) (dev_info->start+pgoff*PAGE_SIZE ); + dev_sz = dev_info->end - dev_info->start; + offset = secnum *512 ; + *kaddr = (void *) (dev_info->start + offset); *pfn =virt_to_phys (*kaddr) >>PAGE_SHIFT ; -return 0 ; +return min_t (long, size, dev_sz - offset); } static void
fs/block_dev.c +34 -0
@@ -427,6 +427,40 @@ int bdev_write_page(struct block_device *bdev, sector_t sector, }EXPORT_SYMBOL_GPL (bdev_write_page); +/** +* bdev_direct_access() - Get the address for directly-accessibly memory +* @bdev: The device containing the memory +* @sector: The offset within the device +* @addr: Where to put the address of the memory +* @pfn: The Page Frame Number for the memory +* @size: The number of bytes requested +* +* If a block device is made up of directly addressable memory, this function +* will tell the caller the PFN and the address of the memory. The address +* may be directly dereferenced within the kernel without the need to call +* ioremap(), kmap() or similar. THe PFN is suitable for inserting into +* page tables. +* +* Return: negative errno if an error occurs, otherwise the number of bytes +* accessible at this address. +*/ +long bdev_direct_access (struct block_device *bdev,sector_t sector, +void **addr,unsigned long *pfn,long size) +{ +const struct block_device_operations *ops = bdev->bd_disk->fops; +if (!ops->direct_access) +return -EOPNOTSUPP ; +if ((sector +DIV_ROUND_UP (size,512 )) > +part_nr_sects_read (bdev->bd_part)) +return -ERANGE ; + sector +=get_start_sect (bdev); +if (sector % (PAGE_SIZE /512 )) +return -EINVAL ; + size = ops->direct_access (bdev, sector, addr, pfn, size); +return size ? size : -ERANGE ; +} +EXPORT_SYMBOL_GPL (bdev_direct_access); +/* * pseudo-fs */
fs/ext2/xip.c +13 -18
@@ -13,18 +13,12 @@#include "ext2.h" #include "xip.h" -static inline int -__inode_direct_access (struct inode *inode,sector_t block, -void **kaddr,unsigned long *pfn) +static inline long __inode_direct_access (struct inode *inode,sector_t block, +void **kaddr,unsigned long *pfn,long size) {struct block_device *bdev = inode->i_sb->s_bdev; -const struct block_device_operations *ops = bdev->bd_disk->fops; -sector_t sector; - - sector = block * (PAGE_SIZE /512 );/* ext2 block to bdev sector */ - -BUG_ON (!ops->direct_access); -return ops->direct_access (bdev, sector, kaddr, pfn); +sector_t sector = block * (PAGE_SIZE /512 ); +return bdev_direct_access (bdev, sector, kaddr, pfn, size); } static inline int
@@ -53,12 +47,13 @@ ext2_clear_xip_target(struct inode *inode, sector_t block) {void *kaddr;unsigned long pfn; -int rc; +long size; - rc =__inode_direct_access (inode, block, &kaddr, &pfn); -if (!rc) -clear_page (kaddr); -return rc; + size =__inode_direct_access (inode, block, &kaddr, &pfn,PAGE_SIZE ); +if (size <0 ) +return size; +clear_page (kaddr); +return 0 ; }void ext2_xip_verify_sb (struct super_block *sb)
@@ -77,7 +72,7 @@ void ext2_xip_verify_sb(struct super_block *sb)int ext2_get_xip_mem (struct address_space *mapping,pgoff_t pgoff,int create,void **kmem,unsigned long *pfn) { -int rc; +long rc;sector_t block;/* first, retrieve the sector number */
@@ -86,6 +81,6 @@ int ext2_get_xip_mem(struct address_space *mapping, pgoff_t pgoff, int create,return rc;/* retrieve address of the target data */ - rc =__inode_direct_access (mapping->host, block, kmem, pfn); -return rc; + rc =__inode_direct_access (mapping->host, block, kmem, pfn,PAGE_SIZE ); +return (rc <0 ) ? rc :0 ; }
include/linux/blkdev.h +4 -2
@@ -1613,8 +1613,8 @@ struct block_device_operations {int (*rw_page)(struct block_device *,sector_t ,struct page *,int rw);int (*ioctl) (struct block_device *,fmode_t ,unsigned ,unsigned long );int (*compat_ioctl) (struct block_device *,fmode_t ,unsigned ,unsigned long ); -int (*direct_access) (struct block_device *,sector_t , -void **,unsigned long *); +long (*direct_access) (struct block_device *,sector_t , +void **,unsigned long *pfn,long size);unsigned int (*check_events) (struct gendisk *disk,unsigned int clearing);/* ->media_changed() is DEPRECATED, use ->check_events() instead */
@@ -1632,6 +1632,8 @@ extern int __blkdev_driver_ioctl(struct block_device *, fmode_t, unsigned int,extern int bdev_read_page (struct block_device *,sector_t ,struct page *);extern int bdev_write_page (struct block_device *,sector_t ,struct page *,struct writeback_control *); +extern long bdev_direct_access (struct block_device *,sector_t ,void **addr, +unsigned long *pfn,long size);#else /* CONFIG_BLOCK */ struct block_device ;
Signature · 7 lines
2.0.1 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v9 3/22 Fix XIP fault vs truncate race
In reply to Matthew WilcoxPagecache faults recheck i_size after taking the page lock to ensure that the fault didn't race against a truncate. We don't have a page to lock in the XIP case, so use the i_mmap_mutex instead. It is locked in the truncate path in unmap_mapping_range() after updating i_size. So while we hold it in the fault path, we are guaranteed that either i_size has already been updated in the truncate path, or that the truncate will subsequently call zap_page_range_single() and so remove the mapping we have just inserted.
There is a window of time in which i_size has been reduced and the thread has a mapping to a page which will be removed from the file, but this is harmless as the page will not be allocated to a different purpose before the thread's access to it is revoked.
--- mm/filemap_xip.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-)
mm/filemap_xip.c +22 -2
@@ -260,8 +260,17 @@ again:__xip_unmap (mapping, vmf->pgoff); found: +/* We must recheck i_size under i_mmap_mutex */ +mutex_lock (&mapping->i_mmap_mutex); + size = (i_size_read (inode) +PAGE_CACHE_SIZE -1 ) >> +PAGE_CACHE_SHIFT ; +if (unlikely (vmf->pgoff >= size)) { +mutex_unlock (&mapping->i_mmap_mutex); +return VM_FAULT_SIGBUS ; + } err =vm_insert_mixed (vma, (unsigned long )vmf->virtual_address, xip_pfn); +mutex_unlock (&mapping->i_mmap_mutex);if (err == -ENOMEM )return VM_FAULT_OOM ;/*
@@ -285,16 +294,27 @@ found: }if (error != -ENODATA ) goto out; + +/* We must recheck i_size under i_mmap_mutex */ +mutex_lock (&mapping->i_mmap_mutex); + size = (i_size_read (inode) +PAGE_CACHE_SIZE -1 ) >> +PAGE_CACHE_SHIFT ; +if (unlikely (vmf->pgoff >= size)) { + ret =VM_FAULT_SIGBUS ; + goto unlock; + }/* not shared and writable, use xip_sparse_page() */ page =xip_sparse_page ();if (!page) - goto out; + goto unlock; err =vm_insert_page (vma, (unsigned long )vmf->virtual_address, page);if (err == -ENOMEM ) - goto out; + goto unlock; ret =VM_FAULT_NOPAGE ; +unlock: +mutex_unlock (&mapping->i_mmap_mutex); out:write_seqcount_end (&xip_sparse_seq);mutex_unlock (&xip_sparse_mutex);
Signature · 7 lines
2.0.1 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v9 4/22 Allow page fault handlers to perform the COW
In reply to Matthew WilcoxCurrently COW of an XIP file is done by first bringing in a read-only mapping, then retrying the fault and copying the page. It is much more efficient to tell the fault handler that a COW is being attempted (by passing in the pre-allocated page in the vm_fault structure), and allow the handler to perform the COW operation itself.
The handler cannot insert the page itself if there is already a read-only mapping at that address, so allow the handler to return VM_FAULT_LOCKED and set the fault_page to be NULL. This indicates to the MM code that the i_mmap_mutex is held instead of the page lock.
--- include/linux/mm.h | 1 + mm/memory.c | 33 ++++++++++++++++++++++++--------- 2 files changed, 25 insertions(+), 9 deletions(-)
include/linux/mm.h +1 -0
@@ -208,6 +208,7 @@ struct vm_fault {pgoff_t pgoff;/* Logical page offset based on vma */ void __user *virtual_address;/* Faulting virtual address */ +struct page *cow_page;/* Handler may choose to COW */ struct page *page;/* ->fault handlers should return a * page here, unless VM_FAULT_NOPAGE * is set (which is also implied by
mm/memory.c +24 -9
@@ -2003,6 +2003,7 @@ static int do_page_mkwrite(struct vm_area_struct *vma, struct page *page, vmf.pgoff = page->index; vmf.flags =FAULT_FLAG_WRITE |FAULT_FLAG_MKWRITE ; vmf.page = page; + vmf.cow_page =NULL ; ret = vma->vm_ops->page_mkwrite (vma, &vmf);if (unlikely (ret & (VM_FAULT_ERROR |VM_FAULT_NOPAGE )))
@@ -2689,7 +2690,8 @@ oom: }static int __do_fault (struct vm_area_struct *vma,unsigned long address, -pgoff_t pgoff,unsigned int flags,struct page **page) +pgoff_t pgoff,unsigned int flags, +struct page *cow_page,struct page **page) {struct vm_fault vmf;int ret;
@@ -2698,10 +2700,13 @@ static int __do_fault(struct vm_area_struct *vma, unsigned long address, vmf.pgoff = pgoff; vmf.flags = flags; vmf.page =NULL ; + vmf.cow_page = cow_page; ret = vma->vm_ops->fault (vma, &vmf);if (unlikely (ret & (VM_FAULT_ERROR |VM_FAULT_NOPAGE |VM_FAULT_RETRY )))return ret; +if (!vmf.page) + goto out;if (unlikely (PageHWPoison (vmf.page))) {if (ret &VM_FAULT_LOCKED )
@@ -2715,6 +2720,7 @@ static int __do_fault(struct vm_area_struct *vma, unsigned long address,else VM_BUG_ON_PAGE (!PageLocked (vmf.page), vmf.page); + out: *page = vmf.page;return ret; }
@@ -2894,7 +2900,7 @@ static int do_read_fault(struct mm_struct *mm, struct vm_area_struct *vma,pte_unmap_unlock (pte, ptl); } - ret =__do_fault (vma, address, pgoff, flags, &fault_page); + ret =__do_fault (vma, address, pgoff, flags,NULL , &fault_page);if (unlikely (ret & (VM_FAULT_ERROR |VM_FAULT_NOPAGE |VM_FAULT_RETRY )))return ret;
@@ -2933,24 +2939,33 @@ static int do_cow_fault(struct mm_struct *mm, struct vm_area_struct *vma,return VM_FAULT_OOM ; } - ret =__do_fault (vma, address, pgoff, flags, &fault_page); + ret =__do_fault (vma, address, pgoff, flags, new_page, &fault_page);if (unlikely (ret & (VM_FAULT_ERROR |VM_FAULT_NOPAGE |VM_FAULT_RETRY ))) goto uncharge_out; -copy_user_highpage (new_page, fault_page, address, vma); +if (fault_page) +copy_user_highpage (new_page, fault_page, address, vma);__SetPageUptodate (new_page); pte =pte_offset_map_lock (mm, pmd, address, &ptl);if (unlikely (!pte_same (*pte, orig_pte))) {pte_unmap_unlock (pte, ptl); -unlock_page (fault_page); -page_cache_release (fault_page); +if (fault_page) { +unlock_page (fault_page); +page_cache_release (fault_page); + }else { +mutex_unlock (&vma->vm_file->f_mapping->i_mmap_mutex); + } goto uncharge_out; }do_set_pte (vma, address, new_page, pte, true, true);pte_unmap_unlock (pte, ptl); -unlock_page (fault_page); -page_cache_release (fault_page); +if (fault_page) { +unlock_page (fault_page); +page_cache_release (fault_page); + }else { +mutex_unlock (&vma->vm_file->f_mapping->i_mmap_mutex); + }return ret; uncharge_out:mem_cgroup_uncharge_page (new_page);
@@ -2969,7 +2984,7 @@ static int do_shared_fault(struct mm_struct *mm, struct vm_area_struct *vma,int dirtied =0 ;int ret, tmp; - ret =__do_fault (vma, address, pgoff, flags, &fault_page); + ret =__do_fault (vma, address, pgoff, flags,NULL , &fault_page);if (unlikely (ret & (VM_FAULT_ERROR |VM_FAULT_NOPAGE |VM_FAULT_RETRY )))return ret;
Signature · 7 lines
2.0.1 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v9 5/22 Introduce IS_DAX(inode)
In reply to Matthew WilcoxUse an inode flag to tag inodes which should avoid using the page cache. Convert ext2 to use it instead of mapping_is_xip().
--- fs/ext2/inode.c | 9 ++++++--- fs/ext2/xip.h | 2 -- include/linux/fs.h | 6 ++++++ 3 files changed, 12 insertions(+), 5 deletions(-)
fs/ext2/inode.c +6 -3
@@ -731,7 +731,7 @@ static int ext2_get_blocks(struct inode *inode, goto cleanup; } -if (ext2_use_xip (inode->i_sb)) { +if (IS_DAX (inode)) {/* * we need to clear the block */
@@ -1201,7 +1201,7 @@ static int ext2_setsize(struct inode *inode, loff_t newsize)inode_dio_wait (inode); -if (mapping_is_xip (inode->i_mapping)) +if (IS_DAX (inode)) error =xip_truncate_page (inode->i_mapping, newsize);else if (test_opt (inode->i_sb,NOBH )) error = nobh_truncate_page(inode->i_mapping,
@@ -1273,7 +1273,8 @@ void ext2_set_inode_flags(struct inode *inode) {unsigned int flags =EXT2_I (inode)->i_flags; - inode->i_flags &= ~(S_SYNC |S_APPEND |S_IMMUTABLE |S_NOATIME |S_DIRSYNC ); + inode->i_flags &= ~(S_SYNC |S_APPEND |S_IMMUTABLE |S_NOATIME | +S_DIRSYNC |S_DAX );if (flags &EXT2_SYNC_FL ) inode->i_flags |=S_SYNC ;if (flags &EXT2_APPEND_FL )
@@ -1284,6 +1285,8 @@ void ext2_set_inode_flags(struct inode *inode) inode->i_flags |=S_NOATIME ;if (flags &EXT2_DIRSYNC_FL ) inode->i_flags |=S_DIRSYNC ; +if (test_opt (inode->i_sb,XIP )) + inode->i_flags |=S_DAX ; }/* Propagate flags from i_flags to EXT2_I(inode)->i_flags */
fs/ext2/xip.h +0 -2
@@ -16,9 +16,7 @@ static inline int ext2_use_xip (struct super_block *sb) }int ext2_get_xip_mem (struct address_space *,pgoff_t ,int ,void **,unsigned long *); -#define mapping_is_xip (map) unlikely(map->a_ops->get_xip_mem)#else -#define mapping_is_xip (map) 0#define ext2_xip_verify_sb (sb) do { } while (0)#define ext2_use_xip (sb) 0#define ext2_clear_xip_target (inode, chain) 0
include/linux/fs.h +6 -0
@@ -1579,6 +1579,7 @@ struct super_operations {#define S_IMA 1024/* Inode has an associated IMA struct */ #define S_AUTOMOUNT 2048/* Automount/referral quasi-directory */ #define S_NOSEC 4096/* no suid or xattr security attributes */ +#define S_DAX 8192/* Direct Access, avoiding the page cache */ /* * Note that nosuid etc flags are inode-specific: setting some file-system
@@ -1616,6 +1617,11 @@ struct super_operations {#define IS_IMA (inode) ((inode)->i_flags & S_IMA)#define IS_AUTOMOUNT (inode) ((inode)->i_flags & S_AUTOMOUNT)#define IS_NOSEC (inode) ((inode)->i_flags & S_NOSEC) +#ifdef CONFIG_FS_XIP +#define IS_DAX (inode) ((inode)->i_flags & S_DAX) +#else +#define IS_DAX (inode) 0 +#endif /* * Inode state bits. Protected by inode->i_lock
Signature · 7 lines
2.0.1 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v9 6/22 Add copy_to_iter(), copy_from_iter() and iov_iter_zero()
In reply to Matthew WilcoxFrom: Matthew Wilcox <[email protected]>
For DAX, we want to be able to copy between iovecs and kernel addresses that don't necessarily have a struct page. This is a fairly simple rearrangement for bvec iters to kmap the pages outside and pass them in, but for user iovecs it gets more complicated because we might try various different ways to kmap the memory. Duplicating the existing logic works out best in this case.
We need to be able to write zeroes to an iovec for reads from unwritten ranges in a file. This is performed by the new iov_iter_zero() function, again patterned after the existing code that handles iovec iterators.
--- include/linux/uio.h | 3 + mm/iov_iter.c | 237 ++++++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 226 insertions(+), 14 deletions(-)
include/linux/uio.h +3 -0
@@ -80,6 +80,9 @@ size_t copy_page_to_iter(struct page *page, size_t offset, size_t bytes, struct iov_iter *i);size_t copy_page_from_iter (struct page *page,size_t offset,size_t bytes,struct iov_iter *i); +size_t copy_to_iter (void *addr,size_t bytes,struct iov_iter *i); +size_t copy_from_iter (void *addr,size_t bytes,struct iov_iter *i); +size_t iov_iter_zero (size_t bytes,struct iov_iter *);unsigned long iov_iter_alignment (const struct iov_iter *i);void iov_iter_init (struct iov_iter *i,int direction,const struct iovec *iov,unsigned long nr_segs,size_t count);
mm/iov_iter.c +223 -14
303 lines not on this page · raw message
Signature · 7 lines
2.0.1 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v9 7/22 Replace XIP read and write with DAX I/O
In reply to Matthew WilcoxUse the generic AIO infrastructure instead of custom read and write methods. In addition to giving us support for AIO, this adds the missing locking between read() and truncate().
--- fs/Makefile | 1 + fs/dax.c | 195 ++++++++++++++++++++++++++++++++++++++++++++ fs/ext2/file.c | 6 +- fs/ext2/inode.c | 8 +- include/linux/fs.h | 18 ++++- mm/filemap.c | 6 +- mm/filemap_xip.c | 234 ----------------------------------------------------- 7 files changed, 223 insertions(+), 245 deletions(-) create mode 100644 fs/dax.c
fs/Makefile +1 -0
@@ -28,6 +28,7 @@ obj-$(CONFIG_SIGNALFD) += signalfd.o obj-$(CONFIG_TIMERFD) += timerfd.o obj-$(CONFIG_EVENTFD) += eventfd.o obj-$(CONFIG_AIO) += aio.o +obj-$(CONFIG_FS_XIP) += dax.o obj-$(CONFIG_FILE_LOCKING) += locks.o obj-$(CONFIG_COMPAT) += compat.o compat_ioctl.o obj-$(CONFIG_BINFMT_AOUT) += binfmt_aout.o
fs/dax.c new file +195 -0
195 lines not on this page · raw message
fs/ext2/file.c +4 -2
@@ -81,8 +81,10 @@ const struct file_operations ext2_file_operations = {#ifdef CONFIG_EXT2_FS_XIP const struct file_operations ext2_xip_file_operations = { .llseek = generic_file_llseek, - .read = xip_file_read, - .write = xip_file_write, + .read = new_sync_read, + .write = new_sync_write, + .read_iter = generic_file_read_iter, + .write_iter = generic_file_write_iter, .unlocked_ioctl = ext2_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = ext2_compat_ioctl,
fs/ext2/inode.c +7 -1
@@ -859,7 +859,12 @@ ext2_direct_IO(int rw, struct kiocb *iocb, struct iov_iter *iter,size_t count =iov_iter_count (iter);ssize_t ret; - ret =blockdev_direct_IO (rw, iocb, inode, iter, offset, ext2_get_block); +if (IS_DAX (inode)) + ret =dax_do_io (rw, iocb, inode, iter, offset, ext2_get_block, +NULL ,DIO_LOCKING ); +else + ret =blockdev_direct_IO (rw, iocb, inode, iter, offset, + ext2_get_block);if (ret <0 && (rw &WRITE ))ext2_write_failed (mapping, offset + count);return ret;
@@ -888,6 +893,7 @@ const struct address_space_operations ext2_aops = {const struct address_space_operations ext2_aops_xip = { .bmap = ext2_bmap, .get_xip_mem = ext2_get_xip_mem, + .direct_IO = ext2_direct_IO, };const struct address_space_operations ext2_nobh_aops = {
include/linux/fs.h +14 -4
@@ -2463,17 +2463,22 @@ extern int generic_file_open(struct inode * inode, struct file * filp);extern int nonseekable_open (struct inode * inode,struct file * filp);#ifdef CONFIG_FS_XIP -extern ssize_t xip_file_read (struct file *filp,char __user *buf,size_t len, -loff_t *ppos);extern int xip_file_mmap (struct file * file,struct vm_area_struct * vma); -extern ssize_t xip_file_write (struct file *filp,const char __user *buf, -size_t len,loff_t *ppos);extern int xip_truncate_page (struct address_space *mapping,loff_t from); +ssize_t dax_do_io (int rw,struct kiocb *,struct inode *,struct iov_iter *, +loff_t ,get_block_t ,dio_iodone_t ,int flags);#else static inline int xip_truncate_page (struct address_space *mapping,loff_t from) {return 0 ; } + +static inline ssize_t dax_do_io (int rw,struct kiocb *iocb,struct inode *inode, +const struct iovec *iov,loff_t pos,unsigned nr_segs, +get_block_t get_block,dio_iodone_t end_io,int flags) +{ +return -ENOTTY ; +}#endif #ifdef CONFIG_BLOCK
@@ -2630,6 +2635,11 @@ extern int generic_show_options(struct seq_file *m, struct dentry *root);extern void save_mount_options (struct super_block *sb,char *options);extern void replace_mount_options (struct super_block *sb,char *options); +static inline bool io_is_direct (struct file *filp) +{ +return (filp->f_flags &O_DIRECT ) ||IS_DAX (file_inode (filp)); +} +static inline ino_t parent_ino (struct dentry *dentry) {ino_t res;
mm/filemap.c +2 -4
@@ -1684,8 +1684,7 @@ generic_file_read_iter(struct kiocb *iocb, struct iov_iter *iter)loff_t *ppos = &iocb->ki_pos;loff_t pos = *ppos; -/* coalesce the iovecs and go direct-to-BIO for O_DIRECT */ -if (file->f_flags &O_DIRECT ) { +if (io_is_direct (file)) {struct address_space *mapping = file->f_mapping;struct inode *inode = mapping->host;size_t count =iov_iter_count (iter);
@@ -2561,8 +2560,7 @@ ssize_t __generic_file_write_iter(struct kiocb *iocb, struct iov_iter *from)if (err) goto out; -/* coalesce the iovecs and go direct-to-BIO for O_DIRECT */ -if (unlikely (file->f_flags &O_DIRECT )) { +if (io_is_direct (file)) {loff_t endbyte; written =generic_file_direct_write (iocb, from, pos);
mm/filemap_xip.c +0 -234
246 lines not on this page · raw message
Signature · 1 line
2.0.1
PATCH v9 8/22 Replace ext2_clear_xip_target with dax_clear_blocks
In reply to Matthew WilcoxThis is practically generic code; other filesystems will want to call it from other places, but there's nothing ext2-specific about it.
Make it a little more generic by allowing it to take a count of the number of bytes to zero rather than fixing it to a single page. Thanks to Dave Hansen for suggesting that I need to call cond_resched() if zeroing more than one page.
--- fs/dax.c | 35 +++++++++++++++++++++++++++++++++++ fs/ext2/inode.c | 8 +++++--- fs/ext2/xip.c | 14 -------------- fs/ext2/xip.h | 3 --- include/linux/fs.h | 6 ++++++ 5 files changed, 46 insertions(+), 20 deletions(-)
fs/dax.c +35 -0
@@ -20,8 +20,43 @@#include <linux/fs.h> #include <linux/genhd.h> #include <linux/mutex.h> +#include <linux/sched.h> #include <linux/uio.h> +int dax_clear_blocks (struct inode *inode,sector_t block,long size) +{ +struct block_device *bdev = inode->i_sb->s_bdev; +sector_t sector = block << (inode->i_blkbits -9 ); + +might_sleep (); +do { +void *addr; +unsigned long pfn; +long count; + + count =bdev_direct_access (bdev, sector, &addr, &pfn, size); +if (count <0 ) +return count; +while (count >0 ) { +unsigned pgsz =PAGE_SIZE -offset_in_page (addr); +if (pgsz > count) + pgsz = count; +if (pgsz <PAGE_SIZE ) +memset (addr,0 , pgsz); +else +clear_page (addr); + addr += pgsz; + size -= pgsz; + count -= pgsz; + sector += pgsz /512 ; +cond_resched (); + } + }while (size); + +return 0 ; +} +EXPORT_SYMBOL_GPL (dax_clear_blocks); +static long dax_get_addr (struct buffer_head *bh,void **addr,unsigned blkbits) {unsigned long pfn;
fs/ext2/inode.c +5 -3
@@ -733,10 +733,12 @@ static int ext2_get_blocks(struct inode *inode,if (IS_DAX (inode)) {/* -* we need to clear the block +* block must be initialised before we put it in the tree +* so that it's not found by another thread before it's +* initialised */ - err =ext2_clear_xip_target (inode, -le32_to_cpu (chain[depth-1 ].key)); + err =dax_clear_blocks (inode,le32_to_cpu (chain[depth-1 ].key), +1 << inode->i_blkbits);if (err) {mutex_unlock (&ei->truncate_mutex); goto cleanup;
fs/ext2/xip.c +0 -14
@@ -42,20 +42,6 @@ __ext2_get_block(struct inode *inode, pgoff_t pgoff, int create,return rc; } -int -ext2_clear_xip_target (struct inode *inode,sector_t block) -{ -void *kaddr; -unsigned long pfn; -long size; - - size =__inode_direct_access (inode, block, &kaddr, &pfn,PAGE_SIZE ); -if (size <0 ) -return size; -clear_page (kaddr); -return 0 ; -} -void ext2_xip_verify_sb (struct super_block *sb) {struct ext2_sb_info *sbi =EXT2_SB (sb);
fs/ext2/xip.h +0 -3
@@ -7,8 +7,6 @@#ifdef CONFIG_EXT2_FS_XIP extern void ext2_xip_verify_sb (struct super_block *); -extern int ext2_clear_xip_target (struct inode *,sector_t ); -static inline int ext2_use_xip (struct super_block *sb) {struct ext2_sb_info *sbi =EXT2_SB (sb);
@@ -19,6 +17,5 @@ int ext2_get_xip_mem(struct address_space *, pgoff_t, int,#else #define ext2_xip_verify_sb (sb) do { } while (0)#define ext2_use_xip (sb) 0 -#define ext2_clear_xip_target (inode, chain) 0#define ext2_get_xip_mem NULL#endif
include/linux/fs.h +6 -0
@@ -2463,11 +2463,17 @@ extern int generic_file_open(struct inode * inode, struct file * filp);extern int nonseekable_open (struct inode * inode,struct file * filp);#ifdef CONFIG_FS_XIP +int dax_clear_blocks (struct inode *,sector_t block,long size);extern int xip_file_mmap (struct file * file,struct vm_area_struct * vma);extern int xip_truncate_page (struct address_space *mapping,loff_t from);ssize_t dax_do_io (int rw,struct kiocb *,struct inode *,struct iov_iter *,loff_t ,get_block_t ,dio_iodone_t ,int flags);#else +static inline int dax_clear_blocks (struct inode *i,sector_t blk,long sz) +{ +return 0 ; +} +static inline int xip_truncate_page (struct address_space *mapping,loff_t from) {return 0 ;
Signature · 7 lines
2.0.1 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v9 9/22 Replace the XIP page fault handler with the DAX page fault handler
In reply to Matthew WilcoxInstead of calling aops->get_xip_mem from the fault handler, the filesystem passes a get_block_t that is used to find the appropriate blocks.
--- fs/dax.c | 194 +++++++++++++++++++++++++++++++++++++++++++++++++ fs/ext2/file.c | 35 ++++++++- include/linux/fs.h | 4 +- mm/filemap_xip.c | 206 ----------------------------------------------------- 4 files changed, 230 insertions(+), 209 deletions(-)
fs/dax.c +194 -0
@@ -19,9 +19,13 @@#include <linux/buffer_head.h> #include <linux/fs.h> #include <linux/genhd.h> +#include <linux/highmem.h> +#include <linux/memcontrol.h> +#include <linux/mm.h> #include <linux/mutex.h> #include <linux/sched.h> #include <linux/uio.h> +#include <linux/vmstat.h> int dax_clear_blocks (struct inode *inode,sector_t block,long size) {
@@ -64,6 +68,14 @@ static long dax_get_addr(struct buffer_head *bh, void **addr, unsigned blkbits)return bdev_direct_access (bh->b_bdev, sector, addr, &pfn, bh->b_size); } +static long dax_get_pfn (struct buffer_head *bh,unsigned long *pfn, +unsigned blkbits) +{ +void *addr; +sector_t sector = bh->b_blocknr << (blkbits -9 ); +return bdev_direct_access (bh->b_bdev, sector, &addr, pfn, bh->b_size); +} +static void dax_new_buf (void *addr,unsigned size,unsigned first,loff_t pos,loff_t end) {
@@ -228,3 +240,185 @@ ssize_t dax_do_io(int rw, struct kiocb *iocb, struct inode *inode,return retval; }EXPORT_SYMBOL_GPL (dax_do_io); + +/* +* The user has performed a load from a hole in the file. Allocating +* a new page in the file would cause excessive storage usage for +* workloads with sparse files. We allocate a page cache page instead. +* We'll kick it out of the page cache if it's ever written to, +* otherwise it will simply fall out of the page cache under memory +* pressure without ever having been dirtied. +*/ +static int dax_load_hole (struct address_space *mapping,struct page *page, +struct vm_fault *vmf) +{ +unsigned long size; +struct inode *inode = mapping->host; +if (!page) + page =find_or_create_page (mapping, vmf->pgoff, +GFP_KERNEL | __GFP_ZERO); +if (!page) +return VM_FAULT_OOM ; +/* Recheck i_size under page lock to avoid truncate race */ + size = (i_size_read (inode) +PAGE_SIZE -1 ) >>PAGE_SHIFT ; +if (vmf->pgoff >= size) { +unlock_page (page); +page_cache_release (page); +return VM_FAULT_SIGBUS ; + } + + vmf->page = page; +return VM_FAULT_LOCKED ; +} + +static int copy_user_bh (struct page *to,struct buffer_head *bh, +unsigned blkbits,unsigned long vaddr) +{ +void *vfrom, *vto; +if (dax_get_addr (bh, &vfrom, blkbits) <0 ) +return -EIO ; + vto =kmap_atomic (to); +copy_user_page (vto, vfrom, vaddr, to); +kunmap_atomic (vto); +return 0 ; +} + +static int do_dax_fault (struct vm_area_struct *vma,struct vm_fault *vmf, +get_block_t get_block) +{ +struct file *file = vma->vm_file; +struct inode *inode =file_inode (file); +struct address_space *mapping = file->f_mapping; +struct page *page; +struct buffer_head bh; +unsigned long vaddr = (unsigned long )vmf->virtual_address; +unsigned blkbits = inode->i_blkbits; +sector_t block; +pgoff_t size; +unsigned long pfn; +int error; +int major =0 ; + + size = (i_size_read (inode) +PAGE_SIZE -1 ) >>PAGE_SHIFT ; +if (vmf->pgoff >= size) +return VM_FAULT_SIGBUS ; + +memset (&bh,0 ,sizeof (bh)); + block = (sector_t )vmf->pgoff << (PAGE_SHIFT - blkbits); + bh.b_size =PAGE_SIZE ; + + repeat: + page =find_get_page (mapping, vmf->pgoff); +if (page) { +if (!lock_page_or_retry (page, vma->vm_mm, vmf->flags)) { +page_cache_release (page); +return VM_FAULT_RETRY ; + } +if (unlikely (page->mapping != mapping)) { +unlock_page (page); +page_cache_release (page); + goto repeat; + } + }else { +mutex_lock (&mapping->i_mmap_mutex); + } + +/* Guard against a race with truncate */ + size = (i_size_read (inode) +PAGE_SIZE -1 ) >>PAGE_SHIFT ; +if (unlikely (vmf->pgoff >= size)) + goto sigbus; + + error =get_block (inode, block, &bh,0 ); +if (error || bh.b_size <PAGE_SIZE ) + goto sigbus; + +if (!buffer_written (&bh) && !vmf->cow_page) { +if (vmf->flags &FAULT_FLAG_WRITE ) { + error =get_block (inode, block, &bh,1 ); +count_vm_event (PGMAJFAULT ); +mem_cgroup_count_vm_event (vma->vm_mm,PGMAJFAULT ); + major =VM_FAULT_MAJOR ; +if (error || bh.b_size <PAGE_SIZE ) + goto sigbus; + }else { +if (!page) +mutex_unlock (&mapping->i_mmap_mutex); +return dax_load_hole (mapping, page, vmf); + } + } + +if (vmf->cow_page) { +struct page *new_page = vmf->cow_page; +if (buffer_written (&bh)) + error =copy_user_bh (new_page, &bh, blkbits, vaddr); +else +clear_user_highpage (new_page, vaddr); +if (error) + goto sigbus; + vmf->page = page; +return VM_FAULT_LOCKED ; + } + +if (buffer_unwritten (&bh) ||buffer_new (&bh)) +dax_clear_blocks (inode, bh.b_blocknr, bh.b_size); + +if (page) { +unmap_mapping_range (mapping, vmf->pgoff <<PAGE_SHIFT , +PAGE_CACHE_SIZE ,0 ); +delete_from_page_cache (page); +unlock_page (page); +page_cache_release (page); +mutex_lock (&mapping->i_mmap_mutex); + } + + error =dax_get_pfn (&bh, &pfn, blkbits); +if (error >0 ) + error =vm_insert_mixed (vma, vaddr, pfn); + +mutex_unlock (&mapping->i_mmap_mutex); + +if (error == -ENOMEM ) +return VM_FAULT_OOM ; +if (error == -EIO ) +return VM_FAULT_SIGBUS ; +/* -EBUSY is fine, somebody else faulted on the same PTE */ +if (error != -EBUSY ) +BUG_ON (error); +return VM_FAULT_NOPAGE | major; + + sigbus: +if (page) { +unlock_page (page); +page_cache_release (page); + }else { +mutex_unlock (&mapping->i_mmap_mutex); + } +return VM_FAULT_SIGBUS ; +} + +/** +* dax_fault - handle a page fault on a DAX file +* @vma: The virtual memory area where the fault occurred +* @vmf: The description of the fault +* @get_block: The filesystem method used to translate file offsets to blocks +* +* When a page fault occurs, filesystems may call this helper in their +* fault handler for DAX files. +*/ +int dax_fault (struct vm_area_struct *vma,struct vm_fault *vmf, +get_block_t get_block) +{ +int result; +struct super_block *sb =file_inode (vma->vm_file)->i_sb; + +if (vmf->flags &FAULT_FLAG_WRITE ) { +sb_start_pagefault (sb); +file_update_time (vma->vm_file); + } + result =do_dax_fault (vma, vmf, get_block); +if (vmf->flags &FAULT_FLAG_WRITE ) +sb_end_pagefault (sb); + +return result; +} +EXPORT_SYMBOL_GPL (dax_fault);
fs/ext2/file.c +33 -2
@@ -25,6 +25,37 @@#include "xattr.h" #include "acl.h" +#ifdef CONFIG_EXT2_FS_XIP +static int ext2_dax_fault (struct vm_area_struct *vma,struct vm_fault *vmf) +{ +return dax_fault (vma, vmf, ext2_get_block); +} + +static int ext2_dax_mkwrite (struct vm_area_struct *vma,struct vm_fault *vmf) +{ +return dax_mkwrite (vma, vmf, ext2_get_block); +} + +static const struct vm_operations_struct ext2_dax_vm_ops = { + .fault = ext2_dax_fault, + .page_mkwrite = ext2_dax_mkwrite, + .remap_pages = generic_file_remap_pages, +}; + +static int ext2_file_mmap (struct file *file,struct vm_area_struct *vma) +{ +if (!IS_DAX (file_inode (file))) +return generic_file_mmap (file, vma); + +file_accessed (file); + vma->vm_ops = &ext2_dax_vm_ops; + vma->vm_flags |=VM_MIXEDMAP ; +return 0 ; +} +#else +#define ext2_file_mmap generic_file_mmap +#endif +/* * Called when filp is released. This happens when all file descriptors * for a single struct file are closed. Note that different open() calls
@@ -70,7 +101,7 @@ const struct file_operations ext2_file_operations = {#ifdef CONFIG_COMPAT .compat_ioctl = ext2_compat_ioctl, #endif - .mmap = generic_file_mmap, + .mmap = ext2_file_mmap, .open = dquot_file_open, .release = ext2_release_file, .fsync = ext2_fsync,
@@ -89,7 +120,7 @@ const struct file_operations ext2_xip_file_operations = {#ifdef CONFIG_COMPAT .compat_ioctl = ext2_compat_ioctl, #endif - .mmap = xip_file_mmap, + .mmap = ext2_file_mmap, .open = dquot_file_open, .release = ext2_release_file, .fsync = ext2_fsync,
include/linux/fs.h +3 -1
@@ -49,6 +49,7 @@ struct swap_info_struct;struct seq_file ;struct workqueue_struct ;struct iov_iter ; +struct vm_fault ;extern void __init inode_init (void );extern void __init inode_init_early (void );
@@ -2464,10 +2465,11 @@ extern int nonseekable_open(struct inode * inode, struct file * filp);#ifdef CONFIG_FS_XIP int dax_clear_blocks (struct inode *,sector_t block,long size); -extern int xip_file_mmap (struct file * file,struct vm_area_struct * vma);extern int xip_truncate_page (struct address_space *mapping,loff_t from);ssize_t dax_do_io (int rw,struct kiocb *,struct inode *,struct iov_iter *,loff_t ,get_block_t ,dio_iodone_t ,int flags); +int dax_fault (struct vm_area_struct *,struct vm_fault *,get_block_t ); +#define dax_mkwrite (vma, vmf, gb) dax_fault(vma, vmf, gb)#else static inline int dax_clear_blocks (struct inode *i,sector_t blk,long sz) {
mm/filemap_xip.c +0 -206
212 lines not on this page · raw message
Signature · 7 lines
2.0.1 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v9 10/22 Replace xip_truncate_page with dax_truncate_page
In reply to Matthew WilcoxIt takes a get_block parameter just like nobh_truncate_page() and block_truncate_page()
--- fs/dax.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ fs/ext2/inode.c | 2 +- include/linux/fs.h | 4 ++-- mm/filemap_xip.c | 40 ---------------------------------------- 4 files changed, 47 insertions(+), 43 deletions(-)
fs/dax.c +44 -0
@@ -422,3 +422,47 @@ int dax_fault(struct vm_area_struct *vma, struct vm_fault *vmf,return result; }EXPORT_SYMBOL_GPL (dax_fault); + +/** +* dax_truncate_page - handle a partial page being truncated in a DAX file +* @inode: The file being truncated +* @from: The file offset that is being truncated to +* @get_block: The filesystem method used to translate file offsets to blocks +* +* Similar to block_truncate_page(), this function can be called by a +* filesystem when it is truncating an DAX file to handle the partial page. +* +* We work in terms of PAGE_CACHE_SIZE here for commonality with +* block_truncate_page(), but we could go down to PAGE_SIZE if the filesystem +* took care of disposing of the unnecessary blocks. Even if the filesystem +* block size is smaller than PAGE_SIZE, we have to zero the rest of the page +* since the file might be mmaped. +*/ +int dax_truncate_page (struct inode *inode,loff_t from,get_block_t get_block) +{ +struct buffer_head bh; +pgoff_t index = from >>PAGE_CACHE_SHIFT ; +unsigned offset = from & (PAGE_CACHE_SIZE -1 ); +unsigned length =PAGE_CACHE_ALIGN (from) - from; +int err; + +/* Block boundary? Nothing to do */ +if (!length) +return 0 ; + +memset (&bh,0 ,sizeof (bh)); + bh.b_size =PAGE_CACHE_SIZE ; + err =get_block (inode, index, &bh,0 ); +if (err <0 ) +return err; +if (buffer_written (&bh)) { +void *addr; + err =dax_get_addr (&bh, &addr, inode->i_blkbits); +if (err <0 ) +return err; +memset (addr + offset,0 , length); + } + +return 0 ; +} +EXPORT_SYMBOL_GPL (dax_truncate_page);
fs/ext2/inode.c +1 -1
@@ -1210,7 +1210,7 @@ static int ext2_setsize(struct inode *inode, loff_t newsize)inode_dio_wait (inode);if (IS_DAX (inode)) - error =xip_truncate_page (inode->i_mapping, newsize); + error =dax_truncate_page (inode, newsize, ext2_get_block);else if (test_opt (inode->i_sb,NOBH )) error =nobh_truncate_page (inode->i_mapping, newsize, ext2_get_block);
include/linux/fs.h +2 -2
@@ -2465,7 +2465,7 @@ extern int nonseekable_open(struct inode * inode, struct file * filp);#ifdef CONFIG_FS_XIP int dax_clear_blocks (struct inode *,sector_t block,long size); -extern int xip_truncate_page (struct address_space *mapping,loff_t from); +int dax_truncate_page (struct inode *,loff_t from,get_block_t );ssize_t dax_do_io (int rw,struct kiocb *,struct inode *,struct iov_iter *,loff_t ,get_block_t ,dio_iodone_t ,int flags);int dax_fault (struct vm_area_struct *,struct vm_fault *,get_block_t );
@@ -2476,7 +2476,7 @@ static inline int dax_clear_blocks(struct inode *i, sector_t blk, long sz)return 0 ; } -static inline int xip_truncate_page (struct address_space *mapping,loff_t from) +static inline int dax_truncate_page (struct inode *i,loff_t frm,get_block_t gb) {return 0 ; }
mm/filemap_xip.c +0 -40
@@ -21,43 +21,3 @@#include <asm/tlbflush.h> #include <asm/io.h> -/* -* truncate a page used for execute in place -* functionality is analog to block_truncate_page but does use get_xip_mem -* to get the page instead of page cache -*/ -int -xip_truncate_page (struct address_space *mapping,loff_t from) -{ -pgoff_t index = from >>PAGE_CACHE_SHIFT ; -unsigned offset = from & (PAGE_CACHE_SIZE -1 ); -unsigned blocksize; -unsigned length; -void *xip_mem; -unsigned long xip_pfn; -int err; - -BUG_ON (!mapping->a_ops->get_xip_mem); - - blocksize =1 << mapping->host->i_blkbits; - length = offset & (blocksize -1 ); - -/* Block boundary? Nothing to do */ -if (!length) -return 0 ; - - length = blocksize - length; - - err = mapping->a_ops->get_xip_mem (mapping, index,0 , - &xip_mem, &xip_pfn); -if (unlikely (err)) { -if (err == -ENODATA ) -/* Hole? No need to truncate */ -return 0 ; -else -return err; - } -memset (xip_mem + offset,0 , length); -return 0 ; -} -EXPORT_SYMBOL_GPL (xip_truncate_page);
Signature · 7 lines
2.0.1 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v9 11/22 Replace XIP documentation with DAX documentation
In reply to Matthew WilcoxFrom: Matthew Wilcox <[email protected]>
Based on the original XIP documentation, this documents the current state of affairs, and includes instructions on how users can enable DAX if their devices and kernel support it.
--- Documentation/filesystems/dax.txt | 89 +++++++++++++++++++++++++++++++++++++++ Documentation/filesystems/xip.txt | 71 ------------------------------- 2 files changed, 89 insertions(+), 71 deletions(-) create mode 100644 Documentation/filesystems/dax.txt delete mode 100644 Documentation/filesystems/xip.txt
Documentation/filesystems/dax.txt new file +89 -0
@@ -0,0 +1,89 @@ +Direct Access for files +----------------------- + +Motivation +---------- + +The page cache is usually used to buffer reads and writes to files. +It is also used to provide the pages which are mapped into userspace +by a call to mmap. + +For block devices that are memory-like, the page cache pages would be +unnecessary copies of the original storage. The DAX code removes the +extra copy by performing reads and writes directly to the storage device. +For file mappings, the storage device is mapped directly into userspace. + + +Usage +----- + +If you have a block device which supports DAX, you can make a filesystem +on it as usual. When mounting it, use the -o dax option manually +or add 'dax' to the options in /etc/fstab. + + +Implementation Tips for Block Driver Writers +-------------------------------------------- + +To support DAX in your block driver, implement the 'direct_access' +block device operation. It is used to translate the sector number +(expressed in units of 512-byte sectors) to a page frame number (pfn) +that identifies the physical page for the memory. It also returns a +kernel virtual address that can be used to access the memory. + +The direct_access method takes a 'size' parameter that indicates the +number of bytes being requested. The function should return the number +of bytes that it can provide, although it must not exceed the number of +bytes requested. It may also return a negative errno if an error occurs. + +In order to support this method, the storage must be byte-accessible by +the CPU at all times. If your device uses paging techniques to expose +a large amount of memory through a smaller window, then you cannot +implement direct_access. Equally, if your device can occasionally +stall the CPU for an extended period, you should also not attempt to +implement direct_access. + +These block devices may be used for inspiration: +- axonram: Axon DDR2 device driver +- brd: RAM backed block device driver +- dcssblk: s390 dcss block device driver + + +Implementation Tips for Filesystem Writers +------------------------------------------ + +Filesystem support consists of +- adding support to mark inodes as being DAX by setting the S_DAX flag in + i_flags +- implementing the direct_IO address space operation, and calling + dax_do_io() instead of blockdev_direct_IO() if S_DAX is set +- implementing an mmap file operation for DAX files which sets the + VM_MIXEDMAP flag on the VMA, and setting the vm_ops to include handlers + for fault and page_mkwrite (which should probably call dax_fault() and + dax_mkwrite(), passing the appropriate get_block() callback) +- calling dax_truncate_page() instead of block_truncate_page() for DAX files +- ensuring that there is sufficient locking between reads, writes, + truncates and page faults + +The get_block() callback passed to the DAX functions may return +uninitialised extents. If it does, it must ensure that simultaneous +calls to get_block() (for example by a page-fault racing with a read() +or a write()) work correctly. + +These filesystems may be used for inspiration: +- ext2: the second extended filesystem, see Documentation/filesystems/ext2.txt + + +Shortcomings +------------ + +Even if the kernel or its modules are stored on a filesystem that supports +DAX on a block device that supports DAX, they will still be copied into RAM. + +Calling get_user_pages() on a range of user memory that has been mmaped +from a DAX file will fail as there are no 'struct page' to describe +those pages. This problem is being worked on. That means that O_DIRECT +reads/writes to those memory ranges from a non-DAX file will fail (note +that O_DIRECT reads/writes _of a DAX file_ do work, it is the memory +that is being accessed that is key here). Other things that will not +work include RDMA, sendfile() and splice().
Documentation/filesystems/xip.txt deleted +0 -71
71 lines not on this page · raw message
Signature · 1 line
2.0.1
PATCH v9 12/22 Remove get_xip_mem
In reply to Matthew WilcoxAll callers of get_xip_mem() are now gone. Remove checks for it, initialisers of it, documentation of it and the only implementation of it. Also remove mm/filemap_xip.c as it is now empty.
--- Documentation/filesystems/Locking | 3 --- fs/exofs/inode.c | 1 - fs/ext2/inode.c | 1 - fs/ext2/xip.c | 45 --------------------------------------- fs/ext2/xip.h | 3 --- fs/open.c | 5 +---- include/linux/fs.h | 2 -- mm/Makefile | 1 - mm/fadvise.c | 6 ++++-- mm/filemap_xip.c | 23 -------------------- mm/madvise.c | 2 +- 11 files changed, 6 insertions(+), 86 deletions(-) delete mode 100644 mm/filemap_xip.c
Documentation/filesystems/Locking +0 -3
@@ -197,8 +197,6 @@ prototypes: int (*releasepage) (struct page *, int); void (*freepage)(struct page *); int (*direct_IO)(int, struct kiocb *, struct iov_iter *iter, loff_t offset); - int (*get_xip_mem)(struct address_space *, pgoff_t, int, void **, - unsigned long *); int (*migratepage)(struct address_space *, struct page *, struct page *); int (*launder_page)(struct page *); int (*is_partially_uptodate)(struct page *, unsigned long, unsigned long);
@@ -223,7 +221,6 @@ invalidatepage: yes releasepage: yes freepage: yes direct_IO: -get_xip_mem: maybe migratepage: yes (both) launder_page: yes is_partially_uptodate: yes
fs/exofs/inode.c +0 -1
@@ -985,7 +985,6 @@ const struct address_space_operations exofs_aops = { .direct_IO = exofs_direct_IO,/* With these NULL has special meaning or default is not exported */ - .get_xip_mem = NULL, .migratepage = NULL, .launder_page = NULL, .is_partially_uptodate = NULL,
fs/ext2/inode.c +0 -1
@@ -894,7 +894,6 @@ const struct address_space_operations ext2_aops = {const struct address_space_operations ext2_aops_xip = { .bmap = ext2_bmap, - .get_xip_mem = ext2_get_xip_mem, .direct_IO = ext2_direct_IO, };
fs/ext2/xip.c +0 -45
54 lines not on this page · raw message
fs/ext2/xip.h +0 -3
@@ -12,10 +12,7 @@ static inline int ext2_use_xip (struct super_block *sb)struct ext2_sb_info *sbi =EXT2_SB (sb); return (sbi->s_mount_opt & EXT2_MOUNT_XIP); } -int ext2_get_xip_mem (struct address_space *,pgoff_t ,int , -void **,unsigned long *);#else #define ext2_xip_verify_sb (sb) do { } while (0)#define ext2_use_xip (sb) 0 -#define ext2_get_xip_mem NULL#endif
fs/open.c +1 -4
@@ -656,11 +656,8 @@ int open_check_o_direct(struct file *f) {/* NB: we're sure to have correct a_ops only after f_op->open */ if (f->f_flags &O_DIRECT ) { -if (!f->f_mapping->a_ops || - ((!f->f_mapping->a_ops->direct_IO) && - (!f->f_mapping->a_ops->get_xip_mem))) { +if (!f->f_mapping->a_ops || !f->f_mapping->a_ops->direct_IO)return -EINVAL ; - } }return 0 ; }
include/linux/fs.h +0 -2
@@ -349,8 +349,6 @@ struct address_space_operations {int (*releasepage) (struct page *,gfp_t );void (*freepage)(struct page *);ssize_t (*direct_IO)(int ,struct kiocb *,struct iov_iter *iter,loff_t offset); -int (*get_xip_mem)(struct address_space *,pgoff_t ,int , -void **,unsigned long *);/* * migrate the contents of a page to the specified target. If * migrate_mode is MIGRATE_ASYNC, it must not block.
mm/Makefile +0 -1
@@ -47,7 +47,6 @@ obj-$(CONFIG_SLUB) += slub.o obj-$(CONFIG_KMEMCHECK) += kmemcheck.o obj-$(CONFIG_FAILSLAB) += failslab.o obj-$(CONFIG_MEMORY_HOTPLUG) += memory_hotplug.o -obj-$(CONFIG_FS_XIP) += filemap_xip.o obj-$(CONFIG_MIGRATION) += migrate.o obj-$(CONFIG_QUICKLIST) += quicklist.o obj-$(CONFIG_TRANSPARENT_HUGEPAGE) += huge_memory.o
mm/fadvise.c +4 -2
@@ -28,6 +28,7 @@ SYSCALL_DEFINE4(fadvise64_64, int, fd, loff_t, offset, loff_t, len, int, advice) {struct fd f =fdget (fd); +struct inode *inode;struct address_space *mapping;struct backing_dev_info *bdi;loff_t endbyte;/* inclusive */
@@ -39,7 +40,8 @@ SYSCALL_DEFINE4(fadvise64_64, int, fd, loff_t, offset, loff_t, len, int, advice)if (!f.file)return -EBADF ; -if (S_ISFIFO (file_inode (f.file)->i_mode)) { + inode =file_inode (f.file); +if (S_ISFIFO (inode->i_mode)) { ret = -ESPIPE ; goto out; }
@@ -50,7 +52,7 @@ SYSCALL_DEFINE4(fadvise64_64, int, fd, loff_t, offset, loff_t, len, int, advice) goto out; } -if (mapping->a_ops->get_xip_mem) { +if (IS_DAX (inode)) {switch (advice) {case POSIX_FADV_NORMAL :case POSIX_FADV_RANDOM :
mm/filemap_xip.c deleted +0 -23
@@ -1,23 +0,0 @@ -/* -* linux/mm/filemap_xip.c -* -* Copyright (C) 2005 IBM Corporation -* Author: Carsten Otte <[email protected]> -* -* derived from linux/mm/filemap.c - Copyright (C) Linus Torvalds -* -*/ - -#include <linux/fs.h> -#include <linux/pagemap.h> -#include <linux/export.h> -#include <linux/uio.h> -#include <linux/rmap.h> -#include <linux/mmu_notifier.h> -#include <linux/sched.h> -#include <linux/seqlock.h> -#include <linux/mutex.h> -#include <linux/gfp.h> -#include <asm/tlbflush.h> -#include <asm/io.h> -
mm/madvise.c +1 -1
@@ -236,7 +236,7 @@ static long madvise_willneed(struct vm_area_struct *vma,if (!file)return -EBADF ; -if (file->f_mapping->a_ops->get_xip_mem) { +if (IS_DAX (file_inode (file))) {/* no bad return value, but ignore advice */ return 0 ; }
Signature · 7 lines
2.0.1 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v9 13/22 ext2: Remove ext2_xip_verify_sb()
In reply to Matthew WilcoxJan Kara pointed out that calling ext2_xip_verify_sb() in ext2_remount() doesn't make sense, since changing the XIP option on remount isn't allowed. It also doesn't make sense to re-check whether blocksize is supported since it can't change between mounts.
Replace the call to ext2_xip_verify_sb() in ext2_fill_super() with the equivalent check and delete the definition.
--- fs/ext2/super.c | 33 ++++++++++++--------------------- fs/ext2/xip.c | 12 ------------ fs/ext2/xip.h | 2 -- 3 files changed, 12 insertions(+), 35 deletions(-)
fs/ext2/super.c +12 -21
@@ -868,9 +868,6 @@ static int ext2_fill_super(struct super_block *sb, void *data, int silent) ((EXT2_SB (sb)->s_mount_opt &EXT2_MOUNT_POSIX_ACL ) ?MS_POSIXACL :0 ); -ext2_xip_verify_sb (sb);/* see if bdev supports xip, unset -EXT2_MOUNT_XIP if not */ - if (le32_to_cpu(es->s_rev_level) == EXT2_GOOD_OLD_REV && (EXT2_HAS_COMPAT_FEATURE(sb, ~0U) || EXT2_HAS_RO_COMPAT_FEATURE(sb, ~0U) ||
@@ -900,11 +897,17 @@ static int ext2_fill_super(struct super_block *sb, void *data, int silent) blocksize =BLOCK_SIZE <<le32_to_cpu (sbi->s_es->s_log_block_size); -if (ext2_use_xip (sb) && blocksize !=PAGE_SIZE ) { -if (!silent) +if (sbi->s_mount_opt &EXT2_MOUNT_XIP ) { +if (blocksize !=PAGE_SIZE ) {ext2_msg (sb,KERN_ERR , -"error: unsupported blocksize for xip" ); - goto failed_mount; +"error: unsupported blocksize for xip" ); + goto failed_mount; + } +if (!sb->s_bdev->bd_disk->fops->direct_access) { +ext2_msg (sb,KERN_ERR , +"error: device does not support xip" ); + goto failed_mount; + } }/* If the blocksize doesn't match, re-read the thing.. */
@@ -1249,7 +1252,6 @@ static int ext2_remount (struct super_block * sb, int * flags, char * data) {struct ext2_sb_info * sbi =EXT2_SB (sb);struct ext2_super_block * es; -unsigned long old_mount_opt = sbi->s_mount_opt;struct ext2_mount_options old_opts;unsigned long old_sb_flags;int err;
@@ -1274,22 +1276,11 @@ static int ext2_remount (struct super_block * sb, int * flags, char * data) sb->s_flags = (sb->s_flags & ~MS_POSIXACL ) | ((sbi->s_mount_opt &EXT2_MOUNT_POSIX_ACL ) ?MS_POSIXACL :0 ); -ext2_xip_verify_sb (sb);/* see if bdev supports xip, unset -EXT2_MOUNT_XIP if not */ - -if ((ext2_use_xip (sb)) && (sb->s_blocksize !=PAGE_SIZE )) { -ext2_msg (sb,KERN_WARNING , -"warning: unsupported blocksize for xip" ); - err = -EINVAL ; - goto restore_opts; - } - es = sbi->s_es; -if ((sbi->s_mount_opt ^ old_mount_opt) &EXT2_MOUNT_XIP ) { +if ((sbi->s_mount_opt ^ old_opts.s_mount_opt) &EXT2_MOUNT_XIP ) {ext2_msg (sb,KERN_WARNING ,"warning: refusing change of " "xip flag with busy inodes while remounting" ); - sbi->s_mount_opt &= ~EXT2_MOUNT_XIP ; - sbi->s_mount_opt |= old_mount_opt &EXT2_MOUNT_XIP ; + sbi->s_mount_opt ^=EXT2_MOUNT_XIP ; }if ((*flags &MS_RDONLY ) == (sb->s_flags &MS_RDONLY )) {spin_unlock (&sbi->s_lock);
fs/ext2/xip.c +0 -12
@@ -13,15 +13,3 @@#include "ext2.h" #include "xip.h" -void ext2_xip_verify_sb (struct super_block *sb) -{ -struct ext2_sb_info *sbi =EXT2_SB (sb); - -if ((sbi->s_mount_opt &EXT2_MOUNT_XIP ) && - !sb->s_bdev->bd_disk->fops->direct_access) { - sbi->s_mount_opt &= (~EXT2_MOUNT_XIP ); -ext2_msg (sb,KERN_WARNING , -"warning: ignoring xip option - " -"not supported by bdev" ); - } -}
fs/ext2/xip.h +0 -2
@@ -6,13 +6,11 @@*/ #ifdef CONFIG_EXT2_FS_XIP -extern void ext2_xip_verify_sb (struct super_block *);static inline int ext2_use_xip (struct super_block *sb) {struct ext2_sb_info *sbi =EXT2_SB (sb);return (sbi->s_mount_opt &EXT2_MOUNT_XIP ); }#else -#define ext2_xip_verify_sb (sb) do { } while (0)#define ext2_use_xip (sb) 0#endif
Signature · 7 lines
2.0.1 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v9 14/22 ext2: Remove ext2_use_xip
In reply to Matthew WilcoxReplace ext2_use_xip() with test_opt(XIP) which expands to the same code
--- fs/ext2/ext2.h | 4 ++++ fs/ext2/inode.c | 2 +- fs/ext2/namei.c | 4 ++-- 3 files changed, 7 insertions(+), 3 deletions(-)
fs/ext2/ext2.h +4 -0
@@ -380,7 +380,11 @@ struct ext2_inode {#define EXT2_MOUNT_NO_UID32 0x000200/* Disable 32-bit UIDs */ #define EXT2_MOUNT_XATTR_USER 0x004000/* Extended user attributes */ #define EXT2_MOUNT_POSIX_ACL 0x008000/* POSIX Access Control Lists */ +#ifdef CONFIG_FS_XIP #define EXT2_MOUNT_XIP 0x010000/* Execute in place */ +#else +#define EXT2_MOUNT_XIP 0 +#endif #define EXT2_MOUNT_USRQUOTA 0x020000/* user quota */ #define EXT2_MOUNT_GRPQUOTA 0x040000/* group quota */ #define EXT2_MOUNT_RESERVATION 0x080000/* Preallocation */
fs/ext2/inode.c +1 -1
@@ -1394,7 +1394,7 @@ struct inode *ext2_iget (struct super_block *sb, unsigned long ino)if (S_ISREG (inode->i_mode)) { inode->i_op = &ext2_file_inode_operations; -if (ext2_use_xip (inode->i_sb)) { +if (test_opt (inode->i_sb,XIP )) { inode->i_mapping->a_ops = &ext2_aops_xip; inode->i_fop = &ext2_xip_file_operations; }else if (test_opt (inode->i_sb,NOBH )) {
fs/ext2/namei.c +2 -2
@@ -105,7 +105,7 @@ static int ext2_create (struct inode * dir, struct dentry * dentry, umode_t modereturn PTR_ERR (inode); inode->i_op = &ext2_file_inode_operations; -if (ext2_use_xip (inode->i_sb)) { +if (test_opt (inode->i_sb,XIP )) { inode->i_mapping->a_ops = &ext2_aops_xip; inode->i_fop = &ext2_xip_file_operations; }else if (test_opt (inode->i_sb,NOBH )) {
@@ -126,7 +126,7 @@ static int ext2_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode)return PTR_ERR (inode); inode->i_op = &ext2_file_inode_operations; -if (ext2_use_xip (inode->i_sb)) { +if (test_opt (inode->i_sb,XIP )) { inode->i_mapping->a_ops = &ext2_aops_xip; inode->i_fop = &ext2_xip_file_operations; }else if (test_opt (inode->i_sb,NOBH )) {
Signature · 7 lines
2.0.1 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v9 15/22 ext2: Remove xip.c and xip.h
In reply to Matthew WilcoxThese files are now empty, so delete them
--- fs/ext2/Makefile | 1 - fs/ext2/inode.c | 1 - fs/ext2/namei.c | 1 - fs/ext2/super.c | 1 - fs/ext2/xip.c | 15 --------------- fs/ext2/xip.h | 16 ---------------- 6 files changed, 35 deletions(-) delete mode 100644 fs/ext2/xip.c delete mode 100644 fs/ext2/xip.h
fs/ext2/Makefile +0 -1
@@ -10,4 +10,3 @@ ext2-y := balloc.o dir.o file.o ialloc.o inode.o \ ext2-$(CONFIG_EXT2_FS_XATTR) += xattr.o xattr_user.o xattr_trusted.o ext2-$(CONFIG_EXT2_FS_POSIX_ACL) += acl.o ext2-$(CONFIG_EXT2_FS_SECURITY) += xattr_security.o -ext2-$(CONFIG_EXT2_FS_XIP) += xip.o
fs/ext2/inode.c +0 -1
@@ -34,7 +34,6 @@#include <linux/aio.h> #include "ext2.h" #include "acl.h" -#include "xip.h" #include "xattr.h" static int __ext2_write_inode (struct inode *inode,int do_sync);
fs/ext2/namei.c +0 -1
@@ -35,7 +35,6 @@#include "ext2.h" #include "xattr.h" #include "acl.h" -#include "xip.h" static inline int ext2_add_nondir (struct dentry *dentry,struct inode *inode) {
fs/ext2/super.c +0 -1
@@ -35,7 +35,6 @@#include "ext2.h" #include "xattr.h" #include "acl.h" -#include "xip.h" static void ext2_sync_super (struct super_block *sb,struct ext2_super_block *es,int wait);
fs/ext2/xip.c deleted +0 -15
@@ -1,15 +0,0 @@ -/* -* linux/fs/ext2/xip.c -* -* Copyright (C) 2005 IBM Corporation -* Author: Carsten Otte ([email protected]) -*/ - -#include <linux/mm.h> -#include <linux/fs.h> -#include <linux/genhd.h> -#include <linux/buffer_head.h> -#include <linux/blkdev.h> -#include "ext2.h" -#include "xip.h" -
fs/ext2/xip.h deleted +0 -16
@@ -1,16 +0,0 @@ -/* -* linux/fs/ext2/xip.h -* -* Copyright (C) 2005 IBM Corporation -* Author: Carsten Otte ([email protected]) -*/ - -#ifdef CONFIG_EXT2_FS_XIP -static inline int ext2_use_xip (struct super_block *sb) -{ -struct ext2_sb_info *sbi =EXT2_SB (sb); -return (sbi->s_mount_opt &EXT2_MOUNT_XIP ); -} -#else -#define ext2_use_xip (sb) 0 -#endif
Signature · 7 lines
2.0.1 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v9 16/22 Remove CONFIG_EXT2_FS_XIP and rename CONFIG_FS_XIP to CONFIG_FS_DAX
In reply to Matthew WilcoxThe fewer Kconfig options we have the better. Use the generic CONFIG_FS_DAX to enable XIP support in ext2 as well as in the core.
--- fs/Kconfig | 21 ++++++++++++++------- fs/Makefile | 2 +- fs/ext2/Kconfig | 11 ----------- fs/ext2/ext2.h | 2 +- fs/ext2/file.c | 4 ++-- fs/ext2/super.c | 4 ++-- include/linux/fs.h | 4 ++-- 7 files changed, 22 insertions(+), 26 deletions(-)
fs/Kconfig +14 -7
@@ -13,13 +13,6 @@ if BLOCK source "fs/ext2/Kconfig" source "fs/ext3/Kconfig" source "fs/ext4/Kconfig" - -config FS_XIP -# execute in place - bool - depends on EXT2_FS_XIP - default y - source "fs/jbd/Kconfig" source "fs/jbd2/Kconfig"
@@ -40,6 +33,20 @@ source "fs/ocfs2/Kconfig" source "fs/btrfs/Kconfig" source "fs/nilfs2/Kconfig" +config FS_DAX + bool "Direct Access support" + depends on MMU + help + Direct Access (DAX) can be used on memory-backed block devices. + If the block device supports DAX and the filesystem supports DAX, + then you can avoid using the pagecache to buffer I/Os. Turning + on this option will compile in support for DAX; you will need to + mount the filesystem using the -o xip option. + + If you do not have a block device that is capable of using this, + or if unsure, say N. Saying Y will increase the size of the kernel + by about 2kB. + endif # BLOCK # Posix ACL utility routines
fs/Makefile +1 -1
@@ -28,7 +28,7 @@ obj-$(CONFIG_SIGNALFD) += signalfd.o obj-$(CONFIG_TIMERFD) += timerfd.o obj-$(CONFIG_EVENTFD) += eventfd.o obj-$(CONFIG_AIO) += aio.o -obj-$(CONFIG_FS_XIP) += dax.o +obj-$(CONFIG_FS_DAX) += dax.o obj-$(CONFIG_FILE_LOCKING) += locks.o obj-$(CONFIG_COMPAT) += compat.o compat_ioctl.o obj-$(CONFIG_BINFMT_AOUT) += binfmt_aout.o
fs/ext2/Kconfig +0 -11
@@ -42,14 +42,3 @@ config EXT2_FS_SECURITY If you are not using a security module that requires using extended attributes for file security labels, say N. - -config EXT2_FS_XIP - bool "Ext2 execute in place support" - depends on EXT2_FS && MMU - help - Execute in place can be used on memory-backed block devices. If you - enable this option, you can select to mount block devices which are - capable of this feature without using the page cache. - - If you do not use a block device that is capable of using this, - or if unsure, say N.
fs/ext2/ext2.h +1 -1
@@ -380,7 +380,7 @@ struct ext2_inode {#define EXT2_MOUNT_NO_UID32 0x000200/* Disable 32-bit UIDs */ #define EXT2_MOUNT_XATTR_USER 0x004000/* Extended user attributes */ #define EXT2_MOUNT_POSIX_ACL 0x008000/* POSIX Access Control Lists */ -#ifdef CONFIG_FS_XIP +#ifdef CONFIG_FS_DAX #define EXT2_MOUNT_XIP 0x010000/* Execute in place */ #else #define EXT2_MOUNT_XIP 0
fs/ext2/file.c +2 -2
@@ -25,7 +25,7 @@#include "xattr.h" #include "acl.h" -#ifdef CONFIG_EXT2_FS_XIP +#ifdef CONFIG_FS_DAX static int ext2_dax_fault (struct vm_area_struct *vma,struct vm_fault *vmf) {return dax_fault (vma, vmf, ext2_get_block);
@@ -109,7 +109,7 @@ const struct file_operations ext2_file_operations = { .splice_write = iter_file_splice_write, }; -#ifdef CONFIG_EXT2_FS_XIP +#ifdef CONFIG_FS_DAX const struct file_operations ext2_xip_file_operations = { .llseek = generic_file_llseek, .read = new_sync_read,
fs/ext2/super.c +2 -2
@@ -287,7 +287,7 @@ static int ext2_show_options(struct seq_file *seq, struct dentry *root)seq_puts (seq,",grpquota" );#endif -#if defined(CONFIG_EXT2_FS_XIP ) +#ifdef CONFIG_FS_DAX if (sbi->s_mount_opt &EXT2_MOUNT_XIP )seq_puts (seq,",xip" );#endif
@@ -549,7 +549,7 @@ static int parse_options(char *options, struct super_block *sb)break ;#endif case Opt_xip: -#ifdef CONFIG_EXT2_FS_XIP +#ifdef CONFIG_FS_DAX set_opt (sbi->s_mount_opt,XIP );#else ext2_msg (sb,KERN_INFO ,"xip option not supported" );
include/linux/fs.h +2 -2
@@ -1616,7 +1616,7 @@ struct super_operations {#define IS_IMA (inode) ((inode)->i_flags & S_IMA)#define IS_AUTOMOUNT (inode) ((inode)->i_flags & S_AUTOMOUNT)#define IS_NOSEC (inode) ((inode)->i_flags & S_NOSEC) -#ifdef CONFIG_FS_XIP +#ifdef CONFIG_FS_DAX #define IS_DAX (inode) ((inode)->i_flags & S_DAX)#else #define IS_DAX (inode) 0
@@ -2461,7 +2461,7 @@ extern loff_t fixed_size_llseek(struct file *file, loff_t offset,extern int generic_file_open (struct inode * inode,struct file * filp);extern int nonseekable_open (struct inode * inode,struct file * filp); -#ifdef CONFIG_FS_XIP +#ifdef CONFIG_FS_DAX int dax_clear_blocks (struct inode *,sector_t block,long size);int dax_truncate_page (struct inode *,loff_t from,get_block_t ); ssize_t dax_do_io(int rw, struct kiocb *, struct inode *, struct iov_iter *,
Signature · 7 lines
2.0.1 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v9 17/22 ext2: Remove ext2_aops_xip
In reply to Matthew WilcoxWe shouldn't need a special address_space_operations any more
--- fs/ext2/ext2.h | 1 - fs/ext2/inode.c | 7 +------ fs/ext2/namei.c | 4 ++-- 3 files changed, 3 insertions(+), 9 deletions(-)
fs/ext2/ext2.h +0 -1
@@ -793,7 +793,6 @@ extern const struct file_operations ext2_xip_file_operations;/* inode.c */ extern const struct address_space_operations ext2_aops; -extern const struct address_space_operations ext2_aops_xip;extern const struct address_space_operations ext2_nobh_aops;/* namei.c */
fs/ext2/inode.c +1 -6
@@ -891,11 +891,6 @@ const struct address_space_operations ext2_aops = { .error_remove_page = generic_error_remove_page, }; -const struct address_space_operations ext2_aops_xip = { - .bmap = ext2_bmap, - .direct_IO = ext2_direct_IO, -}; -const struct address_space_operations ext2_nobh_aops = { .readpage = ext2_readpage, .readpages = ext2_readpages,
@@ -1394,7 +1389,7 @@ struct inode *ext2_iget (struct super_block *sb, unsigned long ino)if (S_ISREG (inode->i_mode)) { inode->i_op = &ext2_file_inode_operations;if (test_opt (inode->i_sb,XIP )) { - inode->i_mapping->a_ops = &ext2_aops_xip; + inode->i_mapping->a_ops = &ext2_aops; inode->i_fop = &ext2_xip_file_operations; }else if (test_opt (inode->i_sb,NOBH )) { inode->i_mapping->a_ops = &ext2_nobh_aops;
fs/ext2/namei.c +2 -2
@@ -105,7 +105,7 @@ static int ext2_create (struct inode * dir, struct dentry * dentry, umode_t mode inode->i_op = &ext2_file_inode_operations;if (test_opt (inode->i_sb,XIP )) { - inode->i_mapping->a_ops = &ext2_aops_xip; + inode->i_mapping->a_ops = &ext2_aops; inode->i_fop = &ext2_xip_file_operations; }else if (test_opt (inode->i_sb,NOBH )) { inode->i_mapping->a_ops = &ext2_nobh_aops;
@@ -126,7 +126,7 @@ static int ext2_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode) inode->i_op = &ext2_file_inode_operations;if (test_opt (inode->i_sb,XIP )) { - inode->i_mapping->a_ops = &ext2_aops_xip; + inode->i_mapping->a_ops = &ext2_aops; inode->i_fop = &ext2_xip_file_operations; }else if (test_opt (inode->i_sb,NOBH )) { inode->i_mapping->a_ops = &ext2_nobh_aops;
Signature · 7 lines
2.0.1 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v9 18/22 Get rid of most mentions of XIP in ext2
In reply to Matthew WilcoxTo help people transition, accept the 'xip' mount option (and report it in /proc/mounts), but print a message encouraging people to switch over to the 'dax' option.
--- fs/ext2/ext2.h | 13 +++++++------ fs/ext2/file.c | 2 +- fs/ext2/inode.c | 6 +++--- fs/ext2/namei.c | 8 ++++---- fs/ext2/super.c | 25 ++++++++++++++++--------- 5 files changed, 31 insertions(+), 23 deletions(-)
fs/ext2/ext2.h +7 -6
@@ -380,14 +380,15 @@ struct ext2_inode {#define EXT2_MOUNT_NO_UID32 0x000200/* Disable 32-bit UIDs */ #define EXT2_MOUNT_XATTR_USER 0x004000/* Extended user attributes */ #define EXT2_MOUNT_POSIX_ACL 0x008000/* POSIX Access Control Lists */ -#ifdef CONFIG_FS_DAX -#define EXT2_MOUNT_XIP 0x010000/* Execute in place */ -#else -#define EXT2_MOUNT_XIP 0 -#endif +#define EXT2_MOUNT_XIP 0x010000/* Obsolete, use DAX */ #define EXT2_MOUNT_USRQUOTA 0x020000/* user quota */ #define EXT2_MOUNT_GRPQUOTA 0x040000/* group quota */ #define EXT2_MOUNT_RESERVATION 0x080000/* Preallocation */ +#ifdef CONFIG_FS_DAX +#define EXT2_MOUNT_DAX 0x100000/* Direct Access */ +#else +#define EXT2_MOUNT_DAX 0 +#endif #define clear_opt (o, opt) o &= ~EXT2_MOUNT_##opt
@@ -789,7 +790,7 @@ extern int ext2_fsync(struct file *file, loff_t start, loff_t end, int datasync);extern const struct inode_operations ext2_file_inode_operations;extern const struct file_operations ext2_file_operations; -extern const struct file_operations ext2_xip_file_operations; +extern const struct file_operations ext2_dax_file_operations;/* inode.c */ extern const struct address_space_operations ext2_aops;
fs/ext2/file.c +1 -1
@@ -110,7 +110,7 @@ const struct file_operations ext2_file_operations = { };#ifdef CONFIG_FS_DAX -const struct file_operations ext2_xip_file_operations = { +const struct file_operations ext2_dax_file_operations = { .llseek = generic_file_llseek, .read = new_sync_read, .write = new_sync_write,
fs/ext2/inode.c +3 -3
@@ -1286,7 +1286,7 @@ void ext2_set_inode_flags(struct inode *inode) inode->i_flags |=S_NOATIME ;if (flags &EXT2_DIRSYNC_FL ) inode->i_flags |=S_DIRSYNC ; -if (test_opt (inode->i_sb,XIP )) +if (test_opt (inode->i_sb,DAX )) inode->i_flags |=S_DAX ; }
@@ -1388,9 +1388,9 @@ struct inode *ext2_iget (struct super_block *sb, unsigned long ino)if (S_ISREG (inode->i_mode)) { inode->i_op = &ext2_file_inode_operations; -if (test_opt (inode->i_sb,XIP )) { +if (test_opt (inode->i_sb,DAX )) { inode->i_mapping->a_ops = &ext2_aops; - inode->i_fop = &ext2_xip_file_operations; + inode->i_fop = &ext2_dax_file_operations; }else if (test_opt (inode->i_sb,NOBH )) { inode->i_mapping->a_ops = &ext2_nobh_aops; inode->i_fop = &ext2_file_operations;
fs/ext2/namei.c +4 -4
@@ -104,9 +104,9 @@ static int ext2_create (struct inode * dir, struct dentry * dentry, umode_t modereturn PTR_ERR (inode); inode->i_op = &ext2_file_inode_operations; -if (test_opt (inode->i_sb,XIP )) { +if (test_opt (inode->i_sb,DAX )) { inode->i_mapping->a_ops = &ext2_aops; - inode->i_fop = &ext2_xip_file_operations; + inode->i_fop = &ext2_dax_file_operations; }else if (test_opt (inode->i_sb,NOBH )) { inode->i_mapping->a_ops = &ext2_nobh_aops; inode->i_fop = &ext2_file_operations;
@@ -125,9 +125,9 @@ static int ext2_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode)return PTR_ERR (inode); inode->i_op = &ext2_file_inode_operations; -if (test_opt (inode->i_sb,XIP )) { +if (test_opt (inode->i_sb,DAX )) { inode->i_mapping->a_ops = &ext2_aops; - inode->i_fop = &ext2_xip_file_operations; + inode->i_fop = &ext2_dax_file_operations; }else if (test_opt (inode->i_sb,NOBH )) { inode->i_mapping->a_ops = &ext2_nobh_aops; inode->i_fop = &ext2_file_operations;
fs/ext2/super.c +16 -9
@@ -290,6 +290,8 @@ static int ext2_show_options(struct seq_file *seq, struct dentry *root)#ifdef CONFIG_FS_DAX if (sbi->s_mount_opt &EXT2_MOUNT_XIP )seq_puts (seq,",xip" ); +if (sbi->s_mount_opt &EXT2_MOUNT_DAX ) +seq_puts (seq,",dax" );#endif if (!test_opt (sb,RESERVATION ))
@@ -393,7 +395,7 @@ enum { Opt_resgid, Opt_resuid, Opt_sb, Opt_err_cont, Opt_err_panic, Opt_err_ro, Opt_nouid32, Opt_nocheck, Opt_debug, Opt_oldalloc, Opt_orlov, Opt_nobh, Opt_user_xattr, Opt_nouser_xattr, - Opt_acl, Opt_noacl, Opt_xip, Opt_ignore, Opt_err, Opt_quota, + Opt_acl, Opt_noacl, Opt_xip, Opt_dax, Opt_ignore, Opt_err, Opt_quota, Opt_usrquota, Opt_grpquota, Opt_reservation, Opt_noreservation };
@@ -422,6 +424,7 @@ static const match_table_t tokens = { {Opt_acl, "acl"}, {Opt_noacl, "noacl"}, {Opt_xip, "xip"}, + {Opt_dax, "dax"}, {Opt_grpquota, "grpquota"}, {Opt_ignore, "noquota"}, {Opt_quota, "quota"},
@@ -549,10 +552,14 @@ static int parse_options(char *options, struct super_block *sb)break ;#endif case Opt_xip: +ext2_msg (sb,KERN_INFO ,"use dax instead of xip" ); +set_opt (sbi->s_mount_opt,XIP ); +/* Fall through */ +case Opt_dax:#ifdef CONFIG_FS_DAX -set_opt (sbi->s_mount_opt,XIP ); +set_opt (sbi->s_mount_opt,DAX );#else -ext2_msg (sb,KERN_INFO ,"xip option not supported" ); +ext2_msg (sb,KERN_INFO ,"dax option not supported" );#endif break ;
@@ -896,15 +903,15 @@ static int ext2_fill_super(struct super_block *sb, void *data, int silent) blocksize =BLOCK_SIZE <<le32_to_cpu (sbi->s_es->s_log_block_size); -if (sbi->s_mount_opt &EXT2_MOUNT_XIP ) { +if (sbi->s_mount_opt &EXT2_MOUNT_DAX ) {if (blocksize !=PAGE_SIZE ) {ext2_msg (sb,KERN_ERR , -"error: unsupported blocksize for xip" ); +"error: unsupported blocksize for dax" ); goto failed_mount; }if (!sb->s_bdev->bd_disk->fops->direct_access) {ext2_msg (sb,KERN_ERR , -"error: device does not support xip" ); +"error: device does not support dax" ); goto failed_mount; } }
@@ -1276,10 +1283,10 @@ static int ext2_remount (struct super_block * sb, int * flags, char * data) ((sbi->s_mount_opt &EXT2_MOUNT_POSIX_ACL ) ?MS_POSIXACL :0 ); es = sbi->s_es; -if ((sbi->s_mount_opt ^ old_opts.s_mount_opt) &EXT2_MOUNT_XIP ) { +if ((sbi->s_mount_opt ^ old_opts.s_mount_opt) &EXT2_MOUNT_DAX ) {ext2_msg (sb,KERN_WARNING ,"warning: refusing change of " -"xip flag with busy inodes while remounting" ); - sbi->s_mount_opt ^=EXT2_MOUNT_XIP ; +"dax flag with busy inodes while remounting" ); + sbi->s_mount_opt ^=EXT2_MOUNT_DAX ; }if ((*flags &MS_RDONLY ) == (sb->s_flags &MS_RDONLY )) {spin_unlock (&sbi->s_lock);
Signature · 7 lines
2.0.1 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v9 19/22 xip: Add xip_zero_page_range
In reply to Matthew WilcoxThis new function allows us to support hole-punch for XIP files by zeroing a partial page, as opposed to the xip_truncate_page() function which can only truncate to the end of the page. Reimplement xip_truncate_page() as a macro that calls xip_zero_page_range().
--- Documentation/filesystems/dax.txt | 1 + fs/dax.c | 20 ++++++++++++++------ include/linux/fs.h | 9 ++++++++- 3 files changed, 23 insertions(+), 7 deletions(-)
Documentation/filesystems/dax.txt +1 -0
@@ -62,6 +62,7 @@ Filesystem support consists of for fault and page_mkwrite (which should probably call dax_fault() and dax_mkwrite(), passing the appropriate get_block() callback) - calling dax_truncate_page() instead of block_truncate_page() for DAX files +- calling dax_zero_page_range() instead of zero_user() for DAX files - ensuring that there is sufficient locking between reads, writes, truncates and page faults
fs/dax.c +14 -6
@@ -424,13 +424,16 @@ int dax_fault(struct vm_area_struct *vma, struct vm_fault *vmf,EXPORT_SYMBOL_GPL (dax_fault);/** -* dax_truncate_page - handle a partial page being truncated in a DAX file +* dax_zero_page_range - zero a range within a page of a DAX file * @inode: The file being truncated * @from: The file offset that is being truncated to +* @length: The number of bytes to zero * @get_block: The filesystem method used to translate file offsets to blocks * -* Similar to block_truncate_page(), this function can be called by a -* filesystem when it is truncating an DAX file to handle the partial page. +* This function can be called by a filesystem when it is zeroing part of a +* page in a DAX file. This is intended for hole-punch operations. If +* you are truncating a file, the helper function dax_truncate_page() may be +* more convenient. * * We work in terms of PAGE_CACHE_SIZE here for commonality with * block_truncate_page(), but we could go down to PAGE_SIZE if the filesystem
@@ -438,12 +441,12 @@ EXPORT_SYMBOL_GPL(dax_fault);* block size is smaller than PAGE_SIZE, we have to zero the rest of the page * since the file might be mmaped. */ -int dax_truncate_page (struct inode *inode,loff_t from,get_block_t get_block) +int dax_zero_page_range (struct inode *inode,loff_t from,unsigned length, +get_block_t get_block) {struct buffer_head bh;pgoff_t index = from >>PAGE_CACHE_SHIFT ;unsigned offset = from & (PAGE_CACHE_SIZE -1 ); -unsigned length =PAGE_CACHE_ALIGN (from) - from;int err;/* Block boundary? Nothing to do */
@@ -460,9 +463,14 @@ int dax_truncate_page(struct inode *inode, loff_t from, get_block_t get_block) err =dax_get_addr (&bh, &addr, inode->i_blkbits);if (err <0 )return err; +/* +* ext4 sometimes asks to zero past the end of a block. It +* really just wants to zero to the end of the block. +*/ + length =min_t (unsigned, length,PAGE_CACHE_SIZE - offset);memset (addr + offset,0 , length); }return 0 ; } -EXPORT_SYMBOL_GPL (dax_truncate_page); +EXPORT_SYMBOL_GPL (dax_zero_page_range);
include/linux/fs.h +8 -1
@@ -2463,6 +2463,7 @@ extern int nonseekable_open(struct inode * inode, struct file * filp);#ifdef CONFIG_FS_DAX int dax_clear_blocks (struct inode *,sector_t block,long size); +int dax_zero_page_range (struct inode *,loff_t from,unsigned len,get_block_t );int dax_truncate_page (struct inode *,loff_t from,get_block_t );ssize_t dax_do_io (int rw,struct kiocb *,struct inode *,struct iov_iter *,loff_t ,get_block_t ,dio_iodone_t ,int flags);
@@ -2474,7 +2475,8 @@ static inline int dax_clear_blocks(struct inode *i, sector_t blk, long sz)return 0 ; } -static inline int dax_truncate_page (struct inode *i,loff_t frm,get_block_t gb) +static inline int dax_zero_page_range (struct inode *inode,loff_t from, +unsigned len,get_block_t gb) {return 0 ; }
@@ -2487,6 +2489,11 @@ static inline ssize_t dax_do_io(int rw, struct kiocb *iocb, struct inode *inode, }#endif +/* Can't be a function because PAGE_CACHE_SIZE is defined in pagemap.h */ +#define dax_truncate_page (inode, from, get_block) \ + dax_zero_page_range(inode, from, PAGE_CACHE_SIZE, get_block) + +#ifdef CONFIG_BLOCK typedef void (dio_submit_t )(int rw,struct bio *bio,struct inode *inode,loff_t file_offset);
Signature · 7 lines
2.0.1 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v9 20/22 ext4: Avoid lock inversion between i_mmap_mutex and transaction start
In reply to Matthew WilcoxFrom: Jan Kara <[email protected]>
When DAX is enabled, it uses i_mmap_mutex as a protection against truncate during page fault. This inevitably forces i_mmap_mutex to rank outside of a transaction start and thus we have to avoid calling pagecache purging operations when transaction is started.
--- fs/ext4/inode.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-)
fs/ext4/inode.c +10 -4
@@ -3631,13 +3631,19 @@ int ext4_punch_hole(struct inode *inode, loff_t offset, loff_t length)if (IS_SYNC (inode))ext4_handle_sync (handle); -/* Now release the pages again to reduce race window */ + inode->i_mtime = inode->i_ctime =ext4_current_time (inode); +ext4_mark_inode_dirty (handle, inode); +ext4_journal_stop (handle); + +/* +* Now release the pages again to reduce race window. This has to happen +* outside of a transaction to avoid lock inversion on i_mmap_mutex +* when DAX is enabled. +*/ if (last_block_offset > first_block_offset)truncate_pagecache_range (inode, first_block_offset, last_block_offset); - - inode->i_mtime = inode->i_ctime =ext4_current_time (inode); -ext4_mark_inode_dirty (handle, inode); + goto out_dio; out_stop:ext4_journal_stop (handle); out_dio:
Signature · 7 lines
2.0.1 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v9 21/22 ext4: Add DAX functionality
In reply to Matthew WilcoxFrom: Ross Zwisler <[email protected]>
This is a port of the DAX functionality found in the current version of ext2.
--- Documentation/filesystems/dax.txt | 1 + Documentation/filesystems/ext4.txt | 2 ++ fs/ext4/ext4.h | 6 +++++ fs/ext4/file.c | 53 +++++++++++++++++++++++++++++++++----- fs/ext4/indirect.c | 18 +++++++++---- fs/ext4/inode.c | 51 +++++++++++++++++++++++------------- fs/ext4/namei.c | 10 +++++-- fs/ext4/super.c | 39 +++++++++++++++++++++++++++- 8 files changed, 148 insertions(+), 32 deletions(-)
Documentation/filesystems/dax.txt +1 -0
@@ -73,6 +73,7 @@ or a write()) work correctly. These filesystems may be used for inspiration: - ext2: the second extended filesystem, see Documentation/filesystems/ext2.txt +- ext4: the fourth extended filesystem, see Documentation/filesystems/ext4.txt Shortcomings
Documentation/filesystems/ext4.txt +2 -0
@@ -386,6 +386,8 @@ max_dir_size_kb=n This limits the size of directories so that any i_version Enable 64-bit inode version support. This option is off by default. +dax Use direct access if possible + Data Mode ========= There are 3 different data modes:
fs/ext4/ext4.h +6 -0
@@ -970,6 +970,11 @@ struct ext4_inode_info {#define EXT4_MOUNT_ERRORS_MASK 0x00070#define EXT4_MOUNT_MINIX_DF 0x00080/* Mimics the Minix statfs */ #define EXT4_MOUNT_NOLOAD 0x00100/* Don't use existing journal*/ +#ifdef CONFIG_FS_DAX +#define EXT4_MOUNT_DAX 0x00200/* Execute in place */ +#else +#define EXT4_MOUNT_DAX 0 +#endif #define EXT4_MOUNT_DATA_FLAGS 0x00C00/* Mode for data writes: */ #define EXT4_MOUNT_JOURNAL_DATA 0x00400/* Write data to journal */ #define EXT4_MOUNT_ORDERED_DATA 0x00800/* Flush data before commit */
@@ -2557,6 +2562,7 @@ extern const struct file_operations ext4_dir_operations;/* file.c */ extern const struct inode_operations ext4_file_inode_operations;extern const struct file_operations ext4_file_operations; +extern const struct file_operations ext4_dax_file_operations;extern loff_t ext4_llseek (struct file *file,loff_t offset,int origin);/* inline.c */
fs/ext4/file.c +47 -6
78 lines not on this page · raw message
fs/ext4/indirect.c +13 -5
@@ -691,14 +691,22 @@ retry:inode_dio_done (inode); goto locked; } - ret =__blockdev_direct_IO (rw, iocb, inode, - inode->i_sb->s_bdev, iter, offset, - ext4_get_block,NULL ,NULL ,0 ); +if (IS_DAX (inode)) + ret =dax_do_io (rw, iocb, inode, iter, offset, + ext4_get_block,NULL ,0 ); +else + ret =__blockdev_direct_IO (rw, iocb, inode, + inode->i_sb->s_bdev, iter, offset, + ext4_get_block,NULL ,NULL ,0 );inode_dio_done (inode); }else { locked: - ret =blockdev_direct_IO (rw, iocb, inode, iter, - offset, ext4_get_block); +if (IS_DAX (inode)) + ret =dax_do_io (rw, iocb, inode, iter, offset, + ext4_get_block,NULL ,DIO_LOCKING ); +else + ret =blockdev_direct_IO (rw, iocb, inode, iter, + offset, ext4_get_block);if (unlikely ((rw &WRITE ) && ret <0 )) {loff_t isize =i_size_read (inode);
fs/ext4/inode.c +33 -18
88 lines not on this page · raw message
fs/ext4/namei.c +8 -2
@@ -2251,7 +2251,10 @@ retry: err =PTR_ERR (inode);if (!IS_ERR (inode)) { inode->i_op = &ext4_file_inode_operations; - inode->i_fop = &ext4_file_operations; +if (test_opt (inode->i_sb,DAX )) + inode->i_fop = &ext4_dax_file_operations; +else + inode->i_fop = &ext4_file_operations;ext4_set_aops (inode); err =ext4_add_nondir (handle, dentry, inode);if (!err &&IS_DIRSYNC (dir))
@@ -2315,7 +2318,10 @@ retry: err =PTR_ERR (inode);if (!IS_ERR (inode)) { inode->i_op = &ext4_file_inode_operations; - inode->i_fop = &ext4_file_operations; +if (test_opt (inode->i_sb,DAX )) + inode->i_fop = &ext4_dax_file_operations; +else + inode->i_fop = &ext4_file_operations;ext4_set_aops (inode);d_tmpfile (dentry, inode); err =ext4_orphan_add (handle, inode);
fs/ext4/super.c +38 -1
@@ -1162,7 +1162,7 @@ enum { Opt_usrjquota, Opt_grpjquota, Opt_offusrjquota, Opt_offgrpjquota, Opt_jqfmt_vfsold, Opt_jqfmt_vfsv0, Opt_jqfmt_vfsv1, Opt_quota, Opt_noquota, Opt_barrier, Opt_nobarrier, Opt_err, - Opt_usrquota, Opt_grpquota, Opt_i_version, + Opt_usrquota, Opt_grpquota, Opt_i_version, Opt_dax, Opt_stripe, Opt_delalloc, Opt_nodelalloc, Opt_mblk_io_submit, Opt_nomblk_io_submit, Opt_block_validity, Opt_noblock_validity, Opt_inode_readahead_blks, Opt_journal_ioprio,
@@ -1224,6 +1224,7 @@ static const match_table_t tokens = { {Opt_barrier, "barrier"}, {Opt_nobarrier, "nobarrier"}, {Opt_i_version, "i_version"}, + {Opt_dax, "dax"}, {Opt_stripe, "stripe=%u"}, {Opt_delalloc, "delalloc"}, {Opt_nodelalloc, "nodelalloc"},
@@ -1406,6 +1407,7 @@ static const struct mount_opts { {Opt_min_batch_time, 0, MOPT_GTE0}, {Opt_inode_readahead_blks, 0, MOPT_GTE0}, {Opt_init_itable, 0, MOPT_GTE0}, + {Opt_dax, EXT4_MOUNT_DAX, MOPT_SET}, {Opt_stripe, 0, MOPT_GTE0}, {Opt_resuid, 0, MOPT_GTE0}, {Opt_resgid, 0, MOPT_GTE0},
@@ -1642,6 +1644,11 @@ static int handle_mount_opt(struct super_block *sb, char *opt, int token, } sbi->s_jquota_fmt = m->mount_opt;#endif +#ifndef CONFIG_FS_DAX + }else if (token == Opt_dax) { +ext4_msg (sb,KERN_INFO ,"dax option not supported" ); +return -1 ; +#endif }else {if (!args->from) arg =1 ;
@@ -3575,6 +3582,11 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) "both data=journal and dioread_nolock"); goto failed_mount; } +if (test_opt (sb,DAX )) { +ext4_msg (sb,KERN_ERR ,"can't mount with " +"both data=journal and dax" ); + goto failed_mount; + }if (test_opt (sb,DELALLOC ))clear_opt (sb,DELALLOC ); }
@@ -3638,6 +3650,19 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) goto failed_mount; } +if (sbi->s_mount_opt &EXT4_MOUNT_DAX ) { +if (blocksize !=PAGE_SIZE ) { +ext4_msg (sb,KERN_ERR , +"error: unsupported blocksize for dax" ); + goto failed_mount; + } +if (!sb->s_bdev->bd_disk->fops->direct_access) { +ext4_msg (sb,KERN_ERR , +"error: device does not support dax" ); + goto failed_mount; + } + } +if (sb->s_blocksize != blocksize) {/* Validate the filesystem blocksize */ if (!sb_set_blocksize (sb, blocksize)) {
@@ -4846,6 +4871,18 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data) err = -EINVAL ; goto restore_opts; } +if (test_opt (sb,DAX )) { +ext4_msg (sb,KERN_ERR ,"can't mount with " +"both data=journal and dax" ); + err = -EINVAL ; + goto restore_opts; + } + } + +if ((sbi->s_mount_opt ^ old_opts.s_mount_opt) &EXT4_MOUNT_DAX ) { +ext4_msg (sb,KERN_WARNING ,"warning: refusing change of " +"dax flag with busy inodes while remounting" ); + sbi->s_mount_opt ^=EXT4_MOUNT_DAX ; }if (sbi->s_mount_flags &EXT4_MF_FS_ABORTED )
Signature · 7 lines
2.0.1 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v9 22/22 brd: Rename XIP to DAX
In reply to Matthew WilcoxFrom: Matthew Wilcox <[email protected]>
Since this is relating to FS_XIP, not KERNEL_XIP, it should be called DAX instead of XIP.
--- drivers/block/Kconfig | 13 +++++++------ drivers/block/brd.c | 14 +++++++------- fs/Kconfig | 4 ++-- 3 files changed, 16 insertions(+), 15 deletions(-)
drivers/block/Kconfig +7 -6
@@ -393,14 +393,15 @@ config BLK_DEV_RAM_SIZE The default value is 4096 kilobytes. Only change this if you know what you are doing. -config BLK_DEV_XIP - bool "Support XIP filesystems on RAM block device" - depends on BLK_DEV_RAM +config BLK_DEV_RAM_DAX + bool "Support Direct Access (DAX) to RAM block devices" + depends on BLK_DEV_RAM && FS_DAX default n help - Support XIP filesystems (such as ext2 with XIP support on) on - top of block ram device. This will slightly enlarge the kernel, and - will prevent RAM block device backing store memory from being + Support filesystems using DAX to access RAM block devices. This + avoids double-buffering data in the page cache before copying it + to the block device. Answering Y will slightly enlarge the kernel, + and will prevent RAM block device backing store memory from being allocated from highmem (only a problem for highmem systems). config CDROM_PKTCDVD
drivers/block/brd.c +7 -7
@@ -97,13 +97,13 @@ static struct page *brd_insert_page(struct brd_device *brd, sector_t sector)* Must use NOIO because we don't want to recurse back into the * block or filesystem layers from page reclaim. * -* Cannot support XIP and highmem, because our ->direct_access -* routine for XIP must return memory that is always addressable. -* If XIP was reworked to use pfns and kmap throughout, this +* Cannot support DAX and highmem, because our ->direct_access +* routine for DAX must return memory that is always addressable. +* If DAX was reworked to use pfns and kmap throughout, this * restriction might be able to be lifted. */ gfp_flags =GFP_NOIO | __GFP_ZERO; -#ifndef CONFIG_BLK_DEV_XIP +#ifndef CONFIG_BLK_DEV_RAM_DAX gfp_flags |= __GFP_HIGHMEM;#endif page =alloc_page (gfp_flags);
@@ -369,7 +369,7 @@ static int brd_rw_page(struct block_device *bdev, sector_t sector,return err; } -#ifdef CONFIG_BLK_DEV_XIP +#ifdef CONFIG_BLK_DEV_RAM_DAX static long brd_direct_access (struct block_device *bdev,sector_t sector,void **kaddr,unsigned long *pfn,long size) {
@@ -388,6 +388,8 @@ static long brd_direct_access(struct block_device *bdev, sector_t sector,* file is mapped to the next page of physical RAM */ return min_t (long,PAGE_SIZE , size); } +#else +#define brd_direct_access NULL#endif static int brd_ioctl(struct block_device *bdev, fmode_t mode,
@@ -428,9 +430,7 @@ static const struct block_device_operations brd_fops = { .owner = THIS_MODULE, .rw_page = brd_rw_page, .ioctl = brd_ioctl, -#ifdef CONFIG_BLK_DEV_XIP .direct_access = brd_direct_access, -#endif };/*
fs/Kconfig +2 -2
@@ -34,7 +34,7 @@ source "fs/btrfs/Kconfig" source "fs/nilfs2/Kconfig" config FS_DAX - bool "Direct Access support" + bool "Direct Access (DAX) support" depends on MMU help Direct Access (DAX) can be used on memory-backed block devices.
@@ -45,7 +45,7 @@ config FS_DAX If you do not have a block device that is capable of using this, or if unsure, say N. Saying Y will increase the size of the kernel - by about 2kB. + by about 5kB. endif # BLOCK
Signature · 7 lines
2.0.1 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Quoted 91 lines · Matthew Wilcox
From: Matthew Wilcox <[email protected]>
One of the primary uses for NV-DIMMs is to expose them as a block device and use a filesystem to store files on the NV-DIMM. While that works, it currently wastes memory and CPU time buffering the files in the page cache. We have support in ext2 for bypassing the page cache, but it has some races which are unfixable in the current design. This series of patches rewrite the underlying support, and add support for direct access to ext4.
This iteration of the patchset rebases to 3.16-rc7 and makes substantial changes based on feedback from Jan Kara, Boaz Harrosh and Kirill Shutemov:
- Fixes a double-unlock on
i_mmap_mutex
- Switch the order of callingdelete_from_page_cache()andunmap_mapping_range()to match the truncate path
- Makedax_mkwritea macro (Kirill)
- Dropvm_replace_mixed(); instead callunmap_mapping_range()before callingvm_insert_mixed()(Kirill)
- Avoid lock inversion betweeni_mmap_mutexand transaction start (Jan)
- Move alignment & length checks intobdev_direct_access()(Boaz)
- Fix bugs in COW code; unfortunately this means reintroducing the knowledge that thei_mmap_mutexprotects PFNs to the core MM code.Jan Kara (1):
ext4: Avoid lock inversion betweeni_mmap_mutexand transaction startMatthew Wilcox (20):
axonram: Fix bug indirect_access
Changedirect_accesscalling convention
Fix XIP fault vs truncate race
Allow page fault handlers to perform the COW
IntroduceIS_DAX(inode)
Addcopy_to_iter(),copy_from_iter()andiov_iter_zero()Replace XIP read and write with DAX I/O
Replaceext2_clear_xip_targetwithdax_clear_blocksReplace the XIP page fault handler with the DAX page fault handler Replacexip_truncate_pagewithdax_truncate_page
Replace XIP documentation with DAX documentation
Removeget_xip_mem
ext2: Removeext2_xip_verify_sb()
ext2: Removeext2_use_xip
ext2: Removexip.candxip.h
RemoveCONFIG_EXT2_FS_XIPand renameCONFIG_FS_XIPtoCONFIG_FS_DAX
ext2: Removeext2_aops_xip
Get rid of most mentions of XIP in ext2
xip: Addxip_zero_page_range
brd: Rename XIP to DAXRoss Zwisler (1):
ext4: Add DAX functionalityDocumentation/filesystems/Locking | 3 - Documentation/filesystems/dax.txt | 91 +++++++ Documentation/filesystems/ext4.txt | 2 + Documentation/filesystems/xip.txt | 68 ------ arch/powerpc/sysdev/axonram.c | 19 +- drivers/block/Kconfig | 13 +- drivers/block/brd.c | 26 +- drivers/s390/block/dcssblk.c | 21 +- fs/Kconfig | 21 +- fs/Makefile | 1 + fs/block_dev.c | 34 +++ fs/dax.c | 476 ++++++++++++++++++++++++++++++++++++ fs/exofs/inode.c | 1 - fs/ext2/Kconfig | 11 - fs/ext2/Makefile | 1 - fs/ext2/ext2.h | 10 +- fs/ext2/file.c | 45 +++- fs/ext2/inode.c | 38 +-- fs/ext2/namei.c | 13 +- fs/ext2/super.c | 53 ++-- fs/ext2/xip.c | 91 ------- fs/ext2/xip.h | 26 -- fs/ext4/ext4.h | 6 + fs/ext4/file.c | 53 +++- fs/ext4/indirect.c | 18 +- fs/ext4/inode.c | 65 +++-- fs/ext4/namei.c | 10 +- fs/ext4/super.c | 39 ++- fs/open.c | 5 +- include/linux/blkdev.h | 6 +- include/linux/fs.h | 49 +++- include/linux/mm.h | 1 + include/linux/uio.h | 3 + mm/Makefile | 1 - mm/fadvise.c | 6 +- mm/filemap.c | 6 +- mm/filemap_xip.c | 483 ------------------------------------- mm/iov_iter.c | 237 ++++++++++++++++-- mm/madvise.c | 2 +-
Quoted 8 lines
mm/memory.c | 33 ++- 40 files changed, 1206 insertions(+), 881 deletions(-) create mode 100644 Documentation/filesystems/dax.txt delete mode 100644 Documentation/filesystems/xip.txt create mode 100644 fs/dax.c delete mode 100644 fs/ext2/xip.c delete mode 100644 fs/ext2/xip.h delete mode 100644 mm/filemap_xip.c
I've updated the master branch of PRD's GitHub repo
(https://github.com/01org/prd) so that it is Linus's tip + DAX v9 + PRD.
I've also added a patch to PRD to enable dynamic allocation of partition numbers.
- Ross