xref: /haiku/src/add-ons/kernel/file_systems/userlandfs/private/RequestAllocator.cpp (revision dd2a1e350b303b855a50fd64e6cb55618be1ae6a)
1 /*
2  * Copyright 2001-2009, Ingo Weinhold, ingo_weinhold@gmx.de.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 #include <stdlib.h>
7 #include <KernelExport.h>
8 
9 #include <kernel.h>
10 
11 #include "AreaSupport.h"
12 #include "Compatibility.h"
13 #include "Debug.h"
14 #include "Port.h"
15 #include "RequestAllocator.h"
16 
17 
18 // constructor
19 RequestAllocator::RequestAllocator(Port* port)
20 	:
21 	fError(B_NO_INIT),
22 	fPort(NULL),
23 	fRequest(NULL),
24 	fRequestSize(0),
25 	fPortReservedOffset(0),
26 	fAllocatedAreaCount(0),
27 	fDeferredInitInfoCount(0),
28 	fRequestInPortBuffer(false)
29 {
30 	Init(port);
31 }
32 
33 // destructor
34 RequestAllocator::~RequestAllocator()
35 {
36 	Uninit();
37 }
38 
39 // Init
40 status_t
41 RequestAllocator::Init(Port* port)
42 {
43 	Uninit();
44 	if (port) {
45 		fPort = port;
46 		fError = fPort->InitCheck();
47 		fPortReservedOffset = fPort->ReservedSize();
48 	}
49 	return fError;
50 }
51 
52 // Uninit
53 void
54 RequestAllocator::Uninit()
55 {
56 	if (fRequestInPortBuffer)
57 		fPort->Unreserve(fPortReservedOffset);
58 	else
59 		free(fRequest);
60 
61 	for (int32 i = 0; i < fAllocatedAreaCount; i++)
62 		delete_area(fAllocatedAreas[i]);
63 	fAllocatedAreaCount = 0;
64 
65 	for (int32 i = 0; i < fDeferredInitInfoCount; i++) {
66 		if (fDeferredInitInfos[i].inPortBuffer)
67 			free(fDeferredInitInfos[i].data);
68 	}
69 
70 	fDeferredInitInfoCount = 0;
71 	fError = B_NO_INIT;
72 	fPort = NULL;
73 	fRequest = NULL;
74 	fRequestSize = 0;
75 	fPortReservedOffset = 0;
76 }
77 
78 // Error
79 status_t
80 RequestAllocator::Error() const
81 {
82 	return fError;
83 }
84 
85 // FinishDeferredInit
86 void
87 RequestAllocator::FinishDeferredInit()
88 {
89 	if (fError != B_OK)
90 		return;
91 	for (int32 i = 0; i < fDeferredInitInfoCount; i++) {
92 		DeferredInitInfo& info = fDeferredInitInfos[i];
93 		if (info.inPortBuffer) {
94 			if (info.size > 0)
95 				memcpy((uint8*)fRequest + info.offset, info.data, info.size);
96 			free(info.data);
97 		}
98 		PRINT(("RequestAllocator::FinishDeferredInit(): area: %" B_PRId32 ", "
99 			"offset: %" B_PRId32 ", size: %" B_PRId32 "\n", info.area,
100 			info.offset, info.size));
101 		info.target->SetTo(info.area, info.offset, info.size);
102 	}
103 	fDeferredInitInfoCount = 0;
104 }
105 
106 // AllocateRequest
107 status_t
108 RequestAllocator::AllocateRequest(int32 size)
109 {
110 	if (fError != B_OK)
111 		RETURN_ERROR(fError);
112 
113 	fRequestOffset = (fPortReservedOffset + 7) / 8 * 8;
114 
115 	if (size < (int32)sizeof(Request)
116 		|| fRequestOffset + size > fPort->GetCapacity()) {
117 		RETURN_ERROR(fError = B_BAD_VALUE);
118 	}
119 
120 	fRequest = (Request*)((uint8*)fPort->GetBuffer() + fRequestOffset);
121 	fRequestSize = size;
122 	fRequestInPortBuffer = true;
123 	fPort->Reserve(fRequestOffset + fRequestSize);
124 	return B_OK;
125 }
126 
127 // ReadRequest
128 status_t
129 RequestAllocator::ReadRequest(bigtime_t timeout)
130 {
131 	if (fError != B_OK)
132 		RETURN_ERROR(fError);
133 
134 	// read the message from the port
135 	void* message;
136 	size_t messageSize;
137 	status_t error = fPort->Receive(&message, &messageSize, timeout);
138 	if (error != B_OK) {
139 		if (error != B_TIMED_OUT && error != B_WOULD_BLOCK)
140 			RETURN_ERROR(fError = error);
141 		return error;
142 	}
143 
144 	// shouldn't be shorter than the base Request
145 	if (messageSize < (int32)sizeof(Request)) {
146 		free(message);
147 		RETURN_ERROR(fError = B_BAD_DATA);
148 	}
149 
150 	// init the request
151 	fRequest = (Request*)message;
152 	fRequestOffset = 0;
153 	fRequestSize = messageSize;
154 	fRequestInPortBuffer = false;
155 
156 	// relocate the request
157 	fError = relocate_request(fRequest, fRequestSize, fAllocatedAreas,
158 		&fAllocatedAreaCount);
159 	RETURN_ERROR(fError);
160 }
161 
162 // GetRequest
163 Request*
164 RequestAllocator::GetRequest() const
165 {
166 	return fRequest;
167 }
168 
169 // GetRequestSize
170 int32
171 RequestAllocator::GetRequestSize() const
172 {
173 	return fRequestSize;
174 }
175 
176 // AllocateAddress
177 status_t
178 RequestAllocator::AllocateAddress(Address& address, int32 size, int32 align,
179 	void** data, bool deferredInit)
180 {
181 	if (fError != B_OK)
182 		return fError;
183 	if (!fRequest)
184 		RETURN_ERROR(B_NO_INIT);
185 	if (size < 0)
186 		RETURN_ERROR(B_BAD_VALUE);
187 	if (fDeferredInitInfoCount >= MAX_REQUEST_ADDRESS_COUNT)
188 		RETURN_ERROR(B_BAD_VALUE);
189 	// fix the alignment -- valid is 1, 2, 4, 8
190 	if (align <= 0 || size == 0 || (align & 0x1))
191 		align = 1;
192 	else if (align & 0x2)
193 		align = 2;
194 	else if (align & 0x4)
195 		align = 4;
196 	else
197 		align = 8;
198 	// check address location
199 	// Currently we only support relocation of addresses inside the
200 	// port buffer.
201 	int32 addressOffset = (uint8*)&address - (uint8*)fRequest;
202 	if (addressOffset < (int32)sizeof(Request)
203 		|| addressOffset + (int32)sizeof(Address) > fRequestSize) {
204 		RETURN_ERROR(B_BAD_VALUE);
205 	}
206 	// get the next free aligned offset in the port buffer
207 	int32 offset = (fRequestSize + align - 1) / align * align;
208 	// allocate the data
209 	if (fRequestOffset + offset + size <= fPort->GetCapacity()) {
210 		// there's enough free space in the port buffer
211 		fRequestSize = offset + size;
212 		fPort->Reserve(fRequestOffset + fRequestSize);
213 		if (deferredInit) {
214 			DeferredInitInfo& info
215 				= fDeferredInitInfos[fDeferredInitInfoCount];
216 			if (size > 0) {
217 				info.data = (uint8*)malloc(size);
218 				if (!info.data)
219 					RETURN_ERROR(B_NO_MEMORY);
220 			} else
221 				info.data = NULL;
222 			info.area = -1;
223 			info.offset = offset;
224 			info.size = size;
225 			info.inPortBuffer = true;
226 			info.target = &address;
227 			*data = info.data;
228 			fDeferredInitInfoCount++;
229 		} else {
230 			*data = (uint8*)fRequest + offset;
231 			address.SetTo(-1, offset, size);
232 		}
233 	} else {
234 		// not enough room in the port's buffer: we need to allocate an area
235 		if (fAllocatedAreaCount >= MAX_REQUEST_ADDRESS_COUNT)
236 			RETURN_ERROR(B_ERROR);
237 		int32 areaSize = (size + B_PAGE_SIZE - 1) / B_PAGE_SIZE * B_PAGE_SIZE;
238 		area_id area = create_area("request data", data,
239 #ifdef _KERNEL_MODE
240 			B_ANY_KERNEL_ADDRESS,
241 #else
242 			B_ANY_ADDRESS,
243 #endif
244 			areaSize, B_NO_LOCK,
245 #ifdef _KERNEL_MODE
246 			B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA | B_CLONEABLE_AREA
247 #else
248 			B_READ_AREA | B_WRITE_AREA
249 #endif
250 			);
251 		if (area < 0)
252 			RETURN_ERROR(area);
253 		fAllocatedAreas[fAllocatedAreaCount++] = area;
254 		if (deferredInit) {
255 			DeferredInitInfo& info
256 				= fDeferredInitInfos[fDeferredInitInfoCount];
257 			info.data = NULL;
258 			info.area = area;
259 			info.offset = 0;
260 			info.size = size;
261 			info.inPortBuffer = false;
262 			info.target = &address;
263 			fDeferredInitInfoCount++;
264 			PRINT(("  RequestAllocator::AllocateAddress(): deferred allocated "
265 				"area: %" B_PRId32 ", size: %" B_PRId32 " (%" B_PRId32
266 				"), data: %p\n", area, size, areaSize, *data));
267 		} else
268 			address.SetTo(area, 0, size);
269 	}
270 	return B_OK;
271 }
272 
273 // AllocateData
274 status_t
275 RequestAllocator::AllocateData(Address& address, const void* data, int32 size,
276 	int32 align, bool deferredInit)
277 {
278 	status_t error = B_OK;
279 	if (data != NULL) {
280 		void* destination;
281 		error = AllocateAddress(address, size, align, &destination,
282 			deferredInit);
283 		if (error != B_OK)
284 			return error;
285 		if (size > 0) {
286 #ifndef _KERNEL_MODE
287 			memcpy(destination, data, size);
288 #else
289 			if (user_memcpy(destination, data, size) < B_OK)
290 				return B_BAD_ADDRESS;
291 #endif
292 		}
293 	} else
294 		address.SetTo(-1, 0, 0);
295 	return error;
296 }
297 
298 // AllocateString
299 status_t
300 RequestAllocator::AllocateString(Address& address, const char* data,
301 	bool deferredInit)
302 {
303 	int32 size = (data ? strlen(data) + 1 : 0);
304 	return AllocateData(address, data, size, 1, deferredInit);
305 }
306 
307 // SetAddress
308 /*status_t
309 RequestAllocator::SetAddress(Address& address, void* data, int32 size)
310 {
311 	if (fError != B_OK)
312 		return fError;
313 	if (!fRequest)
314 		return (fError = B_NO_INIT);
315 	// check address location
316 	// Currently we only support relocation of addresses inside the
317 	// port buffer.
318 	int32 addressOffset = (uint8*)&address - (uint8*)fRequest;
319 	if (addressOffset < (int32)sizeof(Request)
320 		|| addressOffset + (int32)sizeof(Address) > fRequestSize) {
321 		return (fError = B_BAD_VALUE);
322 	}
323 	// if data does itself lie within the port buffer, we store only the
324 	// request relative offset
325 	int32 inRequestOffset = (uint8*)data - (uint8*)fRequest;
326 	if (!data) {
327 		address.SetTo(-1, 0, 0);
328 	} else if (inRequestOffset >= (int32)sizeof(Request)
329 		&& inRequestOffset <= fRequestSize) {
330 		if (inRequestOffset + size > fRequestSize)
331 			return (fError = B_BAD_VALUE);
332 		address.SetTo(-1, inRequestOffset, size);
333 	} else {
334 		// get the area and in-area offset for the address
335 		area_id area;
336 		int32 offset;
337 		fError = get_area_for_address(data, size, &area, &offset);
338 		if (fError != B_OK)
339 			return fError;
340 		// set the address
341 		address.SetTo(area, offset, size);
342 	}
343 	return fError;
344 }*/
345 
346