on 31/01/2014 17:58 Matthew Ahrens said the following:
> 
> 
> 
> On Thu, Jan 30, 2014 at 11:44 PM, Andriy Gapon <a...@freebsd.org
> <mailto:a...@freebsd.org>> wrote:
> 
>     on 31/01/2014 07:57 Matthew Ahrens said the following:
>     > On Thu, Jan 30, 2014 at 2:02 AM, Andriy Gapon <a...@freebsd.org
>     <mailto:a...@freebsd.org>
>     > <mailto:a...@freebsd.org <mailto:a...@freebsd.org>>> wrote:
>     >
>     >
>     >     I can not figure out how the following code actually works.
>     >     Probably I am missing something in the big picture (again).
>     >
>     >     if (HDR_L2_WRITE_HEAD(ab)) {
>     >             /*
>     >              * We hit a write head node.  Leave it for
>     >              * l2arc_write_done().
>     >              */
>     >             list_remove(buflist, ab);
>     >             mutex_exit(hash_lock);
>     >             continue;
>     >     }
>     >
>     >     So, the write head is left in memory, but it is still removed from
>     l2ad_buflist.
>     >     Supposing there is a corresponding L2 write zio in progress there 
> will
>     be a call
>     >     to l2arc_write_done() with l2wcb_head pointing to the head.
>     >     Wouldn't
>     >             list_prev(buflist, head)
>     >     result in an illegal memory access if head is not on buflist?
>     >
>     >
>     > Yes, it would.  Thankfully, we don't call list_prev() after removing 
> it.  The
>     > loop in l2arc_evict() begins with:
>     >
>     > for (ab = list_tail(buflist); ab; ab = ab_prev) {
>     > ab_prev = list_prev(buflist, ab);
> 
>     Apologies for not being clear, I actually meant the loop in 
> l2arc_write_done().
> 
> 
> l2arc_buflist_mtx should prevent that.


Umm...

l2arc_write_buffers() creates the head object and puts it on the list. Then it
creates a parent zio and the head and the list are tucked away in a callback
data associated with the zio.  Then  l2arc_write_buffers() drops
l2arc_buflist_mtx and calls zio_wait on the zio.

Let's assume that then l2arc_evict() is called and it removes the head from the
list.  This is done under l2arc_buflist_mtx protection, but that's beside the 
point.

After that the zio is done and l2arc_write_done() callback is called.  It
acquires l2arc_buflist_mtx and tries to walk the list starting with head that
was stored in the callback data.  But the head is not on the list...

This was my concern.
But now that I have written the above I see that l2arc_write_buffers() and
l2arc_evict() are called sequentially from l2arc_feed_thread() and thus
l2arc_write_done() can't run concurrently with l2arc_evict().
So, this is what I have missed in the big picture :-)

I am trying to understand now if we can assert that l2arc_evict() should never
encounter ARC_L2_WRITE_HEAD marked buffer on a list.
Although, there is another call l2arc_evict() in l2arc_remove_vdev().  But it
looks like that should be protected by spa_config lock.

-- 
Andriy Gapon
_______________________________________________
developer mailing list
developer@open-zfs.org
http://lists.open-zfs.org/mailman/listinfo/developer

Reply via email to