1use super::drivers::RangeBlockedGuard;
8use crate::io_buffers::{IoBuffer, IoVector, IoVectorMut, IoVectorTrait};
9use crate::Storage;
10use maybe_async::maybe_async;
11use std::ops::Range;
12use std::{cmp, io};
13use tracing::trace;
14
15#[maybe_async(AFIT)]
19pub trait StorageExt: Storage {
20 #[allow(async_fn_in_trait)] async fn readv(&self, bufv: IoVectorMut<'_>, offset: u64) -> io::Result<()>;
29
30 #[allow(async_fn_in_trait)] async fn writev(&self, bufv: IoVector<'_>, offset: u64) -> io::Result<()>;
42
43 #[allow(async_fn_in_trait)] async fn read<'a>(&'a self, buf: impl Into<IoVectorMut<'a>>, offset: u64) -> io::Result<()>;
52
53 #[allow(async_fn_in_trait)] async fn write<'a>(&'a self, buf: impl Into<IoVector<'a>>, offset: u64) -> io::Result<()>;
65
66 #[allow(async_fn_in_trait)] async fn write_zeroes(&self, offset: u64, length: u64) -> io::Result<()>;
69
70 #[allow(async_fn_in_trait)] async fn write_allocated_zeroes(&self, offset: u64, length: u64) -> io::Result<()>;
73
74 #[allow(async_fn_in_trait)] async fn discard(&self, offset: u64, length: u64) -> io::Result<()>;
80
81 #[allow(async_fn_in_trait)] async fn weak_write_blocker(&self, range: Range<u64>) -> RangeBlockedGuard<'_>;
88
89 #[allow(async_fn_in_trait)] async fn strong_write_blocker(&self, range: Range<u64>) -> RangeBlockedGuard<'_>;
95}
96
97#[maybe_async(AFIT)]
98impl<S: Storage> StorageExt for S {
99 async fn readv(&self, mut bufv: IoVectorMut<'_>, offset: u64) -> io::Result<()> {
100 if bufv.is_empty() {
101 return Ok(());
102 }
103
104 let mem_align = self.mem_align();
105 let req_align = self.req_align();
106
107 if is_aligned(&bufv, offset, mem_align, req_align) {
108 return unsafe { self.pure_readv(bufv, offset) }.await;
110 }
111
112 trace!(
113 "Unaligned read: 0x{offset:x} + {} (size: {:#x})",
114 bufv.len(),
115 self.size().unwrap()
116 );
117
118 let req_align_mask = req_align as u64 - 1;
119 let len_align_mask = req_align_mask | (mem_align as u64 - 1);
121 debug_assert!((len_align_mask + 1).is_multiple_of(req_align as u64));
122
123 let unpadded_end = offset + bufv.len();
124 let padded_offset = offset & !req_align_mask;
125 let padded_end = (unpadded_end + req_align_mask) & !req_align_mask;
128 let padded_len = (padded_end - padded_offset + len_align_mask) & !(len_align_mask);
130 let padded_end = padded_offset + padded_len;
131
132 let padded_len: usize = (padded_end - padded_offset)
133 .try_into()
134 .map_err(|e| io::Error::other(format!("Cannot realign read: {e}")))?;
135
136 trace!("Padded read: {padded_offset:#x} + {padded_len}");
137
138 let mut bounce_buf = IoBuffer::new(padded_len, mem_align)?;
139
140 unsafe { self.pure_readv(bounce_buf.as_mut().into(), padded_offset) }.await?;
142
143 let in_buf_ofs = (offset - padded_offset) as usize;
144 let in_buf_end = (unpadded_end - padded_offset) as usize;
146
147 bufv.copy_from_slice(bounce_buf.as_ref_range(in_buf_ofs..in_buf_end).into_slice());
148
149 Ok(())
150 }
151
152 async fn writev(&self, bufv: IoVector<'_>, offset: u64) -> io::Result<()> {
153 if bufv.is_empty() {
154 return Ok(());
155 }
156
157 let mem_align = self.mem_align();
158 let req_align = self.req_align();
159
160 if is_aligned(&bufv, offset, mem_align, req_align) {
161 let _sw_guard = self.weak_write_blocker(offset..(offset + bufv.len())).await;
162
163 return unsafe { self.pure_writev(bufv, offset) }.await;
165 }
166
167 trace!(
168 "Unaligned write: {offset:#x} + {} (size: {:#x})",
169 bufv.len(),
170 self.size().unwrap()
171 );
172
173 let req_align_mask = req_align - 1;
174 let len_align_mask = req_align_mask | (mem_align - 1);
176 let len_align = req_align_mask + 1;
177 debug_assert!(len_align.is_multiple_of(req_align));
178
179 let unpadded_end = offset + bufv.len();
180 let padded_offset = offset & !(req_align_mask as u64);
181 let padded_end = (unpadded_end + req_align_mask as u64) & !(req_align_mask as u64);
185 let padded_len =
187 (padded_end - padded_offset + len_align_mask as u64) & !(len_align_mask as u64);
188 let padded_end = padded_offset + padded_len;
189
190 let padded_len: usize = (padded_end - padded_offset)
191 .try_into()
192 .map_err(|e| io::Error::other(format!("Cannot realign write: {e}")))?;
193
194 trace!("Padded write: {padded_offset:#x} + {padded_len}");
195
196 let mut bounce_buf = IoBuffer::new(padded_len, mem_align)?;
197 assert!(padded_len >= len_align && padded_len & len_align_mask == 0);
198
199 let _sw_guard = self.strong_write_blocker(padded_offset..padded_end).await;
206
207 let in_buf_ofs = (offset - padded_offset) as usize;
208 let in_buf_end = (unpadded_end - padded_offset) as usize;
210
211 let head_len = in_buf_ofs;
214 let aligned_head_len = (head_len + len_align_mask) & !len_align_mask;
215
216 let tail_len = padded_len - in_buf_end;
217 let aligned_tail_len = (tail_len + len_align_mask) & !len_align_mask;
218
219 if aligned_head_len + aligned_tail_len == padded_len {
220 unsafe { self.pure_readv(bounce_buf.as_mut().into(), padded_offset) }.await?;
223 } else {
224 if aligned_head_len > 0 {
225 let head_bufv = bounce_buf.as_mut_range(0..aligned_head_len).into();
226 unsafe { self.pure_readv(head_bufv, padded_offset) }.await?;
228 }
229 if aligned_tail_len > 0 {
230 let tail_start = padded_len - aligned_tail_len;
231 let tail_bufv = bounce_buf.as_mut_range(tail_start..padded_len).into();
232 unsafe { self.pure_readv(tail_bufv, padded_offset + tail_start as u64) }.await?;
234 }
235 }
236
237 bufv.copy_into_slice(bounce_buf.as_mut_range(in_buf_ofs..in_buf_end).into_slice());
239
240 unsafe { self.pure_writev(bounce_buf.as_ref().into(), padded_offset) }.await
243 }
244
245 async fn read<'a>(&'a self, buf: impl Into<IoVectorMut<'a>>, offset: u64) -> io::Result<()> {
246 self.readv(buf.into(), offset).await
247 }
248
249 async fn write<'a>(&'a self, buf: impl Into<IoVector<'a>>, offset: u64) -> io::Result<()> {
250 self.writev(buf.into(), offset).await
251 }
252
253 async fn write_zeroes(&self, offset: u64, length: u64) -> io::Result<()> {
254 write_efficient_zeroes(self, offset, length, false).await
255 }
256
257 async fn write_allocated_zeroes(&self, offset: u64, length: u64) -> io::Result<()> {
258 write_efficient_zeroes(self, offset, length, true).await
259 }
260
261 async fn discard(&self, offset: u64, length: u64) -> io::Result<()> {
262 let discard_align = self.discard_align();
263 debug_assert!(discard_align.is_power_of_two());
264 let align_mask = discard_align as u64 - 1;
265
266 let unaligned_end = offset
267 .checked_add(length)
268 .ok_or_else(|| io::Error::other("Discard wrap-around"))?;
269 let aligned_offset = (offset + align_mask) & !align_mask;
270 let aligned_end = unaligned_end & !align_mask;
271
272 if aligned_end > aligned_offset {
273 let _sw_guard = self.weak_write_blocker(aligned_offset..aligned_end).await;
274 let aligned_len = aligned_end - aligned_offset;
275 if let Err(err) = unsafe { self.pure_discard(aligned_offset, aligned_len) }.await {
277 if err.kind() != io::ErrorKind::Unsupported {
282 return Err(err);
283 }
284 }
285 }
286
287 Ok(())
290 }
291
292 async fn weak_write_blocker(&self, range: Range<u64>) -> RangeBlockedGuard<'_> {
293 self.get_storage_helper().weak_write_blocker(range).await
294 }
295
296 async fn strong_write_blocker(&self, range: Range<u64>) -> RangeBlockedGuard<'_> {
297 self.get_storage_helper().strong_write_blocker(range).await
298 }
299}
300
301fn is_aligned<V: IoVectorTrait>(bufv: &V, offset: u64, mem_align: usize, req_align: usize) -> bool {
303 debug_assert!(mem_align.is_power_of_two() && req_align.is_power_of_two());
304
305 let req_align_mask = req_align as u64 - 1;
306
307 if offset & req_align_mask != 0 {
308 false
309 } else if bufv.len() & req_align_mask == 0 {
310 bufv.is_aligned(mem_align, req_align)
311 } else {
312 false
313 }
314}
315
316#[maybe_async]
321pub(crate) async fn write_full_zeroes<S: StorageExt>(
322 storage: S,
323 mut offset: u64,
324 mut length: u64,
325) -> io::Result<()> {
326 let req_align = storage.req_align();
327 let req_align_mask = (req_align - 1) as u64;
328
329 let mem_align = storage.mem_align();
330 let max_chunk_len = cmp::max(cmp::max(req_align, mem_align), 1048576) as u64;
331 let buflen = cmp::min(length, max_chunk_len) as usize;
332 let mut buf = IoBuffer::new(buflen, storage.mem_align())?;
333 buf.as_mut().into_slice().fill(0);
334
335 while length > 0 {
336 let mut chunk_length = cmp::min(length, max_chunk_len) as usize;
337 if offset & req_align_mask != 0 {
338 chunk_length = cmp::min(chunk_length, req_align - (offset & req_align_mask) as usize);
339 }
340 storage
341 .write(buf.as_ref_range(0..chunk_length), offset)
342 .await?;
343 offset += chunk_length as u64;
344 length -= chunk_length as u64;
345 }
346
347 Ok(())
348}
349
350#[maybe_async]
360pub(crate) async fn write_efficient_zeroes<S: StorageExt>(
361 storage: S,
362 offset: u64,
363 length: u64,
364 allocate: bool,
365) -> io::Result<()> {
366 if length == 0 {
367 return Ok(());
368 }
369
370 let zero_align = storage.zero_align();
371 debug_assert!(zero_align.is_power_of_two());
372 let align_mask = zero_align as u64 - 1;
373
374 let unaligned_end = offset
375 .checked_add(length)
376 .ok_or_else(|| io::Error::other("Zero-write wrap-around"))?;
377 let aligned_offset = (offset + align_mask) & !align_mask;
378 let aligned_end = unaligned_end & !align_mask;
379
380 if aligned_end > aligned_offset {
381 let result = {
382 let _sw_guard = storage
383 .weak_write_blocker(aligned_offset..aligned_end)
384 .await;
385 if allocate {
387 unsafe {
388 storage
389 .pure_write_allocated_zeroes(aligned_offset, aligned_end - aligned_offset)
390 }
391 .await
392 } else {
393 unsafe { storage.pure_write_zeroes(aligned_offset, aligned_end - aligned_offset) }
394 .await
395 }
396 };
397 if let Err(err) = result {
398 return if err.kind() == io::ErrorKind::Unsupported {
399 write_full_zeroes(storage, offset, length).await
400 } else {
401 Err(err)
402 };
403 }
404 }
405
406 let zero_buf = if aligned_offset > offset || aligned_end < unaligned_end {
407 let buflen = if aligned_offset >= aligned_end {
408 unaligned_end - offset
409 } else {
410 cmp::max(aligned_offset - offset, unaligned_end - aligned_end)
411 };
412 let mut buf = IoBuffer::new(buflen as usize, storage.mem_align())?;
413 buf.as_mut().into_slice().fill(0);
414 Some(buf)
415 } else {
416 None
417 };
418
419 if aligned_offset >= aligned_end {
420 let buf = zero_buf
421 .as_ref()
422 .unwrap()
423 .as_ref_range(0..((unaligned_end - offset) as usize));
424 storage.write(buf, offset).await?;
425 } else {
426 if aligned_offset > offset {
427 assert!(aligned_offset <= unaligned_end);
428 let buf = zero_buf
429 .as_ref()
430 .unwrap()
431 .as_ref_range(0..((aligned_offset - offset) as usize));
432 storage.write(buf, offset).await?;
433 }
434 if aligned_end < unaligned_end {
435 assert!(aligned_end >= offset);
436 let buf = zero_buf
437 .as_ref()
438 .unwrap()
439 .as_ref_range(0..((unaligned_end - aligned_end) as usize));
440 storage.write(buf, aligned_end).await?;
441 }
442 }
443
444 Ok(())
445}