Blame source/vdo/base/actionManager.c

Packit Service 310c69
/*
Packit Service 310c69
 * Copyright (c) 2020 Red Hat, Inc.
Packit Service 310c69
 *
Packit Service 310c69
 * This program is free software; you can redistribute it and/or
Packit Service 310c69
 * modify it under the terms of the GNU General Public License
Packit Service 310c69
 * as published by the Free Software Foundation; either version 2
Packit Service 310c69
 * of the License, or (at your option) any later version.
Packit Service 310c69
 * 
Packit Service 310c69
 * This program is distributed in the hope that it will be useful,
Packit Service 310c69
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 310c69
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit Service 310c69
 * GNU General Public License for more details.
Packit Service 310c69
 * 
Packit Service 310c69
 * You should have received a copy of the GNU General Public License
Packit Service 310c69
 * along with this program; if not, write to the Free Software
Packit Service 310c69
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
Packit Service 310c69
 * 02110-1301, USA. 
Packit Service 310c69
 *
Packit Service 310c69
 * $Id: //eng/vdo-releases/aluminum/src/c++/vdo/base/actionManager.c#9 $
Packit Service 310c69
 */
Packit Service 310c69
Packit Service 310c69
#include "actionManager.h"
Packit Service 310c69
Packit Service 310c69
#include "memoryAlloc.h"
Packit Service 310c69
Packit Service 310c69
#include "adminState.h"
Packit Service 310c69
#include "completion.h"
Packit Service 310c69
#include "types.h"
Packit Service 310c69
Packit Service 310c69
/** An action to be performed in each of a set of zones */
Packit Service 310c69
typedef struct action Action;
Packit Service 310c69
struct action {
Packit Service 310c69
  /** Whether this structure is in use */
Packit Service 310c69
  bool              inUse;
Packit Service 310c69
 /** The admin operation associated with this action */
Packit Service 310c69
  AdminStateCode    operation;
Packit Service 310c69
  /**
Packit Service 310c69
   * The method to run on the initiator thread before the action is applied to
Packit Service 310c69
   * each zone.
Packit Service 310c69
   **/
Packit Service 310c69
  ActionPreamble   *preamble;
Packit Service 310c69
  /** The action to be performed in each zone */
Packit Service 310c69
  ZoneAction       *zoneAction;
Packit Service 310c69
  /**
Packit Service 310c69
   * The method to run on the initiator thread after the action has been
Packit Service 310c69
   * applied to each zone
Packit Service 310c69
   **/
Packit Service 310c69
  ActionConclusion *conclusion;
Packit Service 310c69
  /** The object to notify when the action is complete */
Packit Service 310c69
  VDOCompletion    *parent;
Packit Service 310c69
  /** The action specific context */
Packit Service 310c69
  void             *context;
Packit Service 310c69
  /** The action to perform after this one */
Packit Service 310c69
  Action           *next;
Packit Service 310c69
};
Packit Service 310c69
Packit Service 310c69
struct actionManager {
Packit Service 310c69
  /** The completion for performing actions */
Packit Service 310c69
  VDOCompletion     completion;
Packit Service 310c69
  /** The state of this action manager */
Packit Service 310c69
  AdminState        state;
Packit Service 310c69
  /** The two action slots*/
Packit Service 310c69
  Action            actions[2];
Packit Service 310c69
  /** The current action slot */
Packit Service 310c69
  Action           *currentAction;
Packit Service 310c69
  /** The number of zones in which an action is to be applied */
Packit Service 310c69
  ZoneCount         zones;
Packit Service 310c69
  /** A function to schedule a default next action */
Packit Service 310c69
  ActionScheduler  *scheduler;
Packit Service 310c69
  /**
Packit Service 310c69
   * A function to get the id of the thread on which to apply an action to a
Packit Service 310c69
   * zone
Packit Service 310c69
   **/
Packit Service 310c69
  ZoneThreadGetter *getZoneThreadID;
Packit Service 310c69
  /** The ID of the thread on which actions may be initiated */
Packit Service 310c69
  ThreadID          initiatorThreadID;
Packit Service 310c69
  /** Opaque data associated with this action manager */
Packit Service 310c69
  void             *context;
Packit Service 310c69
  /** The zone currently being acted upon */
Packit Service 310c69
  ZoneCount         actingZone;
Packit Service 310c69
};
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Convert a generic VDOCompletion to a ActionManager.
Packit Service 310c69
 *
Packit Service 310c69
 * @param completion The completion to convert
Packit Service 310c69
 *
Packit Service 310c69
 * @return The completion as a ActionManager
Packit Service 310c69
 **/
