225 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			225 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
#ifndef BLOCK_CACHE_H
 | 
						|
#define BLOCK_CACHE_H
 | 
						|
 | 
						|
#include "block-cache/list.h"
 | 
						|
 | 
						|
#include <boost/shared_ptr.hpp>
 | 
						|
#include <boost/noncopyable.hpp>
 | 
						|
 | 
						|
#include <stdexcept>
 | 
						|
#include <libaio.h>
 | 
						|
#include <memory>
 | 
						|
#include <stdint.h>
 | 
						|
#include <stdlib.h>
 | 
						|
#include <vector>
 | 
						|
 | 
						|
//----------------------------------------------------------------
 | 
						|
 | 
						|
namespace bcache {
 | 
						|
	typedef uint64_t block_address;
 | 
						|
	typedef uint64_t sector_t;
 | 
						|
 | 
						|
	class validator {
 | 
						|
	public:
 | 
						|
		typedef boost::shared_ptr<validator> 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;
 | 
						|
		uint64_t get_nr_locked() 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);
 | 
						|
		void exit_free_list();
 | 
						|
		block *__alloc_block();
 | 
						|
		void complete_io(block &b, int result);
 | 
						|
		void issue_low_level(block &b, enum io_iocb_cmd opcode, const char *desc);
 | 
						|
		void issue_read(block &b);
 | 
						|
		void 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 *find_unused_clean_block();
 | 
						|
		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;
 | 
						|
 | 
						|
		void inc_hit_counter(unsigned flags);
 | 
						|
		void inc_miss_counter(unsigned flags);
 | 
						|
 | 
						|
		//--------------------------------
 | 
						|
 | 
						|
		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<io_event> 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<list_head> buckets_;
 | 
						|
 | 
						|
		// Stats
 | 
						|
		unsigned read_hits_;
 | 
						|
		unsigned read_misses_;
 | 
						|
		unsigned write_zeroes_;
 | 
						|
		unsigned write_hits_;
 | 
						|
		unsigned write_misses_;
 | 
						|
		unsigned prefetches_;
 | 
						|
 | 
						|
		validator::ptr noop_validator_;
 | 
						|
	};
 | 
						|
}
 | 
						|
 | 
						|
//----------------------------------------------------------------
 | 
						|
 | 
						|
#endif
 |