[PATCH] Avoid a pipe read in aio result scheduling
Jeremy Allison
jra at samba.org
Tue Aug 23 22:17:33 UTC 2016
On Tue, Aug 23, 2016 at 08:11:41AM +0200, Volker Lendecke wrote:
> On Mon, Aug 22, 2016 at 08:25:03PM -0700, Jeremy Allison wrote:
> > static int pthreadpool_tevent_job_destructor(struct pthreadpool_tevent_job_state *state)
> > {
> > if (state->pool == NULL) {
> > return 0;
> > }
> >
> > /*
> > * We should never be called with state->req != NULL,
> > * state->pool must be cleared before the 2nd talloc_free().
> > */
> > if (state->req != NULL) {
> > abort();
> > }
> >
> > /*
> > * We need to reparent to a long term context.
> > */
> > (void)talloc_reparent(state->req, state->pool, state);
> > state->req = NULL;
> > return -1;
> > }
> >
> > is doing :-(.
> >
> > I get the 'normal' case where pthreadpool_tevent_job_done()
> > is called, so state->pool = NULL and all is well. Can you
> > explain the failure mode you're explicity guarding against
> > here with (state->req != NULL) ?
>
> This is metze's piece :-)
>
> So, the point is that we want to protect against someone doing a
> talloc_free(req) while the job is running. See the comment in
> pthreadpool_tevent_job_done().
OK, pthreadpool_tevent_job_destructor() is the destructor
attached to state, which is owned by the struct tevent_req *req
returned from pthreadpool_tevent_job_send(), so if someone
does a talloc_free(req) it'll get called - makes sense.
1). Can I rename this to pthreadpool_tevent_state_destructor(),
as it's attached to state, not job ?
2). I think that call to talloc_reparent(state->req, state->pool, state);
looks incorrect to me.
The definition of talloc_reparent() is:
_PUBLIC_ void *talloc_reparent(const void *old_parent, const void *new_parent, const void *ptr)
In the pthreadpool_tevent_job_destructor() code we
know that (state->req == NULL) - otherwise we must
have called abort().
In that case we're calling:
(void)talloc_reparent(NULL, state->pool, state);
Internally, talloc_reparent() looks for a pointer matching
old_parent or for the parent of any talloc_reference to
ptr (god how I *hate* talloc_reference() :-) before
doing the reparent. In this case as old_parent is passed
as NULL, I don't think this call will do anything.
As far as I can see this code isn't exercised in the tests
so it's not currently causing a problem.
I think it should be replaced with:
(void)talloc_steal(state->pool, state);
in order to do what the comment says.
3) If someone calls talloc_free(req) on the req
returned from pthreadpool_tevent_job_send(), won't
it always run into the abort() here ?
struct pthreadpool_tevent_job_state *state isn't exposed
externally to anywhere but the code inside pthreadpool_tevent.c,
so I can't see how pthreadpool_tevent_state_destructor can
exer get called with state->req == NULL ?
So if the code:
/*
* We should never be called with state->req != NULL,
* state->pool must be cleared before the 2nd talloc_free().
*/
if (state->req != NULL) {
abort();
}
were changed to be:
/*
* We should never be called with state->req == NULL,
* state->pool must be cleared before the 2nd talloc_free().
*/
if (state->req == NULL) {
abort();
}
it would fix problem #3, and implicitly make the code I pointed
out in problem #2 already correct.
Yeah, I think that's right. With that change, if someone
calls talloc_free(req) whilst req is in flight, then the
pthreadpool_tevent_job_destructor() reparents state to
state->pool, and clears state->req to cope with the case
if someone called talloc_free(req) again before pthreadpool_tevent_job_done()
is called. And when pthreadpool_tevent_job_done() finally
gets called, state->pool gets cleared, state->req is already
equal to NULL and and we go into the case:
if (state->req == NULL) {
/*
* There was a talloc_free() state->req
* while the job was pending,
* which mean we're reparented on a longterm
* talloc context.
*
* We just cleanup here...
*/
talloc_free(state);
return;
}
Sorry for the long message, but I wanted to explain my
thought processes here (it's complicated, as you can see :-).
So if you agree that the code in pthreadpool_tevent_job_destructor()
should be:
/*
* We should never be called with state->req == NULL,
* state->pool must be cleared before the 2nd talloc_free().
*/
if (state->req == NULL) {
abort();
}
then I'm happy to push with my 'Reviewed-by: Jeremy Allison <jra at samba.org>'.
Let me know if I'm wrong.
Cheers,
Jeremy.
More information about the samba-technical
mailing list