FIX: FAT32 cluster management, allocation performance, and AHCI safety

This commit is contained in:
boreddevnl 2026-04-14 14:29:19 +02:00
parent bb187faf79
commit f9bc6c7c38
3 changed files with 69 additions and 36 deletions

BIN
disk.img

Binary file not shown.

View file

@ -8,6 +8,7 @@
#include "paging.h" #include "paging.h"
#include "io.h" #include "io.h"
#include <stddef.h> #include <stddef.h>
#include "../sys/spinlock.h"
extern void serial_write(const char *str); extern void serial_write(const char *str);
extern void serial_write_num(uint64_t num); extern void serial_write_num(uint64_t num);
@ -30,6 +31,7 @@ typedef struct {
HBA_CMD_HEADER *cmd_list; // 1KB, 1KB aligned HBA_CMD_HEADER *cmd_list; // 1KB, 1KB aligned
void *fis_base; // 256B, 256B aligned void *fis_base; // 256B, 256B aligned
HBA_CMD_TBL *cmd_tbl; // Command table for slot 0 HBA_CMD_TBL *cmd_tbl; // Command table for slot 0
spinlock_t lock; // Port-level lock for thread-safety
} ahci_port_state_t; } ahci_port_state_t;
static ahci_port_state_t ports[MAX_AHCI_PORTS]; static ahci_port_state_t ports[MAX_AHCI_PORTS];
@ -150,6 +152,7 @@ int ahci_read_sectors(int port_num, uint64_t lba, uint32_t count, uint8_t *buffe
ahci_port_state_t *ps = &ports[port_num]; ahci_port_state_t *ps = &ports[port_num];
if (!ps->active) return -1; if (!ps->active) return -1;
uint64_t rflags = spinlock_acquire_irqsave(&ps->lock);
HBA_PORT *port = ps->port; HBA_PORT *port = ps->port;
// Clear any pending interrupts/errors // Clear any pending interrupts/errors
@ -198,9 +201,8 @@ int ahci_read_sectors(int port_num, uint64_t lba, uint32_t count, uint8_t *buffe
while (timeout-- > 0) { while (timeout-- > 0) {
if (!(port->ci & (1 << slot))) break; if (!(port->ci & (1 << slot))) break;
if (port->is & (1 << 30)) { // Task File Error if (port->is & (1 << 30)) { // Task File Error
serial_write("[AHCI] Read error on port ");
serial_write_num(port_num);
serial_write("\n"); serial_write("\n");
spinlock_release_irqrestore(&ps->lock, rflags);
return -1; return -1;
} }
} }
@ -209,9 +211,11 @@ int ahci_read_sectors(int port_num, uint64_t lba, uint32_t count, uint8_t *buffe
serial_write("[AHCI] Read timeout on port "); serial_write("[AHCI] Read timeout on port ");
serial_write_num(port_num); serial_write_num(port_num);
serial_write("\n"); serial_write("\n");
spinlock_release_irqrestore(&ps->lock, rflags);
return -1; return -1;
} }
spinlock_release_irqrestore(&ps->lock, rflags);
return 0; return 0;
} }
@ -220,6 +224,7 @@ int ahci_write_sectors(int port_num, uint64_t lba, uint32_t count, const uint8_t
ahci_port_state_t *ps = &ports[port_num]; ahci_port_state_t *ps = &ports[port_num];
if (!ps->active) return -1; if (!ps->active) return -1;
uint64_t rflags = spinlock_acquire_irqsave(&ps->lock);
HBA_PORT *port = ps->port; HBA_PORT *port = ps->port;
port->is = 0xFFFFFFFF; port->is = 0xFFFFFFFF;
@ -266,6 +271,7 @@ int ahci_write_sectors(int port_num, uint64_t lba, uint32_t count, const uint8_t
serial_write("[AHCI] Write error on port "); serial_write("[AHCI] Write error on port ");
serial_write_num(port_num); serial_write_num(port_num);
serial_write("\n"); serial_write("\n");
spinlock_release_irqrestore(&ps->lock, rflags);
return -1; return -1;
} }
} }
@ -274,9 +280,11 @@ int ahci_write_sectors(int port_num, uint64_t lba, uint32_t count, const uint8_t
serial_write("[AHCI] Write timeout on port "); serial_write("[AHCI] Write timeout on port ");
serial_write_num(port_num); serial_write_num(port_num);
serial_write("\n"); serial_write("\n");
spinlock_release_irqrestore(&ps->lock, rflags);
return -1; return -1;
} }
spinlock_release_irqrestore(&ps->lock, rflags);
return 0; return 0;
} }
@ -387,9 +395,8 @@ void ahci_init(void) {
for (int i = 0; i < 32; i++) { for (int i = 0; i < 32; i++) {
ports[i].active = false; ports[i].active = false;
if (!(pi & (1 << i))) continue;
HBA_PORT *port = &abar->ports[i]; HBA_PORT *port = &abar->ports[i];
ports[i].lock = SPINLOCK_INIT;
int type = ahci_check_port_type(port); int type = ahci_check_port_type(port);
if (type == 0) { // SATA drive if (type == 0) { // SATA drive

View file

@ -55,6 +55,7 @@ typedef struct {
bool mounted; bool mounted;
uint32_t cached_fat_sector; uint32_t cached_fat_sector;
uint8_t cached_fat_buf[512]; uint8_t cached_fat_buf[512];
uint32_t last_allocated_cluster; // Hint for faster allocation
spinlock_t lock; // Per-volume lock for physical disk operations spinlock_t lock; // Per-volume lock for physical disk operations
} FAT32_Volume; } FAT32_Volume;
@ -455,6 +456,7 @@ static bool realfs_mount_volume(FAT32_Volume *vol, Disk *disk) {
vol->total_sectors = bpb->total_sectors_32; vol->total_sectors = bpb->total_sectors_32;
vol->mounted = true; vol->mounted = true;
vol->cached_fat_sector = 0xFFFFFFFF; vol->cached_fat_sector = 0xFFFFFFFF;
vol->last_allocated_cluster = 2;
fs_serial_str("[FAT32] Mounted volume: /dev/"); fs_serial_str("[FAT32] Mounted volume: /dev/");
fs_serial_str(disk->devname); fs_serial_str(disk->devname);
@ -971,8 +973,10 @@ static bool realfs_find_contiguous_free(FAT32_Volume *vol, uint32_t dir_start_cl
} }
static uint32_t realfs_allocate_cluster(FAT32_Volume *vol) { static uint32_t realfs_allocate_cluster(FAT32_Volume *vol) {
uint32_t current = 2; uint32_t current = vol->last_allocated_cluster;
if (current < 2) current = 2;
uint32_t fat_entries = (vol->fat_size * 512) / 4; uint32_t fat_entries = (vol->fat_size * 512) / 4;
uint32_t first_search = current;
// Skip cluster 2 as it's reserved for the root directory in FAT32 // Skip cluster 2 as it's reserved for the root directory in FAT32
if (current == vol->root_cluster) current++; if (current == vol->root_cluster) current++;
@ -998,10 +1002,13 @@ static uint32_t realfs_allocate_cluster(FAT32_Volume *vol) {
if (vol->cached_fat_sector == sector) { if (vol->cached_fat_sector == sector) {
vol->cached_fat_sector = 0xFFFFFFFF; vol->cached_fat_sector = 0xFFFFFFFF;
} }
vol->last_allocated_cluster = current;
kfree(fat_buf); kfree(fat_buf);
return current; return current;
} }
current++; current++;
if (current >= fat_entries) current = 2;
if (current == first_search) break;
} }
kfree(fat_buf); kfree(fat_buf);
return 0; // Full return 0; // Full
@ -1043,11 +1050,31 @@ static int realfs_write_file(FAT32_FileHandle *handle, const void *buffer, int s
while (bytes_written < size) { while (bytes_written < size) {
uint32_t offset = handle->position % cluster_size; uint32_t offset = handle->position % cluster_size;
if (offset == 0 && handle->position > 0) {
uint32_t next = realfs_next_cluster(vol, handle->cluster);
if (next >= 0x0FFFFFF8) {
uint32_t new_cluster = realfs_allocate_cluster(vol);
if (new_cluster == 0) break;
uint32_t fs = vol->fat_begin_lba + (handle->cluster * 4) / 512;
uint32_t fo = (handle->cluster * 4) % 512;
uint8_t fbuf[512];
if (vol->disk->read_sector(vol->disk, fs, fbuf) == 0) {
uint32_t old = *(uint32_t*)&fbuf[fo];
*(uint32_t*)&fbuf[fo] = (old & 0xF0000000) | (new_cluster & 0x0FFFFFFF);
vol->disk->write_sector(vol->disk, fs, fbuf);
if (vol->cached_fat_sector == fs) vol->cached_fat_sector = 0xFFFFFFFF;
}
next = new_cluster;
}
handle->cluster = next;
}
// Always zero the buffer first to ensure clean state // Always zero the buffer first to ensure clean state
for (int i = 0; i < (int)cluster_size; i++) cluster_buf[i] = 0; for (int i = 0; i < (int)cluster_size; i++) cluster_buf[i] = 0;
// If we're in the middle of a cluster, read the existing data first // If we're in the middle of a cluster, read the existing data first
if (offset > 0) { if (offset > 0 || (handle->position < handle->size)) {
if (realfs_read_cluster(vol, handle->cluster, cluster_buf) != 0) break; if (realfs_read_cluster(vol, handle->cluster, cluster_buf) != 0) break;
} }
@ -1065,27 +1092,6 @@ static int realfs_write_file(FAT32_FileHandle *handle, const void *buffer, int s
bytes_written += to_copy; bytes_written += to_copy;
handle->position += to_copy; handle->position += to_copy;
if (handle->position > handle->size) handle->size = handle->position; if (handle->position > handle->size) handle->size = handle->position;
if (handle->position % cluster_size == 0 && bytes_written < size) {
uint32_t next = realfs_next_cluster(vol, handle->cluster);
if (next >= 0x0FFFFFF8) {
uint32_t new_cluster = realfs_allocate_cluster(vol);
if (new_cluster == 0) break;
// Link current to new
uint32_t fs = vol->fat_begin_lba + (handle->cluster * 4) / 512;
uint32_t fo = (handle->cluster * 4) % 512;
uint8_t fbuf[512];
if (vol->disk->read_sector(vol->disk, fs, fbuf) == 0) {
uint32_t old = *(uint32_t*)&fbuf[fo];
*(uint32_t*)&fbuf[fo] = (old & 0xF0000000) | (new_cluster & 0x0FFFFFFF);
vol->disk->write_sector(vol->disk, fs, fbuf);
if (vol->cached_fat_sector == fs) vol->cached_fat_sector = 0xFFFFFFFF;
}
next = new_cluster;
}
handle->cluster = next;
}
} }
if (bytes_written > 0) realfs_update_dir_entry_size(vol, handle); if (bytes_written > 0) realfs_update_dir_entry_size(vol, handle);
@ -1646,12 +1652,22 @@ static int vfs_realfs_read(void *fs_private, void *file_handle, void *buf, int s
uint8_t *cluster_buf = (uint8_t*)kmalloc(cluster_size); uint8_t *cluster_buf = (uint8_t*)kmalloc(cluster_size);
if (!cluster_buf) return -1; if (!cluster_buf) return -1;
int total_read = 0;
while (total_read < size) {
int to_read = size - total_read;
if (to_read > (int)cluster_size) to_read = (int)cluster_size;
uint64_t rflags = spinlock_acquire_irqsave(&vol->lock); uint64_t rflags = spinlock_acquire_irqsave(&vol->lock);
int ret = realfs_read_file(handle, buf, size, cluster_buf); int ret = realfs_read_file(handle, (uint8_t*)buf + total_read, to_read, cluster_buf);
spinlock_release_irqrestore(&vol->lock, rflags); spinlock_release_irqrestore(&vol->lock, rflags);
if (ret <= 0) break;
total_read += ret;
if (ret < to_read) break;
}
kfree(cluster_buf); kfree(cluster_buf);
return ret; return total_read;
} }
static int vfs_realfs_write(void *fs_private, void *file_handle, const void *buf, int size) { static int vfs_realfs_write(void *fs_private, void *file_handle, const void *buf, int size) {
@ -1664,12 +1680,22 @@ static int vfs_realfs_write(void *fs_private, void *file_handle, const void *buf
uint8_t *cluster_buf = (uint8_t*)kmalloc(cluster_size); uint8_t *cluster_buf = (uint8_t*)kmalloc(cluster_size);
if (!cluster_buf) return -1; if (!cluster_buf) return -1;
int total_written = 0;
while (total_written < size) {
int to_write = size - total_written;
if (to_write > (int)cluster_size) to_write = (int)cluster_size;
uint64_t rflags = spinlock_acquire_irqsave(&vol->lock); uint64_t rflags = spinlock_acquire_irqsave(&vol->lock);
int ret = realfs_write_file(handle, buf, size, cluster_buf); int ret = realfs_write_file(handle, (const uint8_t*)buf + total_written, to_write, cluster_buf);
spinlock_release_irqrestore(&vol->lock, rflags); spinlock_release_irqrestore(&vol->lock, rflags);
if (ret <= 0) break;
total_written += ret;
if (ret < to_write) break;
}
kfree(cluster_buf); kfree(cluster_buf);
return ret; return total_written;
} }
static int vfs_realfs_seek(void *fs_private, void *file_handle, int offset, int whence) { static int vfs_realfs_seek(void *fs_private, void *file_handle, int offset, int whence) {