On Wed, Jan 11, 2023 at 02:28:35PM +0000, David Howells wrote: > [!] Note that this is tested a bit with ext4, but nothing else. You probably want to also at least test it with block device I/O as that is a slightly different I/O path from iomap. More file systems also never hurt, but aren't quite as important. > +/* > + * Clean up a page appropriately, where the page may be pinned, may have a > + * ref taken on it or neither. > + */ > +static void bio_release_page(struct bio *bio, struct page *page) > +{ > + if (bio_flagged(bio, BIO_PAGE_PINNED)) > + unpin_user_page(page); > + if (bio_flagged(bio, BIO_PAGE_REFFED)) > + put_page(page); > +} > + > void __bio_release_pages(struct bio *bio, bool mark_dirty) > { > struct bvec_iter_all iter_all; > @@ -1183,7 +1197,7 @@ void __bio_release_pages(struct bio *bio, bool mark_dirty) > bio_for_each_segment_all(bvec, bio, iter_all) { > if (mark_dirty && !PageCompound(bvec->bv_page)) > set_page_dirty_lock(bvec->bv_page); > - put_page(bvec->bv_page); > + bio_release_page(bio, bvec->bv_page); So this does look correc an sensible, but given that the new pin/unpin path has a significantly higher overhead I wonder if this might be a good time to switch to folios here as soon as possible in a follow on patch. > + size = iov_iter_extract_pages(iter, &pages, > + UINT_MAX - bio->bi_iter.bi_size, > + nr_pages, gup_flags, > + &offset, &cleanup_mode); > if (unlikely(size <= 0)) > return size ? size : -EFAULT; > > + bio_clear_flag(bio, BIO_PAGE_REFFED); > + bio_clear_flag(bio, BIO_PAGE_PINNED); > + if (cleanup_mode & FOLL_GET) > + bio_set_flag(bio, BIO_PAGE_REFFED); > + if (cleanup_mode & FOLL_PIN) > + bio_set_flag(bio, BIO_PAGE_PINNED); The flags here must not change from one invocation to another, so clearing and resetting them on every iteration seems dangerous. This should probably be a: if (cleanup_mode & FOLL_GET) { WARN_ON_ONCE(bio_test_flag(bio, BIO_PAGE_PINNED)); bio_set_flag(bio, BIO_PAGE_REFFED); } if (cleanup_mode & FOLL_PIN) { WARN_ON_ONCE(bio_test_flag(bio, BIO_PAGE_REFFED)); bio_set_flag(bio, BIO_PAGE_PINNED); }