From fbc425562c5787b9aabb2d3b2d4fb6f18c553852 Mon Sep 17 00:00:00 2001 From: Jakub Radtke Date: Fri, 15 Jan 2021 00:46:56 -0500 Subject: imsm: Write-intent bitmap support This patch enables the bitmap functionality with the IMSM metadata format. The functionality covers "internal" bitmap. There are the following limitation to "internal" metadata: - creating the bitmap using --grow is not supported - parameter --bitmap-chunk is ignored (default value 64MiB is used) Signed-off-by: Jakub Radtke Signed-off-by: Jes Sorensen --- super-intel.c | 619 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 617 insertions(+), 2 deletions(-) (limited to 'super-intel.c') diff --git a/super-intel.c b/super-intel.c index 98af3b5..455bdd2 100644 --- a/super-intel.c +++ b/super-intel.c @@ -96,6 +96,23 @@ * mutliple PPL area */ +/* + * Internal Write-intent bitmap is stored in the same area where PPL. + * Both features are mutually exclusive, so it is not an issue. + * The first 8KiB of the area are reserved and shall not be used. + */ +#define IMSM_BITMAP_AREA_RESERVED_SIZE 8192 + +#define IMSM_BITMAP_HEADER_OFFSET (IMSM_BITMAP_AREA_RESERVED_SIZE) +#define IMSM_BITMAP_HEADER_SIZE MAX_SECTOR_SIZE + +#define IMSM_BITMAP_START_OFFSET (IMSM_BITMAP_HEADER_OFFSET + IMSM_BITMAP_HEADER_SIZE) +#define IMSM_BITMAP_AREA_SIZE (MULTIPLE_PPL_AREA_SIZE_IMSM - IMSM_BITMAP_START_OFFSET) +#define IMSM_BITMAP_AND_HEADER_SIZE (IMSM_BITMAP_AREA_SIZE + IMSM_BITMAP_HEADER_SIZE) + +#define IMSM_DEFAULT_BITMAP_CHUNKSIZE (64 * 1024 * 1024) +#define IMSM_DEFAULT_BITMAP_DAEMON_SLEEP 5 + /* * This macro let's us ensure that no-one accidentally * changes the size of a struct @@ -229,6 +246,7 @@ struct imsm_dev { #define RWH_MULTIPLE_DISTRIBUTED 3 #define RWH_MULTIPLE_PPLS_JOURNALING_DRIVE 4 #define RWH_MULTIPLE_OFF 5 +#define RWH_BITMAP 6 __u8 rwh_policy; /* Raid Write Hole Policy */ __u8 jd_serial[MAX_RAID_SERIAL_LEN]; /* Journal Drive serial number */ __u8 filler1; @@ -1682,6 +1700,8 @@ static void print_imsm_dev(struct intel_super *super, printf("Multiple distributed PPLs\n"); else if (dev->rwh_policy == RWH_MULTIPLE_PPLS_JOURNALING_DRIVE) printf("Multiple PPLs on journaling drive\n"); + else if (dev->rwh_policy == RWH_BITMAP) + printf("Write-intent bitmap\n"); else printf("\n", dev->rwh_policy); @@ -3296,6 +3316,53 @@ static unsigned long long imsm_component_size_alignment_check(int level, return component_size; } +/******************************************************************************* + * Function: get_bitmap_header_sector + * Description: Returns the sector where the bitmap header is placed. + * Parameters: + * st : supertype information + * dev_idx : index of the device with bitmap + * + * Returns: + * The sector where the bitmap header is placed + ******************************************************************************/ +static unsigned long long get_bitmap_header_sector(struct intel_super *super, + int dev_idx) +{ + struct imsm_dev *dev = get_imsm_dev(super, dev_idx); + struct imsm_map *map = get_imsm_map(dev, MAP_0); + + if (!super->sector_size) { + dprintf("sector size is not set\n"); + return 0; + } + + return pba_of_lba0(map) + calc_component_size(map, dev) + + (IMSM_BITMAP_HEADER_OFFSET / super->sector_size); +} + +/******************************************************************************* + * Function: get_bitmap_sector + * Description: Returns the sector where the bitmap is placed. + * Parameters: + * st : supertype information + * dev_idx : index of the device with bitmap + * + * Returns: + * The sector where the bitmap is placed + ******************************************************************************/ +static unsigned long long get_bitmap_sector(struct intel_super *super, + int dev_idx) +{ + if (!super->sector_size) { + dprintf("sector size is not set\n"); + return 0; + } + + return get_bitmap_header_sector(super, dev_idx) + + (IMSM_BITMAP_HEADER_SIZE / super->sector_size); +} + static unsigned long long get_ppl_sector(struct intel_super *super, int dev_idx) { struct imsm_dev *dev = get_imsm_dev(super, dev_idx); @@ -3416,7 +3483,12 @@ static void getinfo_super_imsm_volume(struct supertype *st, struct mdinfo *info, } else if (info->array.level <= 0) { info->consistency_policy = CONSISTENCY_POLICY_NONE; } else { - info->consistency_policy = CONSISTENCY_POLICY_RESYNC; + if (dev->rwh_policy == RWH_BITMAP) { + info->bitmap_offset = get_bitmap_sector(super, super->current_vol); + info->consistency_policy = CONSISTENCY_POLICY_BITMAP; + } else { + info->consistency_policy = CONSISTENCY_POLICY_RESYNC; + } } info->reshape_progress = 0; @@ -6454,6 +6526,60 @@ static int write_init_ppl_imsm_all(struct supertype *st, struct mdinfo *info) return ret; } +/******************************************************************************* + * Function: write_init_bitmap_imsm_vol + * Description: Write a bitmap header and prepares the area for the bitmap. + * Parameters: + * st : supertype information + * vol_idx : the volume index to use + * + * Returns: + * 0 : success + * -1 : fail + ******************************************************************************/ +static int write_init_bitmap_imsm_vol(struct supertype *st, int vol_idx) +{ + struct intel_super *super = st->sb; + int prev_current_vol = super->current_vol; + struct dl *d; + int ret = 0; + + super->current_vol = vol_idx; + for (d = super->disks; d; d = d->next) { + if (d->index < 0 || is_failed(&d->disk)) + continue; + ret = st->ss->write_bitmap(st, d->fd, NoUpdate); + if (ret) + break; + } + super->current_vol = prev_current_vol; + return ret; +} + +/******************************************************************************* + * Function: write_init_bitmap_imsm_all + * Description: Write a bitmap header and prepares the area for the bitmap. + * Operation is executed for volumes with CONSISTENCY_POLICY_BITMAP. + * Parameters: + * st : supertype information + * info : info about the volume where the bitmap should be written + * vol_idx : the volume index to use + * + * Returns: + * 0 : success + * -1 : fail + ******************************************************************************/ +static int write_init_bitmap_imsm_all(struct supertype *st, struct mdinfo *info, + int vol_idx) +{ + int ret = 0; + + if (info && (info->consistency_policy == CONSISTENCY_POLICY_BITMAP)) + ret = write_init_bitmap_imsm_vol(st, vol_idx); + + return ret; +} + static int write_init_super_imsm(struct supertype *st) { struct intel_super *super = st->sb; @@ -6477,7 +6603,10 @@ static int write_init_super_imsm(struct supertype *st) */ rv = mgmt_disk(st); } else { + /* adding the second volume to the array */ rv = write_init_ppl_imsm_all(st, &info); + if (!rv) + rv = write_init_bitmap_imsm_all(st, &info, current_vol); if (!rv) rv = create_array(st, current_vol); } @@ -6485,8 +6614,12 @@ static int write_init_super_imsm(struct supertype *st) struct dl *d; for (d = super->disks; d; d = d->next) Kill(d->devname, NULL, 0, -1, 1); - if (current_vol >= 0) + if (current_vol >= 0) { rv = write_init_ppl_imsm_all(st, &info); + if (!rv) + rv = write_init_bitmap_imsm_all(st, &info, current_vol); + } + if (!rv) rv = write_super_imsm(st, 1); } @@ -12209,6 +12342,483 @@ abort: return ret_val; } +/******************************************************************************* + * Function: calculate_bitmap_min_chunksize + * Description: Calculates the minimal valid bitmap chunk size + * Parameters: + * max_bits : indicate how many bits can be used for the bitmap + * data_area_size : the size of the data area covered by the bitmap + * + * Returns: + * The bitmap chunk size + ******************************************************************************/ +static unsigned long long +calculate_bitmap_min_chunksize(unsigned long long max_bits, + unsigned long long data_area_size) +{ + unsigned long long min_chunk = + 4096; /* sub-page chunks don't work yet.. */ + unsigned long long bits = data_area_size / min_chunk + 1; + + while (bits > max_bits) { + min_chunk *= 2; + bits = (bits + 1) / 2; + } + return min_chunk; +} + +/******************************************************************************* + * Function: calculate_bitmap_chunksize + * Description: Calculates the bitmap chunk size for the given device + * Parameters: + * st : supertype information + * dev : device for the bitmap + * + * Returns: + * The bitmap chunk size + ******************************************************************************/ +static unsigned long long calculate_bitmap_chunksize(struct supertype *st, + struct imsm_dev *dev) +{ + struct intel_super *super = st->sb; + unsigned long long min_chunksize; + unsigned long long result = IMSM_DEFAULT_BITMAP_CHUNKSIZE; + size_t dev_size = imsm_dev_size(dev); + + min_chunksize = calculate_bitmap_min_chunksize( + IMSM_BITMAP_AREA_SIZE * super->sector_size, dev_size); + + if (result < min_chunksize) + result = min_chunksize; + + return result; +} + +/******************************************************************************* + * Function: init_bitmap_header + * Description: Initialize the bitmap header structure + * Parameters: + * st : supertype information + * bms : bitmap header struct to initialize + * dev : device for the bitmap + * + * Returns: + * 0 : success + * -1 : fail + ******************************************************************************/ +static int init_bitmap_header(struct supertype *st, struct bitmap_super_s *bms, + struct imsm_dev *dev) +{ + int vol_uuid[4]; + + if (!bms || !dev) + return -1; + + bms->magic = __cpu_to_le32(BITMAP_MAGIC); + bms->version = __cpu_to_le32(BITMAP_MAJOR_HI); + bms->daemon_sleep = __cpu_to_le32(IMSM_DEFAULT_BITMAP_DAEMON_SLEEP); + bms->sync_size = __cpu_to_le64(IMSM_BITMAP_AREA_SIZE); + bms->write_behind = __cpu_to_le32(0); + + uuid_from_super_imsm(st, vol_uuid); + memcpy(bms->uuid, vol_uuid, 16); + + bms->chunksize = calculate_bitmap_chunksize(st, dev); + + return 0; +} + +/******************************************************************************* + * Function: validate_internal_bitmap_for_drive + * Description: Verify if the bitmap header for a given drive. + * Parameters: + * st : supertype information + * offset : The offset from the beginning of the drive where to look for + * the bitmap header. + * d : the drive info + * + * Returns: + * 0 : success + * -1 : fail + ******************************************************************************/ +static int validate_internal_bitmap_for_drive(struct supertype *st, + unsigned long long offset, + struct dl *d) +{ + struct intel_super *super = st->sb; + int ret = -1; + int vol_uuid[4]; + bitmap_super_t *bms; + int fd; + + if (!d) + return -1; + + void *read_buf; + + if (posix_memalign(&read_buf, MAX_SECTOR_SIZE, IMSM_BITMAP_HEADER_SIZE)) + return -1; + + fd = d->fd; + if (fd < 0) { + fd = open(d->devname, O_RDONLY, 0); + if (fd < 0) { + dprintf("cannot open the device %s\n", d->devname); + goto abort; + } + } + + if (lseek64(fd, offset * super->sector_size, SEEK_SET) < 0) + goto abort; + if (read(fd, read_buf, IMSM_BITMAP_HEADER_SIZE) != + IMSM_BITMAP_HEADER_SIZE) + goto abort; + + uuid_from_super_imsm(st, vol_uuid); + + bms = read_buf; + if ((bms->magic != __cpu_to_le32(BITMAP_MAGIC)) || + (bms->version != __cpu_to_le32(BITMAP_MAJOR_HI)) || + (!same_uuid((int *)bms->uuid, vol_uuid, st->ss->swapuuid))) { + dprintf("wrong bitmap header detected\n"); + goto abort; + } + + ret = 0; +abort: + if ((d->fd < 0) && (fd >= 0)) + close(fd); + if (read_buf) + free(read_buf); + + return ret; +} + +/******************************************************************************* + * Function: validate_internal_bitmap_imsm + * Description: Verify if the bitmap header is in place and with proper data. + * Parameters: + * st : supertype information + * + * Returns: + * 0 : success or device w/o RWH_BITMAP + * -1 : fail + ******************************************************************************/ +static int validate_internal_bitmap_imsm(struct supertype *st) +{ + struct intel_super *super = st->sb; + struct imsm_dev *dev = get_imsm_dev(super, super->current_vol); + unsigned long long offset; + struct dl *d; + + if (!dev) + return -1; + + if (dev->rwh_policy != RWH_BITMAP) + return 0; + + offset = get_bitmap_header_sector(super, super->current_vol); + for (d = super->disks; d; d = d->next) { + if (d->index < 0 || is_failed(&d->disk)) + continue; + + if (validate_internal_bitmap_for_drive(st, offset, d)) { + pr_err("imsm: bitmap validation failed\n"); + return -1; + } + } + return 0; +} + +/******************************************************************************* + * Function: add_internal_bitmap_imsm + * Description: Mark the volume to use the bitmap and updates the chunk size value. + * Parameters: + * st : supertype information + * chunkp : bitmap chunk size + * delay : not used for imsm + * write_behind : not used for imsm + * size : not used for imsm + * may_change : not used for imsm + * amajor : not used for imsm + * + * Returns: + * 0 : success + * -1 : fail + ******************************************************************************/ +static int add_internal_bitmap_imsm(struct supertype *st, int *chunkp, + int delay, int write_behind, + unsigned long long size, int may_change, + int amajor) +{ + struct intel_super *super = st->sb; + int vol_idx = super->current_vol; + struct imsm_dev *dev; + + if (!super->devlist || vol_idx == -1 || !chunkp) + return -1; + + dev = get_imsm_dev(super, vol_idx); + + if (!dev) { + dprintf("cannot find the device for volume index %d\n", + vol_idx); + return -1; + } + dev->rwh_policy = RWH_BITMAP; + + *chunkp = calculate_bitmap_chunksize(st, dev); + + return 0; +} + +/******************************************************************************* + * Function: locate_bitmap_imsm + * Description: Seek 'fd' to start of write-intent-bitmap. + * Parameters: + * st : supertype information + * fd : file descriptor for the device + * node_num : not used for imsm + * + * Returns: + * 0 : success + * -1 : fail + ******************************************************************************/ +static int locate_bitmap_imsm(struct supertype *st, int fd, int node_num) +{ + struct intel_super *super = st->sb; + unsigned long long offset; + int vol_idx = super->current_vol; + + if (!super->devlist || vol_idx == -1) + return -1; + + offset = get_bitmap_header_sector(super, super->current_vol); + dprintf("bitmap header offset is %llu\n", offset); + + lseek64(fd, offset << 9, 0); + + return 0; +} + +/******************************************************************************* + * Function: write_init_bitmap_imsm + * Description: Write a bitmap header and prepares the area for the bitmap. + * Parameters: + * st : supertype information + * fd : file descriptor for the device + * update : not used for imsm + * + * Returns: + * 0 : success + * -1 : fail + ******************************************************************************/ +static int write_init_bitmap_imsm(struct supertype *st, int fd, + enum bitmap_update update) +{ + struct intel_super *super = st->sb; + int vol_idx = super->current_vol; + int ret = 0; + unsigned long long offset; + bitmap_super_t bms = { 0 }; + size_t written = 0; + size_t to_write; + ssize_t rv_num; + void *buf; + + if (!super->devlist || !super->sector_size || vol_idx == -1) + return -1; + + struct imsm_dev *dev = get_imsm_dev(super, vol_idx); + + /* first clear the space for bitmap header */ + unsigned long long bitmap_area_start = + get_bitmap_header_sector(super, vol_idx); + + dprintf("zeroing area start (%llu) and size (%u)\n", bitmap_area_start, + IMSM_BITMAP_AND_HEADER_SIZE / super->sector_size); + if (zero_disk_range(fd, bitmap_area_start, + IMSM_BITMAP_HEADER_SIZE / super->sector_size)) { + pr_err("imsm: cannot zeroing the space for the bitmap\n"); + return -1; + } + + /* The bitmap area should be filled with "1"s to perform initial + * synchronization. + */ + if (posix_memalign(&buf, MAX_SECTOR_SIZE, MAX_SECTOR_SIZE)) + return -1; + memset(buf, 0xFF, MAX_SECTOR_SIZE); + offset = get_bitmap_sector(super, vol_idx); + lseek64(fd, offset << 9, 0); + while (written < IMSM_BITMAP_AREA_SIZE) { + to_write = IMSM_BITMAP_AREA_SIZE - written; + if (to_write > MAX_SECTOR_SIZE) + to_write = MAX_SECTOR_SIZE; + rv_num = write(fd, buf, MAX_SECTOR_SIZE); + if (rv_num != MAX_SECTOR_SIZE) { + ret = -1; + dprintf("cannot initialize bitmap area\n"); + goto abort; + } + written += rv_num; + } + + /* write a bitmap header */ + init_bitmap_header(st, &bms, dev); + memset(buf, 0, MAX_SECTOR_SIZE); + memcpy(buf, &bms, sizeof(bitmap_super_t)); + if (locate_bitmap_imsm(st, fd, 0)) { + ret = -1; + dprintf("cannot locate the bitmap\n"); + goto abort; + } + if (write(fd, buf, MAX_SECTOR_SIZE) != MAX_SECTOR_SIZE) { + ret = -1; + dprintf("cannot write the bitmap header\n"); + goto abort; + } + fsync(fd); + +abort: + free(buf); + + return ret; +} + +/******************************************************************************* + * Function: is_vol_to_setup_bitmap + * Description: Checks if a bitmap should be activated on the dev. + * Parameters: + * info : info about the volume to setup the bitmap + * dev : the device to check against bitmap creation + * + * Returns: + * 0 : bitmap should be set up on the device + * -1 : otherwise + ******************************************************************************/ +static int is_vol_to_setup_bitmap(struct mdinfo *info, struct imsm_dev *dev) +{ + if (!dev || !info) + return -1; + + if ((strcmp((char *)dev->volume, info->name) == 0) && + (dev->rwh_policy == RWH_BITMAP)) + return -1; + + return 0; +} + +/******************************************************************************* + * Function: set_bitmap_sysfs + * Description: Set the sysfs atributes of a given volume to activate the bitmap. + * Parameters: + * info : info about the volume where the bitmap should be setup + * chunksize : bitmap chunk size + * location : location of the bitmap + * + * Returns: + * 0 : success + * -1 : fail + ******************************************************************************/ +static int set_bitmap_sysfs(struct mdinfo *info, unsigned long long chunksize, + char *location) +{ + /* The bitmap/metadata is set to external to allow changing of value for + * bitmap/location. When external is used, the kernel will treat an offset + * related to the device's first lba (in opposition to the "internal" case + * when this value is related to the beginning of the superblock). + */ + if (sysfs_set_str(info, NULL, "bitmap/metadata", "external")) { + dprintf("failed to set bitmap/metadata\n"); + return -1; + } + + /* It can only be changed when no bitmap is active. + * Should be bigger than 512 and must be power of 2. + * It is expecting the value in bytes. + */ + if (sysfs_set_num(info, NULL, "bitmap/chunksize", + __cpu_to_le32(chunksize))) { + dprintf("failed to set bitmap/chunksize\n"); + return -1; + } + + /* It is expecting the value in sectors. */ + if (sysfs_set_num(info, NULL, "bitmap/space", + __cpu_to_le64(IMSM_BITMAP_AREA_SIZE))) { + dprintf("failed to set bitmap/space\n"); + return -1; + } + + /* Determines the delay between the bitmap updates. + * It is expecting the value in seconds. + */ + if (sysfs_set_num(info, NULL, "bitmap/time_base", + __cpu_to_le64(IMSM_DEFAULT_BITMAP_DAEMON_SLEEP))) { + dprintf("failed to set bitmap/time_base\n"); + return -1; + } + + /* It is expecting the value in sectors with a sign at the beginning. */ + if (sysfs_set_str(info, NULL, "bitmap/location", location)) { + dprintf("failed to set bitmap/location\n"); + return -1; + } + + return 0; +} + +/******************************************************************************* + * Function: set_bitmap_imsm + * Description: Setup the bitmap for the given volume + * Parameters: + * st : supertype information + * info : info about the volume where the bitmap should be setup + * + * Returns: + * 0 : success + * -1 : fail + ******************************************************************************/ +static int set_bitmap_imsm(struct supertype *st, struct mdinfo *info) +{ + struct intel_super *super = st->sb; + int prev_current_vol = super->current_vol; + struct imsm_dev *dev; + int ret = -1; + char location[16] = ""; + unsigned long long chunksize; + struct intel_dev *dev_it; + + for (dev_it = super->devlist; dev_it; dev_it = dev_it->next) { + super->current_vol = dev_it->index; + dev = get_imsm_dev(super, super->current_vol); + + if (is_vol_to_setup_bitmap(info, dev)) { + if (validate_internal_bitmap_imsm(st)) { + dprintf("bitmap header validation failed\n"); + goto abort; + } + + chunksize = calculate_bitmap_chunksize(st, dev); + dprintf("chunk size is %llu\n", chunksize); + + snprintf(location, sizeof(location), "+%llu", + get_bitmap_sector(super, super->current_vol)); + dprintf("bitmap offset is %s\n", location); + + if (set_bitmap_sysfs(info, chunksize, location)) { + dprintf("cannot setup the bitmap\n"); + goto abort; + } + } + } + ret = 0; +abort: + super->current_vol = prev_current_vol; + return ret; +} + struct superswitch super_imsm = { .examine_super = examine_super_imsm, .brief_examine_super = brief_examine_super_imsm, @@ -12250,6 +12860,11 @@ struct superswitch super_imsm = { .container_content = container_content_imsm, .validate_container = validate_container_imsm, + .add_internal_bitmap = add_internal_bitmap_imsm, + .locate_bitmap = locate_bitmap_imsm, + .write_bitmap = write_init_bitmap_imsm, + .set_bitmap = set_bitmap_imsm, + .write_init_ppl = write_init_ppl_imsm, .validate_ppl = validate_ppl_imsm, -- cgit v1.2.1