• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //! Driver for VirtIO block devices.
2 
3 use crate::hal::Hal;
4 use crate::queue::VirtQueue;
5 use crate::transport::Transport;
6 use crate::volatile::{volread, Volatile};
7 use crate::{Error, Result};
8 use bitflags::bitflags;
9 use log::info;
10 use zerocopy::{AsBytes, FromBytes};
11 
12 const QUEUE: u16 = 0;
13 const QUEUE_SIZE: u16 = 16;
14 
15 /// Driver for a VirtIO block device.
16 ///
17 /// This is a simple virtual block device, e.g. disk.
18 ///
19 /// Read and write requests (and other exotic requests) are placed in the queue and serviced
20 /// (probably out of order) by the device except where noted.
21 ///
22 /// # Example
23 ///
24 /// ```
25 /// # use virtio_drivers::{Error, Hal};
26 /// # use virtio_drivers::transport::Transport;
27 /// use virtio_drivers::device::blk::{VirtIOBlk, SECTOR_SIZE};
28 ///
29 /// # fn example<HalImpl: Hal, T: Transport>(transport: T) -> Result<(), Error> {
30 /// let mut disk = VirtIOBlk::<HalImpl, _>::new(transport)?;
31 ///
32 /// println!("VirtIO block device: {} kB", disk.capacity() * SECTOR_SIZE as u64 / 2);
33 ///
34 /// // Read sector 0 and then copy it to sector 1.
35 /// let mut buf = [0; SECTOR_SIZE];
36 /// disk.read_block(0, &mut buf)?;
37 /// disk.write_block(1, &buf)?;
38 /// # Ok(())
39 /// # }
40 /// ```
41 pub struct VirtIOBlk<H: Hal, T: Transport> {
42     transport: T,
43     queue: VirtQueue<H, { QUEUE_SIZE as usize }>,
44     capacity: u64,
45     readonly: bool,
46 }
47 
48 impl<H: Hal, T: Transport> VirtIOBlk<H, T> {
49     /// Create a new VirtIO-Blk driver.
new(mut transport: T) -> Result<Self>50     pub fn new(mut transport: T) -> Result<Self> {
51         let mut readonly = false;
52 
53         transport.begin_init(|features| {
54             let features = BlkFeature::from_bits_truncate(features);
55             info!("device features: {:?}", features);
56             readonly = features.contains(BlkFeature::RO);
57             // negotiate these flags only
58             let supported_features = BlkFeature::empty();
59             (features & supported_features).bits()
60         });
61 
62         // read configuration space
63         let config = transport.config_space::<BlkConfig>()?;
64         info!("config: {:?}", config);
65         // Safe because config is a valid pointer to the device configuration space.
66         let capacity = unsafe {
67             volread!(config, capacity_low) as u64 | (volread!(config, capacity_high) as u64) << 32
68         };
69         info!("found a block device of size {}KB", capacity / 2);
70 
71         let queue = VirtQueue::new(&mut transport, QUEUE)?;
72         transport.finish_init();
73 
74         Ok(VirtIOBlk {
75             transport,
76             queue,
77             capacity,
78             readonly,
79         })
80     }
81 
82     /// Gets the capacity of the block device, in 512 byte ([`SECTOR_SIZE`]) sectors.
capacity(&self) -> u6483     pub fn capacity(&self) -> u64 {
84         self.capacity
85     }
86 
87     /// Returns true if the block device is read-only, or false if it allows writes.
readonly(&self) -> bool88     pub fn readonly(&self) -> bool {
89         self.readonly
90     }
91 
92     /// Acknowledges a pending interrupt, if any.
93     ///
94     /// Returns true if there was an interrupt to acknowledge.
ack_interrupt(&mut self) -> bool95     pub fn ack_interrupt(&mut self) -> bool {
96         self.transport.ack_interrupt()
97     }
98 
99     /// Reads a block into the given buffer.
100     ///
101     /// Blocks until the read completes or there is an error.
read_block(&mut self, block_id: usize, buf: &mut [u8]) -> Result102     pub fn read_block(&mut self, block_id: usize, buf: &mut [u8]) -> Result {
103         assert_eq!(buf.len(), SECTOR_SIZE);
104         let req = BlkReq {
105             type_: ReqType::In,
106             reserved: 0,
107             sector: block_id as u64,
108         };
109         let mut resp = BlkResp::default();
110         self.queue.add_notify_wait_pop(
111             &[req.as_bytes()],
112             &mut [buf, resp.as_bytes_mut()],
113             &mut self.transport,
114         )?;
115         resp.status.into()
116     }
117 
118     /// Submits a request to read a block, but returns immediately without waiting for the read to
119     /// complete.
120     ///
121     /// # Arguments
122     ///
123     /// * `block_id` - The identifier of the block to read.
124     /// * `req` - A buffer which the driver can use for the request to send to the device. The
125     ///   contents don't matter as `read_block_nb` will initialise it, but like the other buffers it
126     ///   needs to be valid (and not otherwise used) until the corresponding `complete_read_block`
127     ///   call.
128     /// * `buf` - The buffer in memory into which the block should be read.
129     /// * `resp` - A mutable reference to a variable provided by the caller
130     ///   to contain the status of the request. The caller can safely
131     ///   read the variable only after the request is complete.
132     ///
133     /// # Usage
134     ///
135     /// It will submit request to the VirtIO block device and return a token identifying
136     /// the position of the first Descriptor in the chain. If there are not enough
137     /// Descriptors to allocate, then it returns [`Error::QueueFull`].
138     ///
139     /// The caller can then call `peek_used` with the returned token to check whether the device has
140     /// finished handling the request. Once it has, the caller must call `complete_read_block` with
141     /// the same buffers before reading the response.
142     ///
143     /// ```
144     /// # use virtio_drivers::{Error, Hal};
145     /// # use virtio_drivers::device::blk::VirtIOBlk;
146     /// # use virtio_drivers::transport::Transport;
147     /// use virtio_drivers::device::blk::{BlkReq, BlkResp, RespStatus};
148     ///
149     /// # fn example<H: Hal, T: Transport>(blk: &mut VirtIOBlk<H, T>) -> Result<(), Error> {
150     /// let mut request = BlkReq::default();
151     /// let mut buffer = [0; 512];
152     /// let mut response = BlkResp::default();
153     /// let token = unsafe { blk.read_block_nb(42, &mut request, &mut buffer, &mut response) }?;
154     ///
155     /// // Wait for an interrupt to tell us that the request completed...
156     /// assert_eq!(blk.peek_used(), Some(token));
157     ///
158     /// unsafe {
159     ///   blk.complete_read_block(token, &request, &mut buffer, &mut response)?;
160     /// }
161     /// if response.status() == RespStatus::OK {
162     ///   println!("Successfully read block.");
163     /// } else {
164     ///   println!("Error {:?} reading block.", response.status());
165     /// }
166     /// # Ok(())
167     /// # }
168     /// ```
169     ///
170     /// # Safety
171     ///
172     /// `req`, `buf` and `resp` are still borrowed by the underlying VirtIO block device even after
173     /// this method returns. Thus, it is the caller's responsibility to guarantee that they are not
174     /// accessed before the request is completed in order to avoid data races.
read_block_nb( &mut self, block_id: usize, req: &mut BlkReq, buf: &mut [u8], resp: &mut BlkResp, ) -> Result<u16>175     pub unsafe fn read_block_nb(
176         &mut self,
177         block_id: usize,
178         req: &mut BlkReq,
179         buf: &mut [u8],
180         resp: &mut BlkResp,
181     ) -> Result<u16> {
182         assert_eq!(buf.len(), SECTOR_SIZE);
183         *req = BlkReq {
184             type_: ReqType::In,
185             reserved: 0,
186             sector: block_id as u64,
187         };
188         let token = self
189             .queue
190             .add(&[req.as_bytes()], &mut [buf, resp.as_bytes_mut()])?;
191         if self.queue.should_notify() {
192             self.transport.notify(QUEUE);
193         }
194         Ok(token)
195     }
196 
197     /// Completes a read operation which was started by `read_block_nb`.
198     ///
199     /// # Safety
200     ///
201     /// The same buffers must be passed in again as were passed to `read_block_nb` when it returned
202     /// the token.
complete_read_block( &mut self, token: u16, req: &BlkReq, buf: &mut [u8], resp: &mut BlkResp, ) -> Result<()>203     pub unsafe fn complete_read_block(
204         &mut self,
205         token: u16,
206         req: &BlkReq,
207         buf: &mut [u8],
208         resp: &mut BlkResp,
209     ) -> Result<()> {
210         self.queue
211             .pop_used(token, &[req.as_bytes()], &mut [buf, resp.as_bytes_mut()])?;
212         resp.status.into()
213     }
214 
215     /// Writes the contents of the given buffer to a block.
216     ///
217     /// Blocks until the write is complete or there is an error.
write_block(&mut self, block_id: usize, buf: &[u8]) -> Result218     pub fn write_block(&mut self, block_id: usize, buf: &[u8]) -> Result {
219         assert_eq!(buf.len(), SECTOR_SIZE);
220         let req = BlkReq {
221             type_: ReqType::Out,
222             reserved: 0,
223             sector: block_id as u64,
224         };
225         let mut resp = BlkResp::default();
226         self.queue.add_notify_wait_pop(
227             &[req.as_bytes(), buf],
228             &mut [resp.as_bytes_mut()],
229             &mut self.transport,
230         )?;
231         resp.status.into()
232     }
233 
234     /// Submits a request to write a block, but returns immediately without waiting for the write to
235     /// complete.
236     ///
237     /// # Arguments
238     ///
239     /// * `block_id` - The identifier of the block to write.
240     /// * `req` - A buffer which the driver can use for the request to send to the device. The
241     ///   contents don't matter as `read_block_nb` will initialise it, but like the other buffers it
242     ///   needs to be valid (and not otherwise used) until the corresponding `complete_read_block`
243     ///   call.
244     /// * `buf` - The buffer in memory containing the data to write to the block.
245     /// * `resp` - A mutable reference to a variable provided by the caller
246     ///   to contain the status of the request. The caller can safely
247     ///   read the variable only after the request is complete.
248     ///
249     /// # Usage
250     ///
251     /// See [VirtIOBlk::read_block_nb].
252     ///
253     /// # Safety
254     ///
255     /// See  [VirtIOBlk::read_block_nb].
write_block_nb( &mut self, block_id: usize, req: &mut BlkReq, buf: &[u8], resp: &mut BlkResp, ) -> Result<u16>256     pub unsafe fn write_block_nb(
257         &mut self,
258         block_id: usize,
259         req: &mut BlkReq,
260         buf: &[u8],
261         resp: &mut BlkResp,
262     ) -> Result<u16> {
263         assert_eq!(buf.len(), SECTOR_SIZE);
264         *req = BlkReq {
265             type_: ReqType::Out,
266             reserved: 0,
267             sector: block_id as u64,
268         };
269         let token = self
270             .queue
271             .add(&[req.as_bytes(), buf], &mut [resp.as_bytes_mut()])?;
272         if self.queue.should_notify() {
273             self.transport.notify(QUEUE);
274         }
275         Ok(token)
276     }
277 
278     /// Completes a write operation which was started by `write_block_nb`.
279     ///
280     /// # Safety
281     ///
282     /// The same buffers must be passed in again as were passed to `write_block_nb` when it returned
283     /// the token.
complete_write_block( &mut self, token: u16, req: &BlkReq, buf: &[u8], resp: &mut BlkResp, ) -> Result<()>284     pub unsafe fn complete_write_block(
285         &mut self,
286         token: u16,
287         req: &BlkReq,
288         buf: &[u8],
289         resp: &mut BlkResp,
290     ) -> Result<()> {
291         self.queue
292             .pop_used(token, &[req.as_bytes(), buf], &mut [resp.as_bytes_mut()])?;
293         resp.status.into()
294     }
295 
296     /// Fetches the token of the next completed request from the used ring and returns it, without
297     /// removing it from the used ring. If there are no pending completed requests returns `None`.
peek_used(&mut self) -> Option<u16>298     pub fn peek_used(&mut self) -> Option<u16> {
299         self.queue.peek_used()
300     }
301 
302     /// Returns the size of the device's VirtQueue.
303     ///
304     /// This can be used to tell the caller how many channels to monitor on.
virt_queue_size(&self) -> u16305     pub fn virt_queue_size(&self) -> u16 {
306         QUEUE_SIZE
307     }
308 }
309 
310 impl<H: Hal, T: Transport> Drop for VirtIOBlk<H, T> {
drop(&mut self)311     fn drop(&mut self) {
312         // Clear any pointers pointing to DMA regions, so the device doesn't try to access them
313         // after they have been freed.
314         self.transport.queue_unset(QUEUE);
315     }
316 }
317 
318 #[repr(C)]
319 struct BlkConfig {
320     /// Number of 512 Bytes sectors
321     capacity_low: Volatile<u32>,
322     capacity_high: Volatile<u32>,
323     size_max: Volatile<u32>,
324     seg_max: Volatile<u32>,
325     cylinders: Volatile<u16>,
326     heads: Volatile<u8>,
327     sectors: Volatile<u8>,
328     blk_size: Volatile<u32>,
329     physical_block_exp: Volatile<u8>,
330     alignment_offset: Volatile<u8>,
331     min_io_size: Volatile<u16>,
332     opt_io_size: Volatile<u32>,
333     // ... ignored
334 }
335 
336 /// A VirtIO block device request.
337 #[repr(C)]
338 #[derive(AsBytes, Debug)]
339 pub struct BlkReq {
340     type_: ReqType,
341     reserved: u32,
342     sector: u64,
343 }
344 
345 impl Default for BlkReq {
default() -> Self346     fn default() -> Self {
347         Self {
348             type_: ReqType::In,
349             reserved: 0,
350             sector: 0,
351         }
352     }
353 }
354 
355 /// Response of a VirtIOBlk request.
356 #[repr(C)]
357 #[derive(AsBytes, Debug, FromBytes)]
358 pub struct BlkResp {
359     status: RespStatus,
360 }
361 
362 impl BlkResp {
363     /// Return the status of a VirtIOBlk request.
status(&self) -> RespStatus364     pub fn status(&self) -> RespStatus {
365         self.status
366     }
367 }
368 
369 #[repr(u32)]
370 #[derive(AsBytes, Debug)]
371 enum ReqType {
372     In = 0,
373     Out = 1,
374     Flush = 4,
375     Discard = 11,
376     WriteZeroes = 13,
377 }
378 
379 /// Status of a VirtIOBlk request.
380 #[repr(transparent)]
381 #[derive(AsBytes, Copy, Clone, Debug, Eq, FromBytes, PartialEq)]
382 pub struct RespStatus(u8);
383 
384 impl RespStatus {
385     /// Ok.
386     pub const OK: RespStatus = RespStatus(0);
387     /// IoErr.
388     pub const IO_ERR: RespStatus = RespStatus(1);
389     /// Unsupported yet.
390     pub const UNSUPPORTED: RespStatus = RespStatus(2);
391     /// Not ready.
392     pub const NOT_READY: RespStatus = RespStatus(3);
393 }
394 
395 impl From<RespStatus> for Result {
from(status: RespStatus) -> Self396     fn from(status: RespStatus) -> Self {
397         match status {
398             RespStatus::OK => Ok(()),
399             RespStatus::IO_ERR => Err(Error::IoError),
400             RespStatus::UNSUPPORTED => Err(Error::Unsupported),
401             RespStatus::NOT_READY => Err(Error::NotReady),
402             _ => Err(Error::IoError),
403         }
404     }
405 }
406 
407 impl Default for BlkResp {
default() -> Self408     fn default() -> Self {
409         BlkResp {
410             status: RespStatus::NOT_READY,
411         }
412     }
413 }
414 
415 /// The standard sector size of a VirtIO block device. Data is read and written in multiples of this
416 /// size.
417 pub const SECTOR_SIZE: usize = 512;
418 
419 bitflags! {
420     struct BlkFeature: u64 {
421         /// Device supports request barriers. (legacy)
422         const BARRIER       = 1 << 0;
423         /// Maximum size of any single segment is in `size_max`.
424         const SIZE_MAX      = 1 << 1;
425         /// Maximum number of segments in a request is in `seg_max`.
426         const SEG_MAX       = 1 << 2;
427         /// Disk-style geometry specified in geometry.
428         const GEOMETRY      = 1 << 4;
429         /// Device is read-only.
430         const RO            = 1 << 5;
431         /// Block size of disk is in `blk_size`.
432         const BLK_SIZE      = 1 << 6;
433         /// Device supports scsi packet commands. (legacy)
434         const SCSI          = 1 << 7;
435         /// Cache flush command support.
436         const FLUSH         = 1 << 9;
437         /// Device exports information on optimal I/O alignment.
438         const TOPOLOGY      = 1 << 10;
439         /// Device can toggle its cache between writeback and writethrough modes.
440         const CONFIG_WCE    = 1 << 11;
441         /// Device can support discard command, maximum discard sectors size in
442         /// `max_discard_sectors` and maximum discard segment number in
443         /// `max_discard_seg`.
444         const DISCARD       = 1 << 13;
445         /// Device can support write zeroes command, maximum write zeroes sectors
446         /// size in `max_write_zeroes_sectors` and maximum write zeroes segment
447         /// number in `max_write_zeroes_seg`.
448         const WRITE_ZEROES  = 1 << 14;
449 
450         // device independent
451         const NOTIFY_ON_EMPTY       = 1 << 24; // legacy
452         const ANY_LAYOUT            = 1 << 27; // legacy
453         const RING_INDIRECT_DESC    = 1 << 28;
454         const RING_EVENT_IDX        = 1 << 29;
455         const UNUSED                = 1 << 30; // legacy
456         const VERSION_1             = 1 << 32; // detect legacy
457 
458         // the following since virtio v1.1
459         const ACCESS_PLATFORM       = 1 << 33;
460         const RING_PACKED           = 1 << 34;
461         const IN_ORDER              = 1 << 35;
462         const ORDER_PLATFORM        = 1 << 36;
463         const SR_IOV                = 1 << 37;
464         const NOTIFICATION_DATA     = 1 << 38;
465     }
466 }
467 
468 #[cfg(test)]
469 mod tests {
470     use super::*;
471     use crate::{
472         hal::fake::FakeHal,
473         transport::{
474             fake::{FakeTransport, QueueStatus, State},
475             DeviceStatus, DeviceType,
476         },
477     };
478     use alloc::{sync::Arc, vec};
479     use core::{mem::size_of, ptr::NonNull};
480     use std::{sync::Mutex, thread, time::Duration};
481 
482     #[test]
config()483     fn config() {
484         let mut config_space = BlkConfig {
485             capacity_low: Volatile::new(0x42),
486             capacity_high: Volatile::new(0x02),
487             size_max: Volatile::new(0),
488             seg_max: Volatile::new(0),
489             cylinders: Volatile::new(0),
490             heads: Volatile::new(0),
491             sectors: Volatile::new(0),
492             blk_size: Volatile::new(0),
493             physical_block_exp: Volatile::new(0),
494             alignment_offset: Volatile::new(0),
495             min_io_size: Volatile::new(0),
496             opt_io_size: Volatile::new(0),
497         };
498         let state = Arc::new(Mutex::new(State {
499             status: DeviceStatus::empty(),
500             driver_features: 0,
501             guest_page_size: 0,
502             interrupt_pending: false,
503             queues: vec![QueueStatus::default(); 1],
504         }));
505         let transport = FakeTransport {
506             device_type: DeviceType::Console,
507             max_queue_size: QUEUE_SIZE.into(),
508             device_features: BlkFeature::RO.bits(),
509             config_space: NonNull::from(&mut config_space),
510             state: state.clone(),
511         };
512         let blk = VirtIOBlk::<FakeHal, FakeTransport<BlkConfig>>::new(transport).unwrap();
513 
514         assert_eq!(blk.capacity(), 0x02_0000_0042);
515         assert_eq!(blk.readonly(), true);
516     }
517 
518     #[test]
read()519     fn read() {
520         let mut config_space = BlkConfig {
521             capacity_low: Volatile::new(66),
522             capacity_high: Volatile::new(0),
523             size_max: Volatile::new(0),
524             seg_max: Volatile::new(0),
525             cylinders: Volatile::new(0),
526             heads: Volatile::new(0),
527             sectors: Volatile::new(0),
528             blk_size: Volatile::new(0),
529             physical_block_exp: Volatile::new(0),
530             alignment_offset: Volatile::new(0),
531             min_io_size: Volatile::new(0),
532             opt_io_size: Volatile::new(0),
533         };
534         let state = Arc::new(Mutex::new(State {
535             status: DeviceStatus::empty(),
536             driver_features: 0,
537             guest_page_size: 0,
538             interrupt_pending: false,
539             queues: vec![QueueStatus::default(); 1],
540         }));
541         let transport = FakeTransport {
542             device_type: DeviceType::Console,
543             max_queue_size: QUEUE_SIZE.into(),
544             device_features: 0,
545             config_space: NonNull::from(&mut config_space),
546             state: state.clone(),
547         };
548         let mut blk = VirtIOBlk::<FakeHal, FakeTransport<BlkConfig>>::new(transport).unwrap();
549 
550         // Start a thread to simulate the device waiting for a read request.
551         let handle = thread::spawn(move || {
552             println!("Device waiting for a request.");
553             while !state.lock().unwrap().queues[usize::from(QUEUE)].notified {
554                 thread::sleep(Duration::from_millis(10));
555             }
556             println!("Transmit queue was notified.");
557 
558             state
559                 .lock()
560                 .unwrap()
561                 .read_write_queue::<{ QUEUE_SIZE as usize }>(QUEUE, |request| {
562                     assert_eq!(
563                         request,
564                         BlkReq {
565                             type_: ReqType::In,
566                             reserved: 0,
567                             sector: 42
568                         }
569                         .as_bytes()
570                     );
571 
572                     let mut response = vec![0; SECTOR_SIZE];
573                     response[0..9].copy_from_slice(b"Test data");
574                     response.extend_from_slice(
575                         BlkResp {
576                             status: RespStatus::OK,
577                         }
578                         .as_bytes(),
579                     );
580 
581                     response
582                 });
583         });
584 
585         // Read a block from the device.
586         let mut buffer = [0; 512];
587         blk.read_block(42, &mut buffer).unwrap();
588         assert_eq!(&buffer[0..9], b"Test data");
589 
590         handle.join().unwrap();
591     }
592 
593     #[test]
write()594     fn write() {
595         let mut config_space = BlkConfig {
596             capacity_low: Volatile::new(66),
597             capacity_high: Volatile::new(0),
598             size_max: Volatile::new(0),
599             seg_max: Volatile::new(0),
600             cylinders: Volatile::new(0),
601             heads: Volatile::new(0),
602             sectors: Volatile::new(0),
603             blk_size: Volatile::new(0),
604             physical_block_exp: Volatile::new(0),
605             alignment_offset: Volatile::new(0),
606             min_io_size: Volatile::new(0),
607             opt_io_size: Volatile::new(0),
608         };
609         let state = Arc::new(Mutex::new(State {
610             status: DeviceStatus::empty(),
611             driver_features: 0,
612             guest_page_size: 0,
613             interrupt_pending: false,
614             queues: vec![QueueStatus::default(); 1],
615         }));
616         let transport = FakeTransport {
617             device_type: DeviceType::Console,
618             max_queue_size: QUEUE_SIZE.into(),
619             device_features: 0,
620             config_space: NonNull::from(&mut config_space),
621             state: state.clone(),
622         };
623         let mut blk = VirtIOBlk::<FakeHal, FakeTransport<BlkConfig>>::new(transport).unwrap();
624 
625         // Start a thread to simulate the device waiting for a write request.
626         let handle = thread::spawn(move || {
627             println!("Device waiting for a request.");
628             while !state.lock().unwrap().queues[usize::from(QUEUE)].notified {
629                 thread::sleep(Duration::from_millis(10));
630             }
631             println!("Transmit queue was notified.");
632 
633             state
634                 .lock()
635                 .unwrap()
636                 .read_write_queue::<{ QUEUE_SIZE as usize }>(QUEUE, |request| {
637                     assert_eq!(
638                         &request[0..size_of::<BlkReq>()],
639                         BlkReq {
640                             type_: ReqType::Out,
641                             reserved: 0,
642                             sector: 42
643                         }
644                         .as_bytes()
645                     );
646                     let data = &request[size_of::<BlkReq>()..];
647                     assert_eq!(data.len(), SECTOR_SIZE);
648                     assert_eq!(&data[0..9], b"Test data");
649 
650                     let mut response = Vec::new();
651                     response.extend_from_slice(
652                         BlkResp {
653                             status: RespStatus::OK,
654                         }
655                         .as_bytes(),
656                     );
657 
658                     response
659                 });
660         });
661 
662         // Write a block to the device.
663         let mut buffer = [0; 512];
664         buffer[0..9].copy_from_slice(b"Test data");
665         blk.write_block(42, &mut buffer).unwrap();
666 
667         handle.join().unwrap();
668     }
669 }
670