Packit Service 310c69
static inline ActionManager *asActionManager(VDOCompletion *completion)
Packit Service 310c69
{
Packit Service 310c69
  STATIC_ASSERT(offsetof(ActionManager, completion) == 0);
Packit Service 310c69
  assertCompletionType(completion->type, ACTION_COMPLETION);
Packit Service 310c69
  return (ActionManager *) completion;
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * An action scheduler which does not schedule an action.
Packit Service 310c69
 *
Packit Service 310c69
 * 

Implements ActionScheduler.

Packit Service 310c69
 **/
Packit Service 310c69
static bool noDefaultAction(void *context __attribute__((unused)))
Packit Service 310c69
{
Packit Service 310c69
  return false;
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * A default preamble which does nothing.
Packit Service 310c69
 *
Packit Service 310c69
 * 

Implements ActionPreamble

Packit Service 310c69
 **/
Packit Service 310c69
static void noPreamble(void          *context __attribute__((unused)),
Packit Service 310c69
                       VDOCompletion *completion)
Packit Service 310c69
{
Packit Service 310c69
  completeCompletion(completion);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * A default conclusion which does nothing.
Packit Service 310c69
 *
Packit Service 310c69
 * 

Implements ActionConclusion.

Packit Service 310c69
 **/
Packit Service 310c69
static int noConclusion(void *context __attribute__((unused))) {
Packit Service 310c69
  return VDO_SUCCESS;
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**********************************************************************/
Packit Service 310c69
int makeActionManager(ZoneCount          zones,
Packit Service 310c69
                      ZoneThreadGetter  *getZoneThreadID,
Packit Service 310c69
                      ThreadID           initiatorThreadID,
Packit Service 310c69
                      void              *context,
Packit Service 310c69
                      ActionScheduler   *scheduler,
Packit Service 310c69
                      PhysicalLayer     *layer,
Packit Service 310c69
                      ActionManager    **managerPtr)
Packit Service 310c69
{
Packit Service 310c69
  ActionManager *manager;
Packit Service 310c69
  int result = ALLOCATE(1, ActionManager, __func__, &manager);
Packit Service 310c69
  if (result != VDO_SUCCESS) {
Packit Service 310c69
    return result;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  *manager = (ActionManager) {
Packit Service 310c69
    .zones             = zones,
Packit Service 310c69
    .scheduler         = ((scheduler == NULL) ? noDefaultAction : scheduler),
Packit Service 310c69
    .getZoneThreadID   = getZoneThreadID,
Packit Service 310c69
    .initiatorThreadID = initiatorThreadID,
Packit Service 310c69
    .context           = context,
Packit Service 310c69
  };
Packit Service 310c69
Packit Service 310c69
  manager->actions[0].next = &manager->actions[1];
Packit Service 310c69
  manager->currentAction = manager->actions[1].next = &manager->actions[0];
Packit Service 310c69
Packit Service 310c69
  result = initializeEnqueueableCompletion(&manager->completion,
Packit Service 310c69
                                           ACTION_COMPLETION, layer);
Packit Service 310c69
  if (result != VDO_SUCCESS) {
Packit Service 310c69
    freeActionManager(&manager);
Packit Service 310c69
    return result;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  *managerPtr = manager;
Packit Service 310c69
  return VDO_SUCCESS;
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**********************************************************************/
Packit Service 310c69
void freeActionManager(ActionManager **managerPtr)
Packit Service 310c69
{
Packit Service 310c69
  ActionManager *manager = *managerPtr;
Packit Service 310c69
  if (manager == NULL) {
Packit Service 310c69
    return;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  destroyEnqueueable(&manager->completion);
Packit Service 310c69
  FREE(manager);
Packit Service 310c69
  *managerPtr = NULL;
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**********************************************************************/
Packit Service 310c69
AdminStateCode getCurrentManagerOperation(ActionManager *manager)
Packit Service 310c69
{
Packit Service 310c69
  return manager->state.state;
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**********************************************************************/
Packit Service 310c69
void *getCurrentActionContext(ActionManager *manager)
Packit Service 310c69
{
Packit Service 310c69
  return (manager->currentAction->inUse
Packit Service 310c69
          ? manager->currentAction->context : NULL);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**********************************************************************/
Packit Service 310c69
static void finishActionCallback(VDOCompletion *completion);
Packit Service 310c69
static void applyToZone(VDOCompletion *completion);
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Get the thread ID for the current zone.
Packit Service 310c69
 *
Packit Service 310c69
 * @param manager  The action manager
Packit Service 310c69
 *
Packit Service 310c69
 * @return The ID of the thread on which to run actions for the current zone
Packit Service 310c69
 **/
Packit Service 310c69
static ThreadID getActingZoneThreadID(ActionManager *manager)
Packit Service 310c69
{
Packit Service 310c69
  return manager->getZoneThreadID(manager->context, manager->actingZone);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Prepare the manager's completion to run on the next zone.
Packit Service 310c69
 *
Packit Service 310c69
 * @param manager  The action manager
Packit Service 310c69
 **/
Packit Service 310c69
static void prepareForNextZone(ActionManager *manager)
Packit Service 310c69
{
Packit Service 310c69
  prepareForRequeue(&manager->completion, applyToZone,
Packit Service 310c69
                    preserveErrorAndContinue, getActingZoneThreadID(manager),
Packit Service 310c69
                    manager->currentAction->parent);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Prepare the manager's completion to run the conclusion on the initiator
Packit Service 310c69
 * thread.
Packit Service 310c69
 *
Packit Service 310c69
 * @param manager  The action manager
Packit Service 310c69
 **/
Packit Service 310c69
static void prepareForConclusion(ActionManager *manager)
Packit Service 310c69
{
Packit Service 310c69
  prepareForRequeue(&manager->completion, finishActionCallback,
Packit Service 310c69
                    preserveErrorAndContinue, manager->initiatorThreadID,
Packit Service 310c69
                    manager->currentAction->parent);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Perform an action on the next zone if there is one.
Packit Service 310c69
 *
Packit Service 310c69
 * @param completion  The action completion
Packit Service 310c69
 **/
Packit Service 310c69
static void applyToZone(VDOCompletion *completion)
Packit Service 310c69
{
Packit Service 310c69
  ActionManager *manager = asActionManager(completion);
Packit Service 310c69
  ASSERT_LOG_ONLY((getCallbackThreadID() == getActingZoneThreadID(manager)),
Packit Service 310c69
                  "applyToZone() called on acting zones's thread");
Packit Service 310c69
Packit Service 310c69
  ZoneCount zone = manager->actingZone++;
Packit Service 310c69
  if (manager->actingZone == manager->zones) {
Packit Service 310c69
    // We are about to apply to the last zone. Once that is finished,
Packit Service 310c69
    // we're done, so go back to the initiator thread and finish up.
Packit Service 310c69
    prepareForConclusion(manager);
Packit Service 310c69
  } else {
Packit Service 310c69
    // Prepare to come back on the next zone
Packit Service 310c69
    prepareForNextZone(manager);
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  manager->currentAction->zoneAction(manager->context, zone, completion);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * The error handler for preamble errors.
Packit Service 310c69
 *
Packit Service 310c69
 * @param completion  The manager completion
Packit Service 310c69
 **/
Packit Service 310c69
static void handlePreambleError(VDOCompletion *completion)
Packit Service 310c69
{
Packit Service 310c69
  // Skip the zone actions since the preamble failed.
Packit Service 310c69
  completion->callback = finishActionCallback;
Packit Service 310c69
  preserveErrorAndContinue(completion);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Launch the current action.
Packit Service 310c69
 *
Packit Service 310c69
 * @param manager  The action manager
Packit Service 310c69
 **/
Packit Service 310c69
static void launchCurrentAction(ActionManager *manager)
Packit Service 310c69
{
Packit Service 310c69
  Action *action = manager->currentAction;
Packit Service 310c69
  int     result = startOperation(&manager->state, action->operation);
Packit Service 310c69
  if (result != VDO_SUCCESS) {
Packit Service 310c69
    if (action->parent != NULL) {
Packit Service 310c69
      setCompletionResult(action->parent, result);
Packit Service 310c69
    }
Packit Service 310c69
Packit Service 310c69
    // We aren't going to run the preamble, so don't run the conclusion
Packit Service 310c69
    action->conclusion = noConclusion;
Packit Service 310c69
    finishActionCallback(&manager->completion);
Packit Service 310c69
    return;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  if (action->zoneAction == NULL) {
Packit Service 310c69
    prepareForConclusion(manager);
Packit Service 310c69
  } else {
Packit Service 310c69
    manager->actingZone = 0;
Packit Service 310c69
    prepareForRequeue(&manager->completion, applyToZone, handlePreambleError,
Packit Service 310c69
                      getActingZoneThreadID(manager),
Packit Service 310c69
                      manager->currentAction->parent);
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  action->preamble(manager->context, &manager->completion);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**********************************************************************/
Packit Service 310c69
bool scheduleDefaultAction(ActionManager *manager)
Packit Service 310c69
{
Packit Service 310c69
  // Don't schedule a default action if we are operating or not in normal
Packit Service 310c69
  // operation.
Packit Service 310c69
  return ((manager->state.state == ADMIN_STATE_NORMAL_OPERATION)
Packit Service 310c69
          && manager->scheduler(manager->context));
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**
Packit Service 310c69
 * Finish an action now that it has been applied to all zones. This
Packit Service 310c69
 * callback is registered in applyToZone().
Packit Service 310c69
 *
Packit Service 310c69
 * @param completion  The action manager completion
Packit Service 310c69
 **/
Packit Service 310c69
static void finishActionCallback(VDOCompletion *completion)
Packit Service 310c69
{
Packit Service 310c69
  ActionManager *manager        = asActionManager(completion);
Packit Service 310c69
  Action         action         = *(manager->currentAction);
Packit Service 310c69
  manager->currentAction->inUse = false;
Packit Service 310c69
  manager->currentAction        = manager->currentAction->next;
Packit Service 310c69
Packit Service 310c69
  // We need to check this now to avoid use-after-free issues if running the
Packit Service 310c69
  // conclusion or notifying the parent results in the manager being freed.
Packit Service 310c69
  bool hasNextAction = (manager->currentAction->inUse
Packit Service 310c69
                        || scheduleDefaultAction(manager));
Packit Service 310c69
  int result = action.conclusion(manager->context);
Packit Service 310c69
  finishOperation(&manager->state);
Packit Service 310c69
  if (action.parent != NULL) {
Packit Service 310c69
    finishCompletion(action.parent, result);
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  if (hasNextAction) {
Packit Service 310c69
    launchCurrentAction(manager);
Packit Service 310c69
  }
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**********************************************************************/
Packit Service 310c69
bool scheduleAction(ActionManager    *manager,
Packit Service 310c69
                    ActionPreamble   *preamble,
Packit Service 310c69
                    ZoneAction       *zoneAction,
Packit Service 310c69
                    ActionConclusion *conclusion,
Packit Service 310c69
                    VDOCompletion    *parent)
Packit Service 310c69
{
Packit Service 310c69
  return scheduleOperation(manager, ADMIN_STATE_OPERATING, preamble,
Packit Service 310c69
                           zoneAction, conclusion, parent);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**********************************************************************/
Packit Service 310c69
bool scheduleOperation(ActionManager    *manager,
Packit Service 310c69
                       AdminStateCode    operation,
Packit Service 310c69
                       ActionPreamble   *preamble,
Packit Service 310c69
                       ZoneAction       *zoneAction,
Packit Service 310c69
                       ActionConclusion *conclusion,
Packit Service 310c69
                       VDOCompletion    *parent)
Packit Service 310c69
{
Packit Service 310c69
  return scheduleOperationWithContext(manager, operation, preamble, zoneAction,
Packit Service 310c69
                                      conclusion, NULL, parent);
Packit Service 310c69
}
Packit Service 310c69
Packit Service 310c69
/**********************************************************************/
Packit Service 310c69
bool scheduleOperationWithContext(ActionManager    *manager,
Packit Service 310c69
                                  AdminStateCode    operation,
Packit Service 310c69
                                  ActionPreamble   *preamble,
Packit Service 310c69
                                  ZoneAction       *zoneAction,
Packit Service 310c69
                                  ActionConclusion *conclusion,
Packit Service 310c69
                                  void             *context,
Packit Service 310c69
                                  VDOCompletion    *parent)
Packit Service 310c69
{
Packit Service 310c69
  ASSERT_LOG_ONLY((getCallbackThreadID() == manager->initiatorThreadID),
Packit Service 310c69
                  "action initiated from correct thread");
Packit Service 310c69
  Action *action;
Packit Service 310c69
  if (!manager->currentAction->inUse) {
Packit Service 310c69
    action = manager->currentAction;
Packit Service 310c69
  } else if (!manager->currentAction->next->inUse) {
Packit Service 310c69
    action = manager->currentAction->next;
Packit Service 310c69
  } else {
Packit Service 310c69
    if (parent != NULL) {
Packit Service 310c69
      finishCompletion(parent, VDO_COMPONENT_BUSY);
Packit Service 310c69
    }
Packit Service 310c69
Packit Service 310c69
    return false;
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  *action = (Action) {
Packit Service 310c69
    .inUse      = true,
Packit Service 310c69
    .operation  = operation,
Packit Service 310c69
    .preamble   = (preamble == NULL) ? noPreamble : preamble,
Packit Service 310c69
    .zoneAction = zoneAction,
Packit Service 310c69
    .conclusion = (conclusion == NULL) ? noConclusion : conclusion,
Packit Service 310c69
    .context    = context,
Packit Service 310c69
    .parent     = parent,
Packit Service 310c69
    .next       = action->next,
Packit Service 310c69
  };
Packit Service 310c69
Packit Service 310c69
  if (action == manager->currentAction) {
Packit Service 310c69
    launchCurrentAction(manager);
Packit Service 310c69
  }
Packit Service 310c69
Packit Service 310c69
  return true;
Packit Service 310c69
}