@@ -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