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