Blame source/vdo/base/vdoResize.c

Packit Service 75d76b
/*
Packit Service 75d76b
 * Copyright (c) 2020 Red Hat, Inc.
Packit Service 75d76b
 *
Packit Service 75d76b
 * This program is free software; you can redistribute it and/or
Packit Service 75d76b
 * modify it under the terms of the GNU General Public License
Packit Service 75d76b
 * as published by the Free Software Foundation; either version 2
Packit Service 75d76b
 * of the License, or (at your option) any later version.
Packit Service 75d76b
 * 
Packit Service 75d76b
 * This program is distributed in the hope that it will be useful,
Packit Service 75d76b
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 75d76b
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit Service 75d76b
 * GNU General Public License for more details.
Packit Service 75d76b
 * 
Packit Service 75d76b
 * You should have received a copy of the GNU General Public License
Packit Service 75d76b
 * along with this program; if not, write to the Free Software
Packit Service 75d76b
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
Packit Service 75d76b
 * 02110-1301, USA. 
Packit Service 75d76b
 *
Packit Service 75d76b
 * $Id: //eng/vdo-releases/aluminum/src/c++/vdo/base/vdoResize.c#15 $
Packit Service 75d76b
 */
Packit Service 75d76b
Packit Service 75d76b
#include "vdoResize.h"
Packit Service 75d76b
Packit Service 75d76b
#include "logger.h"
Packit Service 75d76b
Packit Service 75d76b
#include "adminCompletion.h"
Packit Service 75d76b
#include "completion.h"
Packit Service 75d76b
#include "recoveryJournal.h"
Packit Service 75d76b
#include "slabDepot.h"
Packit Service 75d76b
#include "slabSummary.h"
Packit Service 75d76b
#include "vdoInternal.h"
Packit Service 75d76b
#include "vdoLayout.h"
Packit Service 75d76b
Packit Service 75d76b
typedef enum {
Packit Service 75d76b
  GROW_PHYSICAL_PHASE_START = 0,
Packit Service 75d76b
  GROW_PHYSICAL_PHASE_COPY_SUMMARY,
Packit Service 75d76b
  GROW_PHYSICAL_PHASE_UPDATE_COMPONENTS,
Packit Service 75d76b
  GROW_PHYSICAL_PHASE_USE_NEW_SLABS,
Packit Service 75d76b
  GROW_PHYSICAL_PHASE_END,
Packit Service 75d76b
  GROW_PHYSICAL_PHASE_ERROR,
Packit Service 75d76b
} GrowPhysicalPhase;
Packit Service 75d76b
Packit Service 75d76b
static const char *GROW_PHYSICAL_PHASE_NAMES[] = {
Packit Service 75d76b
  "GROW_PHYSICAL_PHASE_START",
Packit Service 75d76b
  "GROW_PHYSICAL_PHASE_COPY_SUMMARY",
Packit Service 75d76b
  "GROW_PHYSICAL_PHASE_UPDATE_COMPONENTS",
Packit Service 75d76b
  "GROW_PHYSICAL_PHASE_USE_NEW_SLABS",
Packit Service 75d76b
  "GROW_PHYSICAL_PHASE_END",
Packit Service 75d76b
  "GROW_PHYSICAL_PHASE_ERROR",
Packit Service 75d76b
};
Packit Service 75d76b
Packit Service 75d76b
/**
Packit Service 75d76b
 * Implements ThreadIDGetterForPhase.
Packit Service 75d76b
 **/
Packit Service 75d76b
__attribute__((warn_unused_result))
Packit Service 75d76b
static ThreadID getThreadIDForPhase(AdminCompletion *adminCompletion)
Packit Service 75d76b
{
Packit Service 75d76b
  return getAdminThread(getThreadConfig(adminCompletion->completion.parent));
Packit Service 75d76b
}
Packit Service 75d76b
Packit Service 75d76b
/**
Packit Service 75d76b
 * Callback to initiate a grow physical, registered in performGrowPhysical().
Packit Service 75d76b
 *
Packit Service 75d76b
 * @param completion  The sub-task completion
Packit Service 75d76b
 **/
