Re: [PATCH v4 2/3] mm: introduce put_user_page*(), placeholder versions
From: John Hubbard
Date: Tue Oct 09 2018 - 20:42:14 EST
On 10/8/18 5:14 PM, Andrew Morton wrote:
> On Mon, 8 Oct 2018 14:16:22 -0700 john.hubbard@xxxxxxxxx wrote:
>
>> From: John Hubbard <jhubbard@xxxxxxxxxx>
[...]
>> +/*
>> + * Pages that were pinned via get_user_pages*() should be released via
>> + * either put_user_page(), or one of the put_user_pages*() routines
>> + * below.
>> + */
>> +static inline void put_user_page(struct page *page)
>> +{
>> + put_page(page);
>> +}
>> +
>> +static inline void put_user_pages_dirty(struct page **pages,
>> + unsigned long npages)
>> +{
>> + unsigned long index;
>> +
>> + for (index = 0; index < npages; index++) {
>> + if (!PageDirty(pages[index]))
>
> Both put_page() and set_page_dirty() handle compound pages. But
> because of the above statement, put_user_pages_dirty() might misbehave?
> Or maybe it won't - perhaps the intent here is to skip dirtying the
> head page if the sub page is clean? Please clarify, explain and add
> comment if so.
>
Yes, technically, the accounting is wrong: we normally use the head page to
track dirtiness, and here, that is not done. (Nor was it done before this
patch). However, it's not causing problems in code today because sub pages
are released at about the same time as head pages, so the head page does get
properly checked at some point. And that means that set_page_dirty*() gets
called if it needs to be called.
Obviously this is a little fragile, in that it depends on the caller behaving
a certain way. And in any case, the long-term fix (coming later) *also* only
operates on the head page. So actually, instead of a comment, I think it's good
to just insert
page = compound_head(page);
...into these new routines, right now. I'll do that.
[...]
>
> Otherwise looks OK. Ish. But it would be nice if that comment were to
> explain *why* get_user_pages() pages must be released with
> put_user_page().
>
Yes, will do.
> Also, maintainability. What happens if someone now uses put_page() by
> mistake? Kernel fails in some mysterious fashion? How can we prevent
> this from occurring as code evolves? Is there a cheap way of detecting
> this bug at runtime?
>
It might be possible to do a few run-time checks, such as "does page that came
back to put_user_page() have the correct flags?", but it's harder (without
having a dedicated page flag) to detect the other direction: "did someone page
in a get_user_pages page, to put_page?"
As Jan said in his reply, converting get_user_pages (and put_user_page) to
work with a new data type that wraps struct pages, would solve it, but that's
an awfully large change. Still...given how much of a mess this can turn into
if it's wrong, I wonder if it's worth it--maybe?
thanks,
--
John Hubbard
NVIDIA