Message ID | 20200213113237.58795-3-roger.pau@citrix.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | smp: convert cpu_add_remove_lock int a rw lock | expand |
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
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.
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
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.
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
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
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
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
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.
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.
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
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
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
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
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.
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
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. */
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(-)