Packit Service 75d76b
static void growPhysicalCallback(VDOCompletion *completion)
Packit Service 75d76b
{
Packit Service 75d76b
  AdminCompletion *adminCompletion = adminCompletionFromSubTask(completion);
Packit Service 75d76b
  assertAdminOperationType(adminCompletion, ADMIN_OPERATION_GROW_PHYSICAL);
Packit Service 75d76b
  assertAdminPhaseThread(adminCompletion, __func__, GROW_PHYSICAL_PHASE_NAMES);
Packit Service 75d76b
Packit Service 75d76b
  VDO *vdo = adminCompletion->completion.parent;
Packit Service 75d76b
  switch (adminCompletion->phase++) {
Packit Service 75d76b
  case GROW_PHYSICAL_PHASE_START:
Packit Service 75d76b
    if (isReadOnly(vdo->readOnlyNotifier)) {
Packit Service 75d76b
      logErrorWithStringError(VDO_READ_ONLY,
Packit Service 75d76b
                              "Can't grow physical size of a read-only VDO");
Packit Service 75d76b
      setCompletionResult(resetAdminSubTask(completion), VDO_READ_ONLY);
Packit Service 75d76b
      break;
Packit Service 75d76b
    }
Packit Service 75d76b
Packit Service 75d76b
    if (startOperationWithWaiter(&vdo->adminState,
Packit Service 75d76b
                                 ADMIN_STATE_SUSPENDED_OPERATION,
Packit Service 75d76b
                                 &adminCompletion->completion, NULL)) {
Packit Service 75d76b
      // Copy the journal into the new layout.
Packit Service 75d76b
      copyPartition(vdo->layout, RECOVERY_JOURNAL_PARTITION,
Packit Service 75d76b
                    resetAdminSubTask(completion));
Packit Service 75d76b
    }
Packit Service 75d76b
    return;
Packit Service 75d76b
Packit Service 75d76b
  case GROW_PHYSICAL_PHASE_COPY_SUMMARY:
Packit Service 75d76b
    copyPartition(vdo->layout, SLAB_SUMMARY_PARTITION,
Packit Service 75d76b
                  resetAdminSubTask(completion));
Packit Service 75d76b
    return;
Packit Service 75d76b
Packit Service 75d76b
  case GROW_PHYSICAL_PHASE_UPDATE_COMPONENTS:
Packit Service 75d76b
    vdo->config.physicalBlocks = growVDOLayout(vdo->layout);
Packit Service 75d76b
    updateSlabDepotSize(vdo->depot);
Packit Service 75d76b
    saveVDOComponentsAsync(vdo, resetAdminSubTask(completion));
Packit Service 75d76b
    return;
Packit Service 75d76b
Packit Service 75d76b
  case GROW_PHYSICAL_PHASE_USE_NEW_SLABS:
Packit Service 75d76b
    useNewSlabs(vdo->depot, resetAdminSubTask(completion));
Packit Service 75d76b
    return;
Packit Service 75d76b
Packit Service 75d76b
  case GROW_PHYSICAL_PHASE_END:
Packit Service 75d76b
    setSlabSummaryOrigin(getSlabSummary(vdo->depot),
Packit Service 75d76b
                         getVDOPartition(vdo->layout, SLAB_SUMMARY_PARTITION));
Packit Service 75d76b
    setRecoveryJournalPartition(vdo->recoveryJournal,
Packit Service 75d76b
                                getVDOPartition(vdo->layout,
Packit Service 75d76b
                                                RECOVERY_JOURNAL_PARTITION));
Packit Service 75d76b
    break;
Packit Service 75d76b
Packit Service 75d76b
  case GROW_PHYSICAL_PHASE_ERROR:
Packit Service 75d76b
    enterReadOnlyMode(vdo->readOnlyNotifier, completion->result);
Packit Service 75d76b
    break;
Packit Service 75d76b
Packit Service 75d76b
  default:
Packit Service 75d76b
    setCompletionResult(resetAdminSubTask(completion), UDS_BAD_STATE);
Packit Service 75d76b
  }
Packit Service 75d76b
Packit Service 75d76b
  finishVDOLayoutGrowth(vdo->layout);
Packit Service 75d76b
  finishOperationWithResult(&vdo->adminState, completion->result);
Packit Service 75d76b
}
Packit Service 75d76b
Packit Service 75d76b
/**
Packit Service 75d76b
 * Handle an error during the grow physical process.
Packit Service 75d76b
 *
Packit Service 75d76b
 * @param completion  The sub-task completion
Packit Service 75d76b
 **/
