/** * Melt a frozen device. */ void task_melt(struct work_struct *work) { struct delayed_work *dwork = container_of(work, struct delayed_work, work); struct walb_dev *wdev = container_of(dwork, struct walb_dev, freeze_dwork); ASSERT(wdev); mutex_lock(&wdev->freeze_lock); switch (wdev->freeze_state) { case FRZ_MELTED: LOGn("FRZ_MELTED minor %u.\n", MINOR(wdev->devt)); break; case FRZ_FREEZED: LOGn("FRZ_FREEZED minor %u.\n", MINOR(wdev->devt)); break; case FRZ_FREEZED_WITH_TIMEOUT: LOGn("Melt walb device minor %u.\n", MINOR(wdev->devt)); start_checkpointing(&wdev->cpd); iocore_melt(wdev); wdev->freeze_state = FRZ_MELTED; break; default: BUG(); } mutex_unlock(&wdev->freeze_lock); }
/** * Set checkpoint interval. * * @cpd checkpoint data. * @val new checkpoint interval [ms]. */ void set_checkpoint_interval(struct checkpoint_data *cpd, u32 interval) { down_write(&cpd->lock); cpd->interval = interval; up_write(&cpd->lock); stop_checkpointing(cpd); start_checkpointing(cpd); }
/** * Take a checkpoint immedicately. * * @wdev walb dev. * @ctl ioctl data. * RETURN: * 0 in success, or -EFAULT. */ static int ioctl_wdev_take_checkpoint(struct walb_dev *wdev, struct walb_ctl *ctl) { bool ret; LOGn("WALB_IOCTL_TAKE_CHECKPOINT\n"); ASSERT(ctl->command == WALB_IOCTL_TAKE_CHECKPOINT); stop_checkpointing(&wdev->cpd); #ifdef WALB_DEBUG down_write(&wdev->cpd.lock); ASSERT(wdev->cpd.state == CP_STOPPED); up_write(&wdev->cpd.lock); #endif ret = take_checkpoint(&wdev->cpd); if (!ret) { atomic_set(&wdev->is_read_only, 1); LOGe("superblock sync failed.\n"); return -EFAULT; } start_checkpointing(&wdev->cpd); return 0; }
/** * Melt a device if frozen. * * RETURN: * true in success, or false (due to race condition). */ bool melt_if_frozen( struct walb_dev *wdev, bool restarts_checkpointing) { unsigned int minor; ASSERT(wdev); minor = MINOR(wdev->devt); cancel_melt_work(wdev); /* Melt the device if required. */ mutex_lock(&wdev->freeze_lock); switch (wdev->freeze_state) { case FRZ_MELTED: /* Do nothing. */ LOGn("Already melted minor %u\n", minor); break; case FRZ_FREEZED: /* Melt. */ LOGn("Melt walb device minor %u.\n", minor); if (restarts_checkpointing) { start_checkpointing(&wdev->cpd); } iocore_melt(wdev); wdev->freeze_state = FRZ_MELTED; break; case FRZ_FREEZED_WITH_TIMEOUT: /* Race condition. */ LOGe("Race condition occurred.\n"); mutex_unlock(&wdev->freeze_lock); return false; default: BUG(); } 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; }