Consider the following snippets from xv6:

static struct buf* bget(uint dev, uint blockno){
  struct buf *b;
  ...
  // Recycle an unused, non-dirty buffer (LRU)
  for(b = bcache.head.prev; b != &bcache.head; b = b->prev){
    if(b->refcnt == 0 && (b->flags & B_DIRTY) == 0) {
      ...
      return b;
    }
  }
  ...
}

struct buf* bread(uint dev, uint blockno) {
  struct buf *b = bget(dev, blockno);
  if((b->flags & B_VALID) == 0)
    iderw(b); // Start disk I/O if data isn't valid
  return b;
}