[2/2] smp: convert cpu_hotplug_begin into a blocking lock acquisition
diff mbox series

Message ID 20200213113237.58795-3-roger.pau@citrix.com
State New
Headers show
Series
  • smp: convert cpu_add_remove_lock int a rw lock
Related show

Commit Message

Roger Pau Monné Feb. 13, 2020, 11:32 a.m. UTC
Don't allow cpu_hotplug_begin to fail by converting the trylock into a
blocking lock acquisition. Write users of the cpu_add_remove_lock are
limited to CPU plug/unplug operations, and cannot deadlock between
themselves or other users taking the lock in read mode as
cpu_add_remove_lock is always locked with interrupts enabled. There
are also no other locks taken during the plug/unplug operations.

The exclusive lock usage in register_cpu_notifier is also converted
into a blocking lock acquisition, as it was previously not allowed to
fail anyway.

This is meaningful when running Xen in shim mode, since VCPU_{up/down}
hypercalls use cpu hotplug/unplug operations in the background, and
hence failing to take the lock results in VPCU_{up/down} failing with
-EBUSY, which most users are not prepared to handle.

Signed-off-by: Roger Pau Monné <roger.pau@citrix.com>
---
I've tested this and seems to work fine AFAICT either when running on
native or when used in the shim. I'm not sure if I'm missing something
that would prevent the write lock acquisition from being made
blocking.
---
 xen/arch/x86/smpboot.c |  3 +--
 xen/common/cpu.c       | 13 +++++--------
 xen/include/xen/cpu.h  |  2 +-
 3 files changed, 7 insertions(+), 11 deletions(-)

Comments

Jan Beulich Feb. 19, 2020, 12:59 p.m. UTC | #1
On 13.02.2020 12:32, Roger Pau Monne wrote:
> Don't allow cpu_hotplug_begin to fail by converting the trylock into a
> blocking lock acquisition. Write users of the cpu_add_remove_lock are
> limited to CPU plug/unplug operations, and cannot deadlock between
> themselves or other users taking the lock in read mode as
> cpu_add_remove_lock is always locked with interrupts enabled. There
> are also no other locks taken during the plug/unplug operations.

I don't think the goal was deadlock avoidance, but rather limiting
of the time spent spinning while trying to acquire the lock, in
favor of having the caller retry.

Jan
Roger Pau Monné Feb. 19, 2020, 1:22 p.m. UTC | #2
On Wed, Feb 19, 2020 at 01:59:51PM +0100, Jan Beulich wrote:
> On 13.02.2020 12:32, Roger Pau Monne wrote:
> > Don't allow cpu_hotplug_begin to fail by converting the trylock into a
> > blocking lock acquisition. Write users of the cpu_add_remove_lock are
> > limited to CPU plug/unplug operations, and cannot deadlock between
> > themselves or other users taking the lock in read mode as
> > cpu_add_remove_lock is always locked with interrupts enabled. There
> > are also no other locks taken during the plug/unplug operations.
> 
> I don't think the goal was deadlock avoidance, but rather limiting
> of the time spent spinning while trying to acquire the lock, in
> favor of having the caller retry.

Now that the contention between read-only users is reduced as those
can take the lock in parallel I think it's safe to switch writers to
blocking mode.

Thanks, Roger.
Jan Beulich Feb. 19, 2020, 1:44 p.m. UTC | #3
On 19.02.2020 14:22, Roger Pau Monné wrote:
> On Wed, Feb 19, 2020 at 01:59:51PM +0100, Jan Beulich wrote:
>> On 13.02.2020 12:32, Roger Pau Monne wrote:
>>> Don't allow cpu_hotplug_begin to fail by converting the trylock into a
>>> blocking lock acquisition. Write users of the cpu_add_remove_lock are
>>> limited to CPU plug/unplug operations, and cannot deadlock between
>>> themselves or other users taking the lock in read mode as
>>> cpu_add_remove_lock is always locked with interrupts enabled. There
>>> are also no other locks taken during the plug/unplug operations.
>>
>> I don't think the goal was deadlock avoidance, but rather limiting
>> of the time spent spinning while trying to acquire the lock, in
>> favor of having the caller retry.
> 
> Now that the contention between read-only users is reduced as those
> can take the lock in parallel I think it's safe to switch writers to
> blocking mode.

I'd agree if writers couldn't be starved by (many) readers.

Jan
Roger Pau Monné Feb. 19, 2020, 2:45 p.m. UTC | #4
On Wed, Feb 19, 2020 at 02:44:12PM +0100, Jan Beulich wrote:
> On 19.02.2020 14:22, Roger Pau Monné wrote:
> > On Wed, Feb 19, 2020 at 01:59:51PM +0100, Jan Beulich wrote:
> >> On 13.02.2020 12:32, Roger Pau Monne wrote:
> >>> Don't allow cpu_hotplug_begin to fail by converting the trylock into a
> >>> blocking lock acquisition. Write users of the cpu_add_remove_lock are
> >>> limited to CPU plug/unplug operations, and cannot deadlock between
> >>> themselves or other users taking the lock in read mode as
> >>> cpu_add_remove_lock is always locked with interrupts enabled. There
> >>> are also no other locks taken during the plug/unplug operations.
> >>
> >> I don't think the goal was deadlock avoidance, but rather limiting
> >> of the time spent spinning while trying to acquire the lock, in
> >> favor of having the caller retry.
> > 
> > Now that the contention between read-only users is reduced as those
> > can take the lock in parallel I think it's safe to switch writers to
> > blocking mode.
> 
> I'd agree if writers couldn't be starved by (many) readers.

AFAICT from the rw lock implementation readers won't be able to pick
the lock as soon as there's a writer waiting, which should avoid this
starvation?

You still need to wait for current readers to drop the lock, but no
new readers would be able to lock it, which I think should givbe us
enough fairness. OTOH when using _trylock new readers can still pick
the lock in read mode, and hence I think using blocking mode for
writers is actually better, as you can assure that readers won't be
able to starve writers.

