summaryrefslogtreecommitdiff
path: root/fs/read_write.c
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2019-09-10 14:03:08 +0300
committerTakashi Iwai <tiwai@suse.de>2019-09-10 14:03:08 +0300
commit7711fb7dac1ab77fd1b4d948f4647a569e4a1ae2 (patch)
treedd35ad21e0eb13572757dab279ae41f6d953baed /fs/read_write.c
parent789492f0c86505e63369907bcb1afdf52dec9366 (diff)
parentbb831786117519fc16dfd3eaa7b84e4f6bbb8d99 (diff)
downloadlinux-7711fb7dac1ab77fd1b4d948f4647a569e4a1ae2.tar.xz
Merge tag 'asoc-v5.4' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound into for-next
ASoC: Updates for v5.4 Quite a big update this time around, particularly in the core where we've had a lot of cleanups from Morimoto-san - there's not much functional change but quite a bit of modernization going on. We've also seen a lot of driver work, a lot of it cleanups but also some particular drivers. - Lots and lots of cleanups from Morimoto-san and Yue Haibing. - Lots of cleanups and enhancements to the Freescale, sunxi dnd Intel rivers. - Initial Sound Open Firmware suppot for i.MX8. - Removal of w90x900 and nuc900 drivers as the platforms are being removed. - New support for Cirrus Logic CS47L15 and CS47L92, Freescale i.MX 7ULP and 8MQ, Meson G12A and NXP UDA1334
Diffstat (limited to 'fs/read_write.c')
-rw-r--r--fs/read_write.c49
1 files changed, 41 insertions, 8 deletions
diff --git a/fs/read_write.c b/fs/read_write.c
index 1f5088dec566..5bbf587f5bc1 100644
--- a/fs/read_write.c
+++ b/fs/read_write.c
@@ -1811,10 +1811,7 @@ static int generic_remap_check_len(struct inode *inode_in,
return (remap_flags & REMAP_FILE_DEDUP) ? -EBADE : -EINVAL;
}
-/*
- * Read a page's worth of file data into the page cache. Return the page
- * locked.
- */
+/* Read a page's worth of file data into the page cache. */
static struct page *vfs_dedupe_get_page(struct inode *inode, loff_t offset)
{
struct page *page;
@@ -1826,11 +1823,33 @@ static struct page *vfs_dedupe_get_page(struct inode *inode, loff_t offset)
put_page(page);
return ERR_PTR(-EIO);
}
- lock_page(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.
* 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,
dest_page = vfs_dedupe_get_page(dest, destoff);
if (IS_ERR(dest_page)) {
error = PTR_ERR(dest_page);
- unlock_page(src_page);
put_page(src_page);
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);
dest_addr = kmap_atomic(dest_page);
@@ -1882,8 +1915,8 @@ static int vfs_dedupe_file_range_compare(struct inode *src, loff_t srcoff,
kunmap_atomic(dest_addr);
kunmap_atomic(src_addr);
- unlock_page(dest_page);
- unlock_page(src_page);
+unlock:
+ vfs_unlock_two_pages(src_page, dest_page);
put_page(dest_page);
put_page(src_page);