Packit Service 75d76b
static void handleGrowthError(VDOCompletion *completion)
Packit Service 75d76b
{
Packit Service 75d76b
  adminCompletionFromSubTask(completion)->phase = GROW_PHYSICAL_PHASE_ERROR;
Packit Service 75d76b
  growPhysicalCallback(completion);
Packit Service 75d76b
}
Packit Service 75d76b
Packit Service 75d76b
/**********************************************************************/
Packit Service 75d76b
int performGrowPhysical(VDO *vdo, BlockCount newPhysicalBlocks)
Packit Service 75d76b
{
Packit Service 75d76b
  BlockCount oldPhysicalBlocks = vdo->config.physicalBlocks;
Packit Service 75d76b
Packit Service 75d76b
  // Skip any noop grows.
Packit Service 75d76b
  if (oldPhysicalBlocks == newPhysicalBlocks) {
Packit Service 75d76b
    return VDO_SUCCESS;
Packit Service 75d76b
  }
Packit Service 75d76b
Packit Service 75d76b
  if (newPhysicalBlocks != getNextVDOLayoutSize(vdo->layout)) {
Packit Service 75d76b
    /*
Packit Service 75d76b
     * Either the VDO isn't prepared to grow, or it was prepared to grow
Packit Service 75d76b
     * to a different size. Doing this check here relies on the fact that
Packit Service 75d76b
     * the call to this method is done under the dmsetup message lock.
Packit Service 75d76b
     */
Packit Service 75d76b
    finishVDOLayoutGrowth(vdo->layout);
Packit Service 75d76b
    abandonNewSlabs(vdo->depot);
Packit Service 75d76b
    return VDO_PARAMETER_MISMATCH;
Packit Service 75d76b
  }
Packit Service 75d76b
Packit Service 75d76b
  // Validate that we are prepared to grow appropriately.
Packit Service 75d76b
  BlockCount newDepotSize = getNextBlockAllocatorPartitionSize(vdo->layout);
Packit Service 75d76b
  BlockCount preparedDepotSize = getNewDepotSize(vdo->depot);
Packit Service 75d76b
  if (preparedDepotSize != newDepotSize) {
Packit Service 75d76b
    return VDO_PARAMETER_MISMATCH;
Packit Service 75d76b
  }
Packit Service 75d76b
Packit Service 75d76b
  int result = performAdminOperation(vdo, ADMIN_OPERATION_GROW_PHYSICAL,
Packit Service 75d76b
                                     getThreadIDForPhase, growPhysicalCallback,
Packit Service 75d76b
                                     handleGrowthError);
Packit Service 75d76b
  if (result != VDO_SUCCESS) {
Packit Service 75d76b
    return result;
Packit Service 75d76b
  }
Packit Service 75d76b
Packit Service 75d76b
  logInfo("Physical block count was %llu, now %llu",
Packit Service 75d76b
          oldPhysicalBlocks, newPhysicalBlocks);
Packit Service 75d76b
  return VDO_SUCCESS;
Packit Service 75d76b
}
Packit Service 75d76b
Packit Service 75d76b
/**
Packit Service 75d76b
 * Callback to check that we're not in recovery mode, used in
Packit Service 75d76b
 * prepareToGrowPhysical().
Packit Service 75d76b
 *
Packit Service 75d76b
 * @param completion  The sub-task completion
Packit Service 75d76b
 **/