Thanks, Roger.
Jan Beulich Feb. 19, 2020, 2:57 p.m. UTC | #5
On 19.02.2020 15:45, Roger Pau Monné wrote:
> On Wed, Feb 19, 2020 at 02:44:12PM +0100, Jan Beulich wrote:
>> On 19.02.2020 14:22, Roger Pau Monné wrote:
>>> On Wed, Feb 19, 2020 at 01:59:51PM +0100, Jan Beulich wrote:
>>>> On 13.02.2020 12:32, Roger Pau Monne wrote:
>>>>> Don't allow cpu_hotplug_begin to fail by converting the trylock into a
>>>>> blocking lock acquisition. Write users of the cpu_add_remove_lock are
>>>>> limited to CPU plug/unplug operations, and cannot deadlock between
>>>>> themselves or other users taking the lock in read mode as
>>>>> cpu_add_remove_lock is always locked with interrupts enabled. There
>>>>> are also no other locks taken during the plug/unplug operations.
>>>>
>>>> I don't think the goal was deadlock avoidance, but rather limiting
>>>> of the time spent spinning while trying to acquire the lock, in
>>>> favor of having the caller retry.
>>>
>>> Now that the contention between read-only users is reduced as those
>>> can take the lock in parallel I think it's safe to switch writers to
>>> blocking mode.
>>
>> I'd agree if writers couldn't be starved by (many) readers.
> 
> AFAICT from the rw lock implementation readers won't be able to pick
> the lock as soon as there's a writer waiting, which should avoid this
> starvation?
> 
> You still need to wait for current readers to drop the lock, but no
> new readers would be able to lock it, which I think should givbe us
> enough fairness.

Ah, right, it was rather the other way around - back-to-back
writers can starve readers with our current implementation.

> OTOH when using _trylock new readers can still pick
> the lock in read mode, and hence I think using blocking mode for
> writers is actually better, as you can assure that readers won't be
> able to starve writers.

