Make backtraces slightly more reliable.
[pintos-anon] / src / devices / disk.c
index 7d445692688f31a8413d13198efb1da87b8962bd..14fc63158be840c550d7eb7a9d42d6ecb23b0c77 100644 (file)
@@ -1,11 +1,15 @@
-#include "disk.h"
+#include "devices/disk.h"
+#include <ctype.h>
+#include <debug.h>
 #include <stdbool.h>
-#include "debug.h"
-#include "io.h"
-#include "interrupt.h"
-#include "lib.h"
-#include "synch.h"
-#include "timer.h"
+#include <stdio.h>
+#include "devices/timer.h"
+#include "threads/io.h"
+#include "threads/interrupt.h"
+#include "threads/synch.h"
+
+/* The code in this file is an interface to an ATA (IDE)
+   controller.  It attempts to comply to [ATA-3]. */
 
 /* ATA command block port addresses. */
 #define reg_data(CHANNEL) ((CHANNEL)->reg_base + 0)     /* Data. */
@@ -26,6 +30,7 @@
 
 /* Alternate Status Register bits. */
 #define STA_BSY 0x80            /* Busy. */
+#define STA_DRDY 0x40           /* Device Ready. */
 #define STA_DRQ 0x08            /* Data Request. */
 
 /* Control Register bits. */
@@ -51,7 +56,10 @@ struct disk
     int dev_no;                 /* Device 0 or 1 for master or slave. */
 
     bool is_ata;                /* 1=This device is an ATA disk. */
-    disk_sector_t capacity;    /* Capacity in sectors (if is_ata is true). */
+    disk_sector_t capacity;     /* Capacity in sectors (if is_ata). */
+
+    long long read_cnt;         /* Number of sectors read. */
+    long long write_cnt;        /* Number of sectors written. */
   };
 
 /* An ATA channel (aka controller).
@@ -102,7 +110,7 @@ disk_init (void)
       int dev_no;
 
       /* Initialize channel. */
-      snprintf (c->name, sizeof c->name, "hd%d", chan_no);
+      snprintf (c->name, sizeof c->name, "hd%zu", chan_no);
       switch (chan_no) 
         {
         case 0:
@@ -116,9 +124,9 @@ disk_init (void)
         default:
           NOT_REACHED ();
         }
-      lock_init (&c->lock, c->name);
+      lock_init (&c->lock);
       c->expecting_interrupt = false;
-      sema_init (&c->completion_wait, 0, c->name);
+      sema_init (&c->completion_wait, 0);
  
       /* Initialize devices. */
       for (dev_no = 0; dev_no < 2; dev_no++)
@@ -130,10 +138,12 @@ disk_init (void)
 
           d->is_ata = false;
           d->capacity = 0;
+
+          d->read_cnt = d->write_cnt = 0;
         }
 
       /* Register interrupt handler. */
-      intr_register (c->irq, 0, IF_OFF, interrupt_handler, c->name);
+      intr_register_ext (c->irq, interrupt_handler, c->name);
 
       /* Reset hardware. */
       reset_channel (c);
@@ -149,8 +159,35 @@ disk_init (void)
     }
 }
 
+/* Prints disk statistics. */
+void
+disk_print_stats (void) 
+{
+  int chan_no;
+
+  for (chan_no = 0; chan_no < CHANNEL_CNT; chan_no++) 
+    {
+      int dev_no;
+
+      for (dev_no = 0; dev_no < 2; dev_no++) 
+        {
+          struct disk *d = disk_get (chan_no, dev_no);
+          if (d != NULL && d->is_ata) 
+            printf ("%s: %lld reads, %lld writes\n",
+                    d->name, d->read_cnt, d->write_cnt);
+        }
+    }
+}
+
 /* Returns the disk numbered DEV_NO--either 0 or 1 for master or
-   slave, respectively--within the channel numbered CHAN_NO. */
+   slave, respectively--within the channel numbered CHAN_NO.
+
+   Pintos uses disks this way:
+        0:0 - boot loader, command line args, and operating system kernel
+        0:1 - file system
+        1:0 - scratch
+        1:1 - swap
+*/
 struct disk *
 disk_get (int chan_no, int dev_no) 
 {
@@ -176,7 +213,9 @@ disk_size (struct disk *d)
 }
 
 /* Reads sector SEC_NO from disk D into BUFFER, which must have
-   room for DISK_SECTOR_SIZE bytes. */
+   room for DISK_SECTOR_SIZE bytes.
+   Internally synchronizes accesses to disks, so external
+   per-disk locking is unneeded. */
 void
 disk_read (struct disk *d, disk_sector_t sec_no, void *buffer) 
 {
@@ -193,12 +232,15 @@ disk_read (struct disk *d, disk_sector_t sec_no, void *buffer)
   if (!wait_while_busy (d))
     PANIC ("%s: disk read failed, sector=%"PRDSNu, d->name, sec_no);
   input_sector (c, buffer);
+  d->read_cnt++;
   lock_release (&c->lock);
 }
 
 /* Write sector SEC_NO to disk D from BUFFER, which must contain
    DISK_SECTOR_SIZE bytes.  Returns after the disk has
-   acknowledged receiving the data. */
+   acknowledged receiving the data.
+   Internally synchronizes accesses to disks, so external
+   per-disk locking is unneeded. */
 void
 disk_write (struct disk *d, disk_sector_t sec_no, const void *buffer)
 {
@@ -215,12 +257,13 @@ disk_write (struct disk *d, disk_sector_t sec_no, const void *buffer)
     PANIC ("%s: disk write failed, sector=%"PRDSNu, d->name, sec_no);
   output_sector (c, buffer);
   sema_down (&c->completion_wait);
+  d->write_cnt++;
   lock_release (&c->lock);
 }
 \f
 /* Disk detection and identification. */
 
