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