On Thu, 26 Nov 2020, Matthew Wilcox wrote: > On Wed, Nov 25, 2020 at 04:11:57PM -0800, Hugh Dickins wrote: > > > + index = truncate_inode_partial_page(mapping, > > > + page, lstart, lend); > > > + if (index > end) > > > + end = indices[i] - 1; > > > } > > > - unlock_page(page); > > > > The fix needed is here: instead of deleting that unlock_page(page) > > line, it needs to be } else { unlock_page(page); } > > It also needs a put_page(page); Oh yes indeed, sorry for getting that wrong. I'd misread the pagevec_reinit() at the end as the old pagevec_release(). Do you really need to do pagevec_remove_exceptionals() there if you're not using pagevec_release()? > > That's now taken care of by truncate_inode_partial_page(), so if we're > not calling that, we need to put the page as well. ie this: Right, but I do find it confusing that truncate_inode_partial_page() does the unlock_page(),put_page() whereas truncate_inode_page() does not: I think you would do better to leave them out of _partial_page(), even if including them there saves a couple of lines somewhere else. But right now it's the right fix that's important: ack to yours below. I've not yet worked out the other issues I saw: will report when I have. Rebooted this laptop, pretty sure it missed freeing a shmem swap entry, not yet reproduced, will study the change there later, but the non-swap hang in generic/476 (later seen also in generic/112) more important. Hugh > > +++ b/mm/shmem.c > @@ -954,6 +954,9 @@ static void shmem_undo_range(struct inode *inode, loff_t lstart, loff_t lend, > page, lstart, lend); > if (index > end) > end = indices[i] - 1; > + } else { > + unlock_page(page); > + put_page(page); > } > } > index = indices[i - 1] + 1; > > > > } > > > + index = indices[i - 1] + 1; > > > pagevec_remove_exceptionals(&pvec); > > > - pagevec_release(&pvec); > > > - index++; > > > + pagevec_reinit(&pvec);