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