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