Packit Service 75d76b
static void checkMayGrowPhysical(VDOCompletion *completion)
Packit Service 75d76b
{
Packit Service 75d76b
  AdminCompletion *adminCompletion = adminCompletionFromSubTask(completion);
Packit Service 75d76b
  assertAdminOperationType(adminCompletion,
Packit Service 75d76b
                           ADMIN_OPERATION_PREPARE_GROW_PHYSICAL);
Packit Service 75d76b
Packit Service 75d76b
  VDO *vdo = adminCompletion->completion.parent;
Packit Service 75d76b
  assertOnAdminThread(vdo, __func__);
Packit Service 75d76b
Packit Service 75d76b
  resetAdminSubTask(completion);
Packit Service 75d76b
Packit Service 75d76b
  // This check can only be done from a base code thread.
Packit Service 75d76b
  if (isReadOnly(vdo->readOnlyNotifier)) {
Packit Service 75d76b
    finishCompletion(completion->parent, VDO_READ_ONLY);
Packit Service 75d76b
    return;
Packit Service 75d76b
  }
Packit Service 75d76b
Packit Service 75d76b
  // This check should only be done from a base code thread.
Packit Service 75d76b
  if (inRecoveryMode(vdo)) {
Packit Service 75d76b
    finishCompletion(completion->parent, VDO_RETRY_AFTER_REBUILD);
Packit Service 75d76b
    return;
Packit Service 75d76b
  }
Packit Service 75d76b
Packit Service 75d76b
  completeCompletion(completion->parent);
Packit Service 75d76b
}
Packit Service 75d76b
Packit Service 75d76b
/**********************************************************************/
Packit Service 75d76b
int prepareToGrowPhysical(VDO *vdo, BlockCount newPhysicalBlocks)
Packit Service 75d76b
{
Packit Service 75d76b
  BlockCount currentPhysicalBlocks = vdo->config.physicalBlocks;
Packit Service 75d76b
  if (newPhysicalBlocks < currentPhysicalBlocks) {
Packit Service 75d76b
    return logErrorWithStringError(VDO_NOT_IMPLEMENTED,
Packit Service 75d76b
                                   "Removing physical storage from a VDO is "
Packit Service 75d76b
                                   "not supported");
Packit Service 75d76b
  }
Packit Service 75d76b
Packit Service 75d76b
  if (newPhysicalBlocks == currentPhysicalBlocks) {
Packit Service 75d76b
    logWarning("Requested physical block count %" PRIu64
Packit Service 75d76b
               " not greater than %llu",
Packit Service 75d76b
               newPhysicalBlocks, currentPhysicalBlocks);
Packit Service 75d76b
    finishVDOLayoutGrowth(vdo->layout);
Packit Service 75d76b
    abandonNewSlabs(vdo->depot);
Packit Service 75d76b
    return VDO_PARAMETER_MISMATCH;
Packit Service 75d76b
  }
Packit Service 75d76b
Packit Service 75d76b
  int result = performAdminOperation(vdo,
Packit Service 75d76b
                                     ADMIN_OPERATION_PREPARE_GROW_PHYSICAL,
Packit Service 75d76b
                                     getThreadIDForPhase, checkMayGrowPhysical,
Packit Service 75d76b
                                     finishParentCallback);
Packit Service 75d76b
  if (result != VDO_SUCCESS) {
Packit Service 75d76b
    return result;
Packit Service 75d76b
  }
Packit Service 75d76b
Packit Service 75d76b
  result = prepareToGrowVDOLayout(vdo->layout, currentPhysicalBlocks,
Packit Service 75d76b
                                  newPhysicalBlocks, vdo->layer);
Packit Service 75d76b
  if (result != VDO_SUCCESS) {
Packit Service 75d76b
    return result;
Packit Service 75d76b
  }
Packit Service 75d76b
Packit Service 75d76b
  BlockCount newDepotSize = getNextBlockAllocatorPartitionSize(vdo->layout);
Packit Service 75d76b
  result = prepareToGrowSlabDepot(vdo->depot, newDepotSize);
Packit Service 75d76b
  if (result != VDO_SUCCESS) {
Packit Service 75d76b
    finishVDOLayoutGrowth(vdo->layout);
Packit Service 75d76b
    return result;
Packit Service 75d76b
  }
Packit Service 75d76b
Packit Service 75d76b
  return VDO_SUCCESS;
Packit Service 75d76b
}