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  | }