xref: /haiku/src/add-ons/kernel/file_systems/userlandfs/private/RequestAllocator.cpp (revision 85fb3e7df81f8d5b6e47a9a64a53873ea906ea6e)
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: %ld, "
96 			"offset: %ld, size: %ld\n", info.area, info.offset, info.size));
97 		info.target->SetTo(info.area, info.offset, info.size);
98 	}
99 	fDeferredInitInfoCount = 0;
100 }
101 
102 // AllocateRequest
103 status_t
104 RequestAllocator::AllocateRequest(int32 size)
105 {
106 	if (fError != B_OK)
107 		RETURN_ERROR(fError);
108 
109 	fRequestOffset = (fPortReservedOffset + 7) / 8 * 8;
110 
111 	if (size < (int32)sizeof(Request)
112 		|| fRequestOffset + size > fPort->GetCapacity()) {
113 		RETURN_ERROR(fError = B_BAD_VALUE);
114 	}
115 
116 	fRequest = (Request*)((uint8*)fPort->GetBuffer() + fRequestOffset);
117 	fRequestSize = size;
118 	fRequestInPortBuffer = true;
119 	fPort->Reserve(fRequestOffset + fRequestSize);
120 	return B_OK;
121 }
122 
123 // ReadRequest
124 status_t
125 RequestAllocator::ReadRequest(bigtime_t timeout)
126 {
127 	if (fError != B_OK)
128 		RETURN_ERROR(fError);
129 
130 	// read the message from the port
131 	void* message;
132 	size_t messageSize;
133 	status_t error = fPort->Receive(&message, &messageSize, timeout);
134 	if (error != B_OK) {
135 		if (error != B_TIMED_OUT && error != B_WOULD_BLOCK)
136 			RETURN_ERROR(fError = error);
137 		return error;
138 	}
139 
140 	// shouldn't be shorter than the base Request
141 	if (messageSize < (int32)sizeof(Request)) {
142 		free(message);
143 		RETURN_ERROR(fError = B_BAD_DATA);
144 	}
145 
146 	// init the request
147 	fRequest = (Request*)message;
148 	fRequestOffset = 0;
149 	fRequestSize = messageSize;
150 	fRequestInPortBuffer = false;
151 
152 	// relocate the request
153 	fError = relocate_request(fRequest, fRequestSize, fAllocatedAreas,
154 		&fAllocatedAreaCount);
155 	RETURN_ERROR(fError);
156 }
157 
158 // GetRequest
159 Request*
160 RequestAllocator::GetRequest() const
161 {
162 	return fRequest;
163 }
164 
165 // GetRequestSize
166 int32
167 RequestAllocator::GetRequestSize() const
168 {
169 	return fRequestSize;
170 }
171 
172 // AllocateAddress
173 status_t
174 RequestAllocator::AllocateAddress(Address& address, int32 size, int32 align,
175 	void** data, bool deferredInit)
176 {
177 	if (fError != B_OK)
178 		return fError;
179 	if (!fRequest)
180 		RETURN_ERROR(B_NO_INIT);
181 	if (size < 0)
182 		RETURN_ERROR(B_BAD_VALUE);
183 	if (fDeferredInitInfoCount >= MAX_REQUEST_ADDRESS_COUNT)
184 		RETURN_ERROR(B_BAD_VALUE);
185 	// fix the alignment -- valid is 1, 2, 4, 8
186 	if (align <= 0 || size == 0 || (align & 0x1))
187 		align = 1;
188 	else if (align & 0x2)
189 		align = 2;
190 	else if (align & 0x4)
191 		align = 4;
192 	else
193 		align = 8;
194 	// check address location
195 	// Currently we only support relocation of addresses inside the
196 	// port buffer.
197 	int32 addressOffset = (uint8*)&address - (uint8*)fRequest;
198 	if (addressOffset < (int32)sizeof(Request)
199 		|| addressOffset + (int32)sizeof(Address) > fRequestSize) {
200 		RETURN_ERROR(B_BAD_VALUE);
201 	}
202 	// get the next free aligned offset in the port buffer
203 	int32 offset = (fRequestSize + align - 1) / align * align;
204 	// allocate the data
205 	if (fRequestOffset + offset + size <= fPort->GetCapacity()) {
206 		// there's enough free space in the port buffer
207 		fRequestSize = offset + size;
208 		fPort->Reserve(fRequestOffset + fRequestSize);
209 		if (deferredInit) {
210 			DeferredInitInfo& info
211 				= fDeferredInitInfos[fDeferredInitInfoCount];
212 			if (size > 0) {
213 				info.data = (uint8*)malloc(size);
214 				if (!info.data)
215 					RETURN_ERROR(B_NO_MEMORY);
216 			} else
217 				info.data = NULL;
218 			info.area = -1;
219 			info.offset = offset;
220 			info.size = size;
221 			info.inPortBuffer = true;
222 			info.target = &address;
223 			*data = info.data;
224 			fDeferredInitInfoCount++;
225 		} else {
226 			*data = (uint8*)fRequest + offset;
227 			address.SetTo(-1, offset, size);
228 		}
229 	} else {
230 		// not enough room in the port's buffer: we need to allocate an area
231 		if (fAllocatedAreaCount >= MAX_REQUEST_ADDRESS_COUNT)
232 			RETURN_ERROR(B_ERROR);
233 		int32 areaSize = (size + B_PAGE_SIZE - 1) / B_PAGE_SIZE * B_PAGE_SIZE;
234 		area_id area = create_area("request data", data,
235 #ifdef _KERNEL_MODE
236 			B_ANY_KERNEL_ADDRESS,
237 #else
238 			B_ANY_ADDRESS,
239 #endif
240 			areaSize, B_NO_LOCK, B_READ_AREA | B_WRITE_AREA);
241 		if (area < 0)
242 			RETURN_ERROR(area);
243 		fAllocatedAreas[fAllocatedAreaCount++] = area;
244 		if (deferredInit) {
245 			DeferredInitInfo& info
246 				= fDeferredInitInfos[fDeferredInitInfoCount];
247 			info.data = NULL;
248 			info.area = area;
249 			info.offset = 0;
250 			info.size = size;
251 			info.inPortBuffer = false;
252 			info.target = &address;
253 			fDeferredInitInfoCount++;
254 PRINT(("  RequestAllocator::AllocateAddress(): deferred allocated area: "
255 "%ld, size: %ld (%ld), data: %p\n", area, size, areaSize, *data));
256 		} else
257 			address.SetTo(area, 0, size);
258 	}
259 	return B_OK;
260 }
261 
262 // AllocateData
263 status_t
264 RequestAllocator::AllocateData(Address& address, const void* data, int32 size,
265 	int32 align, bool deferredInit)
266 {
267 	status_t error = B_OK;
268 	if (data != NULL) {
269 		void* destination;
270 		error = AllocateAddress(address, size, align, &destination,
271 			deferredInit);
272 		if (error != B_OK)
273 			return error;
274 		if (size > 0)
275 			memcpy(destination, data, size);
276 	} else
277 		address.SetTo(-1, 0, 0);
278 	return error;
279 }
280 
281 // AllocateString
282 status_t
283 RequestAllocator::AllocateString(Address& address, const char* data,
284 	bool deferredInit)
285 {
286 	int32 size = (data ? strlen(data) + 1 : 0);
287 	return AllocateData(address, data, size, 1, deferredInit);
288 }
289 
290 // SetAddress
291 /*status_t
292 RequestAllocator::SetAddress(Address& address, void* data, int32 size)
293 {
294 	if (fError != B_OK)
295 		return fError;
296 	if (!fRequest)
297 		return (fError = B_NO_INIT);
298 	// check address location
299 	// Currently we only support relocation of addresses inside the
300 	// port buffer.
301 	int32 addressOffset = (uint8*)&address - (uint8*)fRequest;
302 	if (addressOffset < (int32)sizeof(Request)
303 		|| addressOffset + (int32)sizeof(Address) > fRequestSize) {
304 		return (fError = B_BAD_VALUE);
305 	}
306 	// if data does itself lie within the port buffer, we store only the
307 	// request relative offset
308 	int32 inRequestOffset = (uint8*)data - (uint8*)fRequest;
309 	if (!data) {
310 		address.SetTo(-1, 0, 0);
311 	} else if (inRequestOffset >= (int32)sizeof(Request)
312 		&& inRequestOffset <= fRequestSize) {
313 		if (inRequestOffset + size > fRequestSize)
314 			return (fError = B_BAD_VALUE);
315 		address.SetTo(-1, inRequestOffset, size);
316 	} else {
317 		// get the area and in-area offset for the address
318 		area_id area;
319 		int32 offset;
320 		fError = get_area_for_address(data, size, &area, &offset);
321 		if (fError != B_OK)
322 			return fError;
323 		// set the address
324 		address.SetTo(area, offset, size);
325 	}
326 	return fError;
327 }*/
328 
329