#ifndef VDO_BLOCK_MAP_H
#define VDO_BLOCK_MAP_H
#include <linux/list.h>
#include "numeric.h"
#include "admin-state.h"
#include "completion.h"
#include "encodings.h"
#include "int-map.h"
#include "statistics.h"
#include "types.h"
#include "vio.h"
#include "wait-queue.h"
enum {
BLOCK_MAP_VIO_POOL_SIZE = 64,
};
typedef u32 vdo_page_generation;
extern const struct block_map_entry UNMAPPED_BLOCK_MAP_ENTRY;
struct vdo_page_cache {
struct vdo *vdo;
page_count_t page_count;
page_count_t pages_in_batch;
bool rebuilding;
struct page_info *infos;
char *pages;
struct page_info *last_found;
struct int_map *page_map;
struct list_head lru_list;
struct list_head free_list;
struct list_head outgoing_list;
page_count_t outstanding_reads;
page_count_t outstanding_writes;
page_count_t pages_in_flush;
page_count_t pages_to_flush;
unsigned int discard_count;
unsigned int waiter_count;
struct vdo_wait_queue free_waiters;
struct block_map_statistics stats;
u32 pressure_report;
struct block_map_zone *zone;
};
enum vdo_page_buffer_state {
PS_FREE,
PS_INCOMING,
PS_FAILED,
PS_RESIDENT,
PS_DIRTY,
PS_OUTGOING,
PAGE_STATE_COUNT,
} __packed;
enum vdo_page_write_status {
WRITE_STATUS_NORMAL,
WRITE_STATUS_DISCARD,
WRITE_STATUS_DEFERRED,
} __packed;
struct page_info {
struct vio *vio;
struct vdo_page_cache *cache;
physical_block_number_t pbn;
u16 busy;
enum vdo_page_write_status write_status;
enum vdo_page_buffer_state state;
struct vdo_wait_queue waiting;
struct list_head state_entry;
struct list_head lru_entry;
sequence_number_t recovery_lock;
};
struct vdo_page_completion {
struct vdo_completion completion;
struct vdo_page_cache *cache;
struct vdo_waiter waiter;
physical_block_number_t pbn;
bool writable;
bool ready;
struct page_info *info;
};
struct forest;
struct tree_page {
struct vdo_waiter waiter;
struct list_head entry;
u8 generation;
bool writing;
u8 writing_generation;
sequence_number_t recovery_lock;
sequence_number_t writing_recovery_lock;
char page_buffer[VDO_BLOCK_SIZE];
};
enum block_map_page_type {
VDO_TREE_PAGE,
VDO_CACHE_PAGE,
};
typedef struct list_head dirty_era_t[2];
struct dirty_lists {
block_count_t maximum_age;
sequence_number_t oldest_period;
sequence_number_t next_period;
block_count_t offset;
dirty_era_t expired;
dirty_era_t eras[];
};
struct block_map_zone {
zone_count_t zone_number;
thread_id_t thread_id;
struct admin_state state;
struct block_map *block_map;
struct dirty_lists *dirty_lists;
struct vdo_page_cache page_cache;
data_vio_count_t active_lookups;
struct int_map *loading_pages;
struct vio_pool *vio_pool;
struct tree_page *flusher;
struct vdo_wait_queue flush_waiters;
u8 generation;
u8 oldest_generation;
u32 dirty_page_counts[256];
};
struct block_map {
struct vdo *vdo;
struct action_manager *action_manager;
physical_block_number_t root_origin;
block_count_t root_count;
sequence_number_t current_era_point;
sequence_number_t pending_era_point;
block_count_t entry_count;
nonce_t nonce;
struct recovery_journal *journal;
struct forest *forest;
struct forest *next_forest;
block_count_t next_entry_count;
zone_count_t zone_count;
struct block_map_zone zones[];
};
typedef int (*vdo_entry_callback_fn)(physical_block_number_t pbn,
struct vdo_completion *completion);
static inline struct vdo_page_completion *as_vdo_page_completion(struct vdo_completion *completion)
{
vdo_assert_completion_type(completion, VDO_PAGE_COMPLETION);
return container_of(completion, struct vdo_page_completion, completion);
}
void vdo_release_page_completion(struct vdo_completion *completion);
void vdo_get_page(struct vdo_page_completion *page_completion,
struct block_map_zone *zone, physical_block_number_t pbn,
bool writable, void *parent, vdo_action_fn callback,
vdo_action_fn error_handler, bool requeue);
void vdo_request_page_write(struct vdo_completion *completion);
int __must_check vdo_get_cached_page(struct vdo_completion *completion,
struct block_map_page **page_ptr);
int __must_check vdo_invalidate_page_cache(struct vdo_page_cache *cache);
static inline struct block_map_page * __must_check
vdo_as_block_map_page(struct tree_page *tree_page)
{
return (struct block_map_page *) tree_page->page_buffer;
}
bool vdo_copy_valid_page(char *buffer, nonce_t nonce,
physical_block_number_t pbn,
struct block_map_page *page);
void vdo_find_block_map_slot(struct data_vio *data_vio);
physical_block_number_t vdo_find_block_map_page_pbn(struct block_map *map,
page_number_t page_number);
void vdo_write_tree_page(struct tree_page *page, struct block_map_zone *zone);
void vdo_traverse_forest(struct block_map *map, vdo_entry_callback_fn callback,
struct vdo_completion *completion);
int __must_check vdo_decode_block_map(struct block_map_state_2_0 state,
block_count_t logical_blocks, struct vdo *vdo,
struct recovery_journal *journal, nonce_t nonce,
page_count_t cache_size, block_count_t maximum_age,
struct block_map **map_ptr);
void vdo_drain_block_map(struct block_map *map, const struct admin_state_code *operation,
struct vdo_completion *parent);
void vdo_resume_block_map(struct block_map *map, struct vdo_completion *parent);
int __must_check vdo_prepare_to_grow_block_map(struct block_map *map,
block_count_t new_logical_blocks);
void vdo_grow_block_map(struct block_map *map, struct vdo_completion *parent);
void vdo_abandon_block_map_growth(struct block_map *map);
void vdo_free_block_map(struct block_map *map);
struct block_map_state_2_0 __must_check vdo_record_block_map(const struct block_map *map);
void vdo_initialize_block_map_from_journal(struct block_map *map,
struct recovery_journal *journal);
zone_count_t vdo_compute_logical_zone(struct data_vio *data_vio);
void vdo_advance_block_map_era(struct block_map *map,
sequence_number_t recovery_block_number);
void vdo_update_block_map_page(struct block_map_page *page, struct data_vio *data_vio,
physical_block_number_t pbn,
enum block_mapping_state mapping_state,
sequence_number_t *recovery_lock);
void vdo_get_mapped_block(struct data_vio *data_vio);
void vdo_put_mapped_block(struct data_vio *data_vio);
struct block_map_statistics __must_check vdo_get_block_map_statistics(struct block_map *map);
static inline block_count_t vdo_convert_maximum_age(block_count_t age)
{
return DIV_ROUND_UP(age * RECOVERY_JOURNAL_1_ENTRIES_PER_BLOCK,
2 * RECOVERY_JOURNAL_ENTRIES_PER_BLOCK);
}
#endif