cregit-Linux how code gets into the kernel

Release 4.11 fs/xfs/xfs_file.c

Directory: fs/xfs
/*
 * Copyright (c) 2000-2005 Silicon Graphics, Inc.
 * All Rights Reserved.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it would be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write the Free Software Foundation,
 * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */
#include "xfs.h"
#include "xfs_fs.h"
#include "xfs_shared.h"
#include "xfs_format.h"
#include "xfs_log_format.h"
#include "xfs_trans_resv.h"
#include "xfs_mount.h"
#include "xfs_da_format.h"
#include "xfs_da_btree.h"
#include "xfs_inode.h"
#include "xfs_trans.h"
#include "xfs_inode_item.h"
#include "xfs_bmap.h"
#include "xfs_bmap_util.h"
#include "xfs_error.h"
#include "xfs_dir2.h"
#include "xfs_dir2_priv.h"
#include "xfs_ioctl.h"
#include "xfs_trace.h"
#include "xfs_log.h"
#include "xfs_icache.h"
#include "xfs_pnfs.h"
#include "xfs_iomap.h"
#include "xfs_reflink.h"

#include <linux/dcache.h>
#include <linux/falloc.h>
#include <linux/pagevec.h>
#include <linux/backing-dev.h>


static const struct vm_operations_struct xfs_file_vm_ops;

/*
 * Clear the specified ranges to zero through either the pagecache or DAX.
 * Holes and unwritten extents will be left as-is as they already are zeroed.
 */

int xfs_zero_range( struct xfs_inode *ip, xfs_off_t pos, xfs_off_t count, bool *did_zero) { return iomap_zero_range(VFS_I(ip), pos, count, NULL, &xfs_iomap_ops); }

Contributors

PersonTokensPropCommitsCommitProp
Christoph Hellwig2873.68%480.00%
Dave Chinner1026.32%120.00%
Total38100.00%5100.00%


int xfs_update_prealloc_flags( struct xfs_inode *ip, enum xfs_prealloc_flags flags) { struct xfs_trans *tp; int error; error = xfs_trans_alloc(ip->i_mount, &M_RES(ip->i_mount)->tr_writeid, 0, 0, 0, &tp); if (error) return error; xfs_ilock(ip, XFS_ILOCK_EXCL); xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL); if (!(flags & XFS_PREALLOC_INVISIBLE)) { VFS_I(ip)->i_mode &= ~S_ISUID; if (VFS_I(ip)->i_mode & S_IXGRP) VFS_I(ip)->i_mode &= ~S_ISGID; xfs_trans_ichgtime(tp, ip, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG); } if (flags & XFS_PREALLOC_SET) ip->i_d.di_flags |= XFS_DIFLAG_PREALLOC; if (flags & XFS_PREALLOC_CLEAR) ip->i_d.di_flags &= ~XFS_DIFLAG_PREALLOC; xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE); if (flags & XFS_PREALLOC_SYNC) xfs_trans_set_sync(tp); return xfs_trans_commit(tp); }

Contributors

PersonTokensPropCommitsCommitProp
Christoph Hellwig16993.37%266.67%
Dave Chinner126.63%133.33%
Total181100.00%3100.00%

/* * Fsync operations on directories are much simpler than on regular files, * as there is no file data to flush, and thus also no need for explicit * cache flush operations, and there are no non-transaction metadata updates * on directories either. */
STATIC int xfs_dir_fsync( struct file *file, loff_t start, loff_t end, int datasync) { struct xfs_inode *ip = XFS_I(file->f_mapping->host); struct xfs_mount *mp = ip->i_mount; xfs_lsn_t lsn = 0; trace_xfs_dir_fsync(ip); xfs_ilock(ip, XFS_ILOCK_SHARED); if (xfs_ipincount(ip)) lsn = ip->i_itemp->ili_last_lsn; xfs_iunlock(ip, XFS_ILOCK_SHARED); if (!lsn) return 0; return _xfs_log_force_lsn(mp, lsn, XFS_LOG_SYNC, NULL); }

Contributors

PersonTokensPropCommitsCommitProp
Christoph Hellwig102100.00%1100.00%
Total102100.00%1100.00%


STATIC int xfs_file_fsync( struct file *file, loff_t start, loff_t end, int datasync) { struct inode *inode = file->f_mapping->host; struct xfs_inode *ip = XFS_I(inode); struct xfs_mount *mp = ip->i_mount; int error = 0; int log_flushed = 0; xfs_lsn_t lsn = 0; trace_xfs_file_fsync(ip); error = filemap_write_and_wait_range(inode->i_mapping, start, end); if (error) return error; if (XFS_FORCED_SHUTDOWN(mp)) return -EIO; xfs_iflags_clear(ip, XFS_ITRUNCATED); /* * If we have an RT and/or log subvolume we need to make sure to flush * the write cache the device used for file data first. This is to * ensure newly written file data make it to disk before logging the new * inode size in case of an extending write. */ if (XFS_IS_REALTIME_INODE(ip)) xfs_blkdev_issue_flush(mp->m_rtdev_targp); else if (mp->m_logdev_targp != mp->m_ddev_targp) xfs_blkdev_issue_flush(mp->m_ddev_targp); /* * All metadata updates are logged, which means that we just have to * flush the log up to the latest LSN that touched the inode. If we have * concurrent fsync/fdatasync() calls, we need them to all block on the * log force before we clear the ili_fsync_fields field. This ensures * that we don't get a racing sync operation that does not wait for the * metadata to hit the journal before returning. If we race with * clearing the ili_fsync_fields, then all that will happen is the log * force will do nothing as the lsn will already be on disk. We can't * race with setting ili_fsync_fields because that is done under * XFS_ILOCK_EXCL, and that can't happen because we hold the lock shared * until after the ili_fsync_fields is cleared. */ xfs_ilock(ip, XFS_ILOCK_SHARED); if (xfs_ipincount(ip)) { if (!datasync || (ip->i_itemp->ili_fsync_fields & ~XFS_ILOG_TIMESTAMP)) lsn = ip->i_itemp->ili_last_lsn; } if (lsn) { error = _xfs_log_force_lsn(mp, lsn, XFS_LOG_SYNC, &log_flushed); ip->i_itemp->ili_fsync_fields = 0; } xfs_iunlock(ip, XFS_ILOCK_SHARED); /* * If we only have a single device, and the log force about was * a no-op we might have to flush the data device cache here. * This can only happen for fdatasync/O_DSYNC if we were overwriting * an already allocated file and thus do not have any metadata to * commit. */ if (!log_flushed && !XFS_IS_REALTIME_INODE(ip) && mp->m_logdev_targp == mp->m_ddev_targp) xfs_blkdev_issue_flush(mp->m_ddev_targp); return error; }

