/** * Freeze if melted and enqueue a melting work if required. * * @wdev walb device. * @timeout_sec timeout to melt the device [sec]. * Specify 0 for no timeout. * * RETURN: * true in success, or false (due to race condition). */ bool freeze_if_melted(struct walb_dev *wdev, u32 timeout_sec) { unsigned int minor; int ret; ASSERT(wdev); minor = MINOR(wdev->devt); /* Freeze and enqueue a melt work if required. */ mutex_lock(&wdev->freeze_lock); switch (wdev->freeze_state) { case FRZ_MELTED: /* Freeze iocore and checkpointing. */ LOGn("Freeze walb device minor %u.\n", minor); iocore_freeze(wdev); stop_checkpointing(&wdev->cpd); wdev->freeze_state = FRZ_FREEZED; break; case FRZ_FREEZED: /* Do nothing. */ LOGn("Already frozen minor %u.\n", minor); break; case FRZ_FREEZED_WITH_TIMEOUT: LOGe("Race condition occured.\n"); mutex_unlock(&wdev->freeze_lock); return false; default: BUG(); } ASSERT(wdev->freeze_state == FRZ_FREEZED); if (timeout_sec > 0) { LOGn("(Re)set frozen timeout to %"PRIu32" seconds.\n", timeout_sec); INIT_DELAYED_WORK(&wdev->freeze_dwork, task_melt); ret = queue_delayed_work( wq_misc_, &wdev->freeze_dwork, msecs_to_jiffies(timeout_sec * 1000)); ASSERT(ret); wdev->freeze_state = FRZ_FREEZED_WITH_TIMEOUT; } ASSERT(wdev->freeze_state != FRZ_MELTED); mutex_unlock(&wdev->freeze_lock); return true; }
/** * 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; }