Skip to content

Commit 738adad

Browse files
Lei HuangMiklos Szeredi
authored andcommitted
fuse: Fix missing FOLL_PIN for direct-io
Our user space filesystem relies on fuse to provide POSIX interface. In our test, a known string is written into a file and the content is read back later to verify correct data returned. We observed wrong data returned in read buffer in rare cases although correct data are stored in our filesystem. Fuse kernel module calls iov_iter_get_pages2() to get the physical pages of the user-space read buffer passed in read(). The pages are not pinned to avoid page migration. When page migration occurs, the consequence are two-folds. 1) Applications do not receive correct data in read buffer. 2) fuse kernel writes data into a wrong place. Using iov_iter_extract_pages() to pin pages fixes the issue in our test. An auxiliary variable "struct page **pt_pages" is used in the patch to prepare the 2nd parameter for iov_iter_extract_pages() since iov_iter_get_pages2() uses a different type for the 2nd parameter. [SzM] add iov_iter_extract_will_pin(ii) and unpin only if true. Signed-off-by: Lei Huang <lei.huang@linux.intel.com> Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
1 parent 8a5fb18 commit 738adad

File tree

2 files changed

+11
-5
lines changed

2 files changed

+11
-5
lines changed

fs/fuse/file.c

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -655,7 +655,8 @@ static void fuse_release_user_pages(struct fuse_args_pages *ap,
655655
for (i = 0; i < ap->num_pages; i++) {
656656
if (should_dirty)
657657
set_page_dirty_lock(ap->pages[i]);
658-
put_page(ap->pages[i]);
658+
if (ap->args.is_pinned)
659+
unpin_user_page(ap->pages[i]);
659660
}
660661
}
661662

@@ -1495,10 +1496,13 @@ static int fuse_get_user_pages(struct fuse_args_pages *ap, struct iov_iter *ii,
14951496
while (nbytes < *nbytesp && ap->num_pages < max_pages) {
14961497
unsigned npages;
14971498
size_t start;
1498-
ret = iov_iter_get_pages2(ii, &ap->pages[ap->num_pages],
1499-
*nbytesp - nbytes,
1500-
max_pages - ap->num_pages,
1501-
&start);
1499+
struct page **pt_pages;
1500+
1501+
pt_pages = &ap->pages[ap->num_pages];
1502+
ret = iov_iter_extract_pages(ii, &pt_pages,
1503+
*nbytesp - nbytes,
1504+
max_pages - ap->num_pages,
1505+
0, &start);
15021506
if (ret < 0)
15031507
break;
15041508

@@ -1515,6 +1519,7 @@ static int fuse_get_user_pages(struct fuse_args_pages *ap, struct iov_iter *ii,
15151519
(PAGE_SIZE - ret) & (PAGE_SIZE - 1);
15161520
}
15171521

1522+
ap->args.is_pinned = iov_iter_extract_will_pin(ii);
15181523
ap->args.user_pages = true;
15191524
if (write)
15201525
ap->args.in_pages = true;

fs/fuse/fuse_i.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,7 @@ struct fuse_args {
314314
bool page_replace:1;
315315
bool may_block:1;
316316
bool is_ext:1;
317+
bool is_pinned:1;
317318
struct fuse_in_arg in_args[3];
318319
struct fuse_arg out_args[2];
319320
void (*end)(struct fuse_mount *fm, struct fuse_args *args, int error);

0 commit comments

Comments
 (0)