Blame source/vdo/kernel/workQueueInternals.h

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/kernel/workQueueInternals.h#4 $
Packit Service 75d76b
 */
Packit Service 75d76b
Packit Service 75d76b
#ifndef WORK_QUEUE_INTERNALS_H
Packit Service 75d76b
#define WORK_QUEUE_INTERNALS_H
Packit Service 75d76b
Packit Service 75d76b
#include <linux/completion.h>
Packit Service 75d76b
#include <linux/kobject.h>
Packit Service 75d76b
#include <linux/list.h>
Packit Service 75d76b
#include <linux/spinlock.h>
Packit Service 75d76b
#include <linux/wait.h>
Packit Service 75d76b
Packit Service 75d76b
#include "workItemStats.h"
Packit Service 75d76b
#include "workQueueStats.h"
Packit Service 75d76b
Packit Service 75d76b
typedef struct kvdoWorkItemList {
Packit Service 75d76b
  KvdoWorkItem *tail;
Packit Service 75d76b
} KvdoWorkItemList;
Packit Service 75d76b
Packit Service 75d76b
/**
Packit Service 75d76b
 * Work queue definition.
Packit Service 75d76b
 *
Packit Service 75d76b
 * There are two types of work queues: simple, with one worker thread, and
Packit Service 75d76b
 * round-robin, which uses a group of the former to do the work, and assigns
Packit Service 75d76b
 * work to them in -- you guessed it -- round-robin fashion. Externally, both
Packit Service 75d76b
 * are represented via the same common sub-structure, though there's actually
Packit Service 75d76b
 * not a great deal of overlap between the two types internally.
Packit Service 75d76b
 **/
