Browse Source

refactor(ahci, fat): make achi read 3kb a time

greatbridf 11 months ago
parent
commit
370949b19a
3 changed files with 82 additions and 51 deletions
  1. 14 7
      include/fs/fat.hpp
  2. 48 27
      src/fs/fat.cpp
  3. 20 17
      src/kernel/hw/ahci.cc

+ 14 - 7
include/fs/fat.hpp

@@ -1,13 +1,19 @@
 #pragma once
 
-#include <kernel/mem.h>
-#include <kernel/vfs.hpp>
+#include <vector>
+
 #include <stdint.h>
 #include <string.h>
 #include <sys/types.h>
+
 #include <types/size.h>
+#include <types/hash_map.hpp>
+
+#include <kernel/mem.h>
+#include <kernel/vfs.hpp>
 
 namespace fs::fat {
+
 using cluster_t = uint32_t;
 
 // for FAT32
@@ -105,7 +111,7 @@ struct PACKED directory_entry {
 // TODO: deallocate inodes when dentry is destroyed
 class fat32 : public virtual fs::vfs {
 private:
-    constexpr static uint32_t SECTOR_SIZE = 512;
+    constexpr static size_t SECTOR_SIZE = 512;
     constexpr static cluster_t EOC = 0xffffff8;
 
 private:
@@ -121,7 +127,7 @@ private:
     uint8_t fat_copies;
     uint8_t sectors_per_cluster;
     char label[12];
-    cluster_t* fat;
+    std::vector<cluster_t> fat;
 
     struct buf_object {
         char* data;
@@ -131,10 +137,12 @@ private:
     types::hash_map<cluster_t, buf_object> buf;
 
     // buf MUST be larger than 512 bytes
-    inline void _raw_read_sector(void* buf, uint32_t sector_no);
+    void _raw_read_sector(void* buf, uint32_t sector_no);
 
     // buf MUST be larger than 4096 bytes
-    inline void _raw_read_cluster(void* buf, cluster_t no);
+    void _raw_read_cluster(void* buf, cluster_t no);
+
+    ssize_t _read_sector_range(void* buf, size_t buf_size, uint32_t sector_offset, size_t sector_cnt);
 
     // buffered version, release_cluster(cluster_no) after used
     char* read_cluster(cluster_t no);
@@ -164,7 +172,6 @@ private:
 public:
     fat32(const fat32&) = delete;
     explicit fat32(dev_t device);
-    ~fat32();
 
     virtual size_t read(inode* file, char* buf, size_t buf_size, size_t offset, size_t n) override;
     virtual int readdir(fs::inode* dir, size_t offset, const fs::vfs::filldir_func& callback) override;

+ 48 - 27
src/fs/fat.cpp

@@ -1,41 +1,64 @@
+#include <algorithm>
+
 #include <assert.h>
 #include <ctype.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#include <types/allocator.hpp>
+#include <types/status.h>
+
 #include <fs/fat.hpp>
 #include <kernel/mem.h>
 #include <kernel/mm.hpp>
 #include <kernel/module.hpp>
 #include <kernel/vfs.hpp>
-#include <stdint.h>
-#include <stdio.h>
-#include <types/allocator.hpp>
-#include <types/hash_map.hpp>
-#include <types/status.h>
 
 #define VFAT_FILENAME_LOWERCASE (0x08)
 #define VFAT_EXTENSION_LOWERCASE (0x10)
 
 namespace fs::fat {
+
 // buf MUST be larger than 512 bytes
-inline void fat32::_raw_read_sector(void* buf, uint32_t sector_no)
+void fat32::_raw_read_sector(void* buf, uint32_t sector_no)
 {
-    size_t n = fs::block_device_read(
-        device,
-        (char*)buf,
-        SECTOR_SIZE,
-        sector_no * SECTOR_SIZE,
-        SECTOR_SIZE);
-    assert(n == SECTOR_SIZE);
+    auto nread = _read_sector_range(
+            buf, SECTOR_SIZE,
+            sector_no, 1
+            );
+
+    assert((size_t)nread == SECTOR_SIZE);
 }
 
 // buf MUST be larger than 4096 bytes
-inline void fat32::_raw_read_cluster(void* buf, cluster_t no)
+void fat32::_raw_read_cluster(void* buf, cluster_t no)
 {
     // data cluster start from cluster #2
     no -= 2;
-    for (int i = 0; i < sectors_per_cluster; ++i) {
-        // skip reserved sectors
-        _raw_read_sector((char*)buf + SECTOR_SIZE * i, data_region_offset + no * sectors_per_cluster + i);
-    }
+
+    auto nread = _read_sector_range(
+            buf, sectors_per_cluster * SECTOR_SIZE,
+            data_region_offset + no * sectors_per_cluster,
+            sectors_per_cluster);
+
+    assert((size_t)nread == SECTOR_SIZE * sectors_per_cluster);
+}
+
+ssize_t fat32::_read_sector_range(void* _buf, size_t buf_size, uint32_t sector_offset, size_t sector_cnt)
+{
+    buf_size &= ~(SECTOR_SIZE - 1);
+
+    sector_cnt = std::min(sector_cnt, buf_size / SECTOR_SIZE);
+
+    auto* buf = (char*)_buf;
+
+    auto n = fs::block_device_read(device,
+            buf, buf_size,
+            sector_offset * SECTOR_SIZE,
+            sector_cnt * SECTOR_SIZE
+        );
+
+    return n;
 }
 
 char* fat32::read_cluster(cluster_t no)
@@ -133,7 +156,7 @@ int fat32::readdir(fs::inode* dir, size_t offset, const fs::vfs::filldir_func& f
 
 fat32::fat32(dev_t _device)
     : device { _device }
-    , label { 0 }
+    , label { }
 {
     auto* buf = new char[SECTOR_SIZE];
     _raw_read_sector(buf, 0);
@@ -149,9 +172,12 @@ fat32::fat32(dev_t _device)
     fat_copies = info->old.fat_copies;
 
     data_region_offset = reserved_sectors + fat_copies * sectors_per_fat;
-    fat = (cluster_t*)new char[SECTOR_SIZE * sectors_per_fat];
-    for (uint32_t i = 0; i < sectors_per_fat; ++i)
-        _raw_read_sector((char*)fat + i * SECTOR_SIZE, reserved_sectors + i);
+
+    // read file allocation table
+    fat.resize(SECTOR_SIZE * sectors_per_fat / sizeof(cluster_t));
+    _read_sector_range(
+            fat.data(), SECTOR_SIZE * sectors_per_fat,
+            reserved_sectors, sectors_per_fat);
 
     int i = 0;
     while (i < 11 && info->label[i] != 0x20) {
@@ -181,11 +207,6 @@ fat32::fat32(dev_t _device)
     register_root_node(n);
 }
 
-fat32::~fat32()
-{
-    delete[]((char*)fat);
-}
-
 size_t fat32::read(inode* file, char* buf, size_t buf_size, size_t offset, size_t n)
 {
     cluster_t next = cl(file);

+ 20 - 17
src/kernel/hw/ahci.cc

@@ -347,17 +347,18 @@ private:
         // clear the received fis
         memset(fis, 0x00, sizeof(received_fis));
 
-        // issue the command
-        port->command_issue = 1 << n;
-
-        // TODO: use interrupt
+        // wait until port is not busy
         uint32_t spins = 0;
         SPIN(port->task_file_data & (ATA_DEV_BSY | ATA_DEV_DRQ), spins)
             return -1;
-        
+
+        // TODO: use interrupt
+        // issue the command
+        port->command_issue = 1 << n;
+
         SPIN(port->command_issue & (1 << n), spins)
             return -1;
-        
+
         memcpy(buf, (char*)cmdtable + 512, count);
 
         kernel::pfree(cmdtable_page);
@@ -390,22 +391,21 @@ public:
     {
         cnt = std::min(buf_size, cnt);
 
-        char b[512] {};
+        constexpr size_t READ_BUF_SECTORS = 6;
+
+        char b[READ_BUF_SECTORS * 512] {};
         char* orig_buf = buf;
         size_t start = offset / 512;
         size_t end = std::min((offset + cnt + 511) / 512, sectors);
 
-        offset %= 512;
-        for (size_t i = start; i < end; ++i) {
-            int status = send_command(b, i, 512, 0xC8, false);
+        offset -= start * 512;
+        for (size_t i = start; i < end; i += READ_BUF_SECTORS) {
+            size_t n_read = std::min(end - i, READ_BUF_SECTORS) * 512;
+            int status = send_command(b, i, n_read, 0xC8, false);
             if (status != 0)
                 return -EIO;
 
-            size_t to_copy = 0;
-            if (offset)
-                to_copy = 512 - offset;
-            else
-                to_copy = std::min(cnt, 512U);
+            size_t to_copy = std::min(cnt, n_read - offset);
             memcpy(buf, b + offset, to_copy);
             offset = 0;
             buf += to_copy;
@@ -418,8 +418,11 @@ public:
     {
         if (stop_command(port) != 0)
             return -1;
-        port->interrupt_status = ~0;
+
         // TODO: use interrupt
+        // this is the PxIE register, setting bits here will make
+        //      it generate corresponding interrupts in PxIS
+        //
         // port->interrupt_enable = 1;
 
         port->command_list_base = page << 12;
@@ -510,7 +513,7 @@ public:
 
                 this->ghc->global_host_control =
                     this->ghc->global_host_control | 2; // set interrupt enable
-                
+
                 return this->probe_disks();
         });