SMB performance optimization: pread/pwrite, tokio::sync::Mutex, direct response, fast-path

- VfsFile trait: add read_at()/write_at() with seek+read default impl
- LocalFs: override with real pread/pwrite (FileExt::read_at/write_at) — 1 syscall vs 2
- smb_server_backend: use read_at/write_at + tokio::sync::Mutex (non-blocking async)
- read handler: build response directly, avoid Bytes→Vec<u8> copy + intermediate struct
- oplock break: fast-path skip when ≤1 open entry (single-user scenario)
This commit is contained in:
Warren
2026-06-23 09:58:19 +08:00
parent e7863a3034
commit d4f60929fa
6 changed files with 58 additions and 31 deletions

View File

@@ -207,8 +207,8 @@ pub trait Handle: Send + Sync {
/// Write `data` at `offset`. Returns bytes written.
async fn write(&self, offset: u64, data: &[u8]) -> SmbResult<u32>;
/// Write owned `data` at `offset`. Backends that need ownership across a
/// blocking boundary can override this to avoid an extra copy.
/// Write owned `data` at `offset`. Backends needing ownership across a
/// blocking boundary should override to avoid an extra copy.
async fn write_owned(&self, offset: u64, data: Vec<u8>) -> SmbResult<u32> {
self.write(offset, &data).await
}

View File

@@ -91,16 +91,15 @@ pub async fn handle(
if bytes.is_empty() && req.length > 0 {
return HandlerResponse::err(ntstatus::STATUS_END_OF_FILE);
}
let resp = ReadResponse {
structure_size: 17,
data_offset: ReadResponse::STANDARD_DATA_OFFSET,
reserved: 0,
data_length: bytes.len() as u32,
data_remaining: 0,
flags: 0,
data: bytes.to_vec(),
};
let mut buf = Vec::new();
resp.write_to(&mut buf).expect("encode");
// Build response directly to avoid Bytes→Vec<u8> copy and intermediate struct
let data_len = bytes.len() as u32;
let mut buf = Vec::with_capacity(16 + bytes.len());
buf.extend_from_slice(&17u16.to_le_bytes()); // structure_size
buf.push(ReadResponse::STANDARD_DATA_OFFSET); // data_offset
buf.push(0); // reserved
buf.extend_from_slice(&data_len.to_le_bytes()); // data_length
buf.extend_from_slice(&0u32.to_le_bytes()); // data_remaining
buf.extend_from_slice(&0u32.to_le_bytes()); // flags
buf.extend_from_slice(&bytes); // data
HandlerResponse::ok(buf)
}

View File

@@ -112,17 +112,23 @@ impl OplockManager {
new_share_access: u32,
new_granted_access: Access,
) -> Vec<OplockBreakNotification> {
// Fast-path: no entries or single entry can't conflict with itself
let entry_count = {
let file_opens = self.file_opens.read().await;
file_opens.get(path).map_or(0, |e| e.len())
};
if entry_count <= 1 {
return Vec::new();
}
let mut notifications = Vec::new();
let mut file_opens = self.file_opens.write().await;
if let Some(entries) = file_opens.get_mut(path) {
for entry in entries.iter_mut() {
// Check if new open conflicts with existing oplock
if !share_access_compatible(entry.share_access, new_share_access) {
// Need to break the oplock
let new_level = OplockLevel::Ii as u8; // Downgrade to Level II
let new_level = OplockLevel::Ii as u8;
// Build notification (MS-SMB2 §2.2.23.1)
notifications.push(OplockBreakNotification {
structure_size: 24,
oplock_level: new_level,
@@ -131,7 +137,6 @@ impl OplockManager {
file_id: entry.file_id,
});
// Update entry's oplock level
entry.oplock_level = new_level;
}
}
@@ -266,6 +271,11 @@ impl LeaseManager {
/// Break lease when conflicting access occurs (MS-SMB2 §3.3.5.10).
pub async fn break_lease(&self, requested_state: u32) -> Vec<LeaseBreakNotification> {
// Fast-path: no leases to break
if self.leases.read().await.is_empty() {
return Vec::new();
}
let mut leases = self.leases.write().await;
let mut notifications = Vec::new();