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