/** * Invalidate lsid inside ring buffer. */ bool invalidate_lsid(struct walb_dev *wdev, u64 lsid) { struct sector_data *zero_sector; struct walb_super_sector *super; u64 off; bool ret; ASSERT(lsid != INVALID_LSID); zero_sector = sector_alloc( wdev->physical_bs, GFP_KERNEL | __GFP_ZERO); if (!zero_sector) { LOGe("sector allocation failed.\n"); return false; } spin_lock(&wdev->lsuper0_lock); super = get_super_sector(wdev->lsuper0); off = get_offset_of_lsid_2(super, lsid); spin_unlock(&wdev->lsuper0_lock); ret = sector_io(WRITE, wdev->ldev, off, zero_sector); if (!ret) { LOGe("sector write failed.\n"); iocore_set_readonly(wdev); } sector_free(zero_sector); return ret; }
/** * Check logpack of the given lsid exists. * * @lsid lsid to check. * * @return Non-zero if valid, or 0. */ int walb_check_lsid_valid(struct walb_dev *wdev, u64 lsid) { struct sector_data *sect; struct walb_logpack_header *logh; u64 off; ASSERT(wdev); sect = sector_alloc(wdev->physical_bs, GFP_NOIO); if (!sect) { LOGe("walb_check_lsid_valid: alloc sector failed.\n"); goto error0; } ASSERT(is_same_size_sector(sect, wdev->lsuper0)); logh = get_logpack_header(sect); spin_lock(&wdev->lsuper0_lock); off = get_offset_of_lsid_2(get_super_sector(wdev->lsuper0), lsid); spin_unlock(&wdev->lsuper0_lock); if (!sector_io(READ, wdev->ldev, off, sect)) { LOGe("walb_check_lsid_valid: read sector failed.\n"); goto error1; } /* Check valid logpack header. */ if (!is_valid_logpack_header_with_checksum( logh, wdev->physical_bs, wdev->log_checksum_salt)) { goto error1; } /* Check lsid. */ if (logh->logpack_lsid != lsid) { goto error1; } sector_free(sect); return 1; error1: sector_free(sect); error0: return 0; }
/** * Write invalid logpack header. * This just fill zero. * * @fd file descriptor of data device (opened). * @super_sect super sector. * @lsid lsid to invalidate. * * RETURN: * true in success, or false. */ bool write_invalid_logpack_header( int fd, const struct sector_data *super_sect, u64 lsid) { struct sector_data *sect; bool ret; const struct walb_super_sector *super = get_super_sector_const(super_sect); u64 off = get_offset_of_lsid_2(super, lsid); sect = sector_alloc_zero(super->physical_bs); if (!sect) { LOGe("Allocate sector failed.\n"); return false; } ret = sector_write(fd, off, sect); if (!ret) { LOGe("Write sector %"PRIu64" for lsid %"PRIu64" failed.\n", off, lsid); } sector_free(sect); return ret; }
/** * Read logpack data. * Padding area will be also read. * * @fd file descriptor of log device. * @super super sector. * @logh logpack header. * @salt checksum salt. * @sect_ary sector array. * * RETURN: * index of invalid record found at first. * logh->n_records if the whole logpack is valid. */ unsigned int read_logpack_data_from_wldev( int fd, const struct walb_super_sector* super, const struct walb_logpack_header* logh, u32 salt, struct sector_data_array *sect_ary) { const int lbs = super->logical_bs; const int pbs = super->physical_bs; int i; int total_pb; ASSERT(lbs == LOGICAL_BLOCK_SIZE); ASSERT_PBS(pbs); if (logh->total_io_size > sect_ary->size) { LOGe("buffer size is not enough.\n"); return false; } total_pb = 0; for (i = 0; i < logh->n_records; i++) { u64 log_off; u32 log_lb, log_pb; if (test_bit_u32(LOG_RECORD_DISCARD, &logh->record[i].flags)) { continue; } log_lb = logh->record[i].io_size; log_pb = capacity_pb(pbs, log_lb); log_off = get_offset_of_lsid_2 (super, logh->record[i].lsid); LOGd_("lsid: %"PRIu64" log_off: %"PRIu64"\n", logh->record[i].lsid, log_off); /* Read data for the log record. */ if (!sector_array_pread( fd, log_off, sect_ary, total_pb, log_pb)) { LOGe("read sectors failed.\n"); return i; } if (test_bit_u32(LOG_RECORD_PADDING, &logh->record[i].flags)) { total_pb += log_pb; continue; } /* Confirm checksum */ u32 csum = sector_array_checksum( sect_ary, total_pb * pbs, log_lb * lbs, salt); if (csum != logh->record[i].checksum) { LOGe("log header checksum is invalid. %08x %08x\n", csum, logh->record[i].checksum); return i; } total_pb += log_pb; } ASSERT(i == logh->n_records); return logh->n_records; }
/** * Clear log and detect resize of log device. * * @wdev walb dev. * @ctl ioctl data. * RETURN: * 0 in success, or -EFAULT. */ static int ioctl_wdev_clear_log(struct walb_dev *wdev, struct walb_ctl *ctl) { u64 new_ldev_size, old_ldev_size; u8 new_uuid[UUID_SIZE], old_uuid[UUID_SIZE]; unsigned int pbs = wdev->physical_bs; bool is_grown = false; struct walb_super_sector *super; u64 lsid0_off; struct lsid_set lsids; u64 old_ring_buffer_size; u32 new_salt; ASSERT(ctl->command == WALB_IOCTL_CLEAR_LOG); LOGn("WALB_IOCTL_CLEAR_LOG.\n"); /* Freeze iocore and checkpointing. */ iocore_freeze(wdev); stop_checkpointing(&wdev->cpd); /* Get old/new log device size. */ old_ldev_size = wdev->ldev_size; new_ldev_size = wdev->ldev->bd_part->nr_sects; if (old_ldev_size > new_ldev_size) { LOGe("Log device shrink not supported.\n"); goto error0; } /* Backup variables. */ old_ring_buffer_size = wdev->ring_buffer_size; backup_lsid_set(wdev, &lsids); /* Initialize lsid(s). */ spin_lock(&wdev->lsid_lock); wdev->lsids.latest = 0; wdev->lsids.flush = 0; wdev->lsids.completed = 0; wdev->lsids.permanent = 0; wdev->lsids.written = 0; wdev->lsids.prev_written = 0; wdev->lsids.oldest = 0; spin_unlock(&wdev->lsid_lock); /* Grow the walblog device. */ if (old_ldev_size < new_ldev_size) { LOGn("Detect log device size change.\n"); /* Grow the disk. */ is_grown = true; if (!resize_disk(wdev->log_gd, new_ldev_size)) { LOGe("grow disk failed.\n"); iocore_set_readonly(wdev); goto error1; } LOGn("Grown log device size from %"PRIu64" to %"PRIu64".\n", old_ldev_size, new_ldev_size); wdev->ldev_size = new_ldev_size; /* Recalculate ring buffer size. */ wdev->ring_buffer_size = addr_pb(pbs, new_ldev_size) - get_ring_buffer_offset(pbs); } /* Generate new uuid and salt. */ get_random_bytes(new_uuid, 16); get_random_bytes(&new_salt, sizeof(new_salt)); wdev->log_checksum_salt = new_salt; /* Update superblock image. */ spin_lock(&wdev->lsuper0_lock); super = get_super_sector(wdev->lsuper0); memcpy(old_uuid, super->uuid, UUID_SIZE); memcpy(super->uuid, new_uuid, UUID_SIZE); super->ring_buffer_size = wdev->ring_buffer_size; super->log_checksum_salt = new_salt; /* super->metadata_size; */ lsid0_off = get_offset_of_lsid_2(super, 0); spin_unlock(&wdev->lsuper0_lock); /* Sync super sector. */ if (!walb_sync_super_block(wdev)) { LOGe("sync superblock failed.\n"); iocore_set_readonly(wdev); goto error2; } /* Invalidate first logpack */ if (!invalidate_lsid(wdev, 0)) { LOGe("invalidate lsid 0 failed.\n"); iocore_set_readonly(wdev); goto error2; } /* Clear log overflow. */ iocore_clear_log_overflow(wdev); /* Melt iocore and checkpointing. */ start_checkpointing(&wdev->cpd); iocore_melt(wdev); return 0; error2: restore_lsid_set(wdev, &lsids); wdev->ring_buffer_size = old_ring_buffer_size; #if 0 wdev->ldev_size = old_ldev_size; if (!resize_disk(wdev->log_gd, old_ldev_size)) { LOGe("resize_disk to shrink failed.\n"); } #endif error1: start_checkpointing(&wdev->cpd); iocore_melt(wdev); error0: return -EFAULT; }