Implement a proper block layer with partition support.
[pintos-anon] / src / devices / block.c
1 #include "devices/block.h"
2 #include <list.h>
3 #include <string.h>
4 #include <stdio.h>
5 #include "devices/ide.h"
6 #include "threads/malloc.h"
7
8 /* A block device. */
9 struct block
10   {
11     struct list_elem list_elem;         /* Element in all_blocks. */
12
13     char name[16];                      /* Block device name. */
14     enum block_type type;                /* Type of block device. */
15     block_sector_t size;                 /* Size in sectors. */
16
17     const struct block_operations *ops;  /* Driver operations. */
18     void *aux;                          /* Extra data owned by driver. */
19
20     unsigned long long read_cnt;        /* Number of sectors read. */
21     unsigned long long write_cnt;       /* Number of sectors written. */
22   };
23
24 /* List of all block devices. */
25 static struct list all_blocks = LIST_INITIALIZER (all_blocks);
26
27 /* The block block assigned to each Pintos role. */
28 static struct block *block_by_role[BLOCK_ROLE_CNT];
29
30 static struct block *list_elem_to_block (struct list_elem *);
31
32 /* Returns a human-readable name for the given block device
33    TYPE. */
34 const char *
35 block_type_name (enum block_type type)
36 {
37   static const char *block_type_names[BLOCK_CNT] =
38     {
39       "kernel",
40       "filesys",
41       "scratch",
42       "swap",
43       "raw",
44       "foreign",
45     };
46
47   ASSERT (type < BLOCK_CNT);
48   return block_type_names[type];
49 }
50
51 /* Returns the block device fulfilling the given ROLE, or a null
52    pointer if no block device has been assigned that role. */
53 struct block *
54 block_get_role (enum block_type role)
55 {
56   ASSERT (role < BLOCK_ROLE_CNT);
57   return block_by_role[role];
58 }
59
60 /* Assigns BLOCK the given ROLE. */
61 void
62 block_set_role (enum block_type role, struct block *block)
63 {
64   ASSERT (role < BLOCK_ROLE_CNT);
65   block_by_role[role] = block;
66 }
67
68 /* Returns the first block device in kernel probe order, or a
69    null pointer if no block devices are registered. */
70 struct block *
71 block_first (void)
72 {
73   return list_elem_to_block (list_begin (&all_blocks));
74 }
75
76 /* Returns the block device following BLOCK in kernel probe
77    order, or a null pointer if BLOCK is the last block device. */
78 struct block *
79 block_next (struct block *block)
80 {
81   return list_elem_to_block (list_next (&block->list_elem));
82 }
83
84 /* Returns the block device with the given NAME, or a null
85    pointer if no block device has that name. */
86 struct block *
87 block_get_by_name (const char *name)
88 {
89   struct list_elem *e;
90
91   for (e = list_begin (&all_blocks); e != list_end (&all_blocks);
92        e = list_next (e))
93     {
94       struct block *block = list_entry (e, struct block, list_elem);
95       if (!strcmp (name, block->name))
96         return block;
97     }
98
99   return NULL;
100 }
101
102 /* Verifies that SECTOR is a valid offset within BLOCK.
103    Panics if not. */
104 static void
105 check_sector (struct block *block, block_sector_t sector)
106 {
107   if (sector >= block->size)
108     {
109       /* We do not use ASSERT because we want to panic here
110          regardless of whether NDEBUG is defined. */
111       PANIC ("Access past end of device %s (sector=%"PRDSNu", "
112              "size=%"PRDSNu")\n", block_name (block), sector, block->size);
113     }
114 }
115
116 /* Reads sector SECTOR from BLOCK into BUFFER, which must
117    have room for BLOCK_SECTOR_SIZE bytes.
118    Internally synchronizes accesses to block devices, so external
119    per-block device locking is unneeded. */
120 void
121 block_read (struct block *block, block_sector_t sector, void *buffer)
122 {
123   check_sector (block, sector);
124   block->ops->read (block->aux, sector, buffer);
125   block->read_cnt++;
126 }
127
128 /* Write sector SECTOR to BLOCK from BUFFER, which must contain
129    BLOCK_SECTOR_SIZE bytes.  Returns after the block device has
130    acknowledged receiving the data.
131    Internally synchronizes accesses to block devices, so external
132    per-block device locking is unneeded. */
133 void
134 block_write (struct block *block, block_sector_t sector, const void *buffer)
135 {
136   check_sector (block, sector);
137   ASSERT (block->type != BLOCK_FOREIGN);
138   block->ops->write (block->aux, sector, buffer);
139   block->write_cnt++;
140 }
141
142 /* Returns the number of sectors in BLOCK. */
143 block_sector_t
144 block_size (struct block *block)
145 {
146   return block->size;
147 }
148
149 /* Returns BLOCK's name (e.g. "hda"). */
150 const char *
151 block_name (struct block *block)
152 {
153   return block->name;
154 }
155
156 /* Returns BLOCK's type. */
157 enum block_type
158 block_type (struct block *block)
159 {
160   return block->type;
161 }
162
163 /* Prints statistics for each block device used for a Pintos role. */
164 void
165 block_print_stats (void)
166 {
167   int i;
168
169   for (i = 0; i < BLOCK_CNT; i++)
170     {
171       struct block *block = block_by_role[i];
172       if (block != NULL)
173         {
174           printf ("%s (%s): %llu reads, %llu writes\n",
175                   block->name, block_type_name (block->type),
176                   block->read_cnt, block->write_cnt);
177         }
178     }
179 }
180
181 /* Registers a new block device with the given NAME.  If
182    EXTRA_INFO is non-null, it is printed as part of a user
183    message.  The block device's SIZE in sectors and its TYPE must
184    be provided, as well as the it operation functions OPS, which
185    will be passed AUX in each function call. */
186 struct block *
187 block_register (const char *name, enum block_type type,
188                 const char *extra_info, block_sector_t size,
189                 const struct block_operations *ops, void *aux)
190 {
191   struct block *block = malloc (sizeof *block);
192   if (block == NULL)
193     PANIC ("Failed to allocate memory for block device descriptor");
194
195   list_push_back (&all_blocks, &block->list_elem);
196   strlcpy (block->name, name, sizeof block->name);
197   block->type = type;
198   block->size = size;
199   block->ops = ops;
200   block->aux = aux;
201   block->read_cnt = 0;
202   block->write_cnt = 0;
203
204   printf ("%s: %'"PRDSNu" sectors (", block->name, block->size);
205   print_human_readable_size ((uint64_t) block->size * BLOCK_SECTOR_SIZE);
206   printf (")");
207   if (extra_info != NULL)
208     printf (", %s", extra_info);
209   printf ("\n");
210
211   return block;
212 }
213 \f
214 /* Returns the block device corresponding to LIST_ELEM, or a null
215    pointer if LIST_ELEM is the list end of all_blocks. */
216 static struct block *
217 list_elem_to_block (struct list_elem *list_elem)
218 {
219   return (list_elem != list_end (&all_blocks)
220           ? list_entry (list_elem, struct block, list_elem)
221           : NULL);
222 }
223