2022-12-06 07:14:48

by Fabio M. De Francesco

[permalink] [raw]
Subject: [PATCH] mm/highmem: Add notes about conversions from kmap{,_atomic}()

kmap() and kmap_atomic() have been deprecated. kmap_local_page() should
always be used in new code and the call sites of the two deprecated
functions should be converted. This latter task can lead to errors if it
is not carried out with the necessary attention to the context around
and between the maps and unmaps.

Therefore, add further information to the Highmem's documentation for the
purpose to make it clearer that (1) kmap() and kmap_atomic() must not
any longer be called in new code and (2) developers doing conversions from
kmap() amd kmap_atomic() are expected to take care of the context around
and between the maps and unmaps, in order to not break the code.

Relevant parts of this patch have been taken from messages exchanged
privately with Ira Weiny (thanks!).

Cc: Andrew Morton <[email protected]>
Cc: Ira Weiny <[email protected]>
Cc: Jonathan Corbet <[email protected]>
Cc: Mike Rapoport <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Cc: Sebastian Andrzej Siewior <[email protected]>
Cc: Thomas Gleixner <[email protected]>
Signed-off-by: Fabio M. De Francesco <[email protected]>
---
Documentation/mm/highmem.rst | 41 +++++++++++++++++++++++++++---------
1 file changed, 31 insertions(+), 10 deletions(-)

diff --git a/Documentation/mm/highmem.rst b/Documentation/mm/highmem.rst
index 0f731d9196b0..9523e92299f6 100644
--- a/Documentation/mm/highmem.rst
+++ b/Documentation/mm/highmem.rst
@@ -57,7 +57,8 @@ list shows them in order of preference of use.
It can be invoked from any context (including interrupts) but the mappings
can only be used in the context which acquired them.

- This function should be preferred, where feasible, over all the others.
+ This function should always be used. kmap_atomic() and kmap() have been
+ deprecated.

These mappings are thread-local and CPU-local, meaning that the mapping
can only be accessed from within this thread and the thread is bound to the
@@ -100,10 +101,21 @@ list shows them in order of preference of use.
(included in the "Functions" section) for details on how to manage nested
mappings.

-* kmap_atomic(). This permits a very short duration mapping of a single
- page. Since the mapping is restricted to the CPU that issued it, it
- performs well, but the issuing task is therefore required to stay on that
- CPU until it has finished, lest some other task displace its mappings.
+* kmap_atomic(). This function has been deprecated; use kmap_local_page().
+
+ NOTE: Conversions to kmap_local_page() must take care to follow the mapping
+ restrictions imposed on kmap_local_page(). Furthermore, code between the
+ map/unmap operations may implicitly depended on the side effects of
+ kmap_atomic(), such as disabling pagefaults, migration, and/or preemption.
+ Such conversions should be changed to make explicit calls for those
+ requirements.
+
+ [Legacy documentation]
+
+ This permits a very short duration mapping of a single page. Since the
+ mapping is restricted to the CPU that issued it, it performs well, but
+ the issuing task is therefore required to stay on that CPU until it has
+ finished, lest some other task displace its mappings.

kmap_atomic() may also be used by interrupt contexts, since it does not
sleep and the callers too may not sleep until after kunmap_atomic() is
@@ -115,11 +127,20 @@ list shows them in order of preference of use.

It is assumed that k[un]map_atomic() won't fail.

-* kmap(). This should be used to make short duration mapping of a single
- page with no restrictions on preemption or migration. It comes with an
- overhead as mapping space is restricted and protected by a global lock
- for synchronization. When mapping is no longer needed, the address that
- the page was mapped to must be released with kunmap().
+* kmap(). This function has been deprecated; use kmap_local_page().
+
+ NOTE: Conversions to kmap_local_page() must take care to follow the mapping
+ restrictions imposed on kmap_local_page(). In particular, it is necessary to
+ make sure that the kernel virtual memory pointer is only valid in the thread
+ that obtained it.
+
+ [Legacy documentation]
+
+ This should be used to make short duration mapping of a single page with no
+ restrictions on preemption or migration. It comes with an overhead as mapping
+ space is restricted and protected by a global lock for synchronization. When
+ mapping is no longer needed, the address that the page was mapped to must be
+ released with kunmap().

Mapping changes must be propagated across all the CPUs. kmap() also
requires global TLB invalidation when the kmap's pool wraps and it might
--
2.38.1


