Subject: Re: linux-next: build warnings after merge of the tip tree

From: Peter Zijlstra <[email-masked]>
Date: Tue, 22 Mar 2022 15:35:54 +0100

On Tue, Mar 22, 2022 at 09:12:42AM -0400, Steven Rostedt wrote:

> > Suppose:
> > 
> > notrace func_B()
> > {
> > 	...
> > }
> > 
> > func_A()
> > {
> > 	...
> > 	return func_B();
> > }
> > 
> > then inhibiting tail calls would end up looking like:
> 
> If we inhibit tail calls, then we do not need to make func_B notrace.

Dude, you're arguing in circles :-( the notrace was a given.

> > func_A:
> > 	call __fentry__
> > 	...
> > 	call func_B
> > 	call __fexit__
> > 	ret
> > 
> > Then A is fully traced, B is invisible, as per spec. What is the
> > problem?
> 
> The above is fine, but then func_B is not a tail call and can also be
> traced.

Again, B is notrace as a given. This was all about how to deal with
notrace functions.

I suggested inhibiting tail-call to notrace, you said no. You now seem to
agree that solves it.

> > The problem you initially had, of doing a tail-call into a notrace, was
> > that the __fexit__ call went missing, because notrace will obviously not
> > have that. But that's avoided by inhibiting all tail-calls between
> > notrace and !notrace functions (note that notrace must also not
> > tail-call !notrace).
> 
> I'm confused by the above. Why can't a notrace tail call a !notrace?
> If we tail call to a
> 
> func_B:
> 	call __fentry__
> 	...
> 	call __fexit__
> 	ret
> 
> then the fentry and fexit show a perfectly valid trace of func_B.

Bah; I thought I had a case this morning, but now I can't seem to recall
:/

> > Your worry seems to stem about loosing visiblilty of !notrace functions,
> > but AFAICT that doesn't happen.
> 
> My worry is:
> 
> func_A:
> 	call __fentry__
> 	...
> 	jmp func_B
> 
> Where do we do the call __fexit__ ?

In B (or wherever if B again does a tail-call).

> That was the original concern, and I think the proposed solutions have
> convoluted our thoughts about what we are trying to fix. So let's go back
> to the beginning, and see how to deal with it.
> 
> That is, we have:
> 
> func_C:
> 	call __fenty__
> 	...
> 	call func_A:
> 	...
> 	call func_B:
> 	...
> 	call __fexit__
> 	ret
> 
> func_A:
> 	call __fentry__
> 	...
	call __ftail__
> 	jmp func_B
> 
> func_B:
> 	call __fentry__
> 	...
> 	call __fexit__
> 	ret
> 
> Where the above is C calling A and B as normal functions, A calling B as a
> tail call and B just being a normal function called by both A and C (and
> many other functions).

We need the __ftail__ thing to mark the trace-stack entry of func_A as
complete, then any future __fexit__ will be able to pop all completed
entries.

In recap:

	__fentry__ -- push on trace-stack
	__ftail__  -- mark top-most entry complete
	__fexit__  -- mark top-most entry complete;
	              pop all completed entries

inhibit tail-calls to notrace.

> And note, I do not want to limit function tracing (which does not rely on
> __fexit__) just because we can't figure out how to handle __fexit__.

I'm not following. Regular function tracing needs none of this.

It's function graph tracing, kretprobes and whatever else this rethook
stuff is about that needs this because return trampolines will stop
working somewhere in the not too distant future.