]> git.saurik.com Git - apple/xnu.git/blob - iokit/Kernel/IOSharedDataQueue.cpp
5eb1c35ed3eb6870ae22d3ab404f514af6df4877
[apple/xnu.git] / iokit / Kernel / IOSharedDataQueue.cpp
1 /*
2 * Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29 #include <IOKit/IOSharedDataQueue.h>
30 #include <IOKit/IODataQueueShared.h>
31 #include <IOKit/IOLib.h>
32 #include <IOKit/IOMemoryDescriptor.h>
33
34 #ifdef enqueue
35 #undef enqueue
36 #endif
37
38 #ifdef dequeue
39 #undef dequeue
40 #endif
41
42 #define super IODataQueue
43
44 OSDefineMetaClassAndStructors(IOSharedDataQueue, IODataQueue)
45
46 IOSharedDataQueue *IOSharedDataQueue::withCapacity(UInt32 size)
47 {
48 IOSharedDataQueue *dataQueue = new IOSharedDataQueue;
49
50 if (dataQueue) {
51 if (!dataQueue->initWithCapacity(size)) {
52 dataQueue->release();
53 dataQueue = 0;
54 }
55 }
56
57 return dataQueue;
58 }
59
60 IOSharedDataQueue *IOSharedDataQueue::withEntries(UInt32 numEntries, UInt32 entrySize)
61 {
62 IOSharedDataQueue *dataQueue = new IOSharedDataQueue;
63
64 if (dataQueue) {
65 if (!dataQueue->initWithEntries(numEntries, entrySize)) {
66 dataQueue->release();
67 dataQueue = 0;
68 }
69 }
70
71 return dataQueue;
72 }
73
74 Boolean IOSharedDataQueue::initWithCapacity(UInt32 size)
75 {
76 IODataQueueAppendix * appendix;
77 vm_size_t allocSize;
78
79 if (!super::init()) {
80 return false;
81 }
82
83 _reserved = (ExpansionData *)IOMalloc(sizeof(struct ExpansionData));
84 if (!_reserved) {
85 return false;
86 }
87
88 if (size > UINT32_MAX - DATA_QUEUE_MEMORY_HEADER_SIZE - DATA_QUEUE_MEMORY_APPENDIX_SIZE) {
89 return false;
90 }
91
92 allocSize = round_page(size + DATA_QUEUE_MEMORY_HEADER_SIZE + DATA_QUEUE_MEMORY_APPENDIX_SIZE);
93
94 if (allocSize < size) {
95 return false;
96 }
97
98 dataQueue = (IODataQueueMemory *)IOMallocAligned(allocSize, PAGE_SIZE);
99 if (dataQueue == 0) {
100 return false;
101 }
102
103 dataQueue->queueSize = size;
104 dataQueue->head = 0;
105 dataQueue->tail = 0;
106
107 if (!setQueueSize(size)) {
108 return false;
109 }
110
111 appendix = (IODataQueueAppendix *)((UInt8 *)dataQueue + size + DATA_QUEUE_MEMORY_HEADER_SIZE);
112 appendix->version = 0;
113 notifyMsg = &(appendix->msgh);
114 setNotificationPort(MACH_PORT_NULL);
115
116 return true;
117 }
118
119 void IOSharedDataQueue::free()
120 {
121 if (dataQueue) {
122 IOFreeAligned(dataQueue, round_page(getQueueSize() + DATA_QUEUE_MEMORY_HEADER_SIZE + DATA_QUEUE_MEMORY_APPENDIX_SIZE));
123 dataQueue = NULL;
124 }
125
126 if (_reserved) {
127 IOFree (_reserved, sizeof(struct ExpansionData));
128 _reserved = NULL;
129 }
130
131 super::free();
132 }
133
134 IOMemoryDescriptor *IOSharedDataQueue::getMemoryDescriptor()
135 {
136 IOMemoryDescriptor *descriptor = 0;
137
138 if (dataQueue != 0) {
139 descriptor = IOMemoryDescriptor::withAddress(dataQueue, getQueueSize() + DATA_QUEUE_MEMORY_HEADER_SIZE + DATA_QUEUE_MEMORY_APPENDIX_SIZE, kIODirectionOutIn);
140 }
141
142 return descriptor;
143 }
144
145
146 IODataQueueEntry * IOSharedDataQueue::peek()
147 {
148 IODataQueueEntry *entry = 0;
149
150 if (dataQueue && (dataQueue->head != dataQueue->tail)) {
151 IODataQueueEntry * head = 0;
152 UInt32 headSize = 0;
153 UInt32 headOffset = dataQueue->head;
154 UInt32 queueSize = getQueueSize();
155
156 if (headOffset >= queueSize) {
157 return NULL;
158 }
159
160 head = (IODataQueueEntry *)((char *)dataQueue->queue + headOffset);
161 headSize = head->size;
162
163 // Check if there's enough room before the end of the queue for a header.
164 // If there is room, check if there's enough room to hold the header and
165 // the data.
166
167 if ((headOffset > UINT32_MAX - DATA_QUEUE_ENTRY_HEADER_SIZE) ||
168 (headOffset + DATA_QUEUE_ENTRY_HEADER_SIZE > queueSize) ||
169 (headOffset + DATA_QUEUE_ENTRY_HEADER_SIZE > UINT32_MAX - headSize) ||
170 (headOffset + headSize + DATA_QUEUE_ENTRY_HEADER_SIZE > queueSize)) {
171 // No room for the header or the data, wrap to the beginning of the queue.
172 // Note: wrapping even with the UINT32_MAX checks, as we have to support
173 // queueSize of UINT32_MAX
174 entry = dataQueue->queue;
175 } else {
176 entry = head;
177 }
178 }
179
180 return entry;
181 }
182
183 Boolean IOSharedDataQueue::enqueue(void * data, UInt32 dataSize)
184 {
185 const UInt32 head = dataQueue->head; // volatile
186 const UInt32 tail = dataQueue->tail;
187 const UInt32 entrySize = dataSize + DATA_QUEUE_ENTRY_HEADER_SIZE;
188 IODataQueueEntry * entry;
189
190 // Check for overflow of entrySize
191 if (dataSize > UINT32_MAX - DATA_QUEUE_ENTRY_HEADER_SIZE) {
192 return false;
193 }
194 // Check for underflow of (getQueueSize() - tail)
195 if (getQueueSize() < tail) {
196 return false;
197 }
198
199 if ( tail >= head )
200 {
201 // Is there enough room at the end for the entry?
202 if ((entrySize <= UINT32_MAX - tail) &&
203 ((tail + entrySize) <= getQueueSize()) )
204 {
205 entry = (IODataQueueEntry *)((UInt8 *)dataQueue->queue + tail);
206
207 entry->size = dataSize;
208 memcpy(&entry->data, data, dataSize);
209
210 // The tail can be out of bound when the size of the new entry
211 // exactly matches the available space at the end of the queue.
212 // The tail can range from 0 to dataQueue->queueSize inclusive.
213
214 OSAddAtomic(entrySize, (SInt32 *)&dataQueue->tail);
215 }
216 else if ( head > entrySize ) // Is there enough room at the beginning?
217 {
218 // Wrap around to the beginning, but do not allow the tail to catch
219 // up to the head.
220
221 dataQueue->queue->size = dataSize;
222
223 // We need to make sure that there is enough room to set the size before
224 // doing this. The user client checks for this and will look for the size
225 // at the beginning if there isn't room for it at the end.
226
227 if ( ( getQueueSize() - tail ) >= DATA_QUEUE_ENTRY_HEADER_SIZE )
228 {
229 ((IODataQueueEntry *)((UInt8 *)dataQueue->queue + tail))->size = dataSize;
230 }
231
232 memcpy(&dataQueue->queue->data, data, dataSize);
233 OSCompareAndSwap(dataQueue->tail, entrySize, &dataQueue->tail);
234 }
235 else
236 {
237 return false; // queue is full
238 }
239 }
240 else
241 {
242 // Do not allow the tail to catch up to the head when the queue is full.
243 // That's why the comparison uses a '>' rather than '>='.
244
245 if ( (head - tail) > entrySize )
246 {
247 entry = (IODataQueueEntry *)((UInt8 *)dataQueue->queue + tail);
248
249 entry->size = dataSize;
250 memcpy(&entry->data, data, dataSize);
251 OSAddAtomic(entrySize, (SInt32 *)&dataQueue->tail);
252 }
253 else
254 {
255 return false; // queue is full
256 }
257 }
258
259 // Send notification (via mach message) that data is available.
260
261 if ( ( head == tail ) /* queue was empty prior to enqueue() */
262 || ( dataQueue->head == tail ) ) /* queue was emptied during enqueue() */
263 {
264 sendDataAvailableNotification();
265 }
266
267 return true;
268 }
269
270 Boolean IOSharedDataQueue::dequeue(void *data, UInt32 *dataSize)
271 {
272 Boolean retVal = TRUE;
273 IODataQueueEntry * entry = 0;
274 UInt32 entrySize = 0;
275 UInt32 newHeadOffset = 0;
276
277 if (dataQueue) {
278 if (dataQueue->head != dataQueue->tail) {
279 IODataQueueEntry * head = 0;
280 UInt32 headSize = 0;
281 UInt32 headOffset = dataQueue->head;
282 UInt32 queueSize = getQueueSize();
283
284 if (headOffset > queueSize) {
285 return false;
286 }
287
288 head = (IODataQueueEntry *)((char *)dataQueue->queue + headOffset);
289 headSize = head->size;
290
291 // we wrapped around to beginning, so read from there
292 // either there was not even room for the header
293 if ((headOffset > UINT32_MAX - DATA_QUEUE_ENTRY_HEADER_SIZE) ||
294 (headOffset + DATA_QUEUE_ENTRY_HEADER_SIZE > queueSize) ||
295 // or there was room for the header, but not for the data
296 (headOffset + DATA_QUEUE_ENTRY_HEADER_SIZE > UINT32_MAX - headSize) ||
297 (headOffset + headSize + DATA_QUEUE_ENTRY_HEADER_SIZE > queueSize)) {
298 // Note: we have to wrap to the beginning even with the UINT32_MAX checks
299 // because we have to support a queueSize of UINT32_MAX.
300 entry = dataQueue->queue;
301 entrySize = entry->size;
302 if ((entrySize > UINT32_MAX - DATA_QUEUE_ENTRY_HEADER_SIZE) ||
303 (entrySize + DATA_QUEUE_ENTRY_HEADER_SIZE > queueSize)) {
304 return false;
305 }
306 newHeadOffset = entrySize + DATA_QUEUE_ENTRY_HEADER_SIZE;
307 // else it is at the end
308 } else {
309 entry = head;
310 entrySize = entry->size;
311 if ((entrySize > UINT32_MAX - DATA_QUEUE_ENTRY_HEADER_SIZE) ||
312 (entrySize + DATA_QUEUE_ENTRY_HEADER_SIZE > UINT32_MAX - headOffset) ||
313 (entrySize + DATA_QUEUE_ENTRY_HEADER_SIZE + headOffset > queueSize)) {
314 return false;
315 }
316 newHeadOffset = headOffset + entrySize + DATA_QUEUE_ENTRY_HEADER_SIZE;
317 }
318 }
319
320 if (entry) {
321 if (data) {
322 if (dataSize) {
323 if (entrySize <= *dataSize) {
324 memcpy(data, &(entry->data), entrySize);
325 OSCompareAndSwap( dataQueue->head, newHeadOffset, (SInt32 *)&dataQueue->head);
326 } else {
327 retVal = FALSE;
328 }
329 } else {
330 retVal = FALSE;
331 }
332 } else {
333 OSCompareAndSwap( dataQueue->head, newHeadOffset, (SInt32 *)&dataQueue->head);
334 }
335
336 if (dataSize) {
337 *dataSize = entrySize;
338 }
339 } else {
340 retVal = FALSE;
341 }
342 } else {
343 retVal = FALSE;
344 }
345
346 return retVal;
347 }
348
349 UInt32 IOSharedDataQueue::getQueueSize()
350 {
351 if (!_reserved) {
352 return 0;
353 }
354 return _reserved->queueSize;
355 }
356
357 Boolean IOSharedDataQueue::setQueueSize(UInt32 size)
358 {
359 if (!_reserved) {
360 return false;
361 }
362 _reserved->queueSize = size;
363 return true;
364 }
365
366 OSMetaClassDefineReservedUnused(IOSharedDataQueue, 0);
367 OSMetaClassDefineReservedUnused(IOSharedDataQueue, 1);
368 OSMetaClassDefineReservedUnused(IOSharedDataQueue, 2);
369 OSMetaClassDefineReservedUnused(IOSharedDataQueue, 3);
370 OSMetaClassDefineReservedUnused(IOSharedDataQueue, 4);
371 OSMetaClassDefineReservedUnused(IOSharedDataQueue, 5);
372 OSMetaClassDefineReservedUnused(IOSharedDataQueue, 6);
373 OSMetaClassDefineReservedUnused(IOSharedDataQueue, 7);