]> git.saurik.com Git - apple/xnu.git/blame - iokit/Kernel/IOSharedDataQueue.cpp
xnu-3248.30.4.tar.gz
[apple/xnu.git] / iokit / Kernel / IOSharedDataQueue.cpp
CommitLineData
2d21ac55
A
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
143464d5
A
34#ifdef enqueue
35#undef enqueue
36#endif
37
2d21ac55
A
38#ifdef dequeue
39#undef dequeue
40#endif
41
42#define super IODataQueue
43
44OSDefineMetaClassAndStructors(IOSharedDataQueue, IODataQueue)
45
46IOSharedDataQueue *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
60IOSharedDataQueue *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
74Boolean IOSharedDataQueue::initWithCapacity(UInt32 size)
75{
76 IODataQueueAppendix * appendix;
143464d5 77 vm_size_t allocSize;
fe8ab488 78
2d21ac55
A
79 if (!super::init()) {
80 return false;
81 }
fe8ab488 82
143464d5
A
83 _reserved = (ExpansionData *)IOMalloc(sizeof(struct ExpansionData));
84 if (!_reserved) {
85 return false;
86 }
fe8ab488 87
143464d5
A
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);
fe8ab488 93
143464d5
A
94 if (allocSize < size) {
95 return false;
96 }
fe8ab488 97
143464d5 98 dataQueue = (IODataQueueMemory *)IOMallocAligned(allocSize, PAGE_SIZE);
2d21ac55
A
99 if (dataQueue == 0) {
100 return false;
101 }
a1c7dba1 102 bzero(dataQueue, allocSize);
2d21ac55
A
103
104 dataQueue->queueSize = size;
a1c7dba1
A
105// dataQueue->head = 0;
106// dataQueue->tail = 0;
fe8ab488 107
143464d5
A
108 if (!setQueueSize(size)) {
109 return false;
110 }
111
2d21ac55
A
112 appendix = (IODataQueueAppendix *)((UInt8 *)dataQueue + size + DATA_QUEUE_MEMORY_HEADER_SIZE);
113 appendix->version = 0;
a1c7dba1
A
114
115 if (!notifyMsg) {
116 notifyMsg = IOMalloc(sizeof(mach_msg_header_t));
117 if (!notifyMsg)
118 return false;
119 }
120 bzero(notifyMsg, sizeof(mach_msg_header_t));
121
2d21ac55
A
122 setNotificationPort(MACH_PORT_NULL);
123
124 return true;
125}
126
127void IOSharedDataQueue::free()
128{
129 if (dataQueue) {
143464d5 130 IOFreeAligned(dataQueue, round_page(getQueueSize() + DATA_QUEUE_MEMORY_HEADER_SIZE + DATA_QUEUE_MEMORY_APPENDIX_SIZE));
2d21ac55 131 dataQueue = NULL;
a1c7dba1
A
132 if (notifyMsg) {
133 IOFree(notifyMsg, sizeof(mach_msg_header_t));
134 notifyMsg = NULL;
135 }
2d21ac55
A
136 }
137
143464d5
A
138 if (_reserved) {
139 IOFree (_reserved, sizeof(struct ExpansionData));
140 _reserved = NULL;
fe8ab488 141 }
143464d5 142
2d21ac55
A
143 super::free();
144}
145
146IOMemoryDescriptor *IOSharedDataQueue::getMemoryDescriptor()
147{
148 IOMemoryDescriptor *descriptor = 0;
149
150 if (dataQueue != 0) {
143464d5 151 descriptor = IOMemoryDescriptor::withAddress(dataQueue, getQueueSize() + DATA_QUEUE_MEMORY_HEADER_SIZE + DATA_QUEUE_MEMORY_APPENDIX_SIZE, kIODirectionOutIn);
2d21ac55
A
152 }
153
154 return descriptor;
155}
156
157
158IODataQueueEntry * IOSharedDataQueue::peek()
159{
160 IODataQueueEntry *entry = 0;
161
162 if (dataQueue && (dataQueue->head != dataQueue->tail)) {
fe8ab488 163 IODataQueueEntry * head = 0;
2d21ac55
A
164 UInt32 headSize = 0;
165 UInt32 headOffset = dataQueue->head;
143464d5 166 UInt32 queueSize = getQueueSize();
fe8ab488 167
143464d5
A
168 if (headOffset >= queueSize) {
169 return NULL;
170 }
171
fe8ab488
A
172 head = (IODataQueueEntry *)((char *)dataQueue->queue + headOffset);
173 headSize = head->size;
2d21ac55 174
fe8ab488 175 // Check if there's enough room before the end of the queue for a header.
2d21ac55
A
176 // If there is room, check if there's enough room to hold the header and
177 // the data.
fe8ab488 178
143464d5
A
179 if ((headOffset > UINT32_MAX - DATA_QUEUE_ENTRY_HEADER_SIZE) ||
180 (headOffset + DATA_QUEUE_ENTRY_HEADER_SIZE > queueSize) ||
181 (headOffset + DATA_QUEUE_ENTRY_HEADER_SIZE > UINT32_MAX - headSize) ||
182 (headOffset + headSize + DATA_QUEUE_ENTRY_HEADER_SIZE > queueSize)) {
2d21ac55 183 // No room for the header or the data, wrap to the beginning of the queue.
143464d5
A
184 // Note: wrapping even with the UINT32_MAX checks, as we have to support
185 // queueSize of UINT32_MAX
2d21ac55
A
186 entry = dataQueue->queue;
187 } else {
188 entry = head;
189 }
190 }
191
192 return entry;
193}
194
143464d5
A
195Boolean IOSharedDataQueue::enqueue(void * data, UInt32 dataSize)
196{
197 const UInt32 head = dataQueue->head; // volatile
198 const UInt32 tail = dataQueue->tail;
199 const UInt32 entrySize = dataSize + DATA_QUEUE_ENTRY_HEADER_SIZE;
200 IODataQueueEntry * entry;
201
202 // Check for overflow of entrySize
203 if (dataSize > UINT32_MAX - DATA_QUEUE_ENTRY_HEADER_SIZE) {
204 return false;
205 }
206 // Check for underflow of (getQueueSize() - tail)
a1c7dba1 207 if (getQueueSize() < tail || getQueueSize() < head) {
143464d5
A
208 return false;
209 }
210
211 if ( tail >= head )
212 {
213 // Is there enough room at the end for the entry?
214 if ((entrySize <= UINT32_MAX - tail) &&
215 ((tail + entrySize) <= getQueueSize()) )
216 {
217 entry = (IODataQueueEntry *)((UInt8 *)dataQueue->queue + tail);
218
219 entry->size = dataSize;
220 memcpy(&entry->data, data, dataSize);
221
222 // The tail can be out of bound when the size of the new entry
223 // exactly matches the available space at the end of the queue.
224 // The tail can range from 0 to dataQueue->queueSize inclusive.
225
226 OSAddAtomic(entrySize, (SInt32 *)&dataQueue->tail);
227 }
228 else if ( head > entrySize ) // Is there enough room at the beginning?
229 {
230 // Wrap around to the beginning, but do not allow the tail to catch
231 // up to the head.
232
233 dataQueue->queue->size = dataSize;
234
235 // We need to make sure that there is enough room to set the size before
236 // doing this. The user client checks for this and will look for the size
237 // at the beginning if there isn't room for it at the end.
238
239 if ( ( getQueueSize() - tail ) >= DATA_QUEUE_ENTRY_HEADER_SIZE )
240 {
241 ((IODataQueueEntry *)((UInt8 *)dataQueue->queue + tail))->size = dataSize;
242 }
243
244 memcpy(&dataQueue->queue->data, data, dataSize);
245 OSCompareAndSwap(dataQueue->tail, entrySize, &dataQueue->tail);
246 }
247 else
248 {
249 return false; // queue is full
250 }
251 }
252 else
253 {
254 // Do not allow the tail to catch up to the head when the queue is full.
255 // That's why the comparison uses a '>' rather than '>='.
256
257 if ( (head - tail) > entrySize )
258 {
259 entry = (IODataQueueEntry *)((UInt8 *)dataQueue->queue + tail);
260
261 entry->size = dataSize;
262 memcpy(&entry->data, data, dataSize);
263 OSAddAtomic(entrySize, (SInt32 *)&dataQueue->tail);
264 }
265 else
266 {
267 return false; // queue is full
268 }
269 }
270
271 // Send notification (via mach message) that data is available.
272
273 if ( ( head == tail ) /* queue was empty prior to enqueue() */
274 || ( dataQueue->head == tail ) ) /* queue was emptied during enqueue() */
275 {
276 sendDataAvailableNotification();
277 }
278
279 return true;
280}
281
2d21ac55
A
282Boolean IOSharedDataQueue::dequeue(void *data, UInt32 *dataSize)
283{
284 Boolean retVal = TRUE;
285 IODataQueueEntry * entry = 0;
286 UInt32 entrySize = 0;
287 UInt32 newHeadOffset = 0;
288
289 if (dataQueue) {
290 if (dataQueue->head != dataQueue->tail) {
fe8ab488 291 IODataQueueEntry * head = 0;
2d21ac55
A
292 UInt32 headSize = 0;
293 UInt32 headOffset = dataQueue->head;
143464d5 294 UInt32 queueSize = getQueueSize();
fe8ab488 295
143464d5
A
296 if (headOffset > queueSize) {
297 return false;
298 }
299
fe8ab488
A
300 head = (IODataQueueEntry *)((char *)dataQueue->queue + headOffset);
301 headSize = head->size;
2d21ac55 302
143464d5
A
303 // we wrapped around to beginning, so read from there
304 // either there was not even room for the header
305 if ((headOffset > UINT32_MAX - DATA_QUEUE_ENTRY_HEADER_SIZE) ||
306 (headOffset + DATA_QUEUE_ENTRY_HEADER_SIZE > queueSize) ||
307 // or there was room for the header, but not for the data
308 (headOffset + DATA_QUEUE_ENTRY_HEADER_SIZE > UINT32_MAX - headSize) ||
309 (headOffset + headSize + DATA_QUEUE_ENTRY_HEADER_SIZE > queueSize)) {
310 // Note: we have to wrap to the beginning even with the UINT32_MAX checks
311 // because we have to support a queueSize of UINT32_MAX.
2d21ac55
A
312 entry = dataQueue->queue;
313 entrySize = entry->size;
143464d5
A
314 if ((entrySize > UINT32_MAX - DATA_QUEUE_ENTRY_HEADER_SIZE) ||
315 (entrySize + DATA_QUEUE_ENTRY_HEADER_SIZE > queueSize)) {
316 return false;
317 }
2d21ac55 318 newHeadOffset = entrySize + DATA_QUEUE_ENTRY_HEADER_SIZE;
143464d5 319 // else it is at the end
2d21ac55
A
320 } else {
321 entry = head;
322 entrySize = entry->size;
143464d5
A
323 if ((entrySize > UINT32_MAX - DATA_QUEUE_ENTRY_HEADER_SIZE) ||
324 (entrySize + DATA_QUEUE_ENTRY_HEADER_SIZE > UINT32_MAX - headOffset) ||
325 (entrySize + DATA_QUEUE_ENTRY_HEADER_SIZE + headOffset > queueSize)) {
326 return false;
327 }
2d21ac55
A
328 newHeadOffset = headOffset + entrySize + DATA_QUEUE_ENTRY_HEADER_SIZE;
329 }
330 }
fe8ab488 331
2d21ac55
A
332 if (entry) {
333 if (data) {
334 if (dataSize) {
335 if (entrySize <= *dataSize) {
336 memcpy(data, &(entry->data), entrySize);
143464d5 337 OSCompareAndSwap( dataQueue->head, newHeadOffset, (SInt32 *)&dataQueue->head);
2d21ac55
A
338 } else {
339 retVal = FALSE;
340 }
341 } else {
342 retVal = FALSE;
343 }
344 } else {
143464d5 345 OSCompareAndSwap( dataQueue->head, newHeadOffset, (SInt32 *)&dataQueue->head);
2d21ac55
A
346 }
347
348 if (dataSize) {
349 *dataSize = entrySize;
350 }
351 } else {
352 retVal = FALSE;
353 }
354 } else {
355 retVal = FALSE;
356 }
357
358 return retVal;
359}
360
143464d5
A
361UInt32 IOSharedDataQueue::getQueueSize()
362{
363 if (!_reserved) {
364 return 0;
365 }
366 return _reserved->queueSize;
367}
368
369Boolean IOSharedDataQueue::setQueueSize(UInt32 size)
370{
371 if (!_reserved) {
372 return false;
373 }
374 _reserved->queueSize = size;
375 return true;
376}
2d21ac55
A
377
378OSMetaClassDefineReservedUnused(IOSharedDataQueue, 0);
379OSMetaClassDefineReservedUnused(IOSharedDataQueue, 1);
380OSMetaClassDefineReservedUnused(IOSharedDataQueue, 2);
381OSMetaClassDefineReservedUnused(IOSharedDataQueue, 3);
382OSMetaClassDefineReservedUnused(IOSharedDataQueue, 4);
383OSMetaClassDefineReservedUnused(IOSharedDataQueue, 5);
384OSMetaClassDefineReservedUnused(IOSharedDataQueue, 6);
385OSMetaClassDefineReservedUnused(IOSharedDataQueue, 7);