%PDF- %PDF-
| Direktori : /var/lib/dkms/blksnap/6.3.0.73/source/ |
| Current File : //var/lib/dkms/blksnap/6.3.0.73/source/cbt_map.c |
// SPDX-License-Identifier: GPL-2.0
#define pr_fmt(fmt) KBUILD_MODNAME "-cbt_map: " fmt
#include <linux/slab.h>
#ifdef STANDALONE_BDEVFILTER
#include "blk_snap.h"
#else
#include <linux/blk_snap.h>
#endif
#include "memory_checker.h"
#include "cbt_map.h"
#include "params.h"
#ifdef STANDALONE_BDEVFILTER
#include "log.h"
#endif
#ifndef HAVE_BDEV_NR_SECTORS
static inline sector_t bdev_nr_sectors(struct block_device *bdev)
{
return i_size_read(bdev->bd_inode) >> 9;
};
#endif
static inline unsigned long long count_by_shift(sector_t capacity,
unsigned long long shift)
{
sector_t blk_size = 1ull << (shift - SECTOR_SHIFT);
return round_up(capacity, blk_size) / blk_size;
}
static void cbt_map_calculate_block_size(struct cbt_map *cbt_map)
{
unsigned long long shift = tracking_block_minimum_shift;
unsigned long long count;
sector_t capacity = cbt_map->device_capacity;
pr_debug("Device capacity %llu sectors\n", capacity);
/**
* The size of the tracking block is calculated based on the size of the disk
* so that the CBT table does not exceed a reasonable size.
*/
count = count_by_shift(capacity, shift);
pr_debug("Blocks count %llu\n", count);
while (count > tracking_block_maximum_count) {
if (shift >= tracking_block_maximum_shift) {
pr_info("The maximum allowable CBT block size has been reached.\n");
break;
}
shift = shift + 1ull;
count = count_by_shift(capacity, shift);
pr_debug("Blocks count %llu\n", count);
}
cbt_map->blk_size_shift = shift;
cbt_map->blk_count = count;
pr_debug("The optimal CBT block size was calculated as %llu bytes\n",
(1ull << cbt_map->blk_size_shift));
}
static int cbt_map_allocate(struct cbt_map *cbt_map)
{
pr_debug("Allocate CBT map of %zu blocks\n", cbt_map->blk_count);
cbt_map->read_map = big_buffer_alloc(cbt_map->blk_count, GFP_KERNEL);
if (cbt_map->read_map != NULL)
big_buffer_memset(cbt_map->read_map, 0);
cbt_map->write_map = big_buffer_alloc(cbt_map->blk_count, GFP_KERNEL);
if (cbt_map->write_map != NULL)
big_buffer_memset(cbt_map->write_map, 0);
if ((cbt_map->read_map == NULL) || (cbt_map->write_map == NULL)) {
pr_err("Cannot allocate CBT map. %zu blocks are required.\n",
cbt_map->blk_count);
return -ENOMEM;
}
cbt_map->snap_number_previous = 0;
cbt_map->snap_number_active = 1;
generate_random_uuid(cbt_map->generation_id.b);
cbt_map->is_corrupted = false;
return 0;
}
static void cbt_map_deallocate(struct cbt_map *cbt_map)
{
cbt_map->is_corrupted = false;
if (cbt_map->read_map != NULL) {
big_buffer_free(cbt_map->read_map);
cbt_map->read_map = NULL;
}
if (cbt_map->write_map != NULL) {
big_buffer_free(cbt_map->write_map);
cbt_map->write_map = NULL;
}
}
int cbt_map_reset(struct cbt_map *cbt_map, sector_t device_capacity)
{
cbt_map_deallocate(cbt_map);
cbt_map->device_capacity = device_capacity;
cbt_map_calculate_block_size(cbt_map);
cbt_map->is_corrupted = false;
return cbt_map_allocate(cbt_map);
}
static inline void cbt_map_destroy(struct cbt_map *cbt_map)
{
pr_debug("CBT map destroy\n");
cbt_map_deallocate(cbt_map);
kfree(cbt_map);
memory_object_dec(memory_object_cbt_map);
}
struct cbt_map *cbt_map_create(struct block_device *bdev)
{
struct cbt_map *cbt_map = NULL;
pr_debug("CBT map create\n");
cbt_map = kzalloc(sizeof(struct cbt_map), GFP_KERNEL);
if (cbt_map == NULL)
return NULL;
memory_object_inc(memory_object_cbt_map);
cbt_map->device_capacity = bdev_nr_sectors(bdev);
cbt_map_calculate_block_size(cbt_map);
if (cbt_map_allocate(cbt_map)) {
cbt_map_destroy(cbt_map);
return NULL;
}
spin_lock_init(&cbt_map->locker);
kref_init(&cbt_map->kref);
cbt_map->is_corrupted = false;
return cbt_map;
}
void cbt_map_destroy_cb(struct kref *kref)
{
cbt_map_destroy(container_of(kref, struct cbt_map, kref));
}
void cbt_map_switch(struct cbt_map *cbt_map)
{
pr_debug("CBT map switch\n");
spin_lock(&cbt_map->locker);
big_buffer_memcpy(cbt_map->read_map, cbt_map->write_map);
cbt_map->snap_number_previous = cbt_map->snap_number_active;
++cbt_map->snap_number_active;
if (cbt_map->snap_number_active == 256) {
cbt_map->snap_number_active = 1;
big_buffer_memset(cbt_map->write_map, 0);
generate_random_uuid(cbt_map->generation_id.b);
pr_debug("CBT reset\n");
}
spin_unlock(&cbt_map->locker);
}
static inline int _cbt_map_set(struct cbt_map *cbt_map, sector_t sector_start,
sector_t sector_cnt, u8 snap_number,
struct big_buffer *map)
{
int res = 0;
u8 num;
size_t cbt_block;
size_t cbt_block_first = (size_t)(
sector_start >> (cbt_map->blk_size_shift - SECTOR_SHIFT));
size_t cbt_block_last =
(size_t)((sector_start + sector_cnt - 1) >>
(cbt_map->blk_size_shift - SECTOR_SHIFT));
for (cbt_block = cbt_block_first; cbt_block <= cbt_block_last;
++cbt_block) {
if (unlikely(cbt_block >= cbt_map->blk_count)) {
pr_err("Block index is too large.\n");
pr_err("Block #%zu was demanded, map size %zu blocks.\n",
cbt_block, cbt_map->blk_count);
res = -EINVAL;
break;
}
res = big_buffer_byte_get(map, cbt_block, &num);
if (unlikely(res)) {
pr_err("CBT table out of range\n");
break;
}
if (num < snap_number) {
res = big_buffer_byte_set(map, cbt_block, snap_number);
if (unlikely(res)) {
pr_err("CBT table out of range\n");
break;
}
}
}
return res;
}
int cbt_map_set(struct cbt_map *cbt_map, sector_t sector_start,
sector_t sector_cnt)
{
int res;
spin_lock(&cbt_map->locker);
if (unlikely(cbt_map->is_corrupted)) {
spin_unlock(&cbt_map->locker);
return -EINVAL;
}
res = _cbt_map_set(cbt_map, sector_start, sector_cnt,
(u8)cbt_map->snap_number_active, cbt_map->write_map);
if (unlikely(res))
cbt_map->is_corrupted = true;
spin_unlock(&cbt_map->locker);
return res;
}
int cbt_map_set_both(struct cbt_map *cbt_map, sector_t sector_start,
sector_t sector_cnt)
{
int res;
spin_lock(&cbt_map->locker);
if (unlikely(cbt_map->is_corrupted)) {
spin_unlock(&cbt_map->locker);
return -EINVAL;
}
res = _cbt_map_set(cbt_map, sector_start, sector_cnt,
(u8)cbt_map->snap_number_active, cbt_map->write_map);
if (!res)
res = _cbt_map_set(cbt_map, sector_start, sector_cnt,
(u8)cbt_map->snap_number_previous,
cbt_map->read_map);
spin_unlock(&cbt_map->locker);
return res;
}
size_t cbt_map_read_to_user(struct cbt_map *cbt_map, char __user *user_buff,
size_t offset, size_t size)
{
size_t readed = 0;
size_t left_size;
size_t real_size;
if (unlikely(cbt_map->is_corrupted)) {
pr_err("CBT table was corrupted\n");
return -EFAULT;
}
if (unlikely(offset >= cbt_map->blk_count)) {
pr_err("The offset exceeds the size of the CBT table\n");
return -EINVAL;
}
real_size = min((cbt_map->blk_count - offset), size);
left_size = real_size - big_buffer_copy_to_user(user_buff, offset,
cbt_map->read_map,
real_size);
if (left_size == 0)
readed = real_size;
else {
pr_err("Not all CBT data was read. Left [%zu] bytes\n",
left_size);
readed = real_size - left_size;
}
return readed;
}
int cbt_map_mark_dirty_blocks(struct cbt_map *cbt_map,
struct blk_snap_block_range *block_ranges,
unsigned int count)
{
int inx;
int ret = 0;
for (inx = 0; inx < count; inx++) {
ret = cbt_map_set_both(
cbt_map, (sector_t)block_ranges[inx].sector_offset,
(sector_t)block_ranges[inx].sector_count);
if (ret)
break;
}
return ret;
}
#ifdef BLK_SNAP_DEBUG_SECTOR_STATE
static inline int _cbt_map_get(struct big_buffer *map, size_t cbt_block,
u8 *snap_number)
{
int ret = 0;
ret = big_buffer_byte_get(map, cbt_block, snap_number);
if (unlikely(ret))
pr_err("CBT table out of range\n");
return ret;
}
int cbt_map_get_sector_state(struct cbt_map *cbt_map, sector_t sector,
u8 *snap_number_prev, u8 *snap_number_curr)
{
int ret;
size_t cbt_block =
(size_t)(sector >> (cbt_map->blk_size_shift - SECTOR_SHIFT));
if (unlikely(cbt_block >= cbt_map->blk_count)) {
pr_err("Block index is too large.\n");
pr_err("Block #%zu was demanded, map size %zu blocks.\n",
cbt_block, cbt_map->blk_count);
return -EINVAL;
}
spin_lock(&cbt_map->locker);
if (unlikely(cbt_map->is_corrupted)) {
ret = -EINVAL;
goto out;
}
ret = _cbt_map_get(cbt_map->write_map, cbt_block, snap_number_curr);
if (!ret)
ret = _cbt_map_get(cbt_map->read_map, cbt_block,
snap_number_prev);
out:
spin_unlock(&cbt_map->locker);
return ret;
}
#endif