tech-kern archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
Re: pserialize(9) vs. TAILQ
Date: Sat, 22 Nov 2014 01:31:48 +0900
From: Masao Uebayashi <uebayasi%gmail.com@localhost>
On Sat, Nov 22, 2014 at 1:22 AM, Taylor R Campbell
<campbell+netbsd-tech-kern%mumble.net@localhost> wrote:
> It matters what's in the ...'s. Are you doing this?
>
> TAILQ_FOREACH(e, head, next) {
> if (e->key == key) {
> mutex_spin_enter(&e->lock);
> ...
>
> If so, the reader may see a stale e->key -- the membar_enter implied
> by mutex_spin_enter is too late.
The promise there is that, e->key is constant while it's read with
pserialize_read_{enter,leave}(9). If those members have to be
changed, they have to be once removed from there, and re-added.
The problem is not that e->key may change while e is in the list -- it
won't (whoever wants to change it must, as you suggest, remove e from
the list, change it, and then re-add it).
The problem is that when CPU 0 does
e = pool_get(&e_pool, PR_WAITOK);
then e->key, at address 0xdeadbeef, will have some garbage
uninitialized value, say 0xa5a5a5a5, which CPU 1 may have cached.
Next, CPU 0 does:
e->key = 12345;
mutex_enter(lock);
e->next = eq;
membar_producer();
eq = e;
mutex_exit(lock);
From CPU 0's perspective, the stores have been issued to memory in the
correct order: nobody who loads e from the memory at eq, and then
loads e->key from memory at 0xdeadbeef, will see 0xa5a5a5a5 -- anyone
who issues loads to memory in that order will see 12345.
But CPU 1 already has the contents of 0xdeadbeef cached, so it won't
load from memory there. Instead, when it does
s = pserialize_read_enter();
for (e = eq; e != NULL; e = e->next) {
if (e->key == 12345)
...
},
it will load e from memory at eq (or maybe from its cache), and then
use its cached value for the address 0xdeadbeef, which is 0xa5a5a5a5.
Nothing prevented CPU 1 from issuing the loads in the `wrong' order --
CPU 1 had already issued a load for 0xdeadbeef a long time ago, which
at the time correctly yielded 0xa5a5a5a5.
In order for CPU 1 to guarantee that, after it loads e, it will load
e->key from memory instead of using the cache, it must issue a
membar_datadep_consumer:
s = pserialize_read_enter();
for (e = eq; e != NULL; e = e->next) {
membar_datadep_consumer();
if (e->key == 12345)
...
}
This is a general heuristic for memory barriers: when communicating
from one CPU to another CPU, there should be a matched pair of memory
barriers.
Home |
Main Index |
Thread Index |
Old Index