Subject: Re: [PATCH] mm/highmem: Add notes about conversions from kmap{,_atomic}()

On 2022-12-06 08:00:29 [+0100], Fabio M. De Francesco wrote:
> diff --git a/Documentation/mm/highmem.rst b/Documentation/mm/highmem.rst
> index 0f731d9196b0..9523e92299f6 100644
> --- a/Documentation/mm/highmem.rst
> +++ b/Documentation/mm/highmem.rst
> @@ -100,10 +101,21 @@ list shows them in order of preference of use.
> (included in the "Functions" section) for details on how to manage nested
> mappings.
>
> -* kmap_atomic(). This permits a very short duration mapping of a single
> - page. Since the mapping is restricted to the CPU that issued it, it
> - performs well, but the issuing task is therefore required to stay on that
> - CPU until it has finished, lest some other task displace its mappings.
> +* kmap_atomic(). This function has been deprecated; use kmap_local_page().
> +
> + NOTE: Conversions to kmap_local_page() must take care to follow the mapping
> + restrictions imposed on kmap_local_page(). Furthermore, code between the
> + map/unmap operations may implicitly depended on the side effects of
> + kmap_atomic(), such as disabling pagefaults, migration, and/or preemption.
> + Such conversions should be changed to make explicit calls for those
> + requirements.

Furthermore, code between the kmap_atomic() and kunmap_atomic()
functions may implicitly depended on the side effects of kmap_atomic()
namely disabling pagefaults or preemption or both.

> + [Legacy documentation]
> +
> + This permits a very short duration mapping of a single page. Since the
> + mapping is restricted to the CPU that issued it, it performs well, but
> + the issuing task is therefore required to stay on that CPU until it has
> + finished, lest some other task displace its mappings.
>
> kmap_atomic() may also be used by interrupt contexts, since it does not
> sleep and the callers too may not sleep until after kunmap_atomic() is

Sebastian

2022-12-06 19:28:40

by Fabio M. De Francesco

[permalink] [raw]
Subject: Re: [PATCH] mm/highmem: Add notes about conversions from kmap{,_atomic}()

On marted? 6 dicembre 2022 09:00:10 CET Sebastian Andrzej Siewior wrote:
> On 2022-12-06 08:00:29 [+0100], Fabio M. De Francesco wrote:
> > diff --git a/Documentation/mm/highmem.rst b/Documentation/mm/highmem.rst
> > index 0f731d9196b0..9523e92299f6 100644
> > --- a/Documentation/mm/highmem.rst
> > +++ b/Documentation/mm/highmem.rst
> > @@ -100,10 +101,21 @@ list shows them in order of preference of use.
> >
> > (included in the "Functions" section) for details on how to manage
nested
> > mappings.
> >
> > -* kmap_atomic(). This permits a very short duration mapping of a single
> > - page. Since the mapping is restricted to the CPU that issued it, it
> > - performs well, but the issuing task is therefore required to stay on
that
> > - CPU until it has finished, lest some other task displace its mappings.
> > +* kmap_atomic(). This function has been deprecated; use
kmap_local_page().
> > +
> > + NOTE: Conversions to kmap_local_page() must take care to follow the
> > mapping + restrictions imposed on kmap_local_page(). Furthermore, code
> > between the + map/unmap operations may implicitly depended on the side
> > effects of + kmap_atomic(), such as disabling pagefaults, migration,
> > and/or preemption. + Such conversions should be changed to make explicit
> > calls for those + requirements.

Sebastian, thanks for taking a look at my patch and replying.

> Furthermore, code between the kmap_atomic() and kunmap_atomic()
> functions may implicitly depended

I suppose it should be "depend"? Shouldn't it?

> on the side effects of kmap_atomic()
> namely disabling pagefaults or preemption or both.

I agree with you for rephrasing, mainly because it is
written in poor English.

However, I still have doubts about why you deleted "migration".
AFAIK, __kmap_local_pfn_prot() always takes care of disabling migration for
HIGHMEM enabled kernels.

How about !HIGHMEM, where kmap_local_page() is an indirect call to
page_address()? Did you mean that, if the code between kmap_atomic() and
kunmap_atomic() depended on migrate_disable() (in PREEMPT_RT) we should always
just stay safe and call preempt_disable() together with conversion to
kmap_local_page()?

If so, I understand and I again agree with you. If not, I'm missing something;
so please let me understand properly.

