(I should maybe send this from something other than a
web client, tabs may be clobbered below.)

I'm not entirely sure how I triggered this in the first place,
since I'm using a system that hides a bunch of setup, but
I was importing an existing pool of (virtual) disks when it
crashed.  This is on a 2 (virtual) cpu VM, as well.

Here's the useful part of the backtrace:

    list_remove() at list_remove+0x1d
    zil_itxg_clean() at zil_itxg_clean+0x2b
    taskq_run()

This means zil_itxg_clean() itself is in the middle
of its first loop (+0x2b returns just after line 1177):

1175        list = &itxs->i_sync_list;
1176        while ((itx = list_head(list)) != NULL) {
1177            list_remove(list, itx);
1178            kmem_free(itx, offsetof(itx_t, itx_lr) +
1179                itx->itx_lr.lrc_reclen);
1180        }

The list_remove() itself is in the middle of removing the node
(.../os/list.c:130, list_remove_node(lold)).  The failing
instruction (at +0x1d) is setting

    node->list_next->list_prev = node->list_prev;

(middle line of expansion of list_remove_node()).  The
actual instruction is just "movq %rdx,0x8(%rcx)" and
the failing address is 0x58 so %rcx must be 0x50; and
%rcx is just node->list_next (and the remaining two
instructions just NULL-out two pointers before we return).

Suspiciously, sizeof(itx_t) is 0x50, so this looks like
we started with a NULL pointer and went forward by
one itx object.

There seems to be only one way to get into zil_itxg_clean
from taskq_run(): sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zil.c,
near line 1350:

void
zil_clean(zilog_t *zilog, uint64_t synced_txg)
{
        ...
        mutex_enter(&itxg->itxg_lock);
        ...
        clean_me = itxg->itxg_itxs;
        itxg->itxg_itxs = NULL;
        itxg->itxg_txg = 0;
        mutex_exit(&itxg->itxg_lock);
        /*
         * Preferably start a task queue to free up the old itxs but
         * if taskq_dispatch can't allocate resources to do that then
         * free it in-line. This should be rare. Note, using TQ_SLEEP
         * created a bad performance problem.
         */
        if (taskq_dispatch(zilog->zl_clean_taskq,
            (void (*)(void *))zil_itxg_clean, clean_me, TQ_NOSLEEP) == 0)
                zil_itxg_clean(clean_me);
}

Obviously clean_me here was not NULL as this would have crashed
earlier (while calling list_head()).  So it looks like it pointed to a
valid(ish)
itxs structure, but the i_sync_list within it was broken.

(Assertions are not turned on in this kernel build.)

If anyone wants me to try to gather more data (perhaps with assertions
enabled...) I can try, though I have no idea how reproducible this is yet.

Chris
_______________________________________________
developer mailing list
[email protected]
http://lists.open-zfs.org/mailman/listinfo/developer

Reply via email to