#ifndef BLOCK_CACHE_H #define BLOCK_CACHE_H #include "block-cache/list.h" #include #include #include #include #include #include #include #include //---------------------------------------------------------------- namespace bcache { typedef uint64_t block_address; typedef uint64_t sector_t; class validator { public: typedef boost::shared_ptr ptr; virtual ~validator() {} virtual void check(void const *data, block_address location) const = 0; virtual void prepare(void *data, block_address location) const = 0; }; class noop_validator : public validator { public: void check(void const *data, block_address location) const {} void prepare(void *data, block_address location) const {} }; //---------------------------------------------------------------- class block_cache : private boost::noncopyable { public: enum block_flags { BF_IO_PENDING = (1 << 0), BF_DIRTY = (1 << 1), BF_FLUSH = (1 << 2), BF_PREVIOUSLY_DIRTY = (1 << 3) }; class block : private boost::noncopyable { public: block() : v_() { INIT_LIST_HEAD(&list_); } // Do not give this class a destructor, it wont get // called because we manage allocation ourselves. uint64_t get_index() const { return index_; } void *get_data() const { return data_; } void mark_dirty() { set_flags(BF_DIRTY); } void set_flags(unsigned flags) { flags_ |= flags; } unsigned test_flags(unsigned flags) const { return flags_ & flags; } void clear_flags(unsigned flags) { flags_ &= ~flags; } void get() { ref_count_++; }; void put() { if (!ref_count_) throw std::runtime_error("bad put"); if (!--ref_count_) bc_->release(*this); } private: friend class block_cache; block_cache *bc_; uint64_t index_; void *data_; list_head list_; list_head hash_list_; unsigned ref_count_; int error_; unsigned flags_; iocb control_block_; validator::ptr v_; }; //-------------------------------- block_cache(int fd, sector_t block_size, uint64_t max_nr_blocks, size_t mem); ~block_cache(); uint64_t get_nr_blocks() const; enum get_flags { GF_ZERO = (1 << 0), GF_DIRTY = (1 << 1), GF_BARRIER = (1 << 2) }; block_cache::block &get(block_address index, unsigned flags, validator::ptr v); /* * Flush can fail if an earlier write failed. You do not know which block * failed. Make sure you build your recovery with this in mind. */ int flush(); void prefetch(block_address index); private: int init_free_list(unsigned count); block *__alloc_block(); void complete_io(block &b, int result); int issue_low_level(block &b, enum io_iocb_cmd opcode, const char *desc); int issue_read(block &b); int issue_write(block &b); void wait_io(); list_head *__categorise(block &b); void hit(block &b); void wait_all(); void wait_specific(block &b); unsigned writeback(unsigned count); void hash_init(unsigned nr_buckets); unsigned hash(uint64_t index); block *hash_lookup(block_address index); void hash_insert(block &b); void hash_remove(block &b); void setup_control_block(block &b); block *new_block(block_address index); void mark_dirty(block &b); unsigned calc_nr_cache_blocks(size_t mem, sector_t block_size); unsigned calc_nr_buckets(unsigned nr_blocks); void zero_block(block &b); block *lookup_or_read_block(block_address index, unsigned flags, validator::ptr v); void preemptive_writeback(); void release(block_cache::block &block); void check_index(block_address index) const; //-------------------------------- int fd_; sector_t block_size_; uint64_t nr_data_blocks_; uint64_t nr_cache_blocks_; // We can't use auto_ptr or unique_ptr because the memory is allocated with malloc void *blocks_memory_; void *blocks_data_; io_context_t aio_context_; std::vector events_; /* * Blocks on the free list are not initialised, apart from the * b.data field. */ list_head free_; list_head errored_; list_head dirty_; list_head clean_; unsigned nr_locked_; unsigned nr_dirty_; unsigned nr_io_pending_; struct list_head io_pending_; /* * Hash table fields. */ unsigned nr_buckets_; unsigned mask_; std::vector buckets_; }; } //---------------------------------------------------------------- #endif