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