vfs: check userland buffers before reading them.
[haiku.git] / src / kits / media / SharedBufferList.cpp
blobd020c5c870bd1ebfca16ea6711d473442dbacb27
1 /*
2 * Copyright 2009-2012, Axel Dörfler, axeld@pinc-software.de.
3 * Copyright 2002, Marcus Overhagen. All Rights Reserved.
4 * Distributed under the terms of the MIT License.
5 */
8 /*! Used for BBufferGroup and BBuffer management across teams.
9 Created in the media server, cloned into each BBufferGroup (visible in
10 all address spaces).
13 // TODO: don't use a simple list!
16 #include <SharedBufferList.h>
18 #include <string.h>
20 #include <Autolock.h>
21 #include <Buffer.h>
22 #include <Locker.h>
24 #include <debug.h>
25 #include <DataExchange.h>
28 static BPrivate::SharedBufferList* sList = NULL;
29 static area_id sArea = -1;
30 static int32 sRefCount = 0;
31 static BLocker sLocker("shared buffer list");
34 namespace BPrivate {
37 /*static*/ area_id
38 SharedBufferList::Create(SharedBufferList** _list)
40 CALLED();
42 size_t size = (sizeof(SharedBufferList) + (B_PAGE_SIZE - 1))
43 & ~(B_PAGE_SIZE - 1);
44 SharedBufferList* list;
46 area_id area = create_area("shared buffer list", (void**)&list,
47 B_ANY_ADDRESS, size, B_LAZY_LOCK, B_READ_AREA | B_WRITE_AREA);
48 if (area < 0)
49 return area;
51 status_t status = list->_Init();
52 if (status != B_OK) {
53 delete_area(area);
54 return status;
57 return area;
61 /*static*/ SharedBufferList*
62 SharedBufferList::Get()
64 CALLED();
66 BAutolock _(sLocker);
68 if (atomic_add(&sRefCount, 1) > 0 && sList != NULL)
69 return sList;
71 // ask media_server to get the area_id of the shared buffer list
72 server_get_shared_buffer_area_request areaRequest;
73 server_get_shared_buffer_area_reply areaReply;
74 if (QueryServer(SERVER_GET_SHARED_BUFFER_AREA, &areaRequest,
75 sizeof(areaRequest), &areaReply, sizeof(areaReply)) != B_OK) {
76 ERROR("SharedBufferList::Get() SERVER_GET_SHARED_BUFFER_AREA failed\n");
77 return NULL;
80 sArea = clone_area("shared buffer list clone", (void**)&sList,
81 B_ANY_ADDRESS, B_READ_AREA | B_WRITE_AREA, areaReply.area);
82 if (sArea < 0) {
83 ERROR("SharedBufferList::Get() clone area %" B_PRId32 ": %s\n",
84 areaReply.area, strerror(sArea));
85 return NULL;
88 return sList;
92 /*static*/ void
93 SharedBufferList::Invalidate()
95 delete_area(sArea);
96 sList = NULL;
100 void
101 SharedBufferList::Put()
103 CALLED();
104 BAutolock _(sLocker);
106 if (atomic_add(&sRefCount, -1) == 1)
107 Invalidate();
111 /*! Deletes all BBuffers of the group specified by \a groupReclaimSem, then
112 unmaps the list from memory.
114 void
115 SharedBufferList::DeleteGroupAndPut(sem_id groupReclaimSem)
117 CALLED();
119 if (Lock() == B_OK) {
120 for (int32 i = 0; i < fCount; i++) {
121 if (fInfos[i].reclaim_sem == groupReclaimSem) {
122 // delete the associated buffer
123 delete fInfos[i].buffer;
125 // Decrement buffer count by one, and fill the gap
126 // in the list with its last entry
127 fCount--;
128 if (fCount > 0)
129 fInfos[i--] = fInfos[fCount];
133 Unlock();
136 Put();
140 status_t
141 SharedBufferList::Lock()
143 if (atomic_add(&fAtom, 1) > 0) {
144 status_t status;
145 do {
146 status = acquire_sem(fSemaphore);
147 } while (status == B_INTERRUPTED);
149 return status;
151 return B_OK;
155 status_t
156 SharedBufferList::Unlock()
158 if (atomic_add(&fAtom, -1) > 1)
159 return release_sem(fSemaphore);
161 return B_OK;
165 status_t
166 SharedBufferList::AddBuffer(sem_id groupReclaimSem,
167 const buffer_clone_info& info, BBuffer** _buffer)
169 status_t status = Lock();
170 if (status != B_OK)
171 return status;
173 // Check if the id exists
174 status = CheckID(groupReclaimSem, info.buffer);
175 if (status != B_OK) {
176 Unlock();
177 return status;
179 BBuffer* buffer = new(std::nothrow) BBuffer(info);
180 if (buffer == NULL) {
181 Unlock();
182 return B_NO_MEMORY;
185 if (buffer->Data() == NULL) {
186 // BBuffer::Data() will return NULL if an error occured
187 ERROR("BBufferGroup: error while creating buffer\n");
188 delete buffer;
189 Unlock();
190 return B_ERROR;
193 status = AddBuffer(groupReclaimSem, buffer);
194 if (status != B_OK) {
195 delete buffer;
196 Unlock();
197 return status;
198 } else if (_buffer != NULL)
199 *_buffer = buffer;
201 return Unlock();
205 status_t
206 SharedBufferList::AddBuffer(sem_id groupReclaimSem, BBuffer* buffer)
208 CALLED();
210 if (buffer == NULL)
211 return B_BAD_VALUE;
213 if (fCount == kMaxBuffers) {
214 return B_MEDIA_TOO_MANY_BUFFERS;
217 fInfos[fCount].id = buffer->ID();
218 fInfos[fCount].buffer = buffer;
219 fInfos[fCount].reclaim_sem = groupReclaimSem;
220 fInfos[fCount].reclaimed = true;
221 fCount++;
223 return release_sem_etc(groupReclaimSem, 1, B_DO_NOT_RESCHEDULE);
227 status_t
228 SharedBufferList::CheckID(sem_id groupSem, media_buffer_id id) const
230 CALLED();
232 if (id == 0)
233 return B_OK;
234 if (id < 0)
235 return B_BAD_VALUE;
237 for (int32 i = 0; i < fCount; i++) {
238 if (fInfos[i].id == id
239 && fInfos[i].reclaim_sem == groupSem) {
240 return B_ERROR;
243 return B_OK;
247 status_t
248 SharedBufferList::RequestBuffer(sem_id groupReclaimSem, int32 buffersInGroup,
249 size_t size, media_buffer_id wantID, BBuffer** _buffer, bigtime_t timeout)
251 CALLED();
252 // We always search for a buffer from the group indicated by groupReclaimSem
253 // first.
254 // If "size" != 0, we search for a buffer that is "size" bytes or larger.
255 // If "wantID" != 0, we search for a buffer with this ID.
256 // If "*_buffer" != NULL, we search for a buffer at this address.
258 // If we found a buffer, we also need to mark it in all other groups as
259 // requested and also once need to acquire the reclaim_sem of the other
260 // groups
262 uint32 acquireFlags;
264 if (timeout <= 0) {
265 timeout = 0;
266 acquireFlags = B_RELATIVE_TIMEOUT;
267 } else if (timeout == B_INFINITE_TIMEOUT) {
268 acquireFlags = B_RELATIVE_TIMEOUT;
269 } else {
270 timeout += system_time();
271 acquireFlags = B_ABSOLUTE_TIMEOUT;
274 // With each itaration we request one more buffer, since we need to skip
275 // the buffers that don't fit the request
276 int32 count = 1;
278 do {
279 status_t status;
280 do {
281 status = acquire_sem_etc(groupReclaimSem, count, acquireFlags,
282 timeout);
283 } while (status == B_INTERRUPTED);
285 if (status != B_OK)
286 return status;
288 // try to exit savely if the lock fails
289 status = Lock();
290 if (status != B_OK) {
291 ERROR("SharedBufferList:: RequestBuffer: Lock failed: %s\n",
292 strerror(status));
293 release_sem_etc(groupReclaimSem, count, 0);
294 return B_ERROR;
297 for (int32 i = 0; i < fCount; i++) {
298 // We need a BBuffer from the group, and it must be marked as
299 // reclaimed
300 if (fInfos[i].reclaim_sem == groupReclaimSem
301 && fInfos[i].reclaimed) {
302 if ((size != 0 && size <= fInfos[i].buffer->SizeAvailable())
303 || (*_buffer != 0 && fInfos[i].buffer == *_buffer)
304 || (wantID != 0 && fInfos[i].id == wantID)) {
305 // we found a buffer
306 fInfos[i].reclaimed = false;
307 *_buffer = fInfos[i].buffer;
309 // if we requested more than one buffer, release the rest
310 if (count > 1) {
311 release_sem_etc(groupReclaimSem, count - 1,
312 B_DO_NOT_RESCHEDULE);
315 // And mark all buffers with the same ID as requested in
316 // all other buffer groups
317 _RequestBufferInOtherGroups(groupReclaimSem,
318 fInfos[i].buffer->ID());
320 Unlock();
321 return B_OK;
326 release_sem_etc(groupReclaimSem, count, B_DO_NOT_RESCHEDULE);
327 if (Unlock() != B_OK) {
328 ERROR("SharedBufferList:: RequestBuffer: unlock failed\n");
329 return B_ERROR;
331 // prepare to request one more buffer next time
332 count++;
333 } while (count <= buffersInGroup);
335 ERROR("SharedBufferList:: RequestBuffer: no buffer found\n");
336 return B_ERROR;
340 status_t
341 SharedBufferList::RecycleBuffer(BBuffer* buffer)
343 CALLED();
345 media_buffer_id id = buffer->ID();
347 if (Lock() != B_OK)
348 return B_ERROR;
350 int32 reclaimedCount = 0;
352 for (int32 i = 0; i < fCount; i++) {
353 // find the buffer id, and reclaim it in all groups it belongs to
354 if (fInfos[i].id == id) {
355 reclaimedCount++;
356 if (fInfos[i].reclaimed) {
357 ERROR("SharedBufferList::RecycleBuffer, BBuffer %p, id = %"
358 B_PRId32 " already reclaimed\n", buffer, id);
359 DEBUG_ONLY(debugger("buffer already reclaimed"));
360 continue;
362 fInfos[i].reclaimed = true;
363 release_sem_etc(fInfos[i].reclaim_sem, 1, B_DO_NOT_RESCHEDULE);
367 if (Unlock() != B_OK)
368 return B_ERROR;
370 if (reclaimedCount == 0) {
371 ERROR("shared_buffer_list::RecycleBuffer, BBuffer %p, id = %" B_PRId32
372 " NOT reclaimed\n", buffer, id);
373 return B_ERROR;
376 return B_OK;
380 /*! Returns exactly \a bufferCount buffers from the group specified via its
381 \a groupReclaimSem if successful.
383 status_t
384 SharedBufferList::GetBufferList(sem_id groupReclaimSem, int32 bufferCount,
385 BBuffer** buffers)
387 CALLED();
389 if (Lock() != B_OK)
390 return B_ERROR;
392 int32 found = 0;
394 for (int32 i = 0; i < fCount; i++)
395 if (fInfos[i].reclaim_sem == groupReclaimSem) {
396 buffers[found++] = fInfos[i].buffer;
397 if (found == bufferCount)
398 break;
401 if (Unlock() != B_OK)
402 return B_ERROR;
404 return found == bufferCount ? B_OK : B_ERROR;
408 status_t
409 SharedBufferList::_Init()
411 CALLED();
413 fSemaphore = create_sem(0, "shared buffer list lock");
414 if (fSemaphore < 0)
415 return fSemaphore;
417 fAtom = 0;
419 for (int32 i = 0; i < kMaxBuffers; i++) {
420 fInfos[i].id = -1;
422 fCount = 0;
424 return B_OK;
428 /*! Used by RequestBuffer, call this one with the list locked!
430 void
431 SharedBufferList::_RequestBufferInOtherGroups(sem_id groupReclaimSem,
432 media_buffer_id id)
434 for (int32 i = 0; i < fCount; i++) {
435 // find buffers with same id, but belonging to other groups
436 if (fInfos[i].id == id && fInfos[i].reclaim_sem != groupReclaimSem) {
437 // and mark them as requested
438 // TODO: this can deadlock if BBuffers with same media_buffer_id
439 // exist in more than one BBufferGroup, and RequestBuffer()
440 // is called on both groups (which should not be done).
441 status_t status;
442 do {
443 status = acquire_sem(fInfos[i].reclaim_sem);
444 } while (status == B_INTERRUPTED);
446 // try to skip entries that belong to crashed teams
447 if (status != B_OK)
448 continue;
450 if (fInfos[i].reclaimed == false) {
451 ERROR("SharedBufferList:: RequestBufferInOtherGroups BBuffer "
452 "%p, id = %" B_PRId32 " not reclaimed while requesting\n",
453 fInfos[i].buffer, id);
454 continue;
457 fInfos[i].reclaimed = false;
463 } // namespace BPrivate