Skip to content

Commit 2007783

Browse files
committed
Fix slow tape write speed: Enable Buffered Mode and optimize write strategy
1. Enabled Buffered Mode (0x10) in SCSI MODE SELECT command in config.rs to allow tape drive to cache writes. 2. Reverted large file and stream writes to single-block mode (512KB) to match LTFSCopyGUI behavior and avoid Windows SCSI pass-through batch write issues. 3. Explicitly call set_block_size before writing in both file and stream modes to ensure Buffered Mode is active. 4. Enhanced progress logging to output every 100MB with current and average speed.
1 parent 2489ce9 commit 2007783

2 files changed

Lines changed: 61 additions & 22 deletions

File tree

src/scsi/commands/config.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,8 @@ impl ScsiInterface {
132132
// Byte 2: Device-Specific Parameter
133133
// Byte 3: Block Descriptor Length
134134

135+
param_list[2] = 0x10; // Buffered Mode = 1 (like LTFSCopyGUI)
136+
// This allows drive to return success after writing to buffer
135137
param_list[3] = 0x08; // Block Descriptor Length = 8 bytes
136138

137139
// Block Descriptor (8 bytes)

src/tape_ops/write_operations.rs

Lines changed: 59 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,11 @@ impl TapeOperations {
166166
// Locate to write position
167167
let _write_state = self.locate_to_write_position().await?;
168168

169+
// Explicitly set block size (and Buffered Mode) before writing
170+
// This corresponds to LTFSCopyGUI: TapeUtils.SetBlockSize(driveHandle, plabel.blocksize)
171+
info!("Setting drive block size to {} (Buffered Mode enabled)", self.block_size);
172+
self.scsi.set_block_size(self.block_size)?;
173+
169174
// Get write start position
170175
let write_start_position = self.scsi.read_position()?;
171176

@@ -192,6 +197,8 @@ impl TapeOperations {
192197
let mut total_blocks_written = 0u32;
193198
let mut total_bytes_written = 0u64;
194199
let write_start_time = std::time::Instant::now();
200+
let mut last_progress_bytes = 0u64;
201+
let mut last_progress_time = std::time::Instant::now();
195202

196203
// Choose processing strategy based on file size
197204
if file_size <= self.block_size as u64 {
@@ -240,24 +247,22 @@ impl TapeOperations {
240247
self.write_progress.files_written += 1;
241248
self.write_progress.bytes_written += bytes_read as u64;
242249
} else {
243-
// Large file: block-wise streaming processing
250+
// Large file: block-wise streaming (same as LTFSCopyGUI)
251+
// Windows SCSI pass-through doesn't support multi-block batch writes
244252
info!(
245253
"Processing large file ({} bytes), using block-wise streaming",
246254
file_size
247255
);
248256

249257
let mut buffer = vec![0u8; self.block_size as usize];
250258
let mut remaining_bytes = file_size;
259+
260+
info!("Starting write loop (Block size: {})", self.block_size);
251261

252262
while remaining_bytes > 0 {
253-
254-
255263
// Calculate bytes to read for current block
256264
let bytes_to_read = std::cmp::min(remaining_bytes, self.block_size as u64) as usize;
257265

258-
// Clear buffer (for last block, this ensures zero padding)
259-
buffer.fill(0);
260-
261266
// Read data from file
262267
let bytes_read = buf_reader
263268
.read(&mut buffer[..bytes_to_read])
@@ -275,10 +280,7 @@ impl TapeOperations {
275280
calc.propagate(&buffer[..bytes_read]);
276281
}
277282

278-
279-
280-
281-
// Write block to tape (use variable-length buffer slice to avoid ILI)
283+
// Write single block to tape (like LTFSCopyGUI)
282284
let blocks_written = self.scsi.write_blocks(1, &buffer[..bytes_read])?;
283285

284286
if blocks_written != 1 {
@@ -295,10 +297,38 @@ impl TapeOperations {
295297
// Update progress
296298
self.write_progress.current_bytes_processed += bytes_read as u64;
297299

298-
debug!(
299-
"Written {} blocks, {} bytes, remaining {} bytes",
300-
total_blocks_written, total_bytes_written, remaining_bytes
301-
);
300+
// Log progress every 100MB
301+
let bytes_since_last_log = total_bytes_written - last_progress_bytes;
302+
if bytes_since_last_log >= 100 * 1024 * 1024 {
303+
let elapsed = write_start_time.elapsed();
304+
let elapsed_secs = elapsed.as_secs_f64();
305+
306+
let overall_speed_mbps = if elapsed_secs > 0.0 {
307+
(total_bytes_written as f64 / (1024.0 * 1024.0)) / elapsed_secs
308+
} else {
309+
0.0
310+
};
311+
312+
let recent_elapsed = last_progress_time.elapsed().as_secs_f64();
313+
let recent_speed_mbps = if recent_elapsed > 0.0 {
314+
(bytes_since_last_log as f64 / (1024.0 * 1024.0)) / recent_elapsed
315+
} else {
316+
0.0
317+
};
318+
319+
let gb_written = total_bytes_written as f64 / (1024.0 * 1024.0 * 1024.0);
320+
321+
info!(
322+
"📊 Write progress: {:.2} GB written | Speed: {:.2} MB/s (avg: {:.2} MB/s) | Blocks: {}",
323+
gb_written,
324+
recent_speed_mbps,
325+
overall_speed_mbps,
326+
total_blocks_written
327+
);
328+
329+
last_progress_bytes = total_bytes_written;
330+
last_progress_time = std::time::Instant::now();
331+
}
302332
}
303333

304334
// Complete hash calculation
@@ -408,6 +438,11 @@ impl TapeOperations {
408438
// Prepare for writing to tape
409439
self.scsi.locate_to_eod(1)?;
410440

441+
// Explicitly set block size (and Buffered Mode) before writing
442+
let block_size_u32 = self.write_options.block_size;
443+
info!("Setting drive block size to {} (Buffered Mode enabled) for stream", block_size_u32);
444+
self.scsi.set_block_size(block_size_u32)?;
445+
411446
let write_start_position = self.scsi.read_position()?;
412447

413448
// Create file entry in index - now guaranteed to have index
@@ -416,9 +451,10 @@ impl TapeOperations {
416451
.and_then(|idx| idx.highestfileuid)
417452
.unwrap_or(0) + 1;
418453

419-
// ⭐ STREAMING WRITE - Ensure full-sized blocks for LTFS compatibility
420-
// LTFSCopyGUI expects consistent block sizes when reading back
454+
// ⭐ STREAMING WRITE - Single block at a time (like LTFSCopyGUI)
455+
// Windows SCSI pass-through doesn't support multi-block batch writes
421456
let block_size = self.write_options.block_size as usize;
457+
422458
let mut write_buffer = vec![0u8; block_size]; // Buffer for writing full blocks
423459
let mut read_buffer = vec![0u8; block_size]; // Buffer for reading from stream
424460
let mut buffer_fill = 0usize; // How many bytes are currently in write_buffer
@@ -428,7 +464,10 @@ impl TapeOperations {
428464
let mut last_progress_bytes = 0u64;
429465
let mut last_progress_time = std::time::Instant::now();
430466

431-
info!("Starting streaming write with block size: {} bytes (fixed transfer size)", block_size);
467+
info!(
468+
"Starting streaming write (Block size: {} bytes, single-block mode)",
469+
block_size
470+
);
432471

433472
loop {
434473
// Read data from the stream
@@ -461,7 +500,7 @@ impl TapeOperations {
461500
buffer_fill += bytes_to_copy;
462501
offset += bytes_to_copy;
463502

464-
// If buffer is full, write it to tape
503+
// If buffer is full, write single block to tape
465504
if buffer_fill == block_size {
466505
let blocks_written = self.scsi.write_blocks(1, &write_buffer)? as u64;
467506
total_blocks_written += blocks_written;
@@ -471,20 +510,18 @@ impl TapeOperations {
471510
}
472511
}
473512

474-
// Log progress every 1GB with detailed statistics
513+
// Log progress every 100MB with detailed statistics
475514
let bytes_since_last_log = total_bytes_written - last_progress_bytes;
476-
if bytes_since_last_log >= 1024 * 1024 * 1024 {
515+
if bytes_since_last_log >= 100 * 1024 * 1024 {
477516
let elapsed = write_start_time.elapsed();
478517
let elapsed_secs = elapsed.as_secs_f64();
479518

480-
// Calculate overall speed
481519
let overall_speed_mbps = if elapsed_secs > 0.0 {
482520
(total_bytes_written as f64 / (1024.0 * 1024.0)) / elapsed_secs
483521
} else {
484522
0.0
485523
};
486524

487-
// Calculate recent speed (since last log)
488525
let recent_elapsed = last_progress_time.elapsed().as_secs_f64();
489526
let recent_speed_mbps = if recent_elapsed > 0.0 {
490527
(bytes_since_last_log as f64 / (1024.0 * 1024.0)) / recent_elapsed

0 commit comments

Comments
 (0)