Commit f576518c authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'xfs-5.3-fixes-4' of git://git.kernel.org/pub/scm/fs/xfs/xfs-linux

Pull xfs fixes from Darrick Wong:
 "Here are a few more bug fixes that trickled in since the last pull.
  They've survived the usual xfstests runs and merge cleanly with this
  morning's master.

  I expect there to be one more pull request tomorrow for the fix to
  that quota related inode unlock bug that we were reviewing last night,
  but it will continue to soak in the testing machine for several more
  hours.

   - Fix missing compat ioctl handling for get/setlabel

   - Fix missing ioctl pointer sanitization on s390

   - Fix a page locking deadlock in the dedupe comparison code

   - Fix inadequate locking in reflink code w.r.t. concurrent directio

   - Fix broken error detection when breaking layouts"

* tag 'xfs-5.3-fixes-4' of git://git.kernel.org/pub/scm/fs/xfs/xfs-linux:
  fs/xfs: Fix return code of xfs_break_leased_layouts()
  xfs: fix reflink source file racing with directio writes
  vfs: fix page locking deadlocks when deduping files
  xfs: compat_ioctl: use compat_ptr()
  xfs: fall back to native ioctls for unhandled compat ones
parents e3fb13b7 b6827160
...@@ -1811,10 +1811,7 @@ static int generic_remap_check_len(struct inode *inode_in, ...@@ -1811,10 +1811,7 @@ static int generic_remap_check_len(struct inode *inode_in,
return (remap_flags & REMAP_FILE_DEDUP) ? -EBADE : -EINVAL; return (remap_flags & REMAP_FILE_DEDUP) ? -EBADE : -EINVAL;
} }
/* /* Read a page's worth of file data into the page cache. */
* Read a page's worth of file data into the page cache. Return the page
* locked.
*/
static struct page *vfs_dedupe_get_page(struct inode *inode, loff_t offset) static struct page *vfs_dedupe_get_page(struct inode *inode, loff_t offset)
{ {
struct page *page; struct page *page;
...@@ -1826,10 +1823,32 @@ static struct page *vfs_dedupe_get_page(struct inode *inode, loff_t offset) ...@@ -1826,10 +1823,32 @@ static struct page *vfs_dedupe_get_page(struct inode *inode, loff_t offset)
put_page(page); put_page(page);
return ERR_PTR(-EIO); return ERR_PTR(-EIO);
} }
lock_page(page);
return page; return page;
} }
/*
* Lock two pages, ensuring that we lock in offset order if the pages are from
* the same file.
*/
static void vfs_lock_two_pages(struct page *page1, struct page *page2)
{
/* Always lock in order of increasing index. */
if (page1->index > page2->index)
swap(page1, page2);
lock_page(page1);
if (page1 != page2)
lock_page(page2);
}
/* Unlock two pages, being careful not to unlock the same page twice. */
static void vfs_unlock_two_pages(struct page *page1, struct page *page2)
{
unlock_page(page1);
if (page1 != page2)
unlock_page(page2);
}
/* /*
* Compare extents of two files to see if they are the same. * Compare extents of two files to see if they are the same.
* Caller must have locked both inodes to prevent write races. * Caller must have locked both inodes to prevent write races.
...@@ -1867,10 +1886,24 @@ static int vfs_dedupe_file_range_compare(struct inode *src, loff_t srcoff, ...@@ -1867,10 +1886,24 @@ static int vfs_dedupe_file_range_compare(struct inode *src, loff_t srcoff,
dest_page = vfs_dedupe_get_page(dest, destoff); dest_page = vfs_dedupe_get_page(dest, destoff);
if (IS_ERR(dest_page)) { if (IS_ERR(dest_page)) {
error = PTR_ERR(dest_page); error = PTR_ERR(dest_page);
unlock_page(src_page);
put_page(src_page); put_page(src_page);
goto out_error; goto out_error;
} }
vfs_lock_two_pages(src_page, dest_page);
/*
* Now that we've locked both pages, make sure they're still
* mapped to the file data we're interested in. If not,
* someone is invalidating pages on us and we lose.
*/
if (!PageUptodate(src_page) || !PageUptodate(dest_page) ||
src_page->mapping != src->i_mapping ||
dest_page->mapping != dest->i_mapping) {
same = false;
goto unlock;
}
src_addr = kmap_atomic(src_page); src_addr = kmap_atomic(src_page);
dest_addr = kmap_atomic(dest_page); dest_addr = kmap_atomic(dest_page);
...@@ -1882,8 +1915,8 @@ static int vfs_dedupe_file_range_compare(struct inode *src, loff_t srcoff, ...@@ -1882,8 +1915,8 @@ static int vfs_dedupe_file_range_compare(struct inode *src, loff_t srcoff,
kunmap_atomic(dest_addr); kunmap_atomic(dest_addr);
kunmap_atomic(src_addr); kunmap_atomic(src_addr);
unlock_page(dest_page); unlock:
unlock_page(src_page); vfs_unlock_two_pages(src_page, dest_page);
put_page(dest_page); put_page(dest_page);
put_page(src_page); put_page(src_page);
......
...@@ -547,63 +547,12 @@ xfs_file_compat_ioctl( ...@@ -547,63 +547,12 @@ xfs_file_compat_ioctl(
struct inode *inode = file_inode(filp); struct inode *inode = file_inode(filp);
struct xfs_inode *ip = XFS_I(inode); struct xfs_inode *ip = XFS_I(inode);
struct xfs_mount *mp = ip->i_mount; struct xfs_mount *mp = ip->i_mount;
void __user *arg = (void __user *)p; void __user *arg = compat_ptr(p);
int error; int error;
trace_xfs_file_compat_ioctl(ip); trace_xfs_file_compat_ioctl(ip);
switch (cmd) { switch (cmd) {
/* No size or alignment issues on any arch */
case XFS_IOC_DIOINFO:
case XFS_IOC_FSGEOMETRY_V4:
case XFS_IOC_FSGEOMETRY:
case XFS_IOC_AG_GEOMETRY:
case XFS_IOC_FSGETXATTR:
case XFS_IOC_FSSETXATTR:
case XFS_IOC_FSGETXATTRA:
case XFS_IOC_FSSETDM:
case XFS_IOC_GETBMAP:
case XFS_IOC_GETBMAPA:
case XFS_IOC_GETBMAPX:
case XFS_IOC_FSCOUNTS:
case XFS_IOC_SET_RESBLKS:
case XFS_IOC_GET_RESBLKS:
case XFS_IOC_FSGROWFSLOG:
case XFS_IOC_GOINGDOWN:
case XFS_IOC_ERROR_INJECTION:
case XFS_IOC_ERROR_CLEARALL:
case FS_IOC_GETFSMAP:
case XFS_IOC_SCRUB_METADATA:
case XFS_IOC_BULKSTAT:
case XFS_IOC_INUMBERS:
return xfs_file_ioctl(filp, cmd, p);
#if !defined(BROKEN_X86_ALIGNMENT) || defined(CONFIG_X86_X32)
/*
* These are handled fine if no alignment issues. To support x32
* which uses native 64-bit alignment we must emit these cases in
* addition to the ia-32 compat set below.
*/
case XFS_IOC_ALLOCSP:
case XFS_IOC_FREESP:
case XFS_IOC_RESVSP:
case XFS_IOC_UNRESVSP:
case XFS_IOC_ALLOCSP64:
case XFS_IOC_FREESP64:
case XFS_IOC_RESVSP64:
case XFS_IOC_UNRESVSP64:
case XFS_IOC_FSGEOMETRY_V1:
case XFS_IOC_FSGROWFSDATA:
case XFS_IOC_FSGROWFSRT:
case XFS_IOC_ZERO_RANGE:
#ifdef CONFIG_X86_X32
/*
* x32 special: this gets a different cmd number from the ia-32 compat
* case below; the associated data will match native 64-bit alignment.
*/
case XFS_IOC_SWAPEXT:
#endif
return xfs_file_ioctl(filp, cmd, p);
#endif
#if defined(BROKEN_X86_ALIGNMENT) #if defined(BROKEN_X86_ALIGNMENT)
case XFS_IOC_ALLOCSP_32: case XFS_IOC_ALLOCSP_32:
case XFS_IOC_FREESP_32: case XFS_IOC_FREESP_32:
...@@ -705,6 +654,7 @@ xfs_file_compat_ioctl( ...@@ -705,6 +654,7 @@ xfs_file_compat_ioctl(
case XFS_IOC_FSSETDM_BY_HANDLE_32: case XFS_IOC_FSSETDM_BY_HANDLE_32:
return xfs_compat_fssetdm_by_handle(filp, arg); return xfs_compat_fssetdm_by_handle(filp, arg);
default: default:
return -ENOIOCTLCMD; /* try the native version */
return xfs_file_ioctl(filp, cmd, (unsigned long)arg);
} }
} }
...@@ -32,7 +32,7 @@ xfs_break_leased_layouts( ...@@ -32,7 +32,7 @@ xfs_break_leased_layouts(
struct xfs_inode *ip = XFS_I(inode); struct xfs_inode *ip = XFS_I(inode);
int error; int error;
while ((error = break_layout(inode, false) == -EWOULDBLOCK)) { while ((error = break_layout(inode, false)) == -EWOULDBLOCK) {
xfs_iunlock(ip, *iolock); xfs_iunlock(ip, *iolock);
*did_unlock = true; *did_unlock = true;
error = break_layout(inode, true); error = break_layout(inode, true);
......
...@@ -1190,11 +1190,11 @@ xfs_reflink_remap_blocks( ...@@ -1190,11 +1190,11 @@ xfs_reflink_remap_blocks(
} }
/* /*
* Grab the exclusive iolock for a data copy from src to dest, making * Grab the exclusive iolock for a data copy from src to dest, making sure to
* sure to abide vfs locking order (lowest pointer value goes first) and * abide vfs locking order (lowest pointer value goes first) and breaking the
* breaking the pnfs layout leases on dest before proceeding. The loop * layout leases before proceeding. The loop is needed because we cannot call
* is needed because we cannot call the blocking break_layout() with the * the blocking break_layout() with the iolocks held, and therefore have to
* src iolock held, and therefore have to back out both locks. * back out both locks.
*/ */
static int static int
xfs_iolock_two_inodes_and_break_layout( xfs_iolock_two_inodes_and_break_layout(
...@@ -1203,33 +1203,44 @@ xfs_iolock_two_inodes_and_break_layout( ...@@ -1203,33 +1203,44 @@ xfs_iolock_two_inodes_and_break_layout(
{ {
int error; int error;
retry: if (src > dest)
if (src < dest) { swap(src, dest);
inode_lock_shared(src);
inode_lock_nested(dest, I_MUTEX_NONDIR2);
} else {
/* src >= dest */
inode_lock(dest);
}
error = break_layout(dest, false); retry:
if (error == -EWOULDBLOCK) { /* Wait to break both inodes' layouts before we start locking. */
inode_unlock(dest); error = break_layout(src, true);
if (src < dest) if (error)
inode_unlock_shared(src); return error;
if (src != dest) {
error = break_layout(dest, true); error = break_layout(dest, true);
if (error) if (error)
return error; return error;
goto retry;
} }
/* Lock one inode and make sure nobody got in and leased it. */
inode_lock(src);
error = break_layout(src, false);
if (error) { if (error) {
inode_unlock(src);
if (error == -EWOULDBLOCK)
goto retry;
return error;
}
if (src == dest)
return 0;
/* Lock the other inode and make sure nobody got in and leased it. */
inode_lock_nested(dest, I_MUTEX_NONDIR2);
error = break_layout(dest, false);
if (error) {
inode_unlock(src);
inode_unlock(dest); inode_unlock(dest);
if (src < dest) if (error == -EWOULDBLOCK)
inode_unlock_shared(src); goto retry;
return error; return error;
} }
if (src > dest)
inode_lock_shared_nested(src, I_MUTEX_NONDIR2);
return 0; return 0;
} }
...@@ -1247,10 +1258,10 @@ xfs_reflink_remap_unlock( ...@@ -1247,10 +1258,10 @@ xfs_reflink_remap_unlock(
xfs_iunlock(dest, XFS_MMAPLOCK_EXCL); xfs_iunlock(dest, XFS_MMAPLOCK_EXCL);
if (!same_inode) if (!same_inode)
xfs_iunlock(src, XFS_MMAPLOCK_SHARED); xfs_iunlock(src, XFS_MMAPLOCK_EXCL);
inode_unlock(inode_out); inode_unlock(inode_out);
if (!same_inode) if (!same_inode)
inode_unlock_shared(inode_in); inode_unlock(inode_in);
} }
/* /*
...@@ -1325,7 +1336,7 @@ xfs_reflink_remap_prep( ...@@ -1325,7 +1336,7 @@ xfs_reflink_remap_prep(
if (same_inode) if (same_inode)
xfs_ilock(src, XFS_MMAPLOCK_EXCL); xfs_ilock(src, XFS_MMAPLOCK_EXCL);
else else
xfs_lock_two_inodes(src, XFS_MMAPLOCK_SHARED, dest, xfs_lock_two_inodes(src, XFS_MMAPLOCK_EXCL, dest,
XFS_MMAPLOCK_EXCL); XFS_MMAPLOCK_EXCL);
/* Check file eligibility and prepare for block sharing. */ /* Check file eligibility and prepare for block sharing. */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment