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