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