Page MenuHomeFreeBSD

Add async drain from within the callout system.
ClosedPublic

Authored by rrs on Nov 3 2015, 5:34 PM.
Tags
None
Referenced Files
Unknown Object (File)
Wed, Apr 24, 11:06 PM
Unknown Object (File)
Fri, Apr 5, 4:50 AM
Unknown Object (File)
Fri, Apr 5, 1:58 AM
Unknown Object (File)
Fri, Apr 5, 12:46 AM
Unknown Object (File)
Fri, Apr 5, 12:32 AM
Unknown Object (File)
Fri, Apr 5, 12:21 AM
Unknown Object (File)
Fri, Apr 5, 12:08 AM
Unknown Object (File)
Mar 19 2024, 8:33 PM

Details

Summary

Currently TCP uses a rather round-about way to accomplish a callout-drain-async. This
adds the functionality that does it more direct supported by the callout-system.

Test Plan

First step is to have some review and test in parallel with my callout_test framework. I will
need to build a case where I can force this set of circumstances and verify the callbacks happen
like they should.

Note I will leave this up for quite some time since we need to have some good validation of this.
After its in we then will need to migrate TCP (and possibly other systems) to using it.

Diff Detail

Repository
rS FreeBSD src repository - subversion
Lint
Lint Not Applicable
Unit
Tests Not Applicable

Event Timeline

rrs retitled this revision from to Add async drain from within the callout system..
rrs updated this object.
rrs edited the test plan for this revision. (Show Details)
rrs added reviewers: jhb, hselasky, imp, glebius, bz.
kern/kern_timeout.c
139 ↗(On Diff #9912)

How about typedef'ing this function?

1259 ↗(On Diff #9912)

How does the caller know if the drain function is supposed to be called or not?

1377 ↗(On Diff #9912)

These three lines should probably be moved under the lock(cc);

sys/callout.h
125 ↗(On Diff #9912)

Minor typo here ))

After thinking about it, we need to move the clear of active down and
only call drain in the duplicate stop case if the caller did not
clear active (which they should have when the callout really completed).

sys/callout.h
125–126 ↗(On Diff #9913)

Whats the typo? I don't see it? (maybe not enough coffee)...

125–126 ↗(On Diff #9913)

oh I see it now.. dup )) :-)

Fixes typo Han's caught.. great catch thanks!

Testing to follow soon :-O

kern/kern_timeout.c
1250 ↗(On Diff #9915)

stupid style nit: blank line.

imp edited edge metadata.
This revision is now accepted and ready to land.Nov 3 2015, 7:04 PM
kern/kern_timeout.c
1252 ↗(On Diff #9915)

These two lines are at least swapped. act is always 0.

1262 ↗(On Diff #9915)

I think it is not a good idea to call the drain() function while the sleepqueue() is locked! See if (sq_locked) below!

rrs edited edge metadata.

After chatting with jhb, some changes he suggested.

This revision now requires review to proceed.Nov 3 2015, 8:31 PM
rrs edited edge metadata.

Ok lets make it so that *always* if you call callout_async_drain() on a stopped/completed timer
then kassert and die. You need to always assure the timer is running before you call
async_drain!

Fix the great bug Han's found in setting drain..

Fix imp's blank line style issue.

This update is now tested. However I still need to an update to
the manual. Kib has a proposal to change where I KASSERT(drain == NULL) and
I think we should do that i.e. make that case return -1.

That way when you call callout_stop (in any form)
1 -- it was stopped
-1 -- it was not running, i.e. it was stopped already or has already expired and was processed
0 -- can't stop it.

For async_drain then 0 means the drain will be called, the others no.

Ok this adds the small section into the manual page. With
that we are about ready :-)

opps forgot the arg's in the man

Gesh, I am going to get more coffee.. .pick the right update silly.

Ok this will update it so we get three return codes out
of callout_stop and friends

  1. 0 -- we could not stop it (and maybe a drain will come if you do the async style)
  2. -1 -- It was not running or has already expired (you were not tracking it silly)
  3. 1 -- It was stopped

This makes it so the callout_stop and friends return codes are reliable to tell us something. In
particular it removes the KASSERT in the -1 return path so that a caller of the async_drain
can count how many 0 returns come back and based on that know how many drain() calls
will happen before it can say purge memory of the reference...

share/man/man9/timeout.9
266 ↗(On Diff #10067)

I think this sentence is incorrect? callout_async_drain() is a non-blocking function and can be called locked.

sys/sys/callout.h
125 ↗(On Diff #10067)

Should callout_async_drain() take an additional "void *" argument, which should be passed to the callback function?

kern/kern_timeout.c
1259 ↗(On Diff #9919)

Possibly we should take this to just always asserting that drain is NULL.
I.e. no matter what if you call async_drain on a stopped or expired
timer you crash.

sys/sys/callout.h
125 ↗(On Diff #10067)

Why?

What purpose does it serve over the original argument?

For the use case most likely to happen, TCP, its the argument which
is the pointer to the PCB that matters.. Most other uses I can envision
are pretty much the same. The state that you use in the call-out should
be exactly whats needed for the async callback..

What other use are you thinking you will need it for?

kern/kern_timeout.c
1259 ↗(On Diff #9919)

I think this issue was fixed by the new negative return value.

sys/sys/callout.h
125 ↗(On Diff #10067)

Hi,

Say you have a child parent structure relationship. The refcount is on the parent, and callout argument is in the child. Then when destroying such an object, you don't want to have the c_arg passed to the drain destructor, but rather a pointer to the parent.

--HPS

Update the man page per Han's suggestion.

This revision was automatically updated to reflect the committed changes.

"RETURN VALUES" man page section still states that "The callout_stop() and callout_drain() functions return non-zero if the callout was still pending when it was called or zero otherwise.". This should also be updated.
Also, I wonder if it would be better to make api consistent in terms of return values.
callout_stop()/callout_drain() now returns -1 for non-scheduled callout, but callout_reset() still returns zero for that case.