This is a good point. Nevertheless I remain unconvinced that
the change is warranted given the original intentions (as far
as we're able to reconstruct them). If the current behavior
gets in the way of sensible shim operation, perhaps the
behavior should be made dependent upon running in shim mode?

In any event I think the commit message here would want
updating. In the meantime I think I'll commit patch 1 with
Andrew's ack.

Jan
Andrew Cooper Feb. 19, 2020, 2:58 p.m. UTC | #6
On 19/02/2020 13:44, Jan Beulich wrote:
> On 19.02.2020 14:22, Roger Pau Monné wrote:
>> On Wed, Feb 19, 2020 at 01:59:51PM +0100, Jan Beulich wrote:
>>> On 13.02.2020 12:32, Roger Pau Monne wrote:
>>>> Don't allow cpu_hotplug_begin to fail by converting the trylock into a
>>>> blocking lock acquisition. Write users of the cpu_add_remove_lock are
>>>> limited to CPU plug/unplug operations, and cannot deadlock between
>>>> themselves or other users taking the lock in read mode as
>>>> cpu_add_remove_lock is always locked with interrupts enabled. There
>>>> are also no other locks taken during the plug/unplug operations.
>>> I don't think the goal was deadlock avoidance, but rather limiting
>>> of the time spent spinning while trying to acquire the lock, in
>>> favor of having the caller retry.
>> Now that the contention between read-only users is reduced as those
>> can take the lock in parallel I think it's safe to switch writers to
>> blocking mode.
> I'd agree if writers couldn't be starved by (many) readers.

XSA-114.

We fixed that vulnerability ages ago.

A writer wanting the lock will prevent further readers from taking it
before the writer drops it.

~Andrew
Andrew Cooper Feb. 19, 2020, 3:07 p.m. UTC | #7
On 19/02/2020 14:57, Jan Beulich wrote:
> On 19.02.2020 15:45, Roger Pau Monné wrote:
>> On Wed, Feb 19, 2020 at 02:44:12PM +0100, Jan Beulich wrote:
>>> On 19.02.2020 14:22, Roger Pau Monné wrote:
>>>> On Wed, Feb 19, 2020 at 01:59:51PM +0100, Jan Beulich wrote:
>>>>> On 13.02.2020 12:32, Roger Pau Monne wrote:
>>>>>> Don't allow cpu_hotplug_begin to fail by converting the trylock into a
>>>>>> blocking lock acquisition. Write users of the cpu_add_remove_lock are
>>>>>> limited to CPU plug/unplug operations, and cannot deadlock between
>>>>>> themselves or other users taking the lock in read mode as
>>>>>> cpu_add_remove_lock is always locked with interrupts enabled. There
>>>>>> are also no other locks taken during the plug/unplug operations.
>>>>> I don't think the goal was deadlock avoidance, but rather limiting
>>>>> of the time spent spinning while trying to acquire the lock, in
>>>>> favor of having the caller retry.
>>>> Now that the contention between read-only users is reduced as those
>>>> can take the lock in parallel I think it's safe to switch writers to
>>>> blocking mode.
>>> I'd agree if writers couldn't be starved by (many) readers.
>> AFAICT from the rw lock implementation readers won't be able to pick
>> the lock as soon as there's a writer waiting, which should avoid this
>> starvation?
>>
>> You still need to wait for current readers to drop the lock, but no
>> new readers would be able to lock it, which I think should givbe us
>> enough fairness.
> Ah, right, it was rather the other way around - back-to-back
> writers can starve readers with our current implementation.
>
>> OTOH when using _trylock new readers can still pick
>> the lock in read mode, and hence I think using blocking mode for
>> writers is actually better, as you can assure that readers won't be
>> able to starve writers.
> This is a good point. Nevertheless I remain unconvinced that
> the change is warranted given the original intentions (as far
> as we're able to reconstruct them). If the current behavior
> gets in the way of sensible shim operation, perhaps the
> behavior should be made dependent upon running in shim mode?

Hotplug isn't generally used at all, so there is 0 write pressure on the
lock.

When it is used, it is all at explicit request from the controlling
entity in the system (hardware domain, or singleton shim domain).

If that entity is trying to DoS you, you've already lost.

There might be attempts to justify why the locking was done like that in
the first place, but it doesn't mean they were necessarily correct to
being with, and they don't match up with the realistic usage of the lock.

~Andrew
Jan Beulich Feb. 19, 2020, 4:06 p.m. UTC | #8
On 19.02.2020 16:07, Andrew Cooper wrote:
> On 19/02/2020 14:57, Jan Beulich wrote:
>> On 19.02.2020 15:45, Roger Pau Monné wrote:
>>> On Wed, Feb 19, 2020 at 02:44:12PM +0100, Jan Beulich wrote:
>>>> On 19.02.2020 14:22, Roger Pau Monné wrote:
>>>>> On Wed, Feb 19, 2020 at 01:59:51PM +0100, Jan Beulich wrote:
>>>>>> On 13.02.2020 12:32, Roger Pau Monne wrote:
>>>>>>> Don't allow cpu_hotplug_begin to fail by converting the trylock into a
>>>>>>> blocking lock acquisition. Write users of the cpu_add_remove_lock are
>>>>>>> limited to CPU plug/unplug operations, and cannot deadlock between
>>>>>>> themselves or other users taking the lock in read mode as
>>>>>>> cpu_add_remove_lock is always locked with interrupts enabled. There
>>>>>>> are also no other locks taken during the plug/unplug operations.
>>>>>> I don't think the goal was deadlock avoidance, but rather limiting
>>>>>> of the time spent spinning while trying to acquire the lock, in
>>>>>> favor of having the caller retry.
>>>>> Now that the contention between read-only users is reduced as those
>>>>> can take the lock in parallel I think it's safe to switch writers to
>>>>> blocking mode.
>>>> I'd agree if writers couldn't be starved by (many) readers.
>>> AFAICT from the rw lock implementation readers won't be able to pick
>>> the lock as soon as there's a writer waiting, which should avoid this
>>> starvation?
>>>
>>> You still need to wait for current readers to drop the lock, but no
>>> new readers would be able to lock it, which I think should givbe us
>>> enough fairness.
>> Ah, right, it was rather the other way around - back-to-back
>> writers can starve readers with our current implementation.
>>
>>> OTOH when using _trylock new readers can still pick
>>> the lock in read mode, and hence I think using blocking mode for
>>> writers is actually better, as you can assure that readers won't be
>>> able to starve writers.
>> This is a good point. Nevertheless I remain unconvinced that
>> the change is warranted given the original intentions (as far
>> as we're able to reconstruct them). If the current behavior
>> gets in the way of sensible shim operation, perhaps the
>> behavior should be made dependent upon running in shim mode?
> 
> Hotplug isn't generally used at all, so there is 0 write pressure on the
> lock.
> 
> When it is used, it is all at explicit request from the controlling
> entity in the system (hardware domain, or singleton shim domain).
> 
> If that entity is trying to DoS you, you've already lost.

But write pressure was never in question. My concern is with
how long it might take for all readers to drop their locks.

Jan
Roger Pau Monné Feb. 19, 2020, 4:08 p.m. UTC | #9
On Wed, Feb 19, 2020 at 03:07:14PM +0000, Andrew Cooper wrote:
> On 19/02/2020 14:57, Jan Beulich wrote:
> > On 19.02.2020 15:45, Roger Pau Monné wrote:
> >> On Wed, Feb 19, 2020 at 02:44:12PM +0100, Jan Beulich wrote:
> >>> On 19.02.2020 14:22, Roger Pau Monné wrote:
> >>>> On Wed, Feb 19, 2020 at 01:59:51PM +0100, Jan Beulich wrote:
> >>>>> On 13.02.2020 12:32, Roger Pau Monne wrote:
> >>>>>> Don't allow cpu_hotplug_begin to fail by converting the trylock into a
> >>>>>> blocking lock acquisition. Write users of the cpu_add_remove_lock are
> >>>>>> limited to CPU plug/unplug operations, and cannot deadlock between
> >>>>>> themselves or other users taking the lock in read mode as
> >>>>>> cpu_add_remove_lock is always locked with interrupts enabled. There
> >>>>>> are also no other locks taken during the plug/unplug operations.
> >>>>> I don't think the goal was deadlock avoidance, but rather limiting
> >>>>> of the time spent spinning while trying to acquire the lock, in
> >>>>> favor of having the caller retry.
> >>>> Now that the contention between read-only users is reduced as those
> >>>> can take the lock in parallel I think it's safe to switch writers to
> >>>> blocking mode.
> >>> I'd agree if writers couldn't be starved by (many) readers.
> >> AFAICT from the rw lock implementation readers won't be able to pick
> >> the lock as soon as there's a writer waiting, which should avoid this
> >> starvation?
> >>
> >> You still need to wait for current readers to drop the lock, but no
> >> new readers would be able to lock it, which I think should givbe us
> >> enough fairness.
> > Ah, right, it was rather the other way around - back-to-back
> > writers can starve readers with our current implementation.
> >
> >> OTOH when using _trylock new readers can still pick
> >> the lock in read mode, and hence I think using blocking mode for
> >> writers is actually better, as you can assure that readers won't be
> >> able to starve writers.
> > This is a good point. Nevertheless I remain unconvinced that
> > the change is warranted given the original intentions (as far
> > as we're able to reconstruct them). If the current behavior
> > gets in the way of sensible shim operation, perhaps the
> > behavior should be made dependent upon running in shim mode?
> 
> Hotplug isn't generally used at all, so there is 0 write pressure on the
> lock.
> 
> When it is used, it is all at explicit request from the controlling
> entity in the system (hardware domain, or singleton shim domain).
> 
> If that entity is trying to DoS you, you've already lost.
> 
> There might be attempts to justify why the locking was done like that in
> the first place, but it doesn't mean they were necessarily correct to
> being with, and they don't match up with the realistic usage of the lock.

I'm happy to rewrite the commit message in order to include the
discussion here. What about adding:

Note that when using rw locks a writer wanting to take the lock will
prevent further reads from locking it, hence preventing readers from
starving writers. Writers OTOH could starve readers, but since the
lock is only picked in write mode by actions requested by privileged
domains such entities already have the ability to DoS the hypervisor
in many other ways.

Thanks, Roger.
Roger Pau Monné Feb. 19, 2020, 4:26 p.m. UTC | #10
On Wed, Feb 19, 2020 at 05:06:20PM +0100, Jan Beulich wrote:
> On 19.02.2020 16:07, Andrew Cooper wrote:
> > On 19/02/2020 14:57, Jan Beulich wrote:
> >> On 19.02.2020 15:45, Roger Pau Monné wrote:
> >>> On Wed, Feb 19, 2020 at 02:44:12PM +0100, Jan Beulich wrote:
> >>>> On 19.02.2020 14:22, Roger Pau Monné wrote:
> >>>>> On Wed, Feb 19, 2020 at 01:59:51PM +0100, Jan Beulich wrote:
> >>>>>> On 13.02.2020 12:32, Roger Pau Monne wrote:
> >>>>>>> Don't allow cpu_hotplug_begin to fail by converting the trylock into a
> >>>>>>> blocking lock acquisition. Write users of the cpu_add_remove_lock are
> >>>>>>> limited to CPU plug/unplug operations, and cannot deadlock between
> >>>>>>> themselves or other users taking the lock in read mode as
> >>>>>>> cpu_add_remove_lock is always locked with interrupts enabled. There
> >>>>>>> are also no other locks taken during the plug/unplug operations.
> >>>>>> I don't think the goal was deadlock avoidance, but rather limiting
> >>>>>> of the time spent spinning while trying to acquire the lock, in
> >>>>>> favor of having the caller retry.
> >>>>> Now that the contention between read-only users is reduced as those
> >>>>> can take the lock in parallel I think it's safe to switch writers to
> >>>>> blocking mode.
> >>>> I'd agree if writers couldn't be starved by (many) readers.
> >>> AFAICT from the rw lock implementation readers won't be able to pick
> >>> the lock as soon as there's a writer waiting, which should avoid this
> >>> starvation?
> >>>
> >>> You still need to wait for current readers to drop the lock, but no
> >>> new readers would be able to lock it, which I think should givbe us
> >>> enough fairness.
> >> Ah, right, it was rather the other way around - back-to-back
> >> writers can starve readers with our current implementation.
> >>
> >>> OTOH when using _trylock new readers can still pick
> >>> the lock in read mode, and hence I think using blocking mode for
> >>> writers is actually better, as you can assure that readers won't be
> >>> able to starve writers.
> >> This is a good point. Nevertheless I remain unconvinced that
> >> the change is warranted given the original intentions (as far
> >> as we're able to reconstruct them). If the current behavior
> >> gets in the way of sensible shim operation, perhaps the
> >> behavior should be made dependent upon running in shim mode?
> > 
> > Hotplug isn't generally used at all, so there is 0 write pressure on the
> > lock.
> > 
> > When it is used, it is all at explicit request from the controlling
> > entity in the system (hardware domain, or singleton shim domain).
> > 
> > If that entity is trying to DoS you, you've already lost.
> 
> But write pressure was never in question. My concern is with
> how long it might take for all readers to drop their locks.

The only long running operation that takes the CPU maps read lock is
microcode updating or livepatching, and since those are also started
by a privileged domain I think it's safe. Any sane admin wouldn't do a
CPU plug/unplug while updating microcode or doing a livepatching.

Thanks, Roger.
Andrew Cooper Feb. 19, 2020, 4:54 p.m. UTC | #11
On 19/02/2020 16:06, Jan Beulich wrote:
> On 19.02.2020 16:07, Andrew Cooper wrote:
>> On 19/02/2020 14:57, Jan Beulich wrote:
>>> On 19.02.2020 15:45, Roger Pau Monné wrote:
>>>> On Wed, Feb 19, 2020 at 02:44:12PM +0100, Jan Beulich wrote:
>>>>> On 19.02.2020 14:22, Roger Pau Monné wrote:
>>>>>> On Wed, Feb 19, 2020 at 01:59:51PM +0100, Jan Beulich wrote:
>>>>>>> On 13.02.2020 12:32, Roger Pau Monne wrote:
>>>>>>>> Don't allow cpu_hotplug_begin to fail by converting the trylock into a
>>>>>>>> blocking lock acquisition. Write users of the cpu_add_remove_lock are
>>>>>>>> limited to CPU plug/unplug operations, and cannot deadlock between
>>>>>>>> themselves or other users taking the lock in read mode as
>>>>>>>> cpu_add_remove_lock is always locked with interrupts enabled. There
>>>>>>>> are also no other locks taken during the plug/unplug operations.
>>>>>>> I don't think the goal was deadlock avoidance, but rather limiting
>>>>>>> of the time spent spinning while trying to acquire the lock, in
>>>>>>> favor of having the caller retry.
>>>>>> Now that the contention between read-only users is reduced as those
>>>>>> can take the lock in parallel I think it's safe to switch writers to
>>>>>> blocking mode.
>>>>> I'd agree if writers couldn't be starved by (many) readers.
>>>> AFAICT from the rw lock implementation readers won't be able to pick
>>>> the lock as soon as there's a writer waiting, which should avoid this
>>>> starvation?
>>>>
>>>> You still need to wait for current readers to drop the lock, but no
>>>> new readers would be able to lock it, which I think should givbe us
>>>> enough fairness.
>>> Ah, right, it was rather the other way around - back-to-back
>>> writers can starve readers with our current implementation.
>>>
>>>> OTOH when using _trylock new readers can still pick
>>>> the lock in read mode, and hence I think using blocking mode for
>>>> writers is actually better, as you can assure that readers won't be
>>>> able to starve writers.
>>> This is a good point. Nevertheless I remain unconvinced that
>>> the change is warranted given the original intentions (as far
>>> as we're able to reconstruct them). If the current behavior
>>> gets in the way of sensible shim operation, perhaps the
>>> behavior should be made dependent upon running in shim mode?
>> Hotplug isn't generally used at all, so there is 0 write pressure on the
>> lock.
>>
>> When it is used, it is all at explicit request from the controlling
>> entity in the system (hardware domain, or singleton shim domain).
>>
>> If that entity is trying to DoS you, you've already lost.
> But write pressure was never in question. My concern is with
> how long it might take for all readers to drop their locks.

Why is that of concern?

Failing with -EBUSY is not a useful property for any caller - if you
initiate a hotplug request, then you're going to loop until it
completes.  (again - scarcity of hotplug requests in the first place is
why this interface works in the general case.)

With a spinlock (and indeed, ticketlock in our case), everyone is
serialised and you've got to wait until your turn to do anything. 
trylock and backoff makes the situation probabilistic as to whether it
succeeds, and gets increasingly worse as the lock becomes more contended.

With a rwlock, the maximum amount of time a writer needs to wait is the
longest remaining critical region of the readers, which by definition is
something short enough to not be an issue for said reader.

~Andrew
Jan Beulich Feb. 19, 2020, 5:03 p.m. UTC | #12
On 19.02.2020 17:08, Roger Pau Monné wrote:
> On Wed, Feb 19, 2020 at 03:07:14PM +0000, Andrew Cooper wrote:
>> On 19/02/2020 14:57, Jan Beulich wrote:
>>> On 19.02.2020 15:45, Roger Pau Monné wrote:
>>>> On Wed, Feb 19, 2020 at 02:44:12PM +0100, Jan Beulich wrote:
>>>>> On 19.02.2020 14:22, Roger Pau Monné wrote:
>>>>>> On Wed, Feb 19, 2020 at 01:59:51PM +0100, Jan Beulich wrote:
>>>>>>> On 13.02.2020 12:32, Roger Pau Monne wrote:
>>>>>>>> Don't allow cpu_hotplug_begin to fail by converting the trylock into a
>>>>>>>> blocking lock acquisition. Write users of the cpu_add_remove_lock are
>>>>>>>> limited to CPU plug/unplug operations, and cannot deadlock between
>>>>>>>> themselves or other users taking the lock in read mode as
>>>>>>>> cpu_add_remove_lock is always locked with interrupts enabled. There
>>>>>>>> are also no other locks taken during the plug/unplug operations.
>>>>>>> I don't think the goal was deadlock avoidance, but rather limiting
>>>>>>> of the time spent spinning while trying to acquire the lock, in
>>>>>>> favor of having the caller retry.
>>>>>> Now that the contention between read-only users is reduced as those
>>>>>> can take the lock in parallel I think it's safe to switch writers to
>>>>>> blocking mode.
>>>>> I'd agree if writers couldn't be starved by (many) readers.
>>>> AFAICT from the rw lock implementation readers won't be able to pick
>>>> the lock as soon as there's a writer waiting, which should avoid this
>>>> starvation?
>>>>
>>>> You still need to wait for current readers to drop the lock, but no
>>>> new readers would be able to lock it, which I think should givbe us
>>>> enough fairness.
>>> Ah, right, it was rather the other way around - back-to-back
>>> writers can starve readers with our current implementation.
>>>
>>>> OTOH when using _trylock new readers can still pick
>>>> the lock in read mode, and hence I think using blocking mode for
>>>> writers is actually better, as you can assure that readers won't be
>>>> able to starve writers.
>>> This is a good point. Nevertheless I remain unconvinced that
>>> the change is warranted given the original intentions (as far
>>> as we're able to reconstruct them). If the current behavior
>>> gets in the way of sensible shim operation, perhaps the
>>> behavior should be made dependent upon running in shim mode?
>>
>> Hotplug isn't generally used at all, so there is 0 write pressure on the
>> lock.
>>
>> When it is used, it is all at explicit request from the controlling
>> entity in the system (hardware domain, or singleton shim domain).
>>
>> If that entity is trying to DoS you, you've already lost.
>>
>> There might be attempts to justify why the locking was done like that in
>> the first place, but it doesn't mean they were necessarily correct to
>> being with, and they don't match up with the realistic usage of the lock.
> 
> I'm happy to rewrite the commit message in order to include the
> discussion here. What about adding:
> 
> Note that when using rw locks a writer wanting to take the lock will
> prevent further reads from locking it, hence preventing readers from
> starving writers. Writers OTOH could starve readers, but since the
> lock is only picked in write mode by actions requested by privileged
> domains such entities already have the ability to DoS the hypervisor
> in many other ways.

While this sounds fine, my primary request was more towards removing
(or at least making less scary) the part about deadlocks.

Jan
Jan Beulich Feb. 19, 2020, 5:06 p.m. UTC | #13
On 19.02.2020 17:26, Roger Pau Monné wrote:
> On Wed, Feb 19, 2020 at 05:06:20PM +0100, Jan Beulich wrote:
>> On 19.02.2020 16:07, Andrew Cooper wrote:
>>> On 19/02/2020 14:57, Jan Beulich wrote:
>>>> On 19.02.2020 15:45, Roger Pau Monné wrote:
>>>>> On Wed, Feb 19, 2020 at 02:44:12PM +0100, Jan Beulich wrote:
>>>>>> On 19.02.2020 14:22, Roger Pau Monné wrote:
>>>>>>> On Wed, Feb 19, 2020 at 01:59:51PM +0100, Jan Beulich wrote:
>>>>>>>> On 13.02.2020 12:32, Roger Pau Monne wrote:
>>>>>>>>> Don't allow cpu_hotplug_begin to fail by converting the trylock into a
>>>>>>>>> blocking lock acquisition. Write users of the cpu_add_remove_lock are
>>>>>>>>> limited to CPU plug/unplug operations, and cannot deadlock between
>>>>>>>>> themselves or other users taking the lock in read mode as
>>>>>>>>> cpu_add_remove_lock is always locked with interrupts enabled. There
>>>>>>>>> are also no other locks taken during the plug/unplug operations.
>>>>>>>> I don't think the goal was deadlock avoidance, but rather limiting
>>>>>>>> of the time spent spinning while trying to acquire the lock, in
>>>>>>>> favor of having the caller retry.
>>>>>>> Now that the contention between read-only users is reduced as those
>>>>>>> can take the lock in parallel I think it's safe to switch writers to
>>>>>>> blocking mode.
>>>>>> I'd agree if writers couldn't be starved by (many) readers.
>>>>> AFAICT from the rw lock implementation readers won't be able to pick
>>>>> the lock as soon as there's a writer waiting, which should avoid this
>>>>> starvation?
>>>>>
>>>>> You still need to wait for current readers to drop the lock, but no
>>>>> new readers would be able to lock it, which I think should givbe us
>>>>> enough fairness.
>>>> Ah, right, it was rather the other way around - back-to-back
>>>> writers can starve readers with our current implementation.
>>>>
>>>>> OTOH when using _trylock new readers can still pick
>>>>> the lock in read mode, and hence I think using blocking mode for
>>>>> writers is actually better, as you can assure that readers won't be
>>>>> able to starve writers.
>>>> This is a good point. Nevertheless I remain unconvinced that
>>>> the change is warranted given the original intentions (as far
>>>> as we're able to reconstruct them). If the current behavior
>>>> gets in the way of sensible shim operation, perhaps the
>>>> behavior should be made dependent upon running in shim mode?
>>>
>>> Hotplug isn't generally used at all, so there is 0 write pressure on the
>>> lock.
>>>
>>> When it is used, it is all at explicit request from the controlling
>>> entity in the system (hardware domain, or singleton shim domain).
>>>
>>> If that entity is trying to DoS you, you've already lost.
>>
>> But write pressure was never in question. My concern is with
>> how long it might take for all readers to drop their locks.
> 
> The only long running operation that takes the CPU maps read lock is
> microcode updating or livepatching, and since those are also started
> by a privileged domain I think it's safe. Any sane admin wouldn't do a
> CPU plug/unplug while updating microcode or doing a livepatching.

Ah, yes, and perhaps one can even imply that further users of
this lock would also be admin invoked (we'd have to watch out
for future violations of this principle).

Jan
Jan Beulich Feb. 20, 2020, 8:16 a.m. UTC | #14
On 19.02.2020 18:03, Jan Beulich wrote:
> On 19.02.2020 17:08, Roger Pau Monné wrote:
>> On Wed, Feb 19, 2020 at 03:07:14PM +0000, Andrew Cooper wrote:
>>> On 19/02/2020 14:57, Jan Beulich wrote:
>>>> On 19.02.2020 15:45, Roger Pau Monné wrote:
>>>>> On Wed, Feb 19, 2020 at 02:44:12PM +0100, Jan Beulich wrote:
>>>>>> On 19.02.2020 14:22, Roger Pau Monné wrote:
>>>>>>> On Wed, Feb 19, 2020 at 01:59:51PM +0100, Jan Beulich wrote:
>>>>>>>> On 13.02.2020 12:32, Roger Pau Monne wrote:
>>>>>>>>> Don't allow cpu_hotplug_begin to fail by converting the trylock into a
>>>>>>>>> blocking lock acquisition. Write users of the cpu_add_remove_lock are
>>>>>>>>> limited to CPU plug/unplug operations, and cannot deadlock between
>>>>>>>>> themselves or other users taking the lock in read mode as
>>>>>>>>> cpu_add_remove_lock is always locked with interrupts enabled. There
>>>>>>>>> are also no other locks taken during the plug/unplug operations.
>>>>>>>> I don't think the goal was deadlock avoidance, but rather limiting
>>>>>>>> of the time spent spinning while trying to acquire the lock, in
>>>>>>>> favor of having the caller retry.
>>>>>>> Now that the contention between read-only users is reduced as those
>>>>>>> can take the lock in parallel I think it's safe to switch writers to
>>>>>>> blocking mode.
>>>>>> I'd agree if writers couldn't be starved by (many) readers.
>>>>> AFAICT from the rw lock implementation readers won't be able to pick
>>>>> the lock as soon as there's a writer waiting, which should avoid this
>>>>> starvation?
>>>>>
>>>>> You still need to wait for current readers to drop the lock, but no
>>>>> new readers would be able to lock it, which I think should givbe us
>>>>> enough fairness.
>>>> Ah, right, it was rather the other way around - back-to-back
>>>> writers can starve readers with our current implementation.
>>>>
>>>>> OTOH when using _trylock new readers can still pick
>>>>> the lock in read mode, and hence I think using blocking mode for
>>>>> writers is actually better, as you can assure that readers won't be
>>>>> able to starve writers.
>>>> This is a good point. Nevertheless I remain unconvinced that
>>>> the change is warranted given the original intentions (as far
>>>> as we're able to reconstruct them). If the current behavior
>>>> gets in the way of sensible shim operation, perhaps the
>>>> behavior should be made dependent upon running in shim mode?
>>>
>>> Hotplug isn't generally used at all, so there is 0 write pressure on the
>>> lock.
>>>
>>> When it is used, it is all at explicit request from the controlling
>>> entity in the system (hardware domain, or singleton shim domain).
>>>
>>> If that entity is trying to DoS you, you've already lost.
>>>
>>> There might be attempts to justify why the locking was done like that in
>>> the first place, but it doesn't mean they were necessarily correct to
>>> being with, and they don't match up with the realistic usage of the lock.
>>
>> I'm happy to rewrite the commit message in order to include the
>> discussion here. What about adding:
>>
>> Note that when using rw locks a writer wanting to take the lock will
>> prevent further reads from locking it, hence preventing readers from
>> starving writers. Writers OTOH could starve readers, but since the
>> lock is only picked in write mode by actions requested by privileged
>> domains such entities already have the ability to DoS the hypervisor
>> in many other ways.
> 
> While this sounds fine, my primary request was more towards removing
> (or at least making less scary) the part about deadlocks.

Actually, having thought about this some more over night, I'm fine
with the mentioning of the deadlock scenario as you have it right now.
I'm not overly fussed as to the addition (or not) of the above extra
paragraph.

Jan
Roger Pau Monné Feb. 21, 2020, 10:23 a.m. UTC | #15
On Thu, Feb 20, 2020 at 09:16:48AM +0100, Jan Beulich wrote:
> On 19.02.2020 18:03, Jan Beulich wrote:
> > On 19.02.2020 17:08, Roger Pau Monné wrote:
> >> On Wed, Feb 19, 2020 at 03:07:14PM +0000, Andrew Cooper wrote:
> >>> On 19/02/2020 14:57, Jan Beulich wrote:
> >>>> On 19.02.2020 15:45, Roger Pau Monné wrote:
> >>>>> On Wed, Feb 19, 2020 at 02:44:12PM +0100, Jan Beulich wrote:
> >>>>>> On 19.02.2020 14:22, Roger Pau Monné wrote:
> >>>>>>> On Wed, Feb 19, 2020 at 01:59:51PM +0100, Jan Beulich wrote:
> >>>>>>>> On 13.02.2020 12:32, Roger Pau Monne wrote:
> >>>>>>>>> Don't allow cpu_hotplug_begin to fail by converting the trylock into a
> >>>>>>>>> blocking lock acquisition. Write users of the cpu_add_remove_lock are
> >>>>>>>>> limited to CPU plug/unplug operations, and cannot deadlock between
> >>>>>>>>> themselves or other users taking the lock in read mode as
> >>>>>>>>> cpu_add_remove_lock is always locked with interrupts enabled. There
> >>>>>>>>> are also no other locks taken during the plug/unplug operations.
> >>>>>>>> I don't think the goal was deadlock avoidance, but rather limiting
> >>>>>>>> of the time spent spinning while trying to acquire the lock, in
> >>>>>>>> favor of having the caller retry.
> >>>>>>> Now that the contention between read-only users is reduced as those
> >>>>>>> can take the lock in parallel I think it's safe to switch writers to
> >>>>>>> blocking mode.
> >>>>>> I'd agree if writers couldn't be starved by (many) readers.
> >>>>> AFAICT from the rw lock implementation readers won't be able to pick
> >>>>> the lock as soon as there's a writer waiting, which should avoid this
> >>>>> starvation?
> >>>>>
> >>>>> You still need to wait for current readers to drop the lock, but no
> >>>>> new readers would be able to lock it, which I think should givbe us
> >>>>> enough fairness.
> >>>> Ah, right, it was rather the other way around - back-to-back
> >>>> writers can starve readers with our current implementation.
> >>>>
> >>>>> OTOH when using _trylock new readers can still pick
> >>>>> the lock in read mode, and hence I think using blocking mode for
> >>>>> writers is actually better, as you can assure that readers won't be
> >>>>> able to starve writers.
> >>>> This is a good point. Nevertheless I remain unconvinced that
> >>>> the change is warranted given the original intentions (as far
> >>>> as we're able to reconstruct them). If the current behavior
> >>>> gets in the way of sensible shim operation, perhaps the
> >>>> behavior should be made dependent upon running in shim mode?
> >>>
> >>> Hotplug isn't generally used at all, so there is 0 write pressure on the
> >>> lock.
> >>>
> >>> When it is used, it is all at explicit request from the controlling
> >>> entity in the system (hardware domain, or singleton shim domain).
> >>>
> >>> If that entity is trying to DoS you, you've already lost.
> >>>
> >>> There might be attempts to justify why the locking was done like that in
> >>> the first place, but it doesn't mean they were necessarily correct to
> >>> being with, and they don't match up with the realistic usage of the lock.
> >>
> >> I'm happy to rewrite the commit message in order to include the
> >> discussion here. What about adding:
> >>
> >> Note that when using rw locks a writer wanting to take the lock will
> >> prevent further reads from locking it, hence preventing readers from
> >> starving writers. Writers OTOH could starve readers, but since the
> >> lock is only picked in write mode by actions requested by privileged
> >> domains such entities already have the ability to DoS the hypervisor
> >> in many other ways.
> > 
> > While this sounds fine, my primary request was more towards removing
> > (or at least making less scary) the part about deadlocks.
> 
> Actually, having thought about this some more over night, I'm fine
> with the mentioning of the deadlock scenario as you have it right now.
> I'm not overly fussed as to the addition (or not) of the above extra
> paragraph.

Up to you, I don't have a strong opinion.

AFAICT there's no need for me to resend then?

Thanks, Roger.
Jan Beulich Feb. 21, 2020, 1:06 p.m. UTC | #16
On 21.02.2020 11:23, Roger Pau Monné wrote:
> On Thu, Feb 20, 2020 at 09:16:48AM +0100, Jan Beulich wrote:
>> On 19.02.2020 18:03, Jan Beulich wrote:
>>> On 19.02.2020 17:08, Roger Pau Monné wrote:
>>>> On Wed, Feb 19, 2020 at 03:07:14PM +0000, Andrew Cooper wrote:
>>>>> On 19/02/2020 14:57, Jan Beulich wrote:
>>>>>> On 19.02.2020 15:45, Roger Pau Monné wrote:
>>>>>>> On Wed, Feb 19, 2020 at 02:44:12PM +0100, Jan Beulich wrote:
>>>>>>>> On 19.02.2020 14:22, Roger Pau Monné wrote:
>>>>>>>>> On Wed, Feb 19, 2020 at 01:59:51PM +0100, Jan Beulich wrote:
>>>>>>>>>> On 13.02.2020 12:32, Roger Pau Monne wrote:
>>>>>>>>>>> Don't allow cpu_hotplug_begin to fail by converting the trylock into a
>>>>>>>>>>> blocking lock acquisition. Write users of the cpu_add_remove_lock are
>>>>>>>>>>> limited to CPU plug/unplug operations, and cannot deadlock between
>>>>>>>>>>> themselves or other users taking the lock in read mode as
>>>>>>>>>>> cpu_add_remove_lock is always locked with interrupts enabled. There
>>>>>>>>>>> are also no other locks taken during the plug/unplug operations.
>>>>>>>>>> I don't think the goal was deadlock avoidance, but rather limiting
>>>>>>>>>> of the time spent spinning while trying to acquire the lock, in
>>>>>>>>>> favor of having the caller retry.
>>>>>>>>> Now that the contention between read-only users is reduced as those
>>>>>>>>> can take the lock in parallel I think it's safe to switch writers to
>>>>>>>>> blocking mode.
>>>>>>>> I'd agree if writers couldn't be starved by (many) readers.
>>>>>>> AFAICT from the rw lock implementation readers won't be able to pick
>>>>>>> the lock as soon as there's a writer waiting, which should avoid this
>>>>>>> starvation?
>>>>>>>
>>>>>>> You still need to wait for current readers to drop the lock, but no
>>>>>>> new readers would be able to lock it, which I think should givbe us
>>>>>>> enough fairness.
>>>>>> Ah, right, it was rather the other way around - back-to-back
>>>>>> writers can starve readers with our current implementation.
>>>>>>
>>>>>>> OTOH when using _trylock new readers can still pick
>>>>>>> the lock in read mode, and hence I think using blocking mode for
>>>>>>> writers is actually better, as you can assure that readers won't be
>>>>>>> able to starve writers.
>>>>>> This is a good point. Nevertheless I remain unconvinced that
>>>>>> the change is warranted given the original intentions (as far
>>>>>> as we're able to reconstruct them). If the current behavior
>>>>>> gets in the way of sensible shim operation, perhaps the
>>>>>> behavior should be made dependent upon running in shim mode?
>>>>>
>>>>> Hotplug isn't generally used at all, so there is 0 write pressure on the
>>>>> lock.
>>>>>
>>>>> When it is used, it is all at explicit request from the controlling
>>>>> entity in the system (hardware domain, or singleton shim domain).
>>>>>
>>>>> If that entity is trying to DoS you, you've already lost.
>>>>>
>>>>> There might be attempts to justify why the locking was done like that in
>>>>> the first place, but it doesn't mean they were necessarily correct to
>>>>> being with, and they don't match up with the realistic usage of the lock.
>>>>
>>>> I'm happy to rewrite the commit message in order to include the
>>>> discussion here. What about adding:
>>>>
>>>> Note that when using rw locks a writer wanting to take the lock will
>>>> prevent further reads from locking it, hence preventing readers from
>>>> starving writers. Writers OTOH could starve readers, but since the
>>>> lock is only picked in write mode by actions requested by privileged
>>>> domains such entities already have the ability to DoS the hypervisor
>>>> in many other ways.
>>>
>>> While this sounds fine, my primary request was more towards removing
>>> (or at least making less scary) the part about deadlocks.
>>
>> Actually, having thought about this some more over night, I'm fine
>> with the mentioning of the deadlock scenario as you have it right now.
>> I'm not overly fussed as to the addition (or not) of the above extra
>> paragraph.
> 
> Up to you, I don't have a strong opinion.
> 
> AFAICT there's no need for me to resend then?

Indeed, but I wouldn't want to apply this one until the regression
from patch 1 was fixed, as else that change may also still get
reverted. But I'll keep it queued for committing until such time.

Jan

Patch
diff mbox series

diff --git a/xen/arch/x86/smpboot.c b/xen/arch/x86/smpboot.c
index 67ee490f94..cc0d62f9e2 100644
--- a/xen/arch/x86/smpboot.c
+++ b/xen/arch/x86/smpboot.c
@@ -1286,8 +1286,7 @@  int cpu_add(uint32_t apic_id, uint32_t acpi_id, uint32_t pxm)
          (pxm >= 256) )
         return -EINVAL;
 
-    if ( !cpu_hotplug_begin() )
-        return -EBUSY;
+    cpu_hotplug_begin();
 
     /* Detect if the cpu has been added before */
     if ( x86_acpiid_to_apicid[acpi_id] != BAD_APICID )
diff --git a/xen/common/cpu.c b/xen/common/cpu.c
index 0d7a10878c..31953f32e4 100644
--- a/xen/common/cpu.c
+++ b/xen/common/cpu.c
@@ -51,9 +51,9 @@  void put_cpu_maps(void)
     read_unlock(&cpu_add_remove_lock);
 }
 
-bool cpu_hotplug_begin(void)
+void cpu_hotplug_begin(void)
 {
-    return write_trylock(&cpu_add_remove_lock);
+    write_lock(&cpu_add_remove_lock);
 }
 
 void cpu_hotplug_done(void)
@@ -65,8 +65,7 @@  static NOTIFIER_HEAD(cpu_chain);
 
 void __init register_cpu_notifier(struct notifier_block *nb)
 {
-    if ( !write_trylock(&cpu_add_remove_lock) )
-        BUG(); /* Should never fail as we are called only during boot. */
+    write_lock(&cpu_add_remove_lock);
     notifier_chain_register(&cpu_chain, nb);
     write_unlock(&cpu_add_remove_lock);
 }
@@ -100,8 +99,7 @@  int cpu_down(unsigned int cpu)
     int err;
     struct notifier_block *nb = NULL;
 
-    if ( !cpu_hotplug_begin() )
-        return -EBUSY;
+    cpu_hotplug_begin();
 
     err = -EINVAL;
     if ( (cpu >= nr_cpu_ids) || (cpu == 0) )
@@ -142,8 +140,7 @@  int cpu_up(unsigned int cpu)
     int err;
     struct notifier_block *nb = NULL;
 
-    if ( !cpu_hotplug_begin() )
-        return -EBUSY;
+    cpu_hotplug_begin();
 
     err = -EINVAL;
     if ( (cpu >= nr_cpu_ids) || !cpu_present(cpu) )
diff --git a/xen/include/xen/cpu.h b/xen/include/xen/cpu.h
index e49172f64c..e8eeb217a0 100644
--- a/xen/include/xen/cpu.h
+++ b/xen/include/xen/cpu.h
@@ -10,7 +10,7 @@  bool get_cpu_maps(void);
 void put_cpu_maps(void);
 
 /* Safely perform CPU hotplug and update cpu_online_map, etc. */
-bool cpu_hotplug_begin(void);
+void cpu_hotplug_begin(void);
 void cpu_hotplug_done(void);
 
 /* Receive notification of CPU hotplug events. */