The Pedigree Project 0.1
Filter.cc
1/*
2 * Copyright (c) 2008-2014, Pedigree Developers
3 *
4 * Please see the CONTRIB file in the root of the source tree for a full
5 * list of contributors.
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20#include "Filter.h"
21#include "pedigree/kernel/Log.h"
22#include "pedigree/kernel/process/TerminationDeferral.h"
23#include "pedigree/kernel/process/Thread.h"
24#include "pedigree/kernel/processor/Processor.h"
25
26NetworkFilter NetworkFilter::m_Instance;
27
28#if (HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS) || PEDIGREE_CONCURRENCY_SMOKE_TESTS
29NetworkFilter::CallbackPinHook NetworkFilter::m_CallbackPinHook = nullptr;
30#endif
31
33 : m_Callbacks(), m_Lock(), m_NextCallbackId(1), m_pActiveInvocations(nullptr) {}
34
36 m_Lock.acquire();
37 const bool callbackContext = isCallbackInvocation(Processor::information().getCurrentThread());
38 m_Lock.release();
39 if (callbackContext) {
40 FATAL("NetworkFilter cannot be destroyed from callback context.");
41 }
42
43 for (size_t level = 1; level <= 4; ++level) {
44 while (true) {
45 m_Lock.acquire();
46 CallbackItem* item =
47 m_Callbacks[level - 1].count() ? *m_Callbacks[level - 1].begin() : nullptr;
48 const size_t id = item ? item->id : 0;
49 m_Lock.release();
50 if (!item) {
51 break;
52 }
53 if (!removeCallback(level, id)) {
54 FATAL("NetworkFilter callback teardown did not complete.");
55 }
56 }
57 }
58}
59
60bool NetworkFilter::filter(size_t level, uintptr_t packet, size_t sz) {
61 if (!level || level > 4) {
62 return true;
63 }
64
65 m_Lock.acquire();
66 const size_t callbackBoundary = m_NextCallbackId;
67 m_Lock.release();
68
69 TerminationDeferral dispatchDeferral;
70 Thread* current = Processor::information().getCurrentThread();
71 bool accepted = true;
72 size_t afterId = 0;
73 while (accepted) {
74 CallbackItem* item = nullptr;
75 ActiveInvocation invocation = {nullptr, current, nullptr};
76
77 m_Lock.acquire();
78 for (auto candidate : m_Callbacks[level - 1]) {
79 if (candidate->id <= afterId || candidate->id >= callbackBoundary) {
80 continue;
81 }
82
83 afterId = candidate->id;
84 if (!candidate->enabled) {
85 continue;
86 }
87
88 item = candidate;
89 invocation.item = item;
90 ++item->inFlight;
91 invocation.next = m_pActiveInvocations;
92 m_pActiveInvocations = &invocation;
93 break;
94 }
95 m_Lock.release();
96
97 if (!item) {
98 break;
99 }
100
101#if (HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS) || PEDIGREE_CONCURRENCY_SMOKE_TESTS
102 CallbackPinHook hook = __atomic_load_n(&m_CallbackPinHook, __ATOMIC_ACQUIRE);
103 if (hook) {
104 hook(item->callback, item->id);
105 }
106#endif
107 accepted = item->callback(packet, sz);
108
109 {
110 auto completionGuard = item->drainWaiters.acquire();
111 bool wakeDrainers = false;
112 m_Lock.acquire();
113 ActiveInvocation** link = &m_pActiveInvocations;
114 while (*link && *link != &invocation) {
115 link = &((*link)->next);
116 }
117 if (*link) {
118 *link = invocation.next;
119 } else {
120 FATAL("NetworkFilter lost an active callback invocation.");
121 }
122
123 if (!item->inFlight) {
124 FATAL("NetworkFilter callback pin underflow.");
125 }
126 --item->inFlight;
127 if (!item->inFlight) {
128 wakeDrainers = item->draining;
129 }
130 m_Lock.release();
131
132 if (wakeDrainers) {
133 completionGuard.wakeAll(WaitQueue::WakeReason::Signalled, WaitQueue::Channel(item));
134 }
135 }
136 }
137
138 return accepted;
139}
140
141size_t NetworkFilter::installCallback(size_t level, bool (*callback)(uintptr_t, size_t)) {
142 if (!level || level > 4 || !callback) {
143 return static_cast<size_t>(-1);
144 }
145
146 CallbackItem* item = new CallbackItem;
147 item->callback = callback;
148 item->inFlight = 0;
149 item->removers = 0;
150 item->enabled = true;
151 item->draining = false;
152
153 m_Lock.acquire();
154 size_t id = m_NextCallbackId++;
155 if (!id || id == static_cast<size_t>(-1)) {
156 FATAL("NetworkFilter callback identifiers exhausted.");
157 }
158 item->id = id;
159 m_Callbacks[level - 1].pushBack(item);
160 m_Lock.release();
161
162 return id;
163}
164
165bool NetworkFilter::removeCallback(size_t level, size_t id) {
166 if (!level || level > 4 || id == static_cast<size_t>(-1)) {
167 return false;
168 }
169
170 TerminationDeferral terminationDeferral;
171 CallbackItem* item = nullptr;
172 bool deleteNow = false;
173 bool callbackRemoval = false;
174 Thread* current = Processor::information().getCurrentThread();
175
176 m_Lock.acquire();
177 for (List<CallbackItem*>::Iterator it = m_Callbacks[level - 1].begin();
178 it != m_Callbacks[level - 1].end(); ++it) {
179 if ((*it)->id != id) {
180 continue;
181 }
182
183 item = *it;
184 item->enabled = false;
185 callbackRemoval = isCallbackInvocation(current);
186 if (callbackRemoval) {
187 if (!item->draining && !item->inFlight) {
188 m_Callbacks[level - 1].erase(it);
189 deleteNow = true;
190 }
191 } else {
192 item->draining = true;
193 ++item->removers;
194 }
195 break;
196 }
197 m_Lock.release();
198
199 if (!item) {
200 return true;
201 }
202 if (deleteNow) {
203 delete item;
204 return true;
205 } else if (!callbackRemoval) {
206 drainCallback(level, item);
207 return true;
208 }
209 return false;
210}
211
212void NetworkFilter::drainCallback(size_t level, CallbackItem* item) {
213 while (true) {
214 bool complete = false;
215 {
216 auto waitGuard = item->drainWaiters.acquire();
217 m_Lock.acquire();
218 if (!item->inFlight) {
219 complete = true;
220 m_Lock.release();
221 } else {
222 m_Lock.release();
223 const WaitQueue::WakeReason reason =
224 waitGuard.waitForCompletion(WaitQueue::Channel(item), Thread::CallbackDrain,
225 reinterpret_cast<uintptr_t>(item->callback));
226 (void)reason;
227 }
228 }
229 if (complete) {
230 break;
231 }
232 }
233
234 bool deleteItem = false;
235 m_Lock.acquire();
236 if (!item->removers) {
237 FATAL("NetworkFilter callback remover underflow.");
238 }
239 --item->removers;
240 if (!item->removers) {
241 for (List<CallbackItem*>::Iterator it = m_Callbacks[level - 1].begin();
242 it != m_Callbacks[level - 1].end(); ++it) {
243 if (*it == item) {
244 m_Callbacks[level - 1].erase(it);
245 deleteItem = true;
246 break;
247 }
248 }
249 }
250 m_Lock.release();
251
252 if (deleteItem) {
253 delete item;
254 }
255}
256
257bool NetworkFilter::isCallbackInvocation(Thread* thread) const {
258 for (ActiveInvocation* invocation = m_pActiveInvocations; invocation;
259 invocation = invocation->next) {
260 if (invocation->thread == thread) {
261 return true;
262 }
263 }
264 return false;
265}
266
267#if (HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS) || PEDIGREE_CONCURRENCY_SMOKE_TESTS
268void NetworkFilter::setCallbackPinHook(CallbackPinHook hook) {
269 __atomic_store_n(&m_CallbackPinHook, hook, __ATOMIC_RELEASE);
270}
271#endif
::Iterator< T, node_t > Iterator
Definition List.h:67
size_t installCallback(size_t level, bool(*callback)(uintptr_t, size_t))
Definition Filter.cc:141
bool filter(size_t level, uintptr_t packet, size_t sz)
Definition Filter.cc:60
bool removeCallback(size_t level, size_t id)
Definition Filter.cc:165
NetworkFilter()
Default constructor, boring.
Definition Filter.cc:32
virtual ~NetworkFilter()
Destructor, also boring.
Definition Filter.cc:35
static ProcessorInformation & information()
void release()
Definition Spinlock.cc:168
bool acquire(bool recurse=false, bool safe=true)
Definition Spinlock.cc:36