Aside from the above, I'm not sure whether you deleted the last phrase before
your suggestion. What about making it to become "For the above-mentioned
cases, conversions should also explicitly disable page-faults and/or
preemption"?

Thanks again for noticing my mistakes.

Fabio

>
> > + [Legacy documentation]
> > +
> > + This permits a very short duration mapping of a single page. Since the
> > + mapping is restricted to the CPU that issued it, it performs well, but
> > + the issuing task is therefore required to stay on that CPU until it has
> > + finished, lest some other task displace its mappings.
> >
> > kmap_atomic() may also be used by interrupt contexts, since it does not
> > sleep and the callers too may not sleep until after kunmap_atomic() is
>
> Sebastian




2022-12-06 20:08:19

by Fabio M. De Francesco

[permalink] [raw]
Subject: Re: [PATCH] mm/highmem: Add notes about conversions from kmap{,_atomic}()

On martedì 6 dicembre 2022 09:00:10 CET Sebastian Andrzej Siewior wrote:
> On 2022-12-06 08:00:29 [+0100], Fabio M. De Francesco wrote:
> > diff --git a/Documentation/mm/highmem.rst b/Documentation/mm/highmem.rst
> > index 0f731d9196b0..9523e92299f6 100644
> > --- a/Documentation/mm/highmem.rst
> > +++ b/Documentation/mm/highmem.rst
> > @@ -100,10 +101,21 @@ list shows them in order of preference of use.
> >
> > (included in the "Functions" section) for details on how to manage
nested
> > mappings.
> >
> > -* kmap_atomic(). This permits a very short duration mapping of a single
> > - page. Since the mapping is restricted to the CPU that issued it, it
> > - performs well, but the issuing task is therefore required to stay on
that
> > - CPU until it has finished, lest some other task displace its mappings.
> > +* kmap_atomic(). This function has been deprecated; use
kmap_local_page().
> > +
> > + NOTE: Conversions to kmap_local_page() must take care to follow the
> > mapping + restrictions imposed on kmap_local_page(). Furthermore, code
> > between the + map/unmap operations may implicitly depended on the side
> > effects of + kmap_atomic(), such as disabling pagefaults, migration,
> > and/or preemption. + Such conversions should be changed to make explicit
> > calls for those + requirements.

Sebastian, thanks for taking a look at my patch and replying.

> Furthermore, code between the kmap_atomic() and kunmap_atomic()
> functions may implicitly depended

I suppose it should be "depend"? Shouldn't it?

> on the side effects of kmap_atomic()
> namely disabling pagefaults or preemption or both.

I agree with you for rephrasing, mainly because it is
written in poor English.

However, I still have doubts about why you deleted "migration".
AFAIK, __kmap_local_pfn_prot() always takes care of disabling migration for
HIGHMEM enabled kernels.

How about !HIGHMEM, where kmap_local_page() is an indirect call to
page_address()? Did you mean that, if the code between kmap_atomic() and
kunmap_atomic() depended on migrate_disable() (in PREEMPT_RT) we should always
just stay safe and call preempt_disable() together with conversion to
kmap_local_page()?

If so, I understand and I again agree with you. If not, I'm missing something;
so please let me understand properly.

Aside from the above, I'm not sure whether you deleted the last phrase before
your suggestion. What about making it to become "For the above-mentioned
cases, conversions should also explicitly disable page-faults and/or
preemption"?

Thanks again for noticing my mistakes.


Fabio

>
> > + [Legacy documentation]
> > +
> > + This permits a very short duration mapping of a single page. Since the
> > + mapping is restricted to the CPU that issued it, it performs well, but
> > + the issuing task is therefore required to stay on that CPU until it has
> > + finished, lest some other task displace its mappings.
> >
> > kmap_atomic() may also be used by interrupt contexts, since it does not
> > sleep and the callers too may not sleep until after kunmap_atomic() is
>
> Sebastian

Subject: Re: [PATCH] mm/highmem: Add notes about conversions from kmap{,_atomic}()

On 2022-12-06 20:12:13 [+0100], Fabio M. De Francesco wrote:
> > Furthermore, code between the kmap_atomic() and kunmap_atomic()
> > functions may implicitly depended
>
> I suppose it should be "depend"? Shouldn't it?

Ehm, yes, correct.

> > on the side effects of kmap_atomic()
> > namely disabling pagefaults or preemption or both.
>
> I agree with you for rephrasing, mainly because it is
> written in poor English.
>
> However, I still have doubts about why you deleted "migration".
> AFAIK, __kmap_local_pfn_prot() always takes care of disabling migration for
> HIGHMEM enabled kernels.

