summaryrefslogtreecommitdiffstats
path: root/src/include/kernel/deferred.H
blob: f80a47bdbadc67662e8e5907556a7bbb760fbb82 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
/* IBM_PROLOG_BEGIN_TAG                                                   */
/* This is an automatically generated prolog.                             */
/*                                                                        */
/* $Source: src/include/kernel/deferred.H $                               */
/*                                                                        */
/* OpenPOWER HostBoot Project                                             */
/*                                                                        */
/* COPYRIGHT International Business Machines Corp. 2012,2014              */
/*                                                                        */
/* Licensed under the Apache License, Version 2.0 (the "License");        */
/* you may not use this file except in compliance with the License.       */
/* You may obtain a copy of the License at                                */
/*                                                                        */
/*     http://www.apache.org/licenses/LICENSE-2.0                         */
/*                                                                        */
/* Unless required by applicable law or agreed to in writing, software    */
/* distributed under the License is distributed on an "AS IS" BASIS,      */
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or        */
/* implied. See the License for the specific language governing           */
/* permissions and limitations under the License.                         */
/*                                                                        */
/* IBM_PROLOG_END_TAG                                                     */
#ifndef __KERNEL_DEFERRED_H
#define __KERNEL_DEFERRED_H

/** @file deferred.H
 *  @brief Definition of kernel's deferred work constructs.
 *
 *  These classes allow the kernel to synchronize all CPUs into kernel-mode
 *  and execute a task.  These work items are queued up to be executed at
 *  the next time-slice expiration (decrementer exception).
 */

#include <kernel/types.h>
#include <kernel/spinlock.H>
#include <kernel/barrier.H>

/** @class DeferredWork
 *  @brief The base work-item type for deferred work.
 *
 *  This class implements the synchronization and state transition for a
 *  general deferred work object.  This class has methods for defining
 *  the behavior at specific points in its execution.  A real behavior
 *  implementation should be defined by inheriting this class and overloading
 *  the desired virtual methods.
 *
 *  The class is closely associated with the DeferredQueue, which is meant to
 *  hold pending DeferredWork objects.  When a DeferredWork is complete it
 *  will remove itself from the DeferredQueue and self-delete.
 *
 *  The state transition is as follows:
 *
 *  <pre>
 *        MASTER           Both            NON-MASTER
 *  1.                   Sync all
 *  2.  masterPreWork                         block
 *  3.                activeMainWork
 *  4.                   Sync all
 *  5.  masterPostWork                        block
 *  6.              Last thread out deletes
 *  </pre>
 *
 *  With the deferred work there is concern as to what to do with CPUs that
 *  come online while a deferred work item is being executed.  For instance,
 *  the Heap Coalescing work could cause data integrity issues if a freshly
 *  activated CPU were to not block with the rest of the CPUs and instead
 *  started executed user-space code.  To deal with this condition a
 *  CPU which is activated after the DeferredWork object was created will
 *  block at masterPreWork and masterPostWork stages and call
 *  nonactiveMainWork.  Typically, the non-active CPUs perform no action
 *  but an implementation could assert for a race-condition that cannot
 *  be handled properly (though should never occur).
 */
class DeferredWork
{
    public:
        /** Default Constructor
         *  Initializes the work item and determines the CPU-activation
         *  sequence number at the time of creation.
         */
        DeferredWork();

        /** Default destructor
         *  Verifies that the work item has completed properly and is
         *  detached from the DeferredQueue.
         */
        virtual ~DeferredWork();

        /** Entry point for DeferredWork execution. */
        void start();

    protected:
        // Functions to override for desired behavior:
            /** Executed by master before "main" but after all active
             *  CPUs are sync'd.
             */
        virtual void masterPreWork() {};
            /** Executed by master after "main" work is complete. */
        virtual void masterPostWork() {};
            /** Executed by all [active] CPUs as the "main" work. */
        virtual void activeMainWork() {};
            /** Executed by all non-active CPUs as the "main" work. */
        virtual void nonactiveMainWork() {};

    private:
        friend class DeferredQueue;

            /** Barrier for synchronization of active CPUs. */
        Barrier iv_barrier;
            /** Count of CPUs in object and pointer to next object.
             *
             *  Stored as "(cpu count << 32) | (DeferredWork* next)".  This
             *  is done so that we can atomically check and/or set both
             *  values.
             */
        volatile uint64_t iv_cpus_and_next;
            /** Sequence ID at the time of creation. */
        uint32_t iv_activeSeqId;
            /** Boolean to indicate masterPreWork is complete. */
        volatile bool iv_releasePre;
            /** Boolean to indicate masterPostWork is complete. */
        volatile bool iv_releasePost;

            // Internal state transition functions.
        void _waitForCpus();
        void _masterPre();
        void _waitAtPre();
        void _masterPost();
        void _waitAtPost();
        void _cleanup();

};

/** @class DeferredQueue
 *  @brief Queue for DeferredWork objects.
 *
 *  This class should have a Singleton instance for the whole kernel.
 *  Periodically, kernel threads can call the 'execute' function to execute
 *  pending work, if any is available.  Additional work can be 'insert'ed.
 */
class DeferredQueue
{
    public:
        /** Default constructor. */
        DeferredQueue();
        /** Default destructor. */
        ~DeferredQueue();

        /** Insert a work item into the queue.
         *
         *  @param i_work - The item to add.
         *
         *  Ownership of the work item is transfered to the queue, which is
         *  responsible for delete once the work is complete.
         */
        static void insert(DeferredWork* i_work);
        /** Execute any pending work items. */
        static void execute();

    private:
        friend class DeferredWork;

            // Instance functions for static pair.
        void _insert(DeferredWork* i_work);
        void _execute();

        /** Remove a completed work item from the queue.
         *
         *  This function is to be called by the master CPU executing the
         *  work item being completed.
         *
         *  The work item is atomically removed from the queue and the
         *  count of CPUs with a pointer to the work item is compared
         *  against the count of CPUs in the work item.  This function
         *  returns when all CPUs that have a pointer are actually inside
         *  the work item (so we can accurately determine when there is
         *  only a single CPU in the work item for delete).
         *
         *  @param i_work - The item to remove.
         */
        void _complete(DeferredWork* i_work);

            /** Spinlock to protect insert / remove operations.
             *
             *  This is effectively a "write" lock on the structure and all
             *  reads are done atomically.  The writers have also use atomic
             *  update operations to ensure the readers (which are never
             *  locked) operate in a thread-safe manner.
             */
        Spinlock lock;
            /** Count of CPUs in first object and pointer to it.
             *
             *  Stored as "(cpu count << 32) | (DeferredWork* ptr)".  This
             *  is done so that we can atomically check and/or set both
             *  values.
             */
        volatile uint64_t iv_cpus_and_next;
};

#endif
OpenPOWER on IntegriCloud