summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJake Mannens <jakem_5@hotmail.com>2020-03-18 00:21:36 +1100
committerJake Mannens <jakem_5@hotmail.com>2020-03-18 00:21:36 +1100
commiteda36531e8daedd045fb02c9c590cdf4dddd957f (patch)
tree62d4dc49bc7505596d2e5f2213f8f7f0e3aff901
parentb808d33c6bf30691b8cdb6cc0fd65f49e7db29f8 (diff)
Implemented a proper read requests queue in the floppy driver.
Previously, all tasks waiting on the floppy drive, would be woken simultaneously and would compete for access to the drive, potentially causing a dangerous race condition. Now, tasks enter a read queue, where each task wakes the next after it is finished with the drive. This is the basic groundwork for request queueing. Seek queues will be implemented next along with a head scheduling algorithm. Added a timeout for read_data(). Reading status from the FDC will return an error if the FDC does not become available in the allotted time and thus, will no longer potentially hang the system.
-rw-r--r--include/kernel/sched.h3
-rw-r--r--kernel/fd.c69
2 files changed, 50 insertions, 22 deletions
diff --git a/include/kernel/sched.h b/include/kernel/sched.h
index 5429e8b..439da53 100644
--- a/include/kernel/sched.h
+++ b/include/kernel/sched.h
@@ -5,6 +5,9 @@
#include <stdint.h>
#include <unistd.h>
+/* TODO: change const 10 to tick rate */
+#define uptime() ((long) (ticks * 10))
+
#define TSTATE_RUNNING 0
#define TSTATE_INTERRUPTIBLE 1
#define TSTATE_UNINTERRUPTIBLE 2
diff --git a/kernel/fd.c b/kernel/fd.c
index 19068c4..f6563ea 100644
--- a/kernel/fd.c
+++ b/kernel/fd.c
@@ -28,6 +28,9 @@
#define NTYPES (sizeof(ftypes) / sizeof(struct ftype))
#define NFDS (sizeof(fd) / sizeof(struct fdrive))
+/* time (ms) to wait for the FDC to become ready (RQM set) */
+#define READ_TIMEOUT 2
+
/* helper macros for converting LBA into CHS */
#define C(lba, ft) \
(lba / (ft->h * ft->s))
@@ -45,6 +48,9 @@
/* TODO: seek before reading */
/* TODO: check drive ready state in floppy driver (ensure the drive door is closed before any operation) */
/* TODO: cut back on printk() usage */
+/* TODO: implement retries for operations likely to fail */
+/* TODO: eliminate potential context switches in read_sect() */
+/* TODO: test that a drive actually exists before beginning an operation */
/* TODO: implement a timeout to prevent system hangs */
#define WAITIRQ(x) ({ \
@@ -61,6 +67,15 @@ struct sense_result {
uint8_t cyl;
} __attribute__((packed));
+struct req {
+ uint16_t c;
+ uint8_t h;
+ uint8_t s;
+ struct task_struct *wait;
+ struct req *nread;
+ struct req *nseek;
+};
+
struct ftype {
uint16_t c;
uint8_t h;
@@ -72,7 +87,6 @@ struct ftype {
struct fdrive {
struct ftype *type;
- struct task_struct *seek_wait;
uint8_t cyl;
uint8_t st0;
};
@@ -93,9 +107,10 @@ static struct fdrive fd[4];
static char buffer[512] __attribute__((aligned (0x10000)));
-static struct task_struct *read_wait;
+static struct req *read_queue;
static ssize_t read_data(void *buf, size_t len) {
+ long timeout;
ssize_t c = 0;
uint8_t *p = buf;
@@ -105,17 +120,12 @@ static ssize_t read_data(void *buf, size_t len) {
if(len > SSIZE_MAX)
len = SSIZE_MAX;
- /*
- * TODO: fix this.
- * This entire routine really is garbage.
- * It doesn't use a timeout (potentially
- * hanging the entire system) and doesn't
- * handle the DIO flag properly.
- */
while(len--) {
- while(!(inb(FDMSR) & 0x80));
+ timeout = uptime() + READ_TIMEOUT;
+
+ while(!(inb(FDMSR) & 0x80) && uptime() < timeout);
- if(!(inb(FDMSR) & 0x40))
+ if(!(inb(FDMSR) & 0xC0))
break;
*p++ = inb(FDDATA);
@@ -129,9 +139,7 @@ static ssize_t read_data(void *buf, size_t len) {
while((inb(FDMSR) & 0xC0) == 0xC0)
inb(FDDATA);
- if(!c)
- return -1;
- return c;
+ return !c ? -1 : c;
}
static void write_data(void *buf, size_t len) {
@@ -237,7 +245,7 @@ static void reset(void) {
while((inb(FDMSR) & wait));
}
-int fd_read_sect(int sect) {
+int fd_read_sect(int sect, struct req *r) {
ssize_t in;
struct {
uint8_t st0;
@@ -261,14 +269,30 @@ int fd_read_sect(int sect) {
0xFF
};
- cli();
- if(read_wait)
- sleep_on(&read_wait);
- sti();
+ /* prepare a read request */
+ memset(r, 0, sizeof(struct req));
+ r->c = C(sect, fd[0].type);
+ r->h = H(sect, fd[0].type);
+ r->s = S(sect, fd[0].type);
+
+ if(read_queue) {
+ r->nread = read_queue->nread;
+ read_queue->nread = r;
+ sleep_on(&r->wait);
+ } else {
+ read_queue = r;
+ }
printk("[fd] Reading sector 0x%01x (C: 0x%01x, H: 0x%01x, S: 0x%01x)\n", sect, cmd[2], cmd[3], cmd[4]);
- WAITIRQ(write_data(cmd, sizeof(cmd)));
+ cli();
+ write_data(cmd, sizeof(cmd));
+ sti();
+ sleep_on(&r->wait);
+
+ read_queue = r->nread;
+ if(read_queue)
+ wake_up(&read_queue->wait);
in = read_data(&res, sizeof(res));
if(in == sizeof(res) && !(res.st0 & 0xC0)) {
@@ -285,7 +309,8 @@ void fd_interrupt(void) {
struct sense_result sr;
irq_fired = 1;
- wake_up(&read_wait);
+ if(read_queue)
+ wake_up(&read_queue->wait);
if((inb(FDMSR) & 0xC0) != 0x80)
return;
@@ -301,7 +326,7 @@ void fd_block_read(struct buffer *b) {
void *p = b->b_data;
for(i = SECTS(b->b_block); i < SECTS(b->b_block + 1); i++) {
- if(fd_read_sect(i)) {
+ if(fd_read_sect(i, b->b_data)) {
b->b_device = 0;
return;
}