1 /* 2 ** Copyright 2011, The Android Open Source Project 3 ** 4 ** Licensed under the Apache License, Version 2.0 (the "License"); 5 ** you may not use this file except in compliance with the License. 6 ** You may obtain a copy of the License at 7 ** 8 ** http://www.apache.org/licenses/LICENSE-2.0 9 ** 10 ** Unless required by applicable law or agreed to in writing, software 11 ** distributed under the License is distributed on an "AS IS" BASIS, 12 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 ** See the License for the specific language governing permissions and 14 ** limitations under the License. 15 */ 16 17 #include "../egl_impl.h" 18 19 #include "egl_cache.h" 20 #include "egl_display.h" 21 #include "egldefs.h" 22 23 #include <fcntl.h> 24 #include <sys/mman.h> 25 #include <sys/stat.h> 26 #include <sys/types.h> 27 #include <unistd.h> 28 29 #ifndef MAX_EGL_CACHE_ENTRY_SIZE 30 #define MAX_EGL_CACHE_ENTRY_SIZE (16 * 1024); 31 #endif 32 33 #ifndef MAX_EGL_CACHE_KEY_SIZE 34 #define MAX_EGL_CACHE_KEY_SIZE (1024); 35 #endif 36 37 #ifndef MAX_EGL_CACHE_SIZE 38 #define MAX_EGL_CACHE_SIZE (64 * 1024); 39 #endif 40 41 // Cache size limits. 42 static const size_t maxKeySize = MAX_EGL_CACHE_KEY_SIZE; 43 static const size_t maxValueSize = MAX_EGL_CACHE_ENTRY_SIZE; 44 static const size_t maxTotalSize = MAX_EGL_CACHE_SIZE; 45 46 // Cache file header 47 static const char* cacheFileMagic = "EGL$"; 48 static const size_t cacheFileHeaderSize = 8; 49 50 // The time in seconds to wait before saving newly inserted cache entries. 51 static const unsigned int deferredSaveDelay = 4; 52 53 // ---------------------------------------------------------------------------- 54 namespace android { 55 // ---------------------------------------------------------------------------- 56 57 #define BC_EXT_STR "EGL_ANDROID_blob_cache" 58 59 // 60 // Callback functions passed to EGL. 61 // 62 static void setBlob(const void* key, EGLsizeiANDROID keySize, 63 const void* value, EGLsizeiANDROID valueSize) { 64 egl_cache_t::get()->setBlob(key, keySize, value, valueSize); 65 } 66 67 static EGLsizeiANDROID getBlob(const void* key, EGLsizeiANDROID keySize, 68 void* value, EGLsizeiANDROID valueSize) { 69 return egl_cache_t::get()->getBlob(key, keySize, value, valueSize); 70 } 71 72 // 73 // egl_cache_t definition 74 // 75 egl_cache_t::egl_cache_t() : 76 mInitialized(false), 77 mBlobCache(NULL) { 78 } 79 80 egl_cache_t::~egl_cache_t() { 81 } 82 83 egl_cache_t egl_cache_t::sCache; 84 85 egl_cache_t* egl_cache_t::get() { 86 return &sCache; 87 } 88 89 void egl_cache_t::initialize(egl_display_t *display) { 90 Mutex::Autolock lock(mMutex); 91 92 egl_connection_t* const cnx = &gEGLImpl; 93 if (cnx->dso && cnx->major >= 0 && cnx->minor >= 0) { 94 const char* exts = display->disp.queryString.extensions; 95 size_t bcExtLen = strlen(BC_EXT_STR); 96 size_t extsLen = strlen(exts); 97 bool equal = !strcmp(BC_EXT_STR, exts); 98 bool atStart = !strncmp(BC_EXT_STR " ", exts, bcExtLen+1); 99 bool atEnd = (bcExtLen+1) < extsLen && 100 !strcmp(" " BC_EXT_STR, exts + extsLen - (bcExtLen+1)); 101 bool inMiddle = strstr(exts, " " BC_EXT_STR " "); 102 if (equal || atStart || atEnd || inMiddle) { 103 PFNEGLSETBLOBCACHEFUNCSANDROIDPROC eglSetBlobCacheFuncsANDROID; 104 eglSetBlobCacheFuncsANDROID = 105 reinterpret_cast<PFNEGLSETBLOBCACHEFUNCSANDROIDPROC>( 106 cnx->egl.eglGetProcAddress( 107 "eglSetBlobCacheFuncsANDROID")); 108 if (eglSetBlobCacheFuncsANDROID == NULL) { 109 ALOGE("EGL_ANDROID_blob_cache advertised, " 110 "but unable to get eglSetBlobCacheFuncsANDROID"); 111 return; 112 } 113 114 eglSetBlobCacheFuncsANDROID(display->disp.dpy, 115 android::setBlob, android::getBlob); 116 EGLint err = cnx->egl.eglGetError(); 117 if (err != EGL_SUCCESS) { 118 ALOGE("eglSetBlobCacheFuncsANDROID resulted in an error: " 119 "%#x", err); 120 } 121 } 122 } 123 124 mInitialized = true; 125 } 126 127 void egl_cache_t::terminate() { 128 Mutex::Autolock lock(mMutex); 129 saveBlobCacheLocked(); 130 mBlobCache = NULL; 131 } 132 133 void egl_cache_t::setBlob(const void* key, EGLsizeiANDROID keySize, 134 const void* value, EGLsizeiANDROID valueSize) { 135 Mutex::Autolock lock(mMutex); 136 137 if (keySize < 0 || valueSize < 0) { 138 ALOGW("EGL_ANDROID_blob_cache set: negative sizes are not allowed"); 139 return; 140 } 141 142 if (mInitialized) { 143 sp<BlobCache> bc = getBlobCacheLocked(); 144 bc->set(key, keySize, value, valueSize); 145 146 if (!mSavePending) { 147 class DeferredSaveThread : public Thread { 148 public: 149 DeferredSaveThread() : Thread(false) {} 150 151 virtual bool threadLoop() { 152 sleep(deferredSaveDelay); 153 egl_cache_t* c = egl_cache_t::get(); 154 Mutex::Autolock lock(c->mMutex); 155 if (c->mInitialized) { 156 c->saveBlobCacheLocked(); 157 } 158 c->mSavePending = false; 159 return false; 160 } 161 }; 162 163 // The thread will hold a strong ref to itself until it has finished 164 // running, so there's no need to keep a ref around. 165 sp<Thread> deferredSaveThread(new DeferredSaveThread()); 166 mSavePending = true; 167 deferredSaveThread->run(); 168 } 169 } 170 } 171 172 EGLsizeiANDROID egl_cache_t::getBlob(const void* key, EGLsizeiANDROID keySize, 173 void* value, EGLsizeiANDROID valueSize) { 174 Mutex::Autolock lock(mMutex); 175 176 if (keySize < 0 || valueSize < 0) { 177 ALOGW("EGL_ANDROID_blob_cache set: negative sizes are not allowed"); 178 return 0; 179 } 180 181 if (mInitialized) { 182 sp<BlobCache> bc = getBlobCacheLocked(); 183 return bc->get(key, keySize, value, valueSize); 184 } 185 return 0; 186 } 187 188 void egl_cache_t::setCacheFilename(const char* filename) { 189 Mutex::Autolock lock(mMutex); 190 mFilename = filename; 191 } 192 193 sp<BlobCache> egl_cache_t::getBlobCacheLocked() { 194 if (mBlobCache == NULL) { 195 mBlobCache = new BlobCache(maxKeySize, maxValueSize, maxTotalSize); 196 loadBlobCacheLocked(); 197 } 198 return mBlobCache; 199 } 200 201 static uint32_t crc32c(const uint8_t* buf, size_t len) { 202 const uint32_t polyBits = 0x82F63B78; 203 uint32_t r = 0; 204 for (size_t i = 0; i < len; i++) { 205 r ^= buf[i]; 206 for (int j = 0; j < 8; j++) { 207 if (r & 1) { 208 r = (r >> 1) ^ polyBits; 209 } else { 210 r >>= 1; 211 } 212 } 213 } 214 return r; 215 } 216 217 void egl_cache_t::saveBlobCacheLocked() { 218 if (mFilename.length() > 0 && mBlobCache != NULL) { 219 size_t cacheSize = mBlobCache->getFlattenedSize(); 220 size_t headerSize = cacheFileHeaderSize; 221 const char* fname = mFilename.string(); 222 223 // Try to create the file with no permissions so we can write it 224 // without anyone trying to read it. 225 int fd = open(fname, O_CREAT | O_EXCL | O_RDWR, 0); 226 if (fd == -1) { 227 if (errno == EEXIST) { 228 // The file exists, delete it and try again. 229 if (unlink(fname) == -1) { 230 // No point in retrying if the unlink failed. 231 ALOGE("error unlinking cache file %s: %s (%d)", fname, 232 strerror(errno), errno); 233 return; 234 } 235 // Retry now that we've unlinked the file. 236 fd = open(fname, O_CREAT | O_EXCL | O_RDWR, 0); 237 } 238 if (fd == -1) { 239 ALOGE("error creating cache file %s: %s (%d)", fname, 240 strerror(errno), errno); 241 return; 242 } 243 } 244 245 size_t fileSize = headerSize + cacheSize; 246 247 uint8_t* buf = new uint8_t [fileSize]; 248 if (!buf) { 249 ALOGE("error allocating buffer for cache contents: %s (%d)", 250 strerror(errno), errno); 251 close(fd); 252 unlink(fname); 253 return; 254 } 255 256 status_t err = mBlobCache->flatten(buf + headerSize, cacheSize); 257 if (err != OK) { 258 ALOGE("error writing cache contents: %s (%d)", strerror(-err), 259 -err); 260 delete [] buf; 261 close(fd); 262 unlink(fname); 263 return; 264 } 265 266 // Write the file magic and CRC 267 memcpy(buf, cacheFileMagic, 4); 268 uint32_t* crc = reinterpret_cast<uint32_t*>(buf + 4); 269 *crc = crc32c(buf + headerSize, cacheSize); 270 271 if (write(fd, buf, fileSize) == -1) { 272 ALOGE("error writing cache file: %s (%d)", strerror(errno), 273 errno); 274 delete [] buf; 275 close(fd); 276 unlink(fname); 277 return; 278 } 279 280 delete [] buf; 281 fchmod(fd, S_IRUSR); 282 close(fd); 283 } 284 } 285 286 void egl_cache_t::loadBlobCacheLocked() { 287 if (mFilename.length() > 0) { 288 size_t headerSize = cacheFileHeaderSize; 289 290 int fd = open(mFilename.string(), O_RDONLY, 0); 291 if (fd == -1) { 292 if (errno != ENOENT) { 293 ALOGE("error opening cache file %s: %s (%d)", mFilename.string(), 294 strerror(errno), errno); 295 } 296 return; 297 } 298 299 struct stat statBuf; 300 if (fstat(fd, &statBuf) == -1) { 301 ALOGE("error stat'ing cache file: %s (%d)", strerror(errno), errno); 302 close(fd); 303 return; 304 } 305 306 // Sanity check the size before trying to mmap it. 307 size_t fileSize = statBuf.st_size; 308 if (fileSize > maxTotalSize * 2) { 309 ALOGE("cache file is too large: %#llx", statBuf.st_size); 310 close(fd); 311 return; 312 } 313 314 uint8_t* buf = reinterpret_cast<uint8_t*>(mmap(NULL, fileSize, 315 PROT_READ, MAP_PRIVATE, fd, 0)); 316 if (buf == MAP_FAILED) { 317 ALOGE("error mmaping cache file: %s (%d)", strerror(errno), 318 errno); 319 close(fd); 320 return; 321 } 322 323 // Check the file magic and CRC 324 size_t cacheSize = fileSize - headerSize; 325 if (memcmp(buf, cacheFileMagic, 4) != 0) { 326 ALOGE("cache file has bad mojo"); 327 close(fd); 328 return; 329 } 330 uint32_t* crc = reinterpret_cast<uint32_t*>(buf + 4); 331 if (crc32c(buf + headerSize, cacheSize) != *crc) { 332 ALOGE("cache file failed CRC check"); 333 close(fd); 334 return; 335 } 336 337 status_t err = mBlobCache->unflatten(buf + headerSize, cacheSize); 338 if (err != OK) { 339 ALOGE("error reading cache contents: %s (%d)", strerror(-err), 340 -err); 341 munmap(buf, fileSize); 342 close(fd); 343 return; 344 } 345 346 munmap(buf, fileSize); 347 close(fd); 348 } 349 } 350 351 // ---------------------------------------------------------------------------- 352 }; // namespace android 353 // ---------------------------------------------------------------------------- 354