-static void printk_ata_string (char *string, size_t size);
+static void print_ata_string (char *string, size_t size);
 
 /* Resets an ATA channel and waits for any devices present on it
    to finish the reset. */
@@ -293,15 +336,18 @@ static bool
 check_device_type (struct disk *d) 
 {
   struct channel *c = d->channel;
-  uint8_t error, lbam, lbah;
+  uint8_t error, lbam, lbah, status;
 
   select_device (d);
 
   error = inb (reg_error (c));
   lbam = inb (reg_lbam (c));
   lbah = inb (reg_lbah (c));
+  status = inb (reg_status (c));
 
-  if (error != 1 && (error != 0x81 || d->dev_no == 1)) 
+  if ((error != 1 && (error != 0x81 || d->dev_no == 1))
+      || (status & STA_DRDY) == 0
+      || (status & STA_BSY) != 0)
     {
       d->is_ata = false;
       return error != 0x81;      
@@ -341,28 +387,28 @@ identify_ata_device (struct disk *d)
   d->capacity = id[60] | ((uint32_t) id[61] << 16);
 
   /* Print identification message. */
-  printk ("%s: detected %'"PRDSNu" sector (", d->name, d->capacity);
+  printf ("%s: detected %'"PRDSNu" sector (", d->name, d->capacity);
   if (d->capacity > 1024 / DISK_SECTOR_SIZE * 1024 * 1024)
-    printk ("%"PRDSNu" GB",
+    printf ("%"PRDSNu" GB",
             d->capacity / (1024 / DISK_SECTOR_SIZE * 1024 * 1024));
   else if (d->capacity > 1024 / DISK_SECTOR_SIZE * 1024)
-    printk ("%"PRDSNu" MB", d->capacity / (1024 / DISK_SECTOR_SIZE * 1024));
+    printf ("%"PRDSNu" MB", d->capacity / (1024 / DISK_SECTOR_SIZE * 1024));
   else if (d->capacity > 1024 / DISK_SECTOR_SIZE)
-    printk ("%"PRDSNu" kB", d->capacity / (1024 / DISK_SECTOR_SIZE));
+    printf ("%"PRDSNu" kB", d->capacity / (1024 / DISK_SECTOR_SIZE));
   else
-    printk ("%"PRDSNu" byte", d->capacity * DISK_SECTOR_SIZE);
-  printk (") disk, model \"");
-  printk_ata_string ((char *) &id[27], 40);
-  printk ("\", serial \"");
-  printk_ata_string ((char *) &id[10], 20);
-  printk ("\"\n");
+    printf ("%"PRDSNu" byte", d->capacity * DISK_SECTOR_SIZE);
+  printf (") disk, model \"");
+  print_ata_string ((char *) &id[27], 40);
+  printf ("\", serial \"");
+  print_ata_string ((char *) &id[10], 20);
+  printf ("\"\n");
 }
 
 /* Prints STRING, which consists of SIZE bytes in a funky format:
    each pair of bytes is in reverse order.  Does not print
    trailing whitespace and/or nulls. */
 static void
-printk_ata_string (char *string, size_t size) 
+print_ata_string (char *string, size_t size) 
 {
   size_t i;
 
@@ -376,7 +422,7 @@ printk_ata_string (char *string, size_t size)
 
   /* Print. */
   for (i = 0; i < size; i++)
-    printk ("%c", string[i ^ 1]);
+    printf ("%c", string[i ^ 1]);
 }
 \f
 /* Selects device D, waiting for it to become ready, and then
@@ -406,7 +452,7 @@ issue_pio_command (struct channel *c, uint8_t command)
 {
   /* Interrupts must be enabled or our semaphore will never be
      up'd by the completion handler. */
-  ASSERT (intr_get_level () == IF_ON);
+  ASSERT (intr_get_level () == INTR_ON);
 
   c->expecting_interrupt = true;
   outb (reg_command (c), command);
@@ -447,7 +493,7 @@ wait_until_idle (const struct disk *d)
       timer_usleep (10);
     }
 
-  printk ("%s: idle timeout\n", d->name);
+  printf ("%s: idle timeout\n", d->name);
 }
 
 /* Wait up to 30 seconds for disk D to clear BSY,
@@ -463,17 +509,17 @@ wait_while_busy (const struct disk *d)
   for (i = 0; i < 3000; i++)
     {
       if (i == 700)
-        printk ("%s: busy, waiting...", d->name);
+        printf ("%s: busy, waiting...", d->name);
       if (!(inb (reg_alt_status (c)) & STA_BSY)) 
         {
           if (i >= 700)
-            printk ("ok\n");
+            printf ("ok\n");
           return (inb (reg_alt_status (c)) & STA_DRQ) != 0;
         }
       timer_msleep (10);
     }
 
-  printk ("failed\n");
+  printf ("failed\n");
   return false;
 }
 
@@ -515,7 +561,7 @@ interrupt_handler (struct intr_frame *f)
             sema_up (&c->completion_wait);      /* Wake up waiter. */
           }
         else
-          printk ("%s: unexpected interrupt\n", c->name);
+          printf ("%s: unexpected interrupt\n", c->name);
         return;
       }