-
Notifications
You must be signed in to change notification settings - Fork 40
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
sled-agent: Setup dump dev on M.2, savecore to U.2 #3586
Conversation
e4fa251
to
af47e1f
Compare
FYI: I have turned off the Let me know if you need something else there! |
b915ee6
to
5572094
Compare
FYI: adjacent relevant follow-up fix I have made to Helios: oxidecomputer/helios@6cc29f6 |
ff70527
to
6fc06e7
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The integration pieces of this make sense to me, and look good!
I have some question that are 90% related to me trying to understand the savecore and dumapdm utilities.
illumos-utils/src/dumpadm.rs
Outdated
// ...but do create and use a tmpfs path (rather than the default | ||
// location under /var/crash, which is in the ramdisk pool), because | ||
// dumpadm refuses to do what we ask otherwise. | ||
let tmp_crash = "/tmp/crash"; | ||
std::fs::create_dir_all(tmp_crash).map_err(DumpAdmError::Mkdir)?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
... weird, so it's not possible to run -n
without -s
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
dumpadm doesn't like it if the configured savecore directory doesn't exist, so we're making sure it does (even though we're not using it in this case)
// Include memory from the current process if there is one for the panic | ||
// context, in addition to kernel memory: | ||
cmd.arg("-c").arg("curproc"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this a superset of the all
option?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
curproc
dumps more than kernel
but less than all
:
kernel
Kernel memory pages only.
all
All memory pages.
curproc
Kernel memory pages, and the memory pages of the process whose
thread was currently executing on the CPU on which the crash
dump was initiated. If the thread executing on that CPU is a
kernel thread not associated with any user process, only kernel
pages will be dumped.
pub fn dump_flag_is_valid( | ||
dump_slice: &Utf8PathBuf, | ||
) -> Result<bool, DumpHdrError> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does "true" here imply that there is a dump we should extract via savecore? If so: could we add that as a doc comment?
illumos-utils/src/dumpadm.rs
Outdated
if savecore_dir.is_some() { | ||
if let Ok(true) = dump_flag_is_valid(dump_slice) { | ||
return savecore(); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are these conditionals basically "have we just noticed that a dump already exists on this slice, ready for extraction via savecore?"
|
||
#[derive(Default)] | ||
pub struct DumpSetup { | ||
savecore_lock: Arc<std::sync::Mutex<()>>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this lock is particularly opt-in, can we document what it's doing? Looks like "preventing us from running savecore concurrently", right?
disks: &mut MutexGuard<'_, HashMap<DiskIdentity, DiskWrapper>>, | ||
log: Logger, | ||
) { | ||
let mut dump_slices = Vec::new(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not a major issue, but it feels a little wasteful to call this and re-calculate all state for all disks when we're only actually inserting one disk at a time. We do have the DumpSetup
struct; we could keep a cache of all the information we need?
(Not a blocker for this PR though, the only difference here is a minor efficiency difference)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah, that's what i'm thinking for a cleaner follow-up, the thought behind what's here now is "get dumps configured at all, these machines can probably handle counting to 10 redundantly a few times at startup for now"
illumos-utils/src/dumpadm.rs
Outdated
// in the event that savecore(8) terminates before it finishes copying the | ||
// dump, the incomplete dump will remain in the target directory, but the next | ||
// invocation will overwrite it. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Gotcha, so bounds
is like, "the last successful savecore iteration." seems like this overwrite behavior is kinda what we want then, eh?
Ok(false) => { | ||
info!(log, "Dump slice {dump_slice:?} appears to have already been saved"); | ||
} | ||
Err(err) => { | ||
debug!(log, "Dump slice {dump_slice:?} appears to be unused: {err:?}"); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we be breaking out early in these cases? Or is the goal to map "dump slice" to "dump dir" even if no dump exists?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
on the contrary, in these cases we definitely want to configure the system to use the dump slice, as there's nothing important there to worry about overwriting
// TODO: a more reasonable way of deduplicating the effort. | ||
let _guard = savecore_lock.lock(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like this lock exists to prevent us from running savecore concurrently, but we're configuring dumpadm in such as way that it should be running savecore on reboot, if there was a crash.
Do we risk racing between the "system-initiated savecore" and the "sled-agent-initiated savecore"?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
there's no system-initiated savecore at boot time, actually! i need to change the flags here to be more clear about that. at boot-time, our system has no configured dump slice until sled-agent sets one, and so won't ever invoke savecore by itself.
pub fn dumpadm( | ||
dump_slice: &Utf8PathBuf, | ||
savecore_dir: Option<&Utf8PathBuf>, | ||
) -> Result<Option<OsString>, DumpAdmError> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we document -- what's the returned string here?
let version = | ||
f.read_u32::<LittleEndian>().map_err(DumpHdrError::ReadVersion)?; | ||
if version != DUMP_VERSION { | ||
return Err(DumpHdrError::InvalidVersion(version)); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the point of checking the version that the flag semantics might change in the future? I am curious how often dump_version
changes and whether this check is asking for trouble in the future.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah. i'm operating under the assumption that the position and semantics of the magic number and the version are stable, and that everything else may change if the version changes. ideally (and i've mentioned as much to rm) we wouldn't have any such version-sensitivity and this function would be replaced by shelling out to some kind of savecore --tell-me-if-there-is-one=/on/this/slice
illumos-utils/src/dumpadm.rs
Outdated
// invokes savecore(8) according to the system-wide config set by dumpadm. | ||
// savecore(8) creates a file in the savecore directory called `vmdump.<n>`, | ||
// where `<n>` is the number in the neighboring plaintext file called `bounds`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
probably mention the default if bounds is not present (or parseable) is 0
54bdb28
to
69cb0f9
Compare
Each time the sled-agent upserts a new disk, it enumerates every disk it knows about. If an M.2 is found, it runs dumpadm(8) to set it as the dump device. If a U.2 is *also* found, it has dumpadm(8) invoke savecore(8) to save the previous dump on the M.2, if any, to the U.2, and mark it as empty. This is a bit of a kludge due to savecore(8) not yet exposing a clear way to save-and-clear a dump *partition* other than the one configured system-wide by dumpadm. While redundant, this is for our purposes idempotent - as long as *any* M.2 is configured, dumps have a place to go, and as long as any U.2 is present, we will attempt to save any yet-unsaved kernel core dumps on all known M.2s to it. (see RFD 118 for details of partition layout)
Each time the sled-agent upserts a new disk, it enumerates every disk it knows about. If an M.2 is found, it runs dumpadm(8) to set it as the dump device. If a U.2 is also found, it has dumpadm(8) invoke savecore(8) to save the previous dump on the M.2, if any, to an encrypted zvol on the U.2, and mark the dump slice as empty.
This is a bit of a kludge due to savecore(8) not yet exposing a clear way to save-and-clear a dump partition other than the one configured system-wide by dumpadm. While redundant, this is for our purposes idempotent - as long as any M.2 is configured, dumps have a place to go, and as long as any U.2 is present, we will attempt to save any yet-unsaved kernel core dumps on all known M.2s to it.
(see RFD 118 for details of partition layout) (#2450, #2478)