That is correct. Historically kmap_atomic() never had a
migrate_disable() statement - only preempt_disable(). With disabled
preemption the task migration is implicitly disabled.

> How about !HIGHMEM, where kmap_local_page() is an indirect call to
> page_address()? Did you mean that, if the code between kmap_atomic() and
> kunmap_atomic() depended on migrate_disable() (in PREEMPT_RT) we should always
> just stay safe and call preempt_disable() together with conversion to
> kmap_local_page()?

Even in the !HIGHMEM case it always uses preempt_disable(). With
PREEMPT_RT it is different as it never disabled preemption and always
did a migrate_disable() instead. If you talk about what needs to be
considered while migrating away from kmap_atomic() then I wouldn't add
the PREEMPT_RT bits to it since it was never in the picture while the
code (using kmap_atomic()) was originally written.

> If so, I understand and I again agree with you. If not, I'm missing something;
> so please let me understand properly.
>
> Aside from the above, I'm not sure whether you deleted the last phrase before
> your suggestion. What about making it to become "For the above-mentioned
> cases, conversions should also explicitly disable page-faults and/or
> preemption"?

They need to disable preemption or page-faults or both if it is needed
(not unconditionally) and where it is needed. This means not
unconditionally over the whole kmap-ed section.

> Thanks again for noticing my mistakes.
>
> Fabio

Sebastian

2022-12-07 13:40:25

by Fabio M. De Francesco

[permalink] [raw]
Subject: Re: [PATCH] mm/highmem: Add notes about conversions from kmap{,_atomic}()

On mercoled? 7 dicembre 2022 09:00:29 CET Sebastian Andrzej Siewior wrote:
> On 2022-12-06 20:12:13 [+0100], Fabio M. De Francesco wrote:
> > > Furthermore, code between the kmap_atomic() and kunmap_atomic()
> > > functions may implicitly depended
> >
> > I suppose it should be "depend"? Shouldn't it?
>
> Ehm, yes, correct.
>
> > > on the side effects of kmap_atomic()
> > > namely disabling pagefaults or preemption or both.
> >
> > I agree with you for rephrasing, mainly because it is
> > written in poor English.
> >
> > However, I still have doubts about why you deleted "migration".
> > AFAIK, __kmap_local_pfn_prot() always takes care of disabling migration
for
> > HIGHMEM enabled kernels.
>
> That is correct. Historically kmap_atomic() never had a
> migrate_disable() statement - only preempt_disable(). With disabled
> preemption the task migration is implicitly disabled.

Sure, I understand this mechanism: task migration is implicitly disabled with
disabled preemption.

>
> > How about !HIGHMEM, where kmap_local_page() is an indirect call to
> > page_address()? Did you mean that, if the code between kmap_atomic() and
> > kunmap_atomic() depended on migrate_disable() (in PREEMPT_RT) we should
> > always just stay safe and call preempt_disable() together with conversion
> > to kmap_local_page()?
>
> Even in the !HIGHMEM case it always uses preempt_disable().

With the only exception of PREEMPT_RT kernels, which instead use
migrate_disable().

> With
> PREEMPT_RT it is different as it never disabled preemption and always
> did a migrate_disable() instead.

OK, I see that I'm recalling correctly :-)

> If you talk about what needs to be
> considered while migrating away from kmap_atomic()

Yes, I'm trying to explain what needs to be considered while converting from
kmap_atomic() by looking at all the different cases.

> then I wouldn't add
> the PREEMPT_RT bits to it since it was never in the picture while the
> code (using kmap_atomic()) was originally written.

Ah, OK. Now I understand why you changed my last phrase.
I agree with you, so I won't add anything about the special PREEMPT_RT case.

> > If so, I understand and I again agree with you. If not, I'm missing
> > something; so please let me understand properly.
> >
> > Aside from the above, I'm not sure whether you deleted the last phrase
> > before
> > your suggestion. What about making it to become "For the above-mentioned
> > cases, conversions should also explicitly disable page-faults and/or
> > preemption"?
>
> They need to disable preemption or page-faults or both if it is needed
> (not unconditionally) and where it is needed. This means not
> unconditionally over the whole kmap-ed section.

I never meant to suggest to _unconditionally_ disable page-faults
and/or preemption. I was only trying to say that developers must carefully
check whether or not the whole kmap-ed section depended on those side effects.

