Skip to content

Commit 672324d

Browse files
committed
have to check status during ensure for raw vols as well
1 parent 39fa3e5 commit 672324d

File tree

1 file changed

+169
-25
lines changed

1 file changed

+169
-25
lines changed

illumos-utils/src/zfs.rs

Lines changed: 169 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,21 @@ pub enum EnsureDatasetVolumeErrorInner {
243243

244244
#[error("created but not ready yet: {reason}")]
245245
NotReady { reason: String },
246+
247+
#[error("not a raw zvol")]
248+
NotARawZvol,
249+
250+
#[error("cannot get raw volume status, ioctl returned {err}")]
251+
GettingStatus { err: i32 },
252+
253+
#[error("allocation failed, pool too fragmented")]
254+
TooFragmented,
255+
256+
#[error("allocation error failed with status {err}")]
257+
AllocationError { err: i32 },
258+
259+
#[error("allocation interrupted")]
260+
AllocationInterrupted,
246261
}
247262

248263
/// Error returned by [`Zfs::ensure_dataset_volume`].
@@ -303,26 +318,54 @@ impl EnsureDatasetVolumeError {
303318
}
304319
}
305320

306-
pub fn from_raw_zvol_open(name: String, e: rustix::io::Errno) -> Self {
321+
pub fn from_rustix_errno(name: String, e: rustix::io::Errno) -> Self {
307322
EnsureDatasetVolumeError {
308323
name,
309324
err: EnsureDatasetVolumeErrorInner::RustixErrno(e),
310325
}
311326
}
312327

313-
pub fn from_raw_zvol_read(name: String, e: rustix::io::Errno) -> Self {
314-
if e == rustix::io::Errno::INPROGRESS {
315-
EnsureDatasetVolumeError {
316-
name,
317-
err: EnsureDatasetVolumeErrorInner::NotReady {
318-
reason: String::from("raw volume not done initializing"),
319-
},
320-
}
321-
} else {
322-
EnsureDatasetVolumeError {
323-
name,
324-
err: EnsureDatasetVolumeErrorInner::RustixErrno(e),
325-
}
328+
pub fn not_a_raw_zvol(name: String) -> Self {
329+
EnsureDatasetVolumeError {
330+
name,
331+
err: EnsureDatasetVolumeErrorInner::NotARawZvol,
332+
}
333+
}
334+
335+
pub fn getting_status(name: String, err: i32) -> Self {
336+
EnsureDatasetVolumeError {
337+
name,
338+
err: EnsureDatasetVolumeErrorInner::GettingStatus { err },
339+
}
340+
}
341+
342+
pub fn still_initializing(name: String) -> Self {
343+
EnsureDatasetVolumeError {
344+
name,
345+
err: EnsureDatasetVolumeErrorInner::NotReady {
346+
reason: String::from("still zero initializing"),
347+
},
348+
}
349+
}
350+
351+
pub fn too_fragmented(name: String) -> Self {
352+
EnsureDatasetVolumeError {
353+
name,
354+
err: EnsureDatasetVolumeErrorInner::TooFragmented,
355+
}
356+
}
357+
358+
pub fn allocation_error(name: String, err: i32) -> Self {
359+
EnsureDatasetVolumeError {
360+
name,
361+
err: EnsureDatasetVolumeErrorInner::AllocationError { err },
362+
}
363+
}
364+
365+
pub fn allocation_interrupted(name: String) -> Self {
366+
EnsureDatasetVolumeError {
367+
name,
368+
err: EnsureDatasetVolumeErrorInner::AllocationInterrupted,
326369
}
327370
}
328371
}
@@ -1635,10 +1678,6 @@ impl Zfs {
16351678
}
16361679

16371680
if params.raw {
1638-
// Open the raw zvol and read from it. If EINPROGRESS is seen from
1639-
// the read, then it's not done initializing yet. We can't use it
1640-
// until it's fully done, so return the `NotReady` variant to signal
1641-
// upstack that it should poll.
16421681
let path = format!("/dev/zvol/rdsk/{}", params.name);
16431682

16441683
let fd = rustix::fs::open(
@@ -1647,20 +1686,125 @@ impl Zfs {
16471686
rustix::fs::Mode::empty(),
16481687
)
16491688
.map_err(|e| {
1650-
EnsureDatasetVolumeError::from_raw_zvol_open(
1689+
EnsureDatasetVolumeError::from_rustix_errno(
16511690
params.name.to_string(),
16521691
e,
16531692
)
16541693
})?;
16551694

16561695
// Raw zvols error for arbitrarily sized reads, so read the first 4k
16571696
let mut buf = [0u8; 4096];
1658-
rustix::io::read(fd, &mut buf).map_err(|e| {
1659-
EnsureDatasetVolumeError::from_raw_zvol_read(
1660-
params.name.to_string(),
1661-
e,
1662-
)
1663-
})?;
1697+
match rustix::io::read(&fd, &mut buf) {
1698+
Ok(_) => {
1699+
// Done initializing
1700+
return Ok(());
1701+
}
1702+
1703+
Err(rustix::io::Errno::INPROGRESS) => {
1704+
// The zero initialization thread is still running, fall
1705+
// through to check the status ioctl.
1706+
}
1707+
1708+
Err(e) => {
1709+
// Any other error should be bubbled up
1710+
return Err(EnsureDatasetVolumeError::from_rustix_errno(
1711+
params.name.to_string(),
1712+
e,
1713+
));
1714+
}
1715+
}
1716+
1717+
// Check the values from the status ioctl. We have to wait until the
1718+
// zero initialization thread is complete before the vol can be
1719+
// used.
1720+
let mut status = dk_rawvol_status::default();
1721+
1722+
// Retry if EINTR is seen
1723+
for i in 0..2 {
1724+
// Safety: We are issuing the `DKIOCRAWVOLSTATUS` ioctl which is
1725+
// documented to take a struct of type `dk_rawvol_status`. Assuming
1726+
// our type definitions are correct, this call is safe.
1727+
if unsafe {
1728+
libc::ioctl(
1729+
fd.as_raw_fd(),
1730+
DKIOCRAWVOLSTATUS as _,
1731+
&mut status,
1732+
)
1733+
} == -1
1734+
{
1735+
let err = std::io::Error::last_os_error();
1736+
match err.raw_os_error().unwrap() {
1737+
libc::ENOTSUP => {
1738+
// ENOTSUP is returned when this ioctl is issued against
1739+
// a non-raw zvol
1740+
return Err(
1741+
EnsureDatasetVolumeError::not_a_raw_zvol(
1742+
params.name.to_string(),
1743+
),
1744+
);
1745+
}
1746+
1747+
libc::EINTR if i == 0 => {
1748+
// Retry once if EINTR is seen
1749+
continue;
1750+
}
1751+
1752+
e => {
1753+
// Unexpected error, return up the stack
1754+
return Err(
1755+
EnsureDatasetVolumeError::getting_status(
1756+
params.name.to_string(),
1757+
e,
1758+
),
1759+
);
1760+
}
1761+
}
1762+
}
1763+
}
1764+
1765+
match status.drs_status {
1766+
0 => {
1767+
// The zero init thread was not running - because we saw
1768+
// EINPROGRESS before from the read, and now see 0, that
1769+
// means it finished in the time between the read and that
1770+
// ioctl.
1771+
return Ok(());
1772+
}
1773+
1774+
libc::EINTR => {
1775+
// The zero init thread was running but was interrupted.
1776+
return Err(
1777+
EnsureDatasetVolumeError::allocation_interrupted(
1778+
params.name.to_string(),
1779+
),
1780+
);
1781+
}
1782+
1783+
libc::EINPROGRESS => {
1784+
// The zero init thread is currently running
1785+
return Err(EnsureDatasetVolumeError::still_initializing(
1786+
params.name.to_string(),
1787+
));
1788+
}
1789+
1790+
// usr/src/uts/common/fs/zfs/sys/zio.h defines EFRAGS == EBADR
1791+
libc::EBADR => {
1792+
// EFRAGS means the zero init thread encountered an error
1793+
// allocating a record. This is fatal! The pool is too
1794+
// fragmented to continue.
1795+
return Err(EnsureDatasetVolumeError::too_fragmented(
1796+
params.name.to_string(),
1797+
));
1798+
}
1799+
1800+
e => {
1801+
// Any other error should be bubbled up
1802+
return Err(EnsureDatasetVolumeError::allocation_error(
1803+
params.name.to_string(),
1804+
e,
1805+
));
1806+
}
1807+
}
16641808
}
16651809

16661810
Ok(())

0 commit comments

Comments
 (0)