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