Skip to content

policy: Keep NameManager locked during SelectorCache operations #10501

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged

Conversation

jrajahalme
Copy link
Member

@jrajahalme jrajahalme commented Mar 6, 2020

UpdateGenerateDNS updated the cache and generate selector updates as two
separate lock-holding sections. When updates happened concurrently, via
DNS lookups, DNS GC, or endpoint restores, the snapshot of data that
eventually updated the selectors could be out-of-order. This created an
A-B-A race for selectors that needed an update and those that needed to
be cleared.

These races have been observed on agent starts with pending pods but may
manifest between multiple pods making DNS updates or the DNS garbage
collector controller and any pod.

The code now holds the NameManager lock for the entirety of
UpdateGenerateDNS and ForceRegenerateDNS. This forces the update to
complete in its entirety before allowing another to modify the FQDN
cache or registered selectors.

The locking order between the NameManager lock and the SelectorCache
lock has been accidental so far. When removing a selector user from
the cache, the SelectorCache lock is taken, and if an FQDN selector
has no more users, the NameManager is notified of this, which
internally takes the NameManager lock. Reverse this by explicitly
locking NamaManager for the duration of selector cache operations.

From now on, if the two locks are to be held at the same time, the
NameManager mutex MUST be taken first, the SelectorCache mutex second,
as the name manager is a higher level construct than the selector
cache.

Exposing 'Lock()' and 'Unlock()' operations in an interface is not
ideal, but it helps make the lockinng order explicit, so it also
serves as documentation on the required locking order.

NameManager now return IDs also if the selector has already been
registered. This should help not get into a state where FQDNs are not
passed to the selector cache in case of a race condition.


This change is Reviewable

@jrajahalme jrajahalme added wip release-note/minor This PR changes functionality that users may find relevant to operating Cilium. labels Mar 6, 2020
@jrajahalme jrajahalme requested a review from a team March 6, 2020 19:41
@jrajahalme
Copy link
Member Author

test-me-please

Copy link
Member

@joestringer joestringer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Who owns the fqdnSelector here? Is it guaranteed to be retired if the len(f.users) goes to zero? Or can another goroutine find it and add a user while the lock is no longer held and the selectorManager is calling the callback?

Also, I think it would be helpful to future readers if the expectations around the cb were more clearly defined in comments somewhere here. It appears to be a Finally type function, similar in some ways to some of the regeneration code, where if this CB is non-null then the callers must call it when they're finished executing their own logic.

@coveralls
Copy link

coveralls commented Mar 6, 2020

Coverage Status

Coverage increased (+0.002%) to 45.64% when pulling 5bf90eb on pr/jrajahalme/policy-selector-cache-locking-order into 8ea7239 on master.

@joestringer
Copy link
Member

@jrajahalme Did you have specific rationale around release-note/minor here? This doesn't seem like it's user-facing so would be more suited to release-note/misc.

@jrajahalme jrajahalme added release-note/misc This PR makes changes that have no direct user impact. sig/policy Impacts whether traffic is allowed or denied based on user-defined policies. kind/enhancement This would improve or streamline existing functionality. pending-review and removed release-note/minor This PR changes functionality that users may find relevant to operating Cilium. wip labels Mar 6, 2020
@jrajahalme
Copy link
Member Author

@joestringer Changed the label(s), marking this for backport to 1.6 and 1.7 as well.

@jrajahalme
Copy link
Member Author

@joestringer

Who owns the fqdnSelector here? Is it guaranteed to be retired if the len(f.users) goes to zero? Or can another goroutine find it and add a user while the lock is no longer held and the selectorManager is calling the callback?

if user count gets to zero, the fqdnSelector is removed from the selector cache while holding the lock like before. Only the callback to the name manager is postponed, so the removed selector is not reachable from the cache at that time. I.e., no behavioral change as far as selectors in the cache are concerned.

Also, I think it would be helpful to future readers if the expectations around the cb were more clearly defined in comments somewhere here. It appears to be a Finally type function, similar in some ways to some of the regeneration code, where if this CB is non-null then the callers must call it when they're finished executing their own logic.

Right, must be called if non-nil. Will add comments :-)

@jrajahalme jrajahalme force-pushed the pr/jrajahalme/policy-selector-cache-locking-order branch from 9af49ca to 563231e Compare March 6, 2020 22:06
@jrajahalme jrajahalme requested a review from a team March 6, 2020 22:06
@jrajahalme
Copy link
Member Author

test-me-please

@jrajahalme jrajahalme force-pushed the pr/jrajahalme/policy-selector-cache-locking-order branch from 563231e to 73d10ba Compare March 8, 2020 23:19
@jrajahalme jrajahalme requested a review from a team as a code owner March 8, 2020 23:19
@jrajahalme
Copy link
Member Author

test-me-please

@jrajahalme jrajahalme force-pushed the pr/jrajahalme/policy-selector-cache-locking-order branch from 73d10ba to a6c7362 Compare March 9, 2020 13:48
@jrajahalme jrajahalme changed the title policy: Reverse locking order between selector cache and name manager policy: Keep NameManager locked during SelectorCache operations Mar 9, 2020
@jrajahalme
Copy link
Member Author

test-me-please

Copy link
Contributor

@raybejjani raybejjani left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a small nit, nothing blocking from me.

@jrajahalme jrajahalme force-pushed the pr/jrajahalme/policy-selector-cache-locking-order branch from ad77a6d to 8d67100 Compare March 9, 2020 16:38
@jrajahalme
Copy link
Member Author

@raybejjani Changed interface function names to have "Locked" suffix and discovered one unlocked call site while doing that, thanks!

@jrajahalme
Copy link
Member Author

test-me-please

Copy link
Member

@joestringer joestringer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One specific concern below around refcounting and CIDR identities, did you check on this before?

@joestringer joestringer added release-note/bug This PR fixes an issue in a previous release of Cilium. and removed release-note/misc This PR makes changes that have no direct user impact. labels Mar 9, 2020
@jrajahalme jrajahalme force-pushed the pr/jrajahalme/policy-selector-cache-locking-order branch from 8d67100 to 5bf90eb Compare March 9, 2020 23:45
@jrajahalme jrajahalme requested a review from a team as a code owner March 9, 2020 23:45
delete(d.selectors, selector)
}

func (d *DummyIdentityNotifier) InjectIdentitiesForSelector(fqdnSel api.FQDNSelector, ids []identity.NumericIdentity) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

exported method DummyIdentityNotifier.InjectIdentitiesForSelector should have comment or be unexported

selectors map[api.FQDNSelector][]identity.NumericIdentity
}

func NewDummyIdentityNotifier() *DummyIdentityNotifier {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

exported function NewDummyIdentityNotifier should have comment or be unexported

"github.com/cilium/cilium/pkg/policy/api"
)

type DummyIdentityNotifier struct {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

exported type DummyIdentityNotifier should have comment or be unexported

@jrajahalme
Copy link
Member Author

test-me-please

@joestringer
Copy link
Member

test-gke K8sFQDN.*

Copy link
Member

@joestringer joestringer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@ianvernon
Copy link
Member

test-gke K8sFQDN.*

@joestringer
Copy link
Member

joestringer commented Mar 10, 2020

I guess the GKE target isn't working as we'd hope, so for janitors / maintainers we can ignore that failure:

"Build building or pushing Cilium images failed"
https://jenkins.cilium.io/job/Cilium-PR-K8s-GKE/131/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
kind/enhancement This would improve or streamline existing functionality. release-note/bug This PR fixes an issue in a previous release of Cilium. sig/policy Impacts whether traffic is allowed or denied based on user-defined policies.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants