[Differential] [Commented On] D1711: Changes to the callout code to restore active semantics and also add a test-framework and test to validate thecallout code (and potentially for use by other tests).
rrs (Randall Stewart)
phabric-noreply at FreeBSD.org
Wed Feb 4 12:55:42 UTC 2015
rrs added a comment.
Ok guys, I have puzzled out what that crash *may* be that was posted by Hiren. The same
issue exists in the timeout code rewrite that Han's has up on the board as well. Though
the callout_drain_async() may solve it if the user called that instead. Here is what is happening.
1) CPU1 is running the callout from the callout wheel it gets to
softclock_call_cc() line 635, its all ready to run and unlocks the
callout mutex. It then sees c_lock is populated and thus
calls class->c_lock()...) to switch locks. It begins to spin
in __rw_wlock_hard() in the while loop waiting to get the lock since its held.
2) lltable_free() has been called on CPU 2, it grabs its AFDATA lock and then goes through
each entry and does
LIST_FOREACH_SAFE(..) {
LLE_WLOCK(lle); <-- this is the lock that CPU 1 is waiting on
if(callout_stop(la->la_timer))
LLE_REMREF(lle)
llentry_free(lle);
}
3) The callout_stop() on CPU 2 does what it is supposed to and sets the cc_cancel bit to true and
return 1. This causes callout_stop() to lower the reference count which means when llentry_free()
is called the lle *is* freed. This calls into either in_lltable_free() or in6_lltable_free() which
unlocks the lock (letting CPU 1 go forward) and then promptly destroys the lock
and frees the memory.
4) Finally our spinning CPU 1 loops back around and finds the destroyed mutex and crashes.
Now, some interesting notes on this:
a) If the lle* code had used MPSAFE instead of passing the lock in, then zero would have been
returned since the callout "could not have been stopped". This would have left the reference
and *not* freed the memory.. the timer would have done that has it executed.
b) If the lle* code just did not stop the timer, and instead let it run off, it would have freed itself
on expiration.. of course what you really want here is "stop it if you can" but don't stop it
if its already running. Which is why <a> is true since thats what you get if you control the
lock. I
c) This little hole is also in Han's new code unless we change the user of the code
to use the async_drain routine. Which again is changing the KPI something I don't like to do
since its in *so* many places ;-) Of course we may want an async-drain I have not thought about that.
d) This race has always been there near as I can tell and really is not caused by the migration
code that was added like the other code that was fixed.
I don't really see an easy way to fix this accept to change the caller to use MPSAFE.. or maybe make
a async_drain routine.. but then we still end up changing the caller ;-)
INLINE COMMENTS
kern/kern_timeout.c:1159 sounds good I will make it so.
kern/kern_timeout.c:1234 Imp:
Yes, it looks wrong in the kern_timeout.c code.. let me fix it ;-)
REVISION DETAIL
https://reviews.freebsd.org/D1711
To: rrs, gnn, rwatson, lstewart, jhb, kostikbel, hselasky, adrian, imp, sbruno
Cc: hiren, jhb, kostikbel, emaste, delphij, neel, erj, freebsd-net
More information about the freebsd-net
mailing list