Contributors

PersonTokensPropCommitsCommitProp
Christoph Hellwig19378.14%666.67%
Dave Chinner2811.34%222.22%
Josef Bacik2610.53%111.11%
Total247100.00%9100.00%


STATIC ssize_t xfs_file_dio_aio_read( struct kiocb *iocb, struct iov_iter *to) { struct xfs_inode *ip = XFS_I(file_inode(iocb->ki_filp)); size_t count = iov_iter_count(to); ssize_t ret; trace_xfs_file_direct_read(ip, count, iocb->ki_pos); if (!count) return 0; /* skip atime */ file_accessed(iocb->ki_filp); xfs_ilock(ip, XFS_IOLOCK_SHARED); ret = iomap_dio_rw(iocb, to, &xfs_iomap_ops, NULL); xfs_iunlock(ip, XFS_IOLOCK_SHARED); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Christoph Hellwig8282.00%1062.50%
Al Viro88.00%212.50%
Dave Chinner77.00%212.50%
Chris Mason22.00%16.25%
Bill O'Donnell11.00%16.25%
Total100100.00%16100.00%


static noinline ssize_t xfs_file_dax_read( struct kiocb *iocb, struct iov_iter *to) { struct xfs_inode *ip = XFS_I(iocb->ki_filp->f_mapping->host); size_t count = iov_iter_count(to); ssize_t ret = 0; trace_xfs_file_dax_read(ip, count, iocb->ki_pos); if (!count) return 0; /* skip atime */ xfs_ilock(ip, XFS_IOLOCK_SHARED); ret = dax_iomap_rw(iocb, to, &xfs_iomap_ops); xfs_iunlock(ip, XFS_IOLOCK_SHARED); file_accessed(iocb->ki_filp); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Christoph Hellwig8684.31%861.54%
Dave Chinner109.80%215.38%
Bill O'Donnell32.94%17.69%
Arnd Bergmann21.96%17.69%
Ross Zwisler10.98%17.69%
Total102100.00%13100.00%


STATIC ssize_t xfs_file_buffered_aio_read( struct kiocb *iocb, struct iov_iter *to) { struct xfs_inode *ip = XFS_I(file_inode(iocb->ki_filp)); ssize_t ret; trace_xfs_file_buffered_read(ip, iov_iter_count(to), iocb->ki_pos); xfs_ilock(ip, XFS_IOLOCK_SHARED); ret = generic_file_read_iter(iocb, to); xfs_iunlock(ip, XFS_IOLOCK_SHARED); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Christoph Hellwig6993.24%562.50%
Dave Chinner22.70%112.50%
Al Viro22.70%112.50%
Brian Foster11.35%112.50%
Total74100.00%8100.00%


STATIC ssize_t xfs_file_read_iter( struct kiocb *iocb, struct iov_iter *to) { struct inode *inode = file_inode(iocb->ki_filp); struct xfs_mount *mp = XFS_I(inode)->i_mount; ssize_t ret = 0; XFS_STATS_INC(mp, xs_read_calls); if (XFS_FORCED_SHUTDOWN(mp)) return -EIO; if (IS_DAX(inode)) ret = xfs_file_dax_read(iocb, to); else if (iocb->ki_flags & IOCB_DIRECT) ret = xfs_file_dio_aio_read(iocb, to); else ret = xfs_file_buffered_aio_read(iocb, to); if (ret > 0) XFS_STATS_ADD(mp, xs_read_bytes, ret); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Christoph Hellwig11390.40%450.00%
Dave Chinner54.00%225.00%
Al Viro54.00%112.50%
Bill O'Donnell21.60%112.50%
Total125100.00%8100.00%

/* * Zero any on disk space between the current EOF and the new, larger EOF. * * This handles the normal case of zeroing the remainder of the last block in * the file and the unusual case of zeroing blocks out beyond the size of the * file. This second case only happens with fixed size extents and when the * system crashes before the inode size was updated but after blocks were * allocated. * * Expects the iolock to be held exclusive, and will take the ilock internally. */
int /* error (positive) */ xfs_zero_eof( struct xfs_inode *ip, xfs_off_t offset, /* starting I/O offset */ xfs_fsize_t isize, /* current inode size */ bool *did_zeroing) { ASSERT(xfs_isilocked(ip, XFS_IOLOCK_EXCL)); ASSERT(offset > isize); trace_xfs_zero_eof(ip, isize, offset - isize); return xfs_zero_range(ip, isize, offset - isize, did_zeroing); }

Contributors

PersonTokensPropCommitsCommitProp
David Chinner3046.15%125.00%
Christoph Hellwig2436.92%250.00%
Brian Foster1116.92%125.00%
Total65100.00%4100.00%

/* * Common pre-write limit and setup checks. * * Called with the iolocked held either shared and exclusive according to * @iolock, and returns with it held. Might upgrade the iolock to exclusive * if called for a direct write beyond i_size. */
STATIC ssize_t xfs_file_aio_write_checks( struct kiocb *iocb, struct iov_iter *from, int *iolock) { struct file *file = iocb->ki_filp; struct inode *inode = file->f_mapping->host; struct xfs_inode *ip = XFS_I(inode); ssize_t error = 0; size_t count = iov_iter_count(from); bool drained_dio = false; restart: error = generic_write_checks(iocb, from); if (error <= 0) return error; error = xfs_break_layouts(inode, iolock); if (error) return error; /* * For changing security info in file_remove_privs() we need i_rwsem * exclusively. */ if (*iolock == XFS_IOLOCK_SHARED && !IS_NOSEC(inode)) { xfs_iunlock(ip, *iolock); *iolock = XFS_IOLOCK_EXCL; xfs_ilock(ip, *iolock); goto restart; } /* * If the offset is beyond the size of the file, we need to zero any * blocks that fall between the existing EOF and the start of this * write. If zeroing is needed and we are currently holding the * iolock shared, we need to update it to exclusive which implies * having to redo all checks before. * * We need to serialise against EOF updates that occur in IO * completions here. We want to make sure that nobody is changing the * size while we do this check until we have placed an IO barrier (i.e. * hold the XFS_IOLOCK_EXCL) that prevents new IO from being dispatched. * The spinlock effectively forms a memory barrier once we have the * XFS_IOLOCK_EXCL so we are guaranteed to see the latest EOF value * and hence be able to correctly determine if we need to run zeroing. */ spin_lock(&ip->i_flags_lock); if (iocb->ki_pos > i_size_read(inode)) { bool zero = false; spin_unlock(&ip->i_flags_lock); if (!drained_dio) { if (*iolock == XFS_IOLOCK_SHARED) { xfs_iunlock(ip, *iolock); *iolock = XFS_IOLOCK_EXCL; xfs_ilock(ip, *iolock); iov_iter_reexpand(from, count); } /* * We now have an IO submission barrier in place, but * AIO can do EOF updates during IO completion and hence * we now need to wait for all of them to drain. Non-AIO * DIO will have drained before we are given the * XFS_IOLOCK_EXCL, and so for most cases this wait is a * no-op. */ inode_dio_wait(inode); drained_dio = true; goto restart; } error = xfs_zero_eof(ip, iocb->ki_pos, i_size_read(inode), &zero); if (error) return error; } else spin_unlock(&ip->i_flags_lock); /* * Updating the timestamps will grab the ilock again from * xfs_fs_dirty_inode, so we have to call it after dropping the * lock above. Eventually we should look into a way to avoid * the pointless lock roundtrip. */ if (likely(!(file->f_mode & FMODE_NOCMTIME))) { error = file_update_time(file); if (error) return error; } /* * If we're writing the file then make sure to clear the setuid and * setgid bits if the process is not being run by root. This keeps * people from modifying setuid and setgid binaries. */ if (!IS_NOSEC(inode)) return file_remove_privs(file); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Christoph Hellwig12137.81%738.89%
Dave Chinner8125.31%422.22%
Al Viro4012.50%211.11%
Jan Kara3210.00%211.11%
David Chinner268.12%15.56%
Brian Foster144.38%15.56%
Josef Bacik61.88%15.56%
Total320100.00%18100.00%


static int xfs_dio_write_end_io( struct kiocb *iocb, ssize_t size, unsigned flags) { struct inode *inode = file_inode(iocb->ki_filp); struct xfs_inode *ip = XFS_I(inode); loff_t offset = iocb->ki_pos; bool update_size = false; int error = 0; trace_xfs_end_io_direct_write(ip, offset, size); if (XFS_FORCED_SHUTDOWN(ip->i_mount)) return -EIO; if (size <= 0) return size; /* * We need to update the in-core inode size here so that we don't end up * with the on-disk inode size being outside the in-core inode size. We * have no other method of updating EOF for AIO, so always do it here * if necessary. * * We need to lock the test/set EOF update as we can be racing with * other IO completions here to update the EOF. Failing to serialise * here can result in EOF moving backwards and Bad Things Happen when * that occurs. */ spin_lock(&ip->i_flags_lock); if (offset + size > i_size_read(inode)) { i_size_write(inode, offset + size); update_size = true; } spin_unlock(&ip->i_flags_lock); if (flags & IOMAP_DIO_COW) { error = xfs_reflink_end_cow(ip, offset, size); if (error) return error; } if (flags & IOMAP_DIO_UNWRITTEN) error = xfs_iomap_write_unwritten(ip, offset, size); else if (update_size) error = xfs_setfilesize(ip, offset, size); return error; }

Contributors

PersonTokensPropCommitsCommitProp
Christoph Hellwig192100.00%1100.00%
Total192100.00%1100.00%

/* * xfs_file_dio_aio_write - handle direct IO writes * * Lock the inode appropriately to prepare for and issue a direct IO write. * By separating it from the buffered write path we remove all the tricky to * follow locking changes and looping. * * If there are cached pages or we're extending the file, we need IOLOCK_EXCL * until we're sure the bytes at the new EOF have been zeroed and/or the cached * pages are flushed out. * * In most cases the direct IO writes will be done holding IOLOCK_SHARED * allowing them to be done in parallel with reads and other direct IO writes. * However, if the IO is not aligned to filesystem blocks, the direct IO layer * needs to do sub-block zeroing and that requires serialisation against other * direct IOs to the same block. In this case we need to serialise the * submission of the unaligned IOs so that we don't get racing block zeroing in * the dio layer. To avoid the problem with aio, we also need to wait for * outstanding IOs to complete so that unwritten extent conversion is completed * before we try to map the overlapping block. This is currently implemented by * hitting it with a big hammer (i.e. inode_dio_wait()). * * Returns with locks held indicated by @iolock and errors indicated by * negative return values. */
STATIC ssize_t xfs_file_dio_aio_write( struct kiocb *iocb, struct iov_iter *from) { struct file *file = iocb->ki_filp; struct address_space *mapping = file->f_mapping; struct inode *inode = mapping->host; struct xfs_inode *ip = XFS_I(inode); struct xfs_mount *mp = ip->i_mount; ssize_t ret = 0; int unaligned_io = 0; int iolock; size_t count = iov_iter_count(from); struct xfs_buftarg *target = XFS_IS_REALTIME_INODE(ip) ? mp->m_rtdev_targp : mp->m_ddev_targp; /* DIO must be aligned to device logical sector size */ if ((iocb->ki_pos | count) & target->bt_logical_sectormask) return -EINVAL; /* * Don't take the exclusive iolock here unless the I/O is unaligned to * the file system block size. We don't need to consider the EOF * extension case here because xfs_file_aio_write_checks() will relock * the inode as necessary for EOF zeroing cases and fill out the new * inode size as appropriate. */ if ((iocb->ki_pos & mp->m_blockmask) || ((iocb->ki_pos + count) & mp->m_blockmask)) { unaligned_io = 1; /* * We can't properly handle unaligned direct I/O to reflink * files yet, as we can't unshare a partial block. */ if (xfs_is_reflink_inode(ip)) { trace_xfs_reflink_bounce_dio_write(ip, iocb->ki_pos, count); return -EREMCHG; } iolock = XFS_IOLOCK_EXCL; } else { iolock = XFS_IOLOCK_SHARED; } xfs_ilock(ip, iolock); ret = xfs_file_aio_write_checks(iocb, from, &iolock); if (ret) goto out; count = iov_iter_count(from); /* * If we are doing unaligned IO, wait for all other IO to drain, * otherwise demote the lock if we had to take the exclusive lock * for other reasons in xfs_file_aio_write_checks. */ if (unaligned_io) inode_dio_wait(inode); else if (iolock == XFS_IOLOCK_EXCL) { xfs_ilock_demote(ip, XFS_IOLOCK_EXCL); iolock = XFS_IOLOCK_SHARED; } trace_xfs_file_direct_write(ip, count, iocb->ki_pos); ret = iomap_dio_rw(iocb, from, &xfs_iomap_ops, xfs_dio_write_end_io); out: xfs_iunlock(ip, iolock); /* * No fallback to buffered IO on errors for XFS, direct IO will either * complete fully or fail. */ ASSERT(ret < 0 || ret == count); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Dave Chinner15350.83%519.23%
Christoph Hellwig8227.24%1350.00%
Al Viro3411.30%311.54%
David Chinner175.65%13.85%
Jan Kara61.99%13.85%
Brian Foster51.66%13.85%
Eric Sandeen41.33%27.69%
Total301100.00%26100.00%


static noinline ssize_t xfs_file_dax_write( struct kiocb *iocb, struct iov_iter *from) { struct inode *inode = iocb->ki_filp->f_mapping->host; struct xfs_inode *ip = XFS_I(inode); int iolock = XFS_IOLOCK_EXCL; ssize_t ret, error = 0; size_t count; loff_t pos; xfs_ilock(ip, iolock); ret = xfs_file_aio_write_checks(iocb, from, &iolock); if (ret) goto out; pos = iocb->ki_pos; count = iov_iter_count(from); trace_xfs_file_dax_write(ip, count, pos); ret = dax_iomap_rw(iocb, from, &xfs_iomap_ops); if (ret > 0 && iocb->ki_pos > i_size_read(inode)) { i_size_write(inode, iocb->ki_pos); error = xfs_setfilesize(ip, pos, ret); } out: xfs_iunlock(ip, iolock); return error ? error : ret; }

Contributors

PersonTokensPropCommitsCommitProp
Dave Chinner9052.63%531.25%
Christoph Hellwig7443.27%743.75%
Al Viro42.34%212.50%
Arnd Bergmann21.17%16.25%
Ross Zwisler10.58%16.25%
Total171100.00%16100.00%


STATIC ssize_t xfs_file_buffered_aio_write( struct kiocb *iocb, struct iov_iter *from) { struct file *file = iocb->ki_filp; struct address_space *mapping = file->f_mapping; struct inode *inode = mapping->host; struct xfs_inode *ip = XFS_I(inode); ssize_t ret; int enospc = 0; int iolock; write_retry: iolock = XFS_IOLOCK_EXCL; xfs_ilock(ip, iolock); ret = xfs_file_aio_write_checks(iocb, from, &iolock); if (ret) goto out; /* We can write back this queue in page reclaim */ current->backing_dev_info = inode_to_bdi(inode); trace_xfs_file_buffered_write(ip, iov_iter_count(from), iocb->ki_pos); ret = iomap_file_buffered_write(iocb, from, &xfs_iomap_ops); if (likely(ret >= 0)) iocb->ki_pos += ret; /* * If we hit a space limit, try to free up some lingering preallocated * space before returning an error. In the case of ENOSPC, first try to * write back all dirty inodes to free up some of the excess reserved * metadata space. This reduces the chances that the eofblocks scan * waits on dirty mappings. Since xfs_flush_inodes() is serialized, this * also behaves as a filter to prevent too many eofblocks scans from * running at the same time. */ if (ret == -EDQUOT && !enospc) { xfs_iunlock(ip, iolock); enospc = xfs_inode_free_quota_eofblocks(ip); if (enospc) goto write_retry; enospc = xfs_inode_free_quota_cowblocks(ip); if (enospc) goto write_retry; iolock = 0; } else if (ret == -ENOSPC && !enospc) { struct xfs_eofblocks eofb = {0}; enospc = 1; xfs_flush_inodes(ip->i_mount); xfs_iunlock(ip, iolock); eofb.eof_flags = XFS_EOF_FLAGS_SYNC; xfs_icache_free_eofblocks(ip->i_mount, &eofb); goto write_retry; } current->backing_dev_info = NULL; out: if (iolock) xfs_iunlock(ip, iolock); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Christoph Hellwig14953.41%637.50%
Brian Foster8028.67%212.50%
Al Viro207.17%318.75%
Dave Chinner165.73%425.00%
Darrick J. Wong145.02%16.25%
Total279100.00%16100.00%


STATIC ssize_t xfs_file_write_iter( struct kiocb *iocb, struct iov_iter *from) { struct file *file = iocb->ki_filp; struct address_space *mapping = file->f_mapping; struct inode *inode = mapping->host; struct xfs_inode *ip = XFS_I(inode); ssize_t ret; size_t ocount = iov_iter_count(from); XFS_STATS_INC(ip->i_mount, xs_write_calls); if (ocount == 0) return 0; if (XFS_FORCED_SHUTDOWN(ip->i_mount)) return -EIO; if (IS_DAX(inode)) ret = xfs_file_dax_write(iocb, from); else if (iocb->ki_flags & IOCB_DIRECT) { /* * Allow a directio write to fall back to a buffered * write *only* in the case that we're doing a reflink * CoW. In all other directio scenarios we do not * allow an operation to fall back to buffered mode. */ ret = xfs_file_dio_aio_write(iocb, from); if (ret == -EREMCHG) goto buffered; } else { buffered: ret = xfs_file_buffered_aio_write(iocb, from); } if (ret > 0) { XFS_STATS_ADD(ip->i_mount, xs_write_bytes, ret); /* Handle various SYNC-type writes */ ret = generic_write_sync(iocb, ret); } return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Dave Chinner10455.32%214.29%
Christoph Hellwig4322.87%535.71%
Darrick J. Wong179.04%17.14%
Al Viro157.98%428.57%
Bill O'Donnell84.26%17.14%
Josef Bacik10.53%17.14%
Total188100.00%14100.00%

#define XFS_FALLOC_FL_SUPPORTED \ (FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE | \ FALLOC_FL_COLLAPSE_RANGE | FALLOC_FL_ZERO_RANGE | \ FALLOC_FL_INSERT_RANGE | FALLOC_FL_UNSHARE_RANGE)
STATIC long xfs_file_fallocate( struct file *file, int mode, loff_t offset, loff_t len) { struct inode *inode = file_inode(file); struct xfs_inode *ip = XFS_I(inode); long error; enum xfs_prealloc_flags flags = 0; uint iolock = XFS_IOLOCK_EXCL; loff_t new_size = 0; bool do_file_insert = 0; if (!S_ISREG(inode->i_mode)) return -EINVAL; if (mode & ~XFS_FALLOC_FL_SUPPORTED) return -EOPNOTSUPP; xfs_ilock(ip, iolock); error = xfs_break_layouts(inode, &iolock); if (error) goto out_unlock; xfs_ilock(ip, XFS_MMAPLOCK_EXCL); iolock |= XFS_MMAPLOCK_EXCL; if (mode & FALLOC_FL_PUNCH_HOLE) { error = xfs_free_file_space(ip, offset, len); if (error) goto out_unlock; } else if (mode & FALLOC_FL_COLLAPSE_RANGE) { unsigned int blksize_mask = i_blocksize(inode) - 1; if (offset & blksize_mask || len & blksize_mask) { error = -EINVAL; goto out_unlock; } /* * There is no need to overlap collapse range with EOF, * in which case it is effectively a truncate operation */ if (offset + len >= i_size_read(inode)) { error = -EINVAL; goto out_unlock; } new_size = i_size_read(inode) - len; error = xfs_collapse_file_space(ip, offset, len); if (error) goto out_unlock; } else if (mode & FALLOC_FL_INSERT_RANGE) { unsigned int blksize_mask = i_blocksize(inode) - 1; new_size = i_size_read(inode) + len; if (offset & blksize_mask || len & blksize_mask) { error = -EINVAL; goto out_unlock; } /* check the new inode size does not wrap through zero */ if (new_size > inode->i_sb->s_maxbytes) { error = -EFBIG; goto out_unlock; } /* Offset should be less than i_size */ if (offset >= i_size_read(inode)) { error = -EINVAL; goto out_unlock; } do_file_insert = 1; } else { flags |= XFS_PREALLOC_SET; if (!(mode & FALLOC_FL_KEEP_SIZE) && offset + len > i_size_read(inode)) { new_size = offset + len; error = inode_newsize_ok(inode, new_size); if (error) goto out_unlock; } if (mode & FALLOC_FL_ZERO_RANGE) error = xfs_zero_file_space(ip, offset, len); else { if (mode & FALLOC_FL_UNSHARE_RANGE) { error = xfs_reflink_unshare(ip, offset, len); if (error) goto out_unlock; } error = xfs_alloc_file_space(ip, offset, len, XFS_BMAPI_PREALLOC); } if (error) goto out_unlock; } if (file->f_flags & O_DSYNC) flags |= XFS_PREALLOC_SYNC; error = xfs_update_prealloc_flags(ip, flags); if (error) goto out_unlock; /* Change file size if needed */ if (new_size) { struct iattr iattr; iattr.ia_valid = ATTR_SIZE; iattr.ia_size = new_size; error = xfs_vn_setattr_size(file_dentry(file), &iattr); if (error) goto out_unlock; } /* * Perform hole insertion now that the file size has been * updated so that if we crash during the operation we don't * leave shifted extents past EOF and hence losing access to * the data that is contained within them. */ if (do_file_insert) error = xfs_insert_file_space(ip, offset, len); out_unlock: xfs_iunlock(ip, iolock); return error; }

Contributors

PersonTokensPropCommitsCommitProp
Christoph Hellwig26648.63%635.29%
Namjae Jeon18934.55%211.76%
Lukas Czerner315.67%211.76%
Darrick J. Wong285.12%15.88%
Dave Chinner173.11%317.65%
Fabian Frederick81.46%15.88%
Jan Kara50.91%15.88%
Al Viro30.55%15.88%
Total547100.00%17100.00%


STATIC int xfs_file_clone_range( struct file *file_in, loff_t pos_in, struct file *file_out, loff_t pos_out, u64 len) { return xfs_reflink_remap_range(file_in, pos_in, file_out, pos_out, len, false); }

Contributors

PersonTokensPropCommitsCommitProp
Darrick J. Wong3175.61%266.67%
Christoph Hellwig1024.39%133.33%
Total41100.00%3100.00%


STATIC ssize_t xfs_file_dedupe_range( struct file *src_file, u64 loff, u64 len, struct file *dst_file, u64 dst_loff) { int error; error = xfs_reflink_remap_range(src_file, loff, dst_file, dst_loff, len, true); if (error) return error; return len; }

Contributors

PersonTokensPropCommitsCommitProp
Darrick J. Wong5498.18%266.67%
Christoph Hellwig11.82%133.33%
Total55100.00%3100.00%


STATIC int xfs_file_open( struct inode *inode, struct file *file) { if (!(file->f_flags & O_LARGEFILE) && i_size_read(inode) > MAX_NON_LFS) return -EFBIG; if (XFS_FORCED_SHUTDOWN(XFS_M(inode->i_sb))) return -EIO; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Christoph Hellwig5189.47%350.00%
Stephen Lord35.26%116.67%
Nathan Scott35.26%233.33%
Total57100.00%6100.00%


STATIC int xfs_dir_open( struct inode *inode, struct file *file) { struct xfs_inode *ip = XFS_I(inode); int mode; int error; error = xfs_file_open(inode, file); if (error) return error; /* * If there are any blocks, read-ahead block 0 as we're almost * certain to have the next operation be a read there. */ mode = xfs_ilock_data_map_shared(ip); if (ip->i_d.di_nextents > 0) error = xfs_dir3_data_readahead(ip, 0, -1); xfs_iunlock(ip, mode); return error; }

Contributors

PersonTokensPropCommitsCommitProp
Christoph Hellwig7989.77%450.00%
Nathan Scott33.41%112.50%
Darrick J. Wong33.41%112.50%
Dave Chinner33.41%225.00%
Total88100.00%8100.00%


STATIC int xfs_file_release( struct inode *inode, struct file *filp) { return xfs_release(XFS_I(inode)); }

Contributors

PersonTokensPropCommitsCommitProp
Christoph Hellwig2392.00%250.00%
Nathan Scott28.00%250.00%
Total25100.00%4100.00%


STATIC int xfs_file_readdir( struct file *file, struct dir_context *ctx) { struct inode *inode = file_inode(file); xfs_inode_t *ip = XFS_I(inode); size_t bufsize; /* * The Linux API doesn't pass down the total size of the buffer * we read into down to the filesystem. With the filldir concept * it's not needed for correct information, but the XFS dir2 leaf * code wants an estimate of the buffer size to calculate it's * readahead window and size the buffers used for mapping to * physical blocks. * * Try to give it an estimate that's good enough, maybe at some * point we can change the ->readdir prototype to include the * buffer size. For now we use the current glibc buffer size. */ bufsize = (size_t)min_t(loff_t, 32768, ip->i_d.di_size); return xfs_readdir(ip, ctx, bufsize); }

Contributors

PersonTokensPropCommitsCommitProp
Christoph Hellwig3755.22%538.46%
David Woodhouse1217.91%17.69%
Al Viro913.43%215.38%
Lachlan McIlroy45.97%17.69%
Nathan Scott22.99%215.38%
Eric Sandeen22.99%17.69%
Fengguang Wu11.49%17.69%
Total67100.00%13100.00%

/* * This type is designed to indicate the type of offset we would like * to search from page cache for xfs_seek_hole_data(). */ enum { HOLE_OFF = 0, DATA_OFF, }; /* * Lookup the desired type of offset from the given page. * * On success, return true and the offset argument will point to the * start of the region that was found. Otherwise this function will * return false and keep the offset argument unchanged. */
STATIC bool xfs_lookup_buffer_offset( struct page *page, loff_t *offset, unsigned int type) { loff_t lastoff = page_offset(page); bool found = false; struct buffer_head *bh, *head; bh = head = page_buffers(page); do { /* * Unwritten extents that have data in the page * cache covering them can be identified by the * BH_Unwritten state flag. Pages with multiple * buffers might have a mix of holes, data and * unwritten extents - any buffer with valid * data in it should have BH_Uptodate flag set * on it. */ if (buffer_unwritten(bh) || buffer_uptodate(bh)) { if (type == DATA_OFF) found = true; } else { if (type == HOLE_OFF) found = true; } if (found) { *offset = lastoff; break; } lastoff += bh->b_size; } while ((bh = bh->b_this_page) != head); return found; }

Contributors

PersonTokensPropCommitsCommitProp
Jie Liu124100.00%1100.00%
Total124100.00%1100.00%

/* * This routine is called to find out and return a data or hole offset * from the page cache for unwritten extents according to the desired * type for xfs_seek_hole_data(). * * The argument offset is used to tell where we start to search from the * page cache. Map is used to figure out the end points of the range to * lookup pages. * * Return true if the desired type of offset was found, and the argument * offset is filled with that address. Otherwise, return false and keep * offset unchanged. */
STATIC bool xfs_find_get_desired_pgoff( struct inode *inode, struct xfs_bmbt_irec *map, unsigned int type, loff_t *offset) { struct xfs_inode *ip = XFS_I(inode); struct xfs_mount *mp = ip->i_mount; struct pagevec pvec; pgoff_t index; pgoff_t end; loff_t endoff; loff_t startoff = *offset; loff_t lastoff = startoff; bool found = false; pagevec_init(&pvec, 0); index = startoff >> PAGE_SHIFT; endoff = XFS_FSB_TO_B(mp, map->br_startoff + map->br_blockcount); end = endoff >> PAGE_SHIFT; do { int want; unsigned nr_pages; unsigned int i; want = min_t(pgoff_t, end - index, PAGEVEC_SIZE); nr_pages = pagevec_lookup(&pvec, inode->i_mapping, index, want); /* * No page mapped into given range. If we are searching holes * and if this is the first time we got into the loop, it means * that the given offset is landed in a hole, return it. * * If we have already stepped through some block buffers to find * holes but they all contains data. In this case, the last * offset is already updated and pointed to the end of the last * mapped page, if it does not reach the endpoint to search, * that means there should be a hole between them. */ if (nr_pages == 0) { /* Data search found nothing */ if (type == DATA_OFF) break; ASSERT(type == HOLE_OFF); if (lastoff == startoff || lastoff < endoff) { found = true; *offset = lastoff; } break; } /* * At lease we found one page. If this is the first time we * step into the loop, and if the first page index offset is * greater than the given search offset, a hole was found. */ if (type == HOLE_OFF && lastoff == startoff && lastoff < page_offset(pvec.pages[0])) { found = true; break; } for (i = 0; i < nr_pages; i++) { struct page *page = pvec.pages[i]; loff_t b_offset; /* * At this point, the page may be truncated or * invalidated (changing page->mapping to NULL), * or even swizzled back from swapper_space to tmpfs * file mapping. However, page->index will not change * because we have a reference on the page. * * Searching done if the page index is out of range. * If the current offset is not reaches the end of * the specified search range, there should be a hole * between them. */ if (page->index > end) { if (type == HOLE_OFF && lastoff < endoff) { *offset = lastoff; found = true; } goto out; } lock_page(page); /* * Page truncated or invalidated(page->mapping == NULL). * We can freely skip it and proceed to check the next * page. */ if (unlikely(page->mapping != inode->i_mapping)) { unlock_page(page); continue; } if (!page_has_buffers(page)) { unlock_page(page); continue; } found = xfs_lookup_buffer_offset(page, &b_offset, type); if (found) { /* * The found offset may be less than the start * point to search if this is the first time to * come here. */ *offset = max_t(loff_t, startoff, b_offset); unlock_page(page); goto out; } /* * We either searching data but nothing was found, or * searching hole but found a data buffer. In either * case, probably the next page contains the desired * things, update the last offset to it so. */ lastoff = page_offset(page) + PAGE_SIZE; unlock_page(page); } /* * The number of returned pages less than our desired, search * done. In this case, nothing was found for searching data, * but we found a hole behind the last offset. */ if (nr_pages < want) { if (type == HOLE_OFF) { *offset = lastoff; found = true; } break; } index = pvec.pages[i - 1]->index + 1; pagevec_release(&pvec); } while (index <= end); out: pagevec_release(&pvec); return found; }

Contributors

PersonTokensPropCommitsCommitProp
Jie Liu45199.56%150.00%
Kirill A. Shutemov20.44%150.00%
Total453100.00%2100.00%

/* * caller must lock inode with xfs_ilock_data_map_shared, * can we craft an appropriate ASSERT? * * end is because the VFS-level lseek interface is defined such that any * offset past i_size shall return -ENXIO, but we use this for quota code * which does not maintain i_size, and we want to SEEK_DATA past i_size. */
loff_t __xfs_seek_hole_data( struct inode *inode, loff_t start, loff_t end, int whence) { struct xfs_inode *ip = XFS_I(inode); struct xfs_mount *mp = ip->i_mount; loff_t uninitialized_var(offset); xfs_fileoff_t fsbno; xfs_filblks_t lastbno; int error; if (start >= end) { error = -ENXIO; goto out_error; } /* * Try to read extents from the first block indicated * by fsbno to the end block of the file. */ fsbno = XFS_B_TO_FSBT(mp, start); lastbno = XFS_B_TO_FSB(mp, end); for (;;) { struct xfs_bmbt_irec map[2]; int nmap = 2; unsigned int i; error = xfs_bmapi_read(ip, fsbno, lastbno - fsbno, map, &nmap, XFS_BMAPI_ENTIRE); if (error) goto out_error; /* No extents at given offset, must be beyond EOF */ if (nmap == 0) { error = -ENXIO; goto out_error; } for (i = 0; i < nmap; i++) { offset = max_t(loff_t, start, XFS_FSB_TO_B(mp, map[i].br_startoff)); /* Landed in the hole we wanted? */ if (whence == SEEK_HOLE && map[i].br_startblock == HOLESTARTBLOCK) goto out; /* Landed in the data extent we wanted? */ if (whence == SEEK_DATA && (map[i].br_startblock == DELAYSTARTBLOCK || (map[i].br_state == XFS_EXT_NORM && !isnullstartblock(map[i].br_startblock)))) goto out; /* * Landed in an unwritten extent, try to search * for hole or data from page cache. */ if (map[i].br_state == XFS_EXT_UNWRITTEN) { if (xfs_find_get_desired_pgoff(inode, &map[i], whence == SEEK_HOLE ? HOLE_OFF : DATA_OFF, &offset)) goto out; } } /* * We only received one extent out of the two requested. This * means we've hit EOF and didn't find what we are looking for. */ if (nmap == 1) { /* * If we were looking for a hole, set offset to * the end of the file (i.e., there is an implicit * hole at the end of any file). */ if (whence == SEEK_HOLE) { offset = end; break; } /* * If we were looking for data, it's nowhere to be found */ ASSERT(whence == SEEK_DATA); error = -ENXIO; goto out_error; } ASSERT(i > 1); /* * Nothing was found, proceed to the next round of search * if the next reading offset is not at or beyond EOF. */ fsbno = map[i - 1].br_startoff + map[i - 1].br_blockcount; start = XFS_FSB_TO_B(mp, fsbno); if (start >= end) { if (whence == SEEK_HOLE) { offset = end; break; } ASSERT(whence == SEEK_DATA); error = -ENXIO; goto out_error; } } out: /* * If at this point we have found the hole we wanted, the returned * offset may be bigger than the file size as it may be aligned to * page boundary for unwritten extents. We need to deal with this * situation in particular. */ if (whence == SEEK_HOLE) offset = min_t(loff_t, offset, end); return offset; out_error: return error; }

Contributors

PersonTokensPropCommitsCommitProp
Jie Liu32574.88%350.00%
Eric Sandeen10524.19%233.33%
Dave Chinner40.92%116.67%
Total434100.00%6100.00%


STATIC loff_t xfs_seek_hole_data( struct file *file, loff_t start, int whence) { struct inode *inode = file->f_mapping->host; struct xfs_inode *ip = XFS_I(inode); struct xfs_mount *mp = ip->i_mount; uint lock; loff_t offset, end; int error = 0; if (XFS_FORCED_SHUTDOWN(mp)) return -EIO; lock = xfs_ilock_data_map_shared(ip); end = i_size_read(inode); offset = __xfs_seek_hole_data(inode, start, end, whence); if (offset < 0) { error = offset; goto out_unlock; } offset = vfs_setpos(file, offset, inode->i_sb->s_maxbytes); out_unlock: xfs_iunlock(ip, lock); if (error) return error; return offset; }

Contributors

PersonTokensPropCommitsCommitProp
Eric Sandeen11074.83%125.00%
Jie Liu3624.49%250.00%
Christoph Hellwig10.68%125.00%
Total147100.00%4100.00%


STATIC loff_t xfs_file_llseek( struct file *file, loff_t offset, int whence) { switch (whence) { case SEEK_END: case SEEK_CUR: case SEEK_SET: return generic_file_llseek(file, offset, whence); case SEEK_HOLE: case SEEK_DATA: return xfs_seek_hole_data(file, offset, whence); default: return -EINVAL; } }

Contributors

PersonTokensPropCommitsCommitProp
Jie Liu5587.30%133.33%
Eric Sandeen812.70%266.67%
Total63100.00%3100.00%

/* * Locking for serialisation of IO during page faults. This results in a lock * ordering of: * * mmap_sem (MM) * sb_start_pagefault(vfs, freeze) * i_mmaplock (XFS - truncate serialisation) * page_lock (MM) * i_lock (XFS - extent map serialisation) */ /* * mmap()d file has taken write protection fault and is being made writable. We * can set the page state up correctly for a writable page, which means we can * do correct delalloc accounting (ENOSPC checking!) and unwritten extent * mapping. */
STATIC int xfs_filemap_page_mkwrite( struct vm_fault *vmf) { struct inode *inode = file_inode(vmf->vma->vm_file); int ret; trace_xfs_filemap_page_mkwrite(XFS_I(inode)); sb_start_pagefault(inode->i_sb); file_update_time(vmf->vma->vm_file); xfs_ilock(XFS_I(inode), XFS_MMAPLOCK_SHARED); if (IS_DAX(inode)) { ret = dax_iomap_fault(vmf, PE_SIZE_PTE, &xfs_iomap_ops); } else { ret = iomap_page_mkwrite(vmf, &xfs_iomap_ops); ret = block_page_mkwrite_return(ret); } xfs_iunlock(XFS_I(inode), XFS_MMAPLOCK_SHARED); sb_end_pagefault(inode->i_sb); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Dave Chinner11190.24%228.57%
Dave Jiang64.88%228.57%
Christoph Hellwig54.07%228.57%
Ross Zwisler10.81%114.29%
Total123100.00%7100.00%


STATIC int xfs_filemap_fault( struct vm_fault *vmf) { struct inode *inode = file_inode(vmf->vma->vm_file); int ret; trace_xfs_filemap_fault(XFS_I(inode)); /* DAX can shortcut the normal fault path on write faults! */ if ((vmf->flags & FAULT_FLAG_WRITE) && IS_DAX(inode)) return xfs_filemap_page_mkwrite(vmf); xfs_ilock(XFS_I(inode), XFS_MMAPLOCK_SHARED); if (IS_DAX(inode)) ret = dax_iomap_fault(vmf, PE_SIZE_PTE, &xfs_iomap_ops); else ret = filemap_fault(vmf); xfs_iunlock(XFS_I(inode), XFS_MMAPLOCK_SHARED); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Dave Chinner10193.52%450.00%
Dave Jiang43.70%225.00%
Christoph Hellwig21.85%112.50%
Ross Zwisler10.93%112.50%
Total108100.00%8100.00%

/* * Similar to xfs_filemap_fault(), the DAX fault path can call into here on * both read and write faults. Hence we need to handle both cases. There is no * ->huge_mkwrite callout for huge pages, so we have a single function here to * handle both cases here. @flags carries the information on the type of fault * occuring. */
STATIC int xfs_filemap_huge_fault( struct vm_fault *vmf, enum page_entry_size pe_size) { struct inode *inode = file_inode(vmf->vma->vm_file); struct xfs_inode *ip = XFS_I(inode); int ret; if (!IS_DAX(inode)) return VM_FAULT_FALLBACK; trace_xfs_filemap_huge_fault(ip); if (vmf->flags & FAULT_FLAG_WRITE) { sb_start_pagefault(inode->i_sb); file_update_time(vmf->vma->vm_file); } xfs_ilock(XFS_I(inode), XFS_MMAPLOCK_SHARED); ret = dax_iomap_fault(vmf, pe_size, &xfs_iomap_ops); xfs_iunlock(XFS_I(inode), XFS_MMAPLOCK_SHARED); if (vmf->flags & FAULT_FLAG_WRITE) sb_end_pagefault(inode->i_sb); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Matthew Wilcox9873.13%114.29%
Dave Jiang2014.93%457.14%
Dave Chinner1410.45%114.29%
Ross Zwisler21.49%114.29%
Total134100.00%7100.00%

/* * pfn_mkwrite was originally inteneded to ensure we capture time stamp * updates on write faults. In reality, it's need to serialise against * truncate similar to page_mkwrite. Hence we cycle the XFS_MMAPLOCK_SHARED * to ensure we serialise the fault barrier in place. */
static int xfs_filemap_pfn_mkwrite( struct vm_fault *vmf) { struct inode *inode = file_inode(vmf->vma->vm_file); struct xfs_inode *ip = XFS_I(inode); int ret = VM_FAULT_NOPAGE; loff_t size; trace_xfs_filemap_pfn_mkwrite(ip); sb_start_pagefault(inode->i_sb); file_update_time(vmf->vma->vm_file); /* check if the faulting page hasn't raced with truncate */ xfs_ilock(ip, XFS_MMAPLOCK_SHARED); size = (i_size_read(inode) + PAGE_SIZE - 1) >> PAGE_SHIFT; if (vmf->pgoff >= size) ret = VM_FAULT_SIGBUS; else if (IS_DAX(inode)) ret = dax_pfn_mkwrite(vmf); xfs_iunlock(ip, XFS_MMAPLOCK_SHARED); sb_end_pagefault(inode->i_sb); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Dave Chinner10177.10%125.00%
Ross Zwisler1511.45%125.00%
Matthew Wilcox118.40%125.00%
Dave Jiang43.05%125.00%
Total131100.00%4100.00%

static const struct vm_operations_struct xfs_file_vm_ops = { .fault = xfs_filemap_fault, .huge_fault = xfs_filemap_huge_fault, .map_pages = filemap_map_pages, .page_mkwrite = xfs_filemap_page_mkwrite, .pfn_mkwrite = xfs_filemap_pfn_mkwrite, };
STATIC int xfs_file_mmap( struct file *filp, struct vm_area_struct *vma) { file_accessed(filp); vma->vm_ops = &xfs_file_vm_ops; if (IS_DAX(file_inode(filp))) vma->vm_flags |= VM_MIXEDMAP | VM_HUGEPAGE; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Dave Chinner4795.92%375.00%
Matthew Wilcox24.08%125.00%
Total49100.00%4100.00%

const struct file_operations xfs_file_operations = { .llseek = xfs_file_llseek, .read_iter = xfs_file_read_iter, .write_iter = xfs_file_write_iter, .splice_read = generic_file_splice_read, .splice_write = iter_file_splice_write, .unlocked_ioctl = xfs_file_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = xfs_file_compat_ioctl, #endif .mmap = xfs_file_mmap, .open = xfs_file_open, .release = xfs_file_release, .fsync = xfs_file_fsync, .get_unmapped_area = thp_get_unmapped_area, .fallocate = xfs_file_fallocate, .clone_file_range = xfs_file_clone_range, .dedupe_file_range = xfs_file_dedupe_range, }; const struct file_operations xfs_dir_file_operations = { .open = xfs_dir_open, .read = generic_read_dir, .iterate_shared = xfs_file_readdir, .llseek = generic_file_llseek, .unlocked_ioctl = xfs_file_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = xfs_file_compat_ioctl, #endif .fsync = xfs_dir_fsync, };

Overall Contributors

PersonTokensPropCommitsCommitProp
Christoph Hellwig208738.53%5432.34%
Jie Liu100518.56%52.99%
Dave Chinner97518.00%3621.56%
Eric Sandeen2324.28%63.59%
Namjae Jeon1933.56%21.20%
Darrick J. Wong1612.97%63.59%
Al Viro1522.81%158.98%
Brian Foster1142.10%42.40%
Matthew Wilcox1142.10%10.60%
David Chinner741.37%21.20%
Jan Kara430.79%31.80%
Nathan Scott400.74%52.99%
Dave Jiang370.68%52.99%
Josef Bacik330.61%21.20%
Lukas Czerner310.57%21.20%
Ross Zwisler220.41%31.80%
Stephen Lord160.30%21.20%
Russell Cattelan160.30%10.60%
Bill O'Donnell140.26%10.60%
Andi Kleen130.24%10.60%
David Woodhouse120.22%10.60%
Fabian Frederick80.15%10.60%
Toshi Kani50.09%10.60%
Lachlan McIlroy40.07%10.60%
Arnd Bergmann40.07%10.60%
Tejun Heo30.06%10.60%
Chris Mason20.04%10.60%
Kirill A. Shutemov20.04%10.60%
Arjan van de Ven20.04%10.60%
Alexey Dobriyan10.02%10.60%
Fengguang Wu10.02%10.60%
Total5416100.00%167100.00%
Directory: fs/xfs
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with cregit.