bio.c 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. // Buffer cache.
  2. //
  3. // The buffer cache is a linked list of buf structures holding
  4. // cached copies of disk block contents. Caching disk blocks
  5. // in memory reduces the number of disk reads and also provides
  6. // a synchronization point for disk blocks used by multiple processes.
  7. //
  8. // Interface:
  9. // * To get a buffer for a particular disk block, call bread.
  10. // * After changing buffer data, call bwrite to write it to disk.
  11. // * When done with the buffer, call brelse.
  12. // * Do not use the buffer after calling brelse.
  13. // * Only one process at a time can use a buffer,
  14. // so do not keep them longer than necessary.
  15. //
  16. // The implementation uses three state flags internally:
  17. // * B_BUSY: the block has been returned from bread
  18. // and has not been passed back to brelse.
  19. // * B_VALID: the buffer data has been read from the disk.
  20. // * B_DIRTY: the buffer data has been modified
  21. // and needs to be written to disk.
  22. #include "types.h"
  23. #include "defs.h"
  24. #include "param.h"
  25. #include "spinlock.h"
  26. #include "buf.h"
  27. struct {
  28. struct spinlock lock;
  29. struct buf buf[NBUF];
  30. // Linked list of all buffers, through prev/next.
  31. // head.next is most recently used.
  32. struct buf head;
  33. } bcache;
  34. void
  35. binit(void)
  36. {
  37. struct buf *b;
  38. memset(&bcache, 0, sizeof(bcache));
  39. initlock(&bcache.lock, "bcache");
  40. //PAGEBREAK!
  41. // Create linked list of buffers
  42. bcache.head.prev = &bcache.head;
  43. bcache.head.next = &bcache.head;
  44. for(b = bcache.buf; b < bcache.buf+NBUF; b++){
  45. b->next = bcache.head.next;
  46. b->prev = &bcache.head;
  47. b->dev = -1;
  48. bcache.head.next->prev = b;
  49. bcache.head.next = b;
  50. }
  51. }
  52. // Look through buffer cache for sector on device dev.
  53. // If not found, allocate fresh block.
  54. // In either case, return B_BUSY buffer.
  55. static struct buf*
  56. bget(uint dev, uint sector)
  57. {
  58. struct buf *b;
  59. acquire(&bcache.lock);
  60. loop:
  61. // Is the sector already cached?
  62. for(b = bcache.head.next; b != &bcache.head; b = b->next){
  63. if(b->dev == dev && b->sector == sector){
  64. if(!(b->flags & B_BUSY)){
  65. b->flags |= B_BUSY;
  66. release(&bcache.lock);
  67. return b;
  68. }
  69. sleep(b, &bcache.lock);
  70. goto loop;
  71. }
  72. }
  73. // Not cached; recycle some non-busy and clean buffer.
  74. for(b = bcache.head.prev; b != &bcache.head; b = b->prev){
  75. if((b->flags & B_BUSY) == 0 && (b->flags & B_DIRTY) == 0){
  76. b->dev = dev;
  77. b->sector = sector;
  78. b->flags = B_BUSY;
  79. release(&bcache.lock);
  80. return b;
  81. }
  82. }
  83. panic("bget: no buffers");
  84. return 0;
  85. }
  86. // Return a B_BUSY buf with the contents of the indicated disk sector.
  87. struct buf*
  88. bread(uint dev, uint sector)
  89. {
  90. struct buf *b;
  91. b = bget(dev, sector);
  92. if(!(b->flags & B_VALID))
  93. iderw(b);
  94. return b;
  95. }
  96. // Write b's contents to disk. Must be B_BUSY.
  97. void
  98. bwrite(struct buf *b)
  99. {
  100. if((b->flags & B_BUSY) == 0)
  101. panic("bwrite");
  102. b->flags |= B_DIRTY;
  103. iderw(b);
  104. }
  105. // Release a B_BUSY buffer.
  106. // Move to the head of the MRU list.
  107. void
  108. brelse(struct buf *b)
  109. {
  110. if((b->flags & B_BUSY) == 0)
  111. panic("brelse");
  112. acquire(&bcache.lock);
  113. b->next->prev = b->prev;
  114. b->prev->next = b->next;
  115. b->next = bcache.head.next;
  116. b->prev = &bcache.head;
  117. bcache.head.next->prev = b;
  118. bcache.head.next = b;
  119. b->flags &= ~B_BUSY;
  120. wakeup(b);
  121. release(&bcache.lock);
  122. }