1 | /*
2 | block_dev.c
3 | -----------
4 | $Id: block_dev.c,v 1.15 2003/10/01 05:41:57 stewart Exp $
5 | (C)2003 Stewart Smith
6 | Distributed under the GNU Public License
7 |
8 | Some data structures have been constructed out of those
9 | present in the Linux Kernel (v2.5.69). They are copyright
10 | of their respective owners.
11 | */
12 |
13 | #define _GNU_SOURCE
14 |
15 | #include <stdio.h>
16 | #include <stdlib.h>
17 | #include <sys/types.h>
18 | #include <sys/stat.h>
19 | #include <fcntl.h>
20 | #include <unistd.h>
21 |
22 | #include "block_dev.h"
23 | #include <glib.h>
24 |
25 |
26 |
27 | GList* lru_list;
28 | GHashTable* blk_hash;
29 |
30 | /* hash_bh
31 | -------
32 | Hashes a buffer head. Poor hash function, but
33 | okay for our purposes as we generally only
34 | use one block device at once in simulation.
35 |
36 | Used with the Glib hash.
37 | */
38 | guint hash_bh(gconstpointer a)
39 | {
40 | struct buffer_head *bh = (struct buffer_head*)a;
41 | return (bh->b_bdev->file_on_disk % 32768)+(bh->b_blocknr % 32768);
42 | }
43 |
44 | /* bh_equal
45 | --------
46 | true if the contents of two buffer heads are
47 | the same (i.e. device, block number and block size)
48 | */
49 | gint bh_equal(gconstpointer a,gconstpointer b)
50 | {
51 | struct buffer_head *bha,*bhb;
52 | bha = (struct buffer_head*)a;
53 | bhb = (struct buffer_head*)b;
54 |
55 | if(bha==NULL||bhb==NULL)
56 | return 0;
57 |
58 | return (bha->b_bdev == bhb->b_bdev
59 | && bha->b_blocknr==bhb->b_blocknr
60 | && bha->b_size == bhb->b_size
61 | );
62 | }
63 |
64 | /* block_dev_init
65 | --------------
66 |
67 | Initializes the block device simulator.
68 |
69 | Should be called before any other function.
70 | On failure, aborts
71 | */
72 | void block_dev_init()
73 | {
74 | lru_list = NULL;
75 | if((blk_hash = g_hash_table_new(hash_bh,bh_equal))==NULL)
76 | { fprintf(stderr,"Failed to create blk_hash\n"); abort();}
77 | }
78 |
79 | /* block_dev_new
80 | -------------
81 | Fills out the block_device structure. doesn't allocate memory for it.
82 |
83 | On failure of opening teh device, aborts.
84 | */
85 | int block_dev_new(struct block_device *b, const char* file, u64 block_size, u64 num_blocks)
86 | {
87 | if( (b->file_on_disk=open(file,O_RDWR|O_CREAT,S_IRUSR|S_IWUSR)) < 0)
88 | {
89 | fprintf(stderr,"Unable to open device file %s\n",file);
90 | abort();
91 | }
92 | b->block_size = block_size;
93 | b->num_blocks = num_blocks;
94 | b->name = file;
95 |
96 | b->cache_hit = 0;
97 | b->cache_hit_clear = 0;
98 | b->cache_miss = 0;
99 | b->cache_miss_clear = 0;
100 |
101 | return 1;
102 | }
103 |
104 | /* block_dev_close
105 | ---------------
106 | Closes our simulated block device. Warns if any dirty buffers
107 | removes buffers from the buffer cache.
108 | */
109 | int block_dev_close(struct block_device *b)
110 | {
111 | struct buffer_head* bh;
112 |
113 | if(g_list_first(lru_list))
114 | {
115 | bh = (struct buffer_head*)(g_list_first(lru_list)->data);
116 | do
117 | {
118 | if(bh->b_bdev == b) /* Our block device */
119 | {
120 | if(buffer_dirty(bh)) /* Destroying without flush */
121 | fprintf(stderr, /* So we warn */
122 | "block_dev_close: bdev has Dirty buffers (%x)\n",
123 | bh->b_blocknr);
124 | g_hash_table_remove(blk_hash,bh);
125 | lru_list = g_list_remove(lru_list,bh);
126 | fprintf(stderr,"Removing block from cache 0x%08llx\n",bh->b_blocknr);
127 | }
128 | else
129 | fprintf(stderr,"bh->b_dev = %p not %p\n",bh->b_bdev,b);
130 | if(g_list_first(lru_list))
131 | bh = (struct buffer_head*)g_list_first(lru_list)->data;
132 | else
133 | bh = NULL;
134 | }while(bh!=NULL);
135 | }
136 | fprintf(stderr,"Closed Block Device: %s\n",b->name);
137 | fprintf(stderr,"%s Stats:\n\t%lld hit\n\t%lld miss\n\n\t%lld Clear Hit\n\t%lld Clear Miss\n",b->name,b->cache_hit,b->cache_miss,b->cache_hit_clear,b->cache_miss_clear);
138 | }
139 |
140 | struct buffer_head *bread(struct block_device *b, sector_t block, int size)
141 | {
142 | struct buffer_head *bh , *bh2;
143 | int i;
144 |
145 | #ifdef DEBUG_VERBOSE
146 | fprintf(stderr,"Attempting to get block 0x%llx\t",block);
147 | #endif
148 |
149 | if(block >= b->num_blocks)
150 | {
151 | fprintf(stderr,"block out of device range. b=%d\n",block);
152 | return NULL;
153 | }
154 |
155 | if((bh = (struct buffer_head*) malloc(sizeof(struct buffer_head)))==NULL)
156 | {
157 | fprintf(stderr,"Cannot allocate struct buffer_head\n");
158 | abort();
159 | }
160 | bh->b_blocknr = block;
161 | bh->b_size = size;
162 | bh->b_bdev = b;
163 |
164 | if((bh2 = g_hash_table_lookup(blk_hash,bh))!=NULL)
165 | {
166 | // fprintf(stderr,"Found block in cache 0x%llx at 0x%x\n",block,hash_bh(bh2));
167 | free(bh);
168 | lru_list = g_list_remove(lru_list,bh2);
169 | lru_list = g_list_append(lru_list,bh2);
170 | b->cache_hit++;
171 | return bh2;
172 | }
173 |
174 | bh->b_state=0;
175 | bh->b_count=0;
176 |
177 | if((bh->b_data = (char*)malloc(sizeof(char)*size))==NULL)
178 | {
179 | fprintf(stderr,"Unable to Allocate data buffer\n");
180 | abort();
181 | }
182 |
183 | lseek(b->file_on_disk,b->block_size*block,SEEK_SET);
184 | if((i=(read(b->file_on_disk,bh->b_data,(size_t)size)!=size)))
185 | {
186 | fprintf(stderr,"An unexpected number of bytes was read: %d\n\n",i);
187 | abort();
188 | }
189 |
190 | lru_list = g_list_append(lru_list,bh);
191 | if(g_hash_table_lookup(blk_hash,bh)!=NULL && !bh_equal(g_hash_table_lookup(blk_hash,bh),bh))
192 | fprintf(stderr,"HASH COLLISION!!!!!\n\n");
193 | g_hash_table_insert(blk_hash,bh,bh);
194 | #ifdef DEBUG_VERBOSE
195 | fprintf(stderr,"blocks read: %d, hash size: %d hashed as: 0x%x\n",g_list_length(lru_list),g_hash_table_size(blk_hash),hash_bh(bh));
196 | #endif
197 |
198 | b->cache_miss++;
199 |
200 | return bh;
201 | }
202 |
203 | struct buffer_head *bnew(struct block_device *b, sector_t block, int size)
204 | {
205 | struct buffer_head *bh , *bh2;
206 | int i;
207 |
208 | #ifdef DEBUG_VERBOSE
209 | fprintf(stderr,"Attempting to get block 0x%llx\t",block);
210 | #endif
211 |
212 | if(block >= b->num_blocks)
213 | {
214 | fprintf(stderr,"block out of device range. b=%d\n",block);
215 | return NULL;
216 | }
217 |
218 | if((bh = (struct buffer_head*) malloc(sizeof(struct buffer_head)))==NULL)
219 | {
220 | fprintf(stderr,"Cannot allocate struct buffer_head\n");
221 | abort();
222 | }
223 | bh->b_blocknr = block;
224 | bh->b_size = size;
225 | bh->b_bdev = b;
226 |
227 | if((bh2 = g_hash_table_lookup(blk_hash,bh))!=NULL)
228 | {
229 | // fprintf(stderr,"Found block in cache 0x%llx at 0x%x\n",block,hash_bh(bh2));
230 | free(bh);
231 | lru_list = g_list_remove(lru_list,bh2);
232 | lru_list = g_list_append(lru_list,bh2);
233 | memset(bh2->b_data,0,size);
234 | b->cache_hit_clear++;
235 | return bh2;
236 | }
237 |
238 | bh->b_state=0;
239 | bh->b_count=0;
240 |
241 | if((bh->b_data = (char*)malloc(sizeof(char)*size))==NULL)
242 | {
243 | fprintf(stderr,"Unable to Allocate data buffer\n");
244 | abort();
245 | }
246 |
247 | memset(bh->b_data,0,size);
248 |
249 | lru_list = g_list_append(lru_list,bh);
250 | if(g_hash_table_lookup(blk_hash,bh)!=NULL && !bh_equal(g_hash_table_lookup(blk_hash,bh),bh))
251 | fprintf(stderr,"HASH COLLISION!!!!!\n\n");
252 | g_hash_table_insert(blk_hash,bh,bh);
253 | #ifdef DEBUG_VERBOSE
254 | fprintf(stderr,"blocks read: %d, hash size: %d hashed as: 0x%x\n",g_list_length(lru_list),g_hash_table_size(blk_hash),hash_bh(bh));
255 | #endif
256 |
257 | b->cache_miss_clear++;
258 |
259 | return bh;
260 | }
261 |
262 | void submit_bh(int rw,struct buffer_head * bh)
263 | {
264 | int result;
265 |
266 | switch(rw)
267 | {
268 | case READ:
269 | break;
270 |
271 | case WRITE:
272 | if(!buffer_dirty(bh))
273 | fprintf(stderr,"Warning: Non dirty buffer being written.\n");
274 | #ifdef DEBUG_VERBOSE
275 | fprintf(stderr,"---BLOCK WRITE--- %d %lld\n",bh->b_size,bh->b_blocknr);
276 | #endif
277 | lseek(bh->b_bdev->file_on_disk,bh->b_size*bh->b_blocknr,SEEK_SET);
278 | result = write(bh->b_bdev->file_on_disk,bh->b_data,bh->b_size);
279 | if(result <= 0)
280 | { fprintf(stderr,"Error Writing Block, result = %d\n",result);}
281 | clear_buffer_dirty(bh);
282 | fdatasync(bh->b_bdev->file_on_disk);
283 | break;
284 | default:
285 | fprintf(stderr,"Invalid mode to submit_bh (mode=%x)\n",rw);
286 | abort();
287 | break;
288 | };
289 | }