summaryrefslogtreecommitdiff
path: root/kernel/drivers
diff options
context:
space:
mode:
authorAnton Kling <anton@kling.gg>2024-06-30 19:38:31 +0200
committerAnton Kling <anton@kling.gg>2024-06-30 19:38:31 +0200
commit4a2aefc117144a87d5fc515ddcb60c7d13dcaf1e (patch)
tree4bc6d0ae1cbea3537269e86d0b07d6c9cbaa128a /kernel/drivers
parentf327361a71a407d7b8f5bfcd01fe36bfff4cdf6d (diff)
AHCI: Optimization to writes
Now it avoids waiting for completion if possible and can instead batch commands.
Diffstat (limited to 'kernel/drivers')
-rw-r--r--kernel/drivers/ahci.c65
1 files changed, 47 insertions, 18 deletions
diff --git a/kernel/drivers/ahci.c b/kernel/drivers/ahci.c
index bef11ae..530295d 100644
--- a/kernel/drivers/ahci.c
+++ b/kernel/drivers/ahci.c
@@ -303,20 +303,33 @@ u8 get_free_command_slot(volatile struct HBA_PORT *port, u8 *err) {
return 0;
}
+void ahci_wait_for_completion(volatile struct HBA_PORT *port) {
+ u32 num_slots = ((hba->cap >> 8) & 0x1F);
+ u32 slots = (port->ci);
+ u32 num_free = 0;
+ for (; num_free < 10;) {
+ num_free = 0;
+ for (u8 i = 0; i < num_slots + 1; i++) {
+ if (!((slots >> i) & 1)) {
+ num_free++;
+ }
+ }
+ }
+}
+
// is_write: Determins whether a read or write command will be used.
u8 ahci_perform_command(volatile struct HBA_PORT *port, u32 startl, u32 starth,
- u32 count, u16 *buffer, u8 is_write) {
+ u32 count, u16 *buffer, u8 is_write, u8 async) {
// TODO: The number of PRDT tables are hardcoded at a seemingly
// very low number. It can be up to 65,535. Should it maybe be
// changed?
assert(count <= num_prdt);
port->is = -1; // Clear pending interrupts
u8 err;
- u32 command_slot = get_free_command_slot(port, &err);
- if (err) {
- klog(LOG_WARN, "AHCI No command slot found");
- return 0;
- }
+ u32 command_slot;
+ do {
+ command_slot = get_free_command_slot(port, &err);
+ } while (err);
struct HBA_CMD_HEADER *cmdheader =
(struct HBA_CMD_HEADER *)physical_to_virtual((void *)port->clb);
assert(0x20 == sizeof(struct HBA_CMD_HEADER));
@@ -381,6 +394,10 @@ u8 ahci_perform_command(volatile struct HBA_PORT *port, u32 startl, u32 starth,
port->ci = 1 << command_slot; // Issue command
+ if (async) {
+ return 1;
+ }
+
// Wait for completion
for (;;) {
// In some longer duration reads, it may be helpful to spin on the DPS bit
@@ -404,13 +421,13 @@ u8 ahci_perform_command(volatile struct HBA_PORT *port, u32 startl, u32 starth,
}
u8 ahci_raw_write(volatile struct HBA_PORT *port, u32 startl, u32 starth,
- u32 count, u16 *inbuffer) {
- return ahci_perform_command(port, startl, starth, count, inbuffer, 1);
+ u32 count, u16 *inbuffer, u8 async) {
+ return ahci_perform_command(port, startl, starth, count, inbuffer, 1, async);
}
u8 ahci_raw_read(volatile struct HBA_PORT *port, u32 startl, u32 starth,
- u32 count, u16 *outbuffer) {
- return ahci_perform_command(port, startl, starth, count, outbuffer, 0);
+ u32 count, u16 *outbuffer, u8 async) {
+ return ahci_perform_command(port, startl, starth, count, outbuffer, 0, async);
}
int ahci_write(u8 *buffer, u64 offset, u64 len, vfs_fd_t *fd) {
@@ -428,13 +445,13 @@ int ahci_write(u8 *buffer, u64 offset, u64 len, vfs_fd_t *fd) {
if (offset > 0) {
u8 tmp_buffer[512];
- ahci_raw_read(&hba->ports[port], lba, 0, 1, (u16 *)tmp_buffer);
+ ahci_raw_read(&hba->ports[port], lba, 0, 1, (u16 *)tmp_buffer, 0);
int left = 512 - offset;
int write = min(left, len);
memcpy(tmp_buffer + offset, buffer, write);
- ahci_raw_write(&hba->ports[port], lba, 0, 1, (u16 *)tmp_buffer);
+ ahci_raw_write(&hba->ports[port], lba, 0, 1, (u16 *)tmp_buffer, 0);
offset = 0;
len -= write;
@@ -443,17 +460,28 @@ int ahci_write(u8 *buffer, u64 offset, u64 len, vfs_fd_t *fd) {
}
for (; sector_count >= num_prdt; lba++) {
- ahci_raw_write(&hba->ports[port], lba, 0, num_prdt, (u16 *)buffer);
+ ahci_raw_write(&hba->ports[port], lba, 0, num_prdt, (u16 *)buffer, 1);
buffer += num_prdt * 512;
len -= num_prdt * 512;
sector_count -= num_prdt;
}
+ if (sector_count >= num_prdt) {
+ ahci_wait_for_completion(&hba->ports[port]);
+ }
+
+ if (sector_count > 0 && (0 == len % SECTOR_SIZE)) {
+ ahci_raw_write(&hba->ports[port], lba, 0, sector_count, (u16 *)buffer, 0);
+ return rc;
+ }
+
if (sector_count > 0 && len > 0) {
- u8 tmp_buffer[512 * num_prdt];
- ahci_raw_read(&hba->ports[port], lba, 0, sector_count, (u16 *)tmp_buffer);
+ u8 tmp_buffer[512 * sector_count];
+ ahci_raw_read(&hba->ports[port], lba, 0, sector_count, (u16 *)tmp_buffer,
+ 0);
memcpy(tmp_buffer + offset, buffer, len);
- ahci_raw_write(&hba->ports[port], lba, 0, sector_count, (u16 *)tmp_buffer);
+ ahci_raw_write(&hba->ports[port], lba, 0, sector_count, (u16 *)tmp_buffer,
+ 0);
}
return rc;
}
@@ -472,7 +500,7 @@ int ahci_read(u8 *buffer, u64 offset, u64 len, vfs_fd_t *fd) {
}
u8 tmp_buffer[512 * num_prdt];
for (; sector_count >= num_prdt; lba++) {
- ahci_raw_read(&hba->ports[port], lba, 0, num_prdt, (u16 *)tmp_buffer);
+ ahci_raw_read(&hba->ports[port], lba, 0, num_prdt, (u16 *)tmp_buffer, 0);
memcpy(buffer, tmp_buffer + offset, 512 * num_prdt);
offset = 0;
buffer += num_prdt * 512;
@@ -481,7 +509,8 @@ int ahci_read(u8 *buffer, u64 offset, u64 len, vfs_fd_t *fd) {
}
if (sector_count > 0) {
- ahci_raw_read(&hba->ports[port], lba, 0, sector_count, (u16 *)tmp_buffer);
+ ahci_raw_read(&hba->ports[port], lba, 0, sector_count, (u16 *)tmp_buffer,
+ 0);
memcpy(buffer, tmp_buffer + offset, len);
}
return rc;