Current-Users archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
Re: specfs/spec_vnops.c diagnostic assertion panic
> Date: Sat, 13 Aug 2022 05:59:01 +0700
> From: Robert Elz <kre%munnari.OZ.AU@localhost>
>
> Why is fsck running on the block device though? And devpubd too? Given
> the reference to cdev_close() I'd assumed it was a char (raw) device that
> was being used, which would be what fsck certainly should be using. But
> I see that 02-wedgenames uses the block /dev/dnN device .. should fix that.)
This part of my hypothesis was not quite right -- it looks like there
is no race between fsck and dkctl per se, because they both use the
character device like they should as confirmed by ktrace.
Revised hypothesis:
- fsck opens /dev/rdkN, and
- dkctl opens /dev/rdkM,
where dkN and dkM are different wedges on the same parent.
02-wedgenames doesn't do `dkctl raid0 listwedges'; rather, it does
`dkctl dkN getwedgeinfo', which opens the character device /dev/rdkN
as it should. And fsck is still opening a character device as it
should.
Why is this a problem? When you open or close /dev/rdkN or /dev/rdkM,
that goes through dkopen and dkclose in the kernel. On first open,
dkopen opens the parent disk's _block device_ via dk_open_parent, and
on last close, dkclose closes it via dklastclose -> dk_close_parent.
However, while dkclose -> dklastclose -> dk_close_parent is in
progress, currently nothing prevents a concurrent dkopen on another
wedge of the same parent from trying to open the parent afresh.
And although dk_open_parent is serialized by the mutex dk_rawlock,
dk_close_parent was _deliberately_ made to run outside dk_rawlock back
in 2010:
https://mail-index.netbsd.org/source-changes/2010/08/04/msg012270.html
https://mail-index.netbsd.org/tech-kern/2010/07/27/msg008612.html
Judging by the stack trace there (mutex_destroy -> [presumably
disk_destroy elided by tail call optimization] -> raidclose) was
actually evidence of a bug that had already been fixed a year earlier,
but possibly not in the kernel bouyer@ was running:
https://mail-index.netbsd.org/source-changes/2009/07/23/msg223381.html
This change by dyoung@ moved the disk_destroy call from raidclose to
raid_detach where it more properly belongs. Note that today there has
been _another_ call to dk_close_parent, in dkwedge_read, for the past
seven years.
So I think we should move the call to dk_close_parent in dklastclose
back under dk_rawlock -- and also under dk_openlock. This will allow
us to refactor the code to keep the mutex_enter/exit bracketing much
more obvious.
(Side note: I think we should eliminate dk_rawlock and just use
dk_openlock; they appear to be redundant, except in dkwedge_read, but
I'm not sure what logic uses _only_ dk_openlock and _not_ dk_rawlock
which is fruitfully concurrent with dkwedge_read. Certainly
dkwedge_discover can't be called with either one held, and I don't see
why concurrency between dkwedge_read/add/list/delall/detach is
important.)
Can you try _reverting_ specfs_blockopen.patch, and _applying_ the
attached dkopenclose.patch, and see if you can reproduce any crash?
(I believe specfs_blockopen.patch is still necessary for other reasons
but it gets in the way of testing my hypothesis that dk_close_parent
needs to be serialized with dk_open_parent anyway.)
From 5eaacbd7d9d6d6b3ccfea5564aebf9548f5b7564 Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
Date: Fri, 12 Aug 2022 22:48:25 +0000
Subject: [PATCH] dk(9): Serialize closing parent's dk_rawvp with opening it.
Otherwise, the following events might happen:
- process 123 had /dev/rdkN open, starts close, enters dk_close_parent
- process 456 opens /dev/rdkM (same parent, different wedge), calls
dk_open_parent
At this point, the block device hasn't yet closed, so dk_open_parent
will fail with EBUSY. This is incorrect -- the chardev is never
supposed to fail with EBUSY, and dkopen/dkclose carefully manage
state to avoid opening the block device while it's still open. The
problem is that dkopen in process 456 didn't wait for vn_close
in process 123 to finish before calling VOP_OPEN.
(Note: If it were the _same_ chardev /dev/rdkN in both processes,
then spec_open/close would prevent this. But since it's a
_different_ chardev, spec_open/close assume that concurrency is OK,
and it's the driver's responsibility to serialize access to the
parent disk which, unbeknownst to spec_open/close, is shared between
dkN and dkM.)
It appears that the vn_close call was previously moved outside
dk_rawlock in 2010 to work around an unrelated bug in raidframe that
had already been fixed in HEAD:
Crash pointing to dk_rawlock and raidclose:
https://mail-index.netbsd.org/tech-kern/2010/07/27/msg008612.html
Change working around that crash:
https://mail-index.netbsd.org/source-changes/2010/08/04/msg012270.html
Change removing raidclose -> mutex_destroy(&dk_rawlock) path:
https://mail-index.netbsd.org/source-changes/2009/07/23/msg223381.html
---
sys/dev/dkwedge/dk.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/sys/dev/dkwedge/dk.c b/sys/dev/dkwedge/dk.c
index 97430e76da19..5282da129e45 100644
--- a/sys/dev/dkwedge/dk.c
+++ b/sys/dev/dkwedge/dk.c
@@ -1220,13 +1220,13 @@ dklastclose(struct dkwedge_softc *sc)
}
}
- mutex_exit(&sc->sc_parent->dk_rawlock);
- mutex_exit(&sc->sc_dk.dk_openlock);
-
if (vp) {
dk_close_parent(vp, mode);
}
+ mutex_exit(&sc->sc_parent->dk_rawlock);
+ mutex_exit(&sc->sc_dk.dk_openlock);
+
return error;
}
Home |
Main Index |
Thread Index |
Old Index