If so, they must _explicitly_ disable preemption or page-faults or both
together with the use of kmap_local_page(). Instead, if the section doesn't
depend on preemption and/or page-faults disabling, they must only replace
kmap_atomic() with kmap_local_page().

I had probably used a bad wording when trying to say the same things that you
wrote much more clearly.

Thanks,

Fabio

>
> > Thanks again for noticing my mistakes.
> >
> > Fabio
>
> Sebastian




Subject: Re: [PATCH] mm/highmem: Add notes about conversions from kmap{,_atomic}()

On 2022-12-07 14:01:50 [+0100], Fabio M. De Francesco wrote:
> > > If so, I understand and I again agree with you. If not, I'm missing
> > > something; so please let me understand properly.
> > >
> > > Aside from the above, I'm not sure whether you deleted the last phrase
> > > before
> > > your suggestion. What about making it to become "For the above-mentioned
> > > cases, conversions should also explicitly disable page-faults and/or
> > > preemption"?
> >
> > They need to disable preemption or page-faults or both if it is needed
> > (not unconditionally) and where it is needed. This means not
> > unconditionally over the whole kmap-ed section.
>
> I never meant to suggest to _unconditionally_ disable page-faults
> and/or preemption. I was only trying to say that developers must carefully
> check whether or not the whole kmap-ed section depended on those side effects.

I know. That are the two condition that should be checked/ kept in mind
while replacing the code. Maybe I read it wrongly…

> If so, they must _explicitly_ disable preemption or page-faults or both
> together with the use of kmap_local_page().

Right. The requirement for it should be probably documented in case it
is not obvious. For PREEMPT_RT it will become a problem if the preempt
disabled section additionally acquired a spinlock_t or allocated memory.
So ideally it won't be used ;)

> Instead, if the section doesn't
> depend on preemption and/or page-faults disabling, they must only replace
> kmap_atomic() with kmap_local_page().

Correct and I assumed that you know all this.

> I had probably used a bad wording when trying to say the same things that you
> wrote much more clearly.

Write it as you wish I just made a recommendation. If the wording is
crystal clear then there is less room for interpretations.

> Thanks,
>
> Fabio

Sebastian

2022-12-08 00:21:07

by Fabio M. De Francesco

[permalink] [raw]
Subject: Re: [PATCH] mm/highmem: Add notes about conversions from kmap{,_atomic}()

On mercoledì 7 dicembre 2022 14:51:00 CET Sebastian Andrzej Siewior wrote:
> On 2022-12-07 14:01:50 [+0100], Fabio M. De Francesco wrote:
> > > > If so, I understand and I again agree with you. If not, I'm missing
> > > > something; so please let me understand properly.
> > > >
> > > > Aside from the above, I'm not sure whether you deleted the last phrase
> > > > before
> > > > your suggestion. What about making it to become "For the above-
mentioned
> > > > cases, conversions should also explicitly disable page-faults and/or
> > > > preemption"?
> > >
> > > They need to disable preemption or page-faults or both if it is needed
> > > (not unconditionally) and where it is needed. This means not
> > > unconditionally over the whole kmap-ed section.
> >
> > I never meant to suggest to _unconditionally_ disable page-faults
> > and/or preemption. I was only trying to say that developers must carefully
> > check whether or not the whole kmap-ed section depended on those side
> > effects.
> I know. That are the two condition that should be checked/ kept in mind
> while replacing the code. Maybe I read it wrongly…
>
> > If so, they must _explicitly_ disable preemption or page-faults or both
> > together with the use of kmap_local_page().
>
> Right. The requirement for it should be probably documented in case it
> is not obvious. For PREEMPT_RT it will become a problem if the preempt
> disabled section additionally acquired a spinlock_t or allocated memory.
> So ideally it won't be used ;)
>
> > Instead, if the section
doesn't
> >
> > depend on preemption and/or page-faults disabling, they must only replace
> > kmap_atomic() with kmap_local_page().
>
> Correct and I assumed that you know all this.
>
> > I had probably used a bad wording when trying to say the same things that
> > you
> > wrote much more clearly.
>
> Write it as you wish I just made a recommendation. If the wording is
> crystal clear then there is less room for interpretations.

I just sent v2 of this patch.[1] I hope that now I left less room for
potential misinterpretation by merging your suggestion with the old text.

Again thanks for helping,

Fabio

[1] https://lore.kernel.org/lkml/[email protected]/T/