:: commit 049601814a4074012f0afd5956e73246fa2fff2e

mintsuki <mintsuki@protonmail.com> — 2021-08-22 14:27

parents: dadca8fe17

disk: Add logic to determine fastest transfer size

diff --git a/stage23/drivers/disk.s2.c b/stage23/drivers/disk.s2.c
index 523fe86a..29407211 100644
--- a/stage23/drivers/disk.s2.c
+++ b/stage23/drivers/disk.s2.c
@@ -8,7 +8,9 @@
 #  include <efi.h>
 #endif
 #include <lib/blib.h>
+#include <lib/print.h>
 #include <mm/pmm.h>
+#include <sys/cpu.h>
 
 #if bios == 1
 
@@ -33,9 +35,56 @@ struct dap {
 
 static struct dap dap = {0};
 
-#define XFER_BUF_SIZE 16384
+#define XFER_BUF_SIZE 65536
 static void *xfer_buf = NULL;
 
+static size_t fastest_xfer_size(struct volume *volume) {
+    if (xfer_buf == NULL)
+        xfer_buf = conv_mem_alloc(XFER_BUF_SIZE);
+
+    size_t fastest_size = 1;
+    uint64_t last_speed = (uint64_t)-1;
+
+    static const size_t xfer_sizes[] = { 1, 2, 4, 8, 16, 24, 32, 48, 64, 128 };
+
+    for (size_t i = 0; i < SIZEOF_ARRAY(xfer_sizes); i++) {
+        if (xfer_sizes[i] * volume->sector_size > XFER_BUF_SIZE) {
+            break;
+        }
+
+        dap.size    = 16;
+        dap.count   = xfer_sizes[i];
+        dap.segment = rm_seg(xfer_buf);
+        dap.offset  = rm_off(xfer_buf);
+        dap.lba     = 0;
+
+        struct rm_regs r = {0};
+        r.eax = 0x4200;
+        r.edx = volume->drive;
+        r.esi = (uint32_t)rm_off(&dap);
+        r.ds  = rm_seg(&dap);
+
+        uint64_t start_timestamp = rdtsc();
+        rm_int(0x13, &r, &r);
+        uint64_t end_timestamp = rdtsc();
+
+        if (r.eflags & EFLAGS_CF) {
+            int ah = (r.eax >> 8) & 0xff;
+            printv("Disk error %x. Drive %x", ah, volume->drive);
+            continue;
+        }
+
+        uint64_t speed = (end_timestamp - start_timestamp) / xfer_sizes[i];
+
+        if (speed < last_speed) {
+            last_speed = speed;
+            fastest_size = xfer_sizes[i];
+        }
+    }
+
+    return fastest_size;
+}
+
 bool disk_read_sectors(struct volume *volume, void *buf, uint64_t block, size_t count) {
     if (count * volume->sector_size > XFER_BUF_SIZE)
         panic("XFER");
@@ -111,6 +160,8 @@ void disk_create_index(void) {
             }
         }
 
+        block.fastest_xfer_size = 8;
+
         volume_count++;
 
         for (int part = 0; ; part++) {
@@ -170,6 +221,8 @@ void disk_create_index(void) {
             block->index = hdd_indices++;
         }
 
+        block->fastest_xfer_size = fastest_xfer_size(block);
+
         if (gpt_get_guid(&block->guid, block)) {
             block->guid_valid = true;
         }
@@ -321,6 +374,8 @@ void disk_create_index(void) {
         block.first_sect = 0;
         block.sect_count = block_io->Media->LastBlock + 1;
 
+        block.fastest_xfer_size = 8;
+
         for (int part = 0; ; part++) {
             struct volume trash = {0};
             int ret = part_get(&trash, &block, part);
@@ -382,6 +437,9 @@ void disk_create_index(void) {
             block->guid_valid = true;
         }
 
+        // TODO: get fastest xfer size also for UEFI?
+        block->fastest_xfer_size = 8;
+
         volume_index[volume_index_i++] = block;
 
         for (int part = 0; ; part++) {
diff --git a/stage23/lib/part.h b/stage23/lib/part.h
index 5fbf03d9..72fa2b18 100644
--- a/stage23/lib/part.h
+++ b/stage23/lib/part.h
@@ -20,6 +20,8 @@ struct volume {
     int drive;
 #endif
 
+    size_t fastest_xfer_size;
+
     int index;
 
     bool is_optical;
diff --git a/stage23/lib/part.s2.c b/stage23/lib/part.s2.c
index 8b53a74e..23e346cf 100644
--- a/stage23/lib/part.s2.c
+++ b/stage23/lib/part.s2.c
@@ -11,8 +11,6 @@
 #include <mm/pmm.h>
 #include <fs/file.h>
 
-#define BLOCK_SIZE_IN_SECTORS 8
-
 enum {
     CACHE_NOT_READY = 0,
     CACHE_READY
@@ -26,11 +24,11 @@ static bool cache_block(struct volume *volume, uint64_t block) {
 
     if (volume->cache == NULL)
         volume->cache =
-            ext_mem_alloc(BLOCK_SIZE_IN_SECTORS * volume->sector_size);
+            ext_mem_alloc(volume->fastest_xfer_size * volume->sector_size);
 
     if (!disk_read_sectors(volume, volume->cache,
-                           volume->first_sect + block * BLOCK_SIZE_IN_SECTORS,
-                           BLOCK_SIZE_IN_SECTORS))
+                           volume->first_sect + block * volume->fastest_xfer_size,
+                           volume->fastest_xfer_size))
         return false;
 
     volume->cache_status = CACHE_READY;
@@ -44,7 +42,7 @@ bool volume_read(struct volume *volume, void *buffer, uint64_t loc, uint64_t cou
         panic("Attempted volume_read() on pxe");
     }
 
-    uint64_t block_size = BLOCK_SIZE_IN_SECTORS * volume->sector_size;
+    uint64_t block_size = volume->fastest_xfer_size * volume->sector_size;
 
     uint64_t progress = 0;
     while (progress < count) {
@@ -154,6 +152,7 @@ static int gpt_get_part(struct volume *ret, struct volume *volume, int partition
     ret->efi_handle  = volume->efi_handle;
 #elif bios == 1
     ret->drive       = volume->drive;
+    ret->fastest_xfer_size = volume->fastest_xfer_size;
 #endif
     ret->index       = volume->index;
     ret->is_optical  = volume->is_optical;
@@ -214,6 +213,7 @@ static int mbr_get_logical_part(struct volume *ret, struct volume *extended_part
     ret->efi_handle  = extended_part->efi_handle;
 #elif bios == 1
     ret->drive       = extended_part->drive;
+    ret->fastest_xfer_size = extended_part->fastest_xfer_size;
 #endif
     ret->index       = extended_part->index;
     ret->is_optical  = extended_part->is_optical;
@@ -292,6 +292,7 @@ static int mbr_get_part(struct volume *ret, struct volume *volume, int partition
             extended_part.efi_handle  = volume->efi_handle;
 #elif bios == 1
             extended_part.drive       = volume->drive;
+            extended_part.fastest_xfer_size = volume->fastest_xfer_size;
 #endif
             extended_part.index       = volume->index;
             extended_part.is_optical  = volume->is_optical;
@@ -318,6 +319,7 @@ static int mbr_get_part(struct volume *ret, struct volume *volume, int partition
     ret->efi_handle  = volume->efi_handle;
 #elif bios == 1
     ret->drive       = volume->drive;
+    ret->fastest_xfer_size = volume->fastest_xfer_size;
 #endif
     ret->index       = volume->index;
     ret->is_optical  = volume->is_optical;
diff --git a/stage23/sys/cpu.h b/stage23/sys/cpu.h
index 6e6efba3..b913ecc1 100644
--- a/stage23/sys/cpu.h
+++ b/stage23/sys/cpu.h
@@ -154,6 +154,12 @@ static inline void wrmsr(uint32_t msr, uint64_t value) {
                   : "memory");
 }
 
+inline uint64_t rdtsc(void) {
+    uint32_t edx, eax;
+    asm volatile ("rdtsc" : "=a" (eax), "=d" (edx));
+    return ((uint64_t)edx << 32) | eax;
+}
+
 #define write_cr(reg, val) ({ \
     asm volatile ("mov %0, %%cr" reg :: "r" (val) : "memory"); \
 })
tab: 248 wrap: offon