Packit Service 75d76b
struct kvdoWorkQueue {
Packit Service 75d76b
  /** Name of just the work queue (e.g., "cpuQ12") */
Packit Service 75d76b
  char           *name;
Packit Service 75d76b
  /**
Packit Service 75d76b
   * Whether this is a round-robin work queue or a simple (one-thread)
Packit Service 75d76b
   * work queue.
Packit Service 75d76b
   **/
Packit Service 75d76b
  bool            roundRobinMode;
Packit Service 75d76b
  /** A handle to a sysfs tree for reporting stats and other info */
Packit Service 75d76b
  struct kobject  kobj;
Packit Service 75d76b
  /** The kernel layer owning this work queue */
Packit Service 75d76b
  KernelLayer    *owner;
Packit Service 75d76b
};
Packit Service 75d76b
Packit Service 75d76b
typedef struct simpleWorkQueue     SimpleWorkQueue;
Packit Service 75d76b
typedef struct roundRobinWorkQueue RoundRobinWorkQueue;
Packit Service 75d76b
Packit Service 75d76b
struct simpleWorkQueue {
Packit Service 75d76b
  /** Common work queue bits */
Packit Service 75d76b
  KvdoWorkQueue            common;
Packit Service 75d76b
  /** A copy of .thread->pid, for safety in the sysfs support */
Packit Service 75d76b
  atomic_t                 threadID;
Packit Service 75d76b
  /**
Packit Service 75d76b
   * Number of priorities actually used, so we don't keep re-checking unused
Packit Service 75d76b
   * funnel queues.
Packit Service 75d76b
   **/
Packit Service 75d76b
  unsigned int             numPriorityLists;
Packit Service 75d76b
  /**
Packit Service 75d76b
   * Map from action codes to priorities.
Packit Service 75d76b
   *
Packit Service 75d76b
   * This mapping can be changed at run time in internal builds, for tuning
Packit Service 75d76b
   * purposes.
Packit Service 75d76b
   **/
Packit Service 75d76b
  uint8_t                  priorityMap[WORK_QUEUE_ACTION_COUNT];
Packit Service 75d76b
  /** The funnel queues */
Packit Service 75d76b
  FunnelQueue             *priorityLists[WORK_QUEUE_PRIORITY_COUNT];
Packit Service 75d76b
  /** The kernel thread */
Packit Service 75d76b
  struct task_struct      *thread;
Packit Service 75d76b
  /** Life cycle functions, etc */
Packit Service 75d76b
  const KvdoWorkQueueType *type;
Packit Service 75d76b
  /** Opaque private data pointer, defined by higher level code */
Packit Service 75d76b
  void                    *private;
Packit Service 75d76b
  /** In a subordinate work queue, a link back to the round-robin parent */
Packit Service 75d76b
  KvdoWorkQueue           *parentQueue;
Packit Service 75d76b
  /** Padding for cache line separation */
Packit Service 75d76b
  char                     pad[CACHE_LINE_BYTES - sizeof(KvdoWorkQueue *)];
Packit Service 75d76b
  /** Lock protecting delayedItems, priorityMap, numPriorityLists, started */
Packit Service 75d76b
  spinlock_t               lock;
Packit Service 75d76b
  /** Any worker threads (zero or one) waiting for new work to do */
Packit Service 75d76b
  wait_queue_head_t        waitingWorkerThreads;
Packit Service 75d76b
  /**
Packit Service 75d76b
   * Hack to reduce wakeup calls if the worker thread is running. See comments
Packit Service 75d76b
   * in workQueue.c.
Packit Service 75d76b
   *
Packit Service 75d76b
   * There is a lot of redundancy with "firstWakeup", though, and the pair
Packit Service 75d76b
   * should be re-examined.
Packit Service 75d76b
   **/
Packit Service 75d76b
  atomic_t                 idle;
Packit Service 75d76b
  /** Wait list for synchronization during worker thread startup */
Packit Service 75d76b
  wait_queue_head_t        startWaiters;
Packit Service 75d76b
  /** Worker thread status (boolean) */
Packit Service 75d76b
  bool                     started;
Packit Service 75d76b
Packit Service 75d76b
  /** List of delayed work items; usually only one, if any */
Packit Service 75d76b
  KvdoWorkItemList         delayedItems;
Packit Service 75d76b
  /**
Packit Service 75d76b
   * Timer for pulling delayed work items off their list and submitting them to
Packit Service 75d76b
   * run.
Packit Service 75d76b
   *
Packit Service 75d76b
   * If the spinlock "lock" above is not held, this timer is scheduled (or
Packit Service 75d76b
   * currently firing and the callback about to acquire the lock) iff
Packit Service 75d76b
   * delayedItems is nonempty.
Packit Service 75d76b
   **/
Packit Service 75d76b
  struct timer_list        delayedItemsTimer;
Packit Service 75d76b
Packit Service 75d76b
  /**
Packit Service 75d76b
   * Timestamp (ns) from the submitting thread that decided to wake us up; also
Packit Service 75d76b
   * used as a flag to indicate whether a wakeup is needed.
Packit Service 75d76b
   *
Packit Service 75d76b
   * Written by submitting threads with atomic64_cmpxchg, and by the worker
Packit Service 75d76b
   * thread setting to 0.
Packit Service 75d76b
   *
Packit Service 75d76b
   * If the value is 0, the worker is probably asleep; the submitting thread
Packit Service 75d76b
   * stores a non-zero value and becomes responsible for calling wake_up on the
Packit Service 75d76b
   * worker thread. If the value is non-zero, either the worker is running or
Packit Service 75d76b
   * another thread has the responsibility for issuing the wakeup.
Packit Service 75d76b
   *
Packit Service 75d76b
   * The "sleep" mode has periodic wakeups and the worker thread may happen to
Packit Service 75d76b
   * wake up while a work item is being enqueued. If that happens, the wakeup
Packit Service 75d76b
   * may be unneeded but will be attempted anyway.
Packit Service 75d76b
   *
Packit Service 75d76b
   * So the return value from cmpxchg(firstWakeup,0,nonzero) can always be
Packit Service 75d76b
   * done, and will tell the submitting thread whether to issue the wakeup or
Packit Service 75d76b
   * not; cmpxchg is atomic, so no other synchronization is needed.
Packit Service 75d76b
   *
Packit Service 75d76b
   * A timestamp is used rather than, say, 1, so that the worker thread can
Packit Service 75d76b
   * record stats on how long it takes to actually get the worker thread
Packit Service 75d76b
   * running.
Packit Service 75d76b
   *
Packit Service 75d76b
   * There is some redundancy between this and "idle" above.
Packit Service 75d76b
   **/
Packit Service 75d76b
  atomic64_t               firstWakeup;
Packit Service 75d76b
  /** Padding for cache line separation */
Packit Service 75d76b
  char                     pad2[CACHE_LINE_BYTES - sizeof(atomic64_t)];
Packit Service 75d76b
  /** Scheduling and work-function statistics */
Packit Service 75d76b
  KvdoWorkQueueStats       stats;
Packit Service 75d76b
  /** Last time (ns) the scheduler actually woke us up */
Packit Service 75d76b
  uint64_t                 mostRecentWakeup;
Packit Service 75d76b
};
Packit Service 75d76b
Packit Service 75d76b
struct roundRobinWorkQueue {
Packit Service 75d76b
  /** Common work queue bits */
Packit Service 75d76b
  KvdoWorkQueue     common;
Packit Service 75d76b
  /** Simple work queues, for actually getting stuff done */
Packit Service 75d76b
  SimpleWorkQueue **serviceQueues;
Packit Service 75d76b
  /** Number of subordinate work queues */
Packit Service 75d76b
  unsigned int      numServiceQueues;
Packit Service 75d76b
  /** Padding for cache line separation */
Packit Service 75d76b
  char              pad[CACHE_LINE_BYTES - sizeof(unsigned int)];
Packit Service 75d76b
  /**
Packit Service 75d76b
   * Rotor used for dispatching across subordinate service queues.
Packit Service 75d76b
   *
Packit Service 75d76b
   * Used and updated by submitting threads. (Not atomically or with locking,
Packit Service 75d76b
   * because we don't really care about it being precise, only about getting a
Packit Service 75d76b
   * roughly even spread; if an increment is missed here and there, it's not a
Packit Service 75d76b
   * problem.)
Packit Service 75d76b
   **/
Packit Service 75d76b
  unsigned int      serviceQueueRotor;
Packit Service 75d76b
};
Packit Service 75d76b
Packit Service 75d76b
static inline SimpleWorkQueue *asSimpleWorkQueue(KvdoWorkQueue *queue)
Packit Service 75d76b
{
Packit Service 75d76b
  return ((queue == NULL)
Packit Service 75d76b
          ? NULL
Packit Service 75d76b
          : container_of(queue, SimpleWorkQueue, common));
Packit Service 75d76b
}
Packit Service 75d76b
Packit Service 75d76b
static inline const SimpleWorkQueue *
Packit Service 75d76b
asConstSimpleWorkQueue(const KvdoWorkQueue *queue)
Packit Service 75d76b
{
Packit Service 75d76b
  return ((queue == NULL)
Packit Service 75d76b
          ? NULL
Packit Service 75d76b
          : container_of(queue, SimpleWorkQueue, common));
Packit Service 75d76b
}
Packit Service 75d76b
Packit Service 75d76b
static inline RoundRobinWorkQueue *asRoundRobinWorkQueue(KvdoWorkQueue *queue)
Packit Service 75d76b
{
Packit Service 75d76b
  return ((queue == NULL)
Packit Service 75d76b
          ? NULL
Packit Service 75d76b
          : container_of(queue, RoundRobinWorkQueue, common));
Packit Service 75d76b
}
Packit Service 75d76b
Packit Service 75d76b
#endif // WORK_QUEUE_INTERNALS_H