ShingledFS 2.0
SMR-AwareFUSE-basedFileSystem
|
00001 00015 #define _XOPEN_SOURCE 500 00016 #include <time.h> 00017 #include <errno.h> 00018 #include <fcntl.h> 00019 #include <limits.h> 00020 #include <string.h> 00021 #include <stdlib.h> 00022 #include <unistd.h> 00023 #include <assert.h> 00024 #include <pthread.h> 00025 #include <semaphore.h> 00026 #include <sys/xattr.h> 00027 00028 #include "failstop.h" 00029 #include "buffercache.h" 00030 #include "common.h" 00031 #include "helper.h" 00032 00033 #include "inode.h" 00034 #include "band_bitmap.h" 00035 #include "band_log.h" 00036 00037 #include "emulator.h" 00038 #include "emutypes.h" 00039 #include "emuerror.h" 00040 00041 00042 00043 /* ======================= */ 00044 /* -- Macro Definitions -- */ 00045 /* ======================= */ 00046 00050 #define MAX_FILES 32 00051 00052 00053 00054 /* ====================== */ 00055 /* -- Type Definitions -- */ 00056 /* ====================== */ 00057 00069 typedef struct { 00070 int max_files; 00071 int cached_files; 00072 bc_file_t *list; 00073 00074 pthread_mutex_t lock; 00075 }bc_file_list_t; 00076 00077 00078 00085 typedef struct { 00086 bc_request_t *list; 00087 sem_t count; 00088 pthread_mutex_t lock; 00089 }bc_request_list_t; 00090 00091 00092 00093 /* ====================== */ 00094 /* -- Static Variables -- */ 00095 /* ====================== */ 00096 static bc_file_list_t bc_files; 00097 static bc_request_list_t bc_requests; 00098 00099 00100 00101 /* ========================= */ 00102 /* -- Function Prototypes -- */ 00103 /* ========================= */ 00104 00105 static bc_file_t *bc_load_file(const char *path, inode_t *inode); 00106 static void bc_prepare_tmpFS_file(const char *path, int *fd, 00107 int *fd_xattr); 00108 static void bc_evict_file(bc_file_t *file); 00109 00110 static bc_file_t *bc_file_new(const char *path, int inode); 00111 static void bc_file_enqueue(bc_file_t *file, int insert_at_head); 00112 static void bc_file_dequeue(bc_file_t *file); 00113 static bc_file_t *bc_file_search_toevict(); 00114 00115 static bc_request_t *bc_enqueue_request(bc_file_t *file, bc_operation_t op); 00116 static bc_request_t *bc_dequeue_request(); 00117 00118 static void *bc_worker(); 00119 00120 00121 00122 /* ============================== */ 00123 /* -- Function Implementations -- */ 00124 /* ============================== */ 00125 00134 void 00135 bc_init(size_t cache_size) { 00136 pthread_t tid; 00137 00138 00139 /* Log the call */ 00140 log(LOG_BC_CALLS, "bc_init(cache_size=%ld)\n", cache_size); 00141 00142 00143 /* Validate the inputs */ 00144 if (cache_size == 0) { 00145 log(LOG_BC_ERRORS, "Null arguments passed!\n"); 00146 exit(EXIT_FAILURE); 00147 } 00148 00149 /* Initialize the BC File list */ 00150 bc_files.max_files = MAX_FILES; 00151 bc_files.cached_files = 0; 00152 bc_files.list = NULL; 00153 dod(pthread_mutex_init(&bc_files.lock, NULL) == 0); 00154 00155 00156 /* Initialize the BC Request list */ 00157 bc_requests.list = NULL; 00158 dod(sem_init(&bc_requests.count, 0, 0) == 0); 00159 dod(pthread_mutex_init(&bc_requests.lock, NULL) == 0); 00160 00161 00162 /* Spawn the worker thread */ 00163 dod(pthread_create(&tid, NULL, bc_worker, NULL) == 0); 00164 dod(pthread_detach(tid) == 0); 00165 log(LOG_SFS_ERRORS, "Buffer Cache set to a max. of %d files\n", MAX_FILES); 00166 } 00167 00168 00169 00177 bc_file_t 00178 *bc_open(const char *path) { 00179 int inode_no, fd, fd_xattr; 00180 inode_t inode; 00181 bc_file_t *cur_file, *retval; 00182 char path_xattr[PATH_MAX]; 00183 00184 00185 /* Log the call */ 00186 log(LOG_BC_CALLS, "bc_open(%s)\n", path); 00187 00188 00189 /* Validate the inputs */ 00190 if (path == NULL) { 00191 log(LOG_BC_ERRORS, "Null arguments passed!\n"); 00192 exit(EXIT_FAILURE); 00193 } 00194 00195 00196 /* Identify the inode for the file */ 00197 dod(!xattr_get(path, X_INODE, &inode_no, sizeof(int))); 00198 dod(!get_inode(inode_no, &inode)); 00199 log(LOG_BC_INFO, "Inode %d for %s: Band: %d, RBA: %d, Extent: %d\n", 00200 inode.inode_num, path, inode.band_id, inode.band_offset, 00201 inode.size); 00202 00203 00204 /* Lock the BC Files list */ 00205 dod(!pthread_mutex_lock(&bc_files.lock)); 00206 00207 00208 /* Find the BC File record for the file */ 00209 cur_file = bc_file_search_path(path); 00210 00211 /* Check if the record was found */ 00212 if (cur_file == NULL) { 00213 log(LOG_BC_INFO, "Not one of the %d files in the cache\n", 00214 bc_files.cached_files); 00215 00216 /* Check if there is room to add more files in the cache */ 00217 if (bc_files.max_files == bc_files.cached_files) { 00218 /* Check if there are any files that can be evicted. */ 00219 dod((cur_file = bc_file_search_toevict()) != NULL); 00220 00221 /* Evict the file */ 00222 log(LOG_BC_INFO, "Buffer Cache full! Evicting %s\n", 00223 cur_file->path); 00224 bc_evict_file(cur_file); 00225 } 00226 00227 /* Create the tmpFS file and its symlink on the Unshingled partition */ 00228 bc_prepare_tmpFS_file(path, &fd, &fd_xattr); 00229 00230 /* Insert the record into the list */ 00231 dod((cur_file = bc_load_file(path, &inode)) != NULL); 00232 cur_file->fd = fd; 00233 cur_file->fd_xattr = fd_xattr; 00234 00235 /* Signal to a worker to pick up the newly queued request */ 00236 if (inode.band_id) 00237 dod(!sem_post(&(bc_requests.count))); 00238 } 00239 00240 00241 /* Update the ref-count */ 00242 dod(!pthread_mutex_lock(&(cur_file->mutex))); 00243 cur_file->ref_count++; 00244 dod(!pthread_mutex_unlock(&bc_files.lock)); 00245 log(LOG_BC_INFO, "%s created and opened in Buffer Cache. Ref-count: %d\n", 00246 cur_file->path, cur_file->ref_count); 00247 00248 00249 /* Check if the file needs to be loaded from the EDI */ 00250 if ((inode.band_id != 0) || ((inode.band_id == 0) && (cur_file->dirty))) { 00251 /* Check if the file is being loaded or stored */ 00252 while ((cur_file->state == ST_LOADING) || 00253 (cur_file->state == ST_STORING)) { 00254 log(LOG_BC_INFO, "File state: %s... going to sleep!\n", 00255 (cur_file->state == ST_LOADING) ? "Loading" : "Storing"); 00256 dod(!pthread_cond_wait(&(cur_file->condvar), &(cur_file->mutex))); 00257 log(LOG_BC_INFO, "Awoken by worker...\n"); 00258 } 00259 dod((cur_file->state == ST_LOADED) || (cur_file->state == ST_STORED)); 00260 } 00261 00262 /* Check if the file has been stored. This condition is possible when a file 00263 * is created, opened, and closed without writing anything! */ 00264 if (cur_file->state == ST_STORED) { 00265 /* Re-open the file. If there is an error, it will be handled! */ 00266 log(LOG_BC_INFO, "File already loaded in Buffer Cache!\n"); 00267 dod(cur_file->fd < 0); 00268 00269 xattr_path(cur_file->path, path_xattr); 00270 dod((cur_file->fd = open(cur_file->path, O_RDWR)) > 0); 00271 dod((cur_file->fd_xattr = open(path_xattr, O_RDWR)) > 0); 00272 00273 /* Update the file state */ 00274 cur_file->state = ST_LOADED; 00275 00276 /* Reposition the BC File record at the head of the BC File List */ 00277 dod(!pthread_mutex_lock(&bc_files.lock)); 00278 bc_file_dequeue(cur_file); 00279 bc_file_enqueue(cur_file, 1); 00280 dod(!pthread_mutex_unlock(&bc_files.lock)); 00281 } 00282 00283 /* Ensure that the file was loaded */ 00284 retval = cur_file; 00285 dod(cur_file->fd > 0); 00286 dod(cur_file->fd_xattr > 0); 00287 00288 /* Unlock the BC File record */ 00289 dod(!pthread_mutex_unlock(&(cur_file->mutex))); 00290 00291 return retval; 00292 } 00293 00294 00295 00301 void 00302 bc_close(bc_file_t *file) { 00303 inode_t inode; 00304 struct stat statbuf; 00305 00306 00307 /* Log the call */ 00308 log(LOG_BC_CALLS, "bc_close(%s)\n", file->path); 00309 00310 00311 /* Validate the inputs */ 00312 if (file == NULL) { 00313 log(LOG_BC_ERRORS, "Null arguments passed!\n"); 00314 exit(EXIT_FAILURE); 00315 } 00316 00317 00318 /* Lock the BC Files list */ 00319 dod(!pthread_mutex_lock(&bc_files.lock)); 00320 00321 /* Lock the BC File record */ 00322 dod(!pthread_mutex_lock(&(file->mutex))); 00323 00324 00325 /* Decrement the ref-count */ 00326 file->ref_count--; 00327 00328 /* Check to see if this is the last reference to the file */ 00329 if (file->ref_count == 0) { 00330 /* Check if the file is dirty. If so, hand it off to the worker. If not, 00331 * then handle the closing here. */ 00332 if (file->dirty) { 00333 /* Mark the file state as STORING */ 00334 file->state = ST_STORING; 00335 00336 /* Reposition the file entry */ 00337 bc_file_dequeue(file); 00338 bc_file_enqueue(file, 0); 00339 00340 /* Enqueue a Request to write this file back to the EDI */ 00341 log(LOG_BC_INFO, "Ref-count is 0; signaling worker to save file\n"); 00342 dod(bc_enqueue_request(file, OP_STORE) != NULL); 00343 00344 /* Signal to a worker to pick up the newly queued request */ 00345 dod(!sem_post(&(bc_requests.count))); 00346 00347 } else { 00348 /* Check if the file was deleted */ 00349 if (file->deleted) { 00350 /* Read the file inode */ 00351 dod(!get_inode(file->inode, &inode)); 00352 log(LOG_BC_INFO, "File has been deleted while open. Freeing " 00353 "file inode %d: Band: %d, RBA: %d, Extent: %d" 00354 ", Inode No.: %d, Append Log Entry No.: %d\n", 00355 file->inode, inode.band_id, 00356 inode.band_offset, inode.size, inode.inode_num, 00357 inode.entrynumber); 00358 00359 /* Unlink the inode */ 00360 inode_unlink(inode); 00361 } 00362 00363 /* Mark the file as clean */ 00364 file->dirty = 0; 00365 00366 /* Copy the file's stats to disk */ 00367 if (!file->deleted) { 00368 dod(!fstat(file->fd, &statbuf)); 00369 dod(!fsetxattr(file->fd_xattr, X_STATS, &statbuf, 00370 sizeof(struct stat), 0)); 00371 } 00372 00373 /* Mark the state as STORED and close the open files */ 00374 close(file->fd); 00375 close(file->fd_xattr); 00376 file->fd = -1; 00377 file->fd_xattr = -1; 00378 log(LOG_BC_INFO, "Closed %s on tmpFS.\n", file->path); 00379 file->state = ST_STORED; 00380 } 00381 } 00382 00383 00384 /* Unlock the BC File record */ 00385 dod(!pthread_mutex_unlock(&(file->mutex))); 00386 00387 /* Unlock the BC File list */ 00388 dod(!pthread_mutex_unlock(&bc_files.lock)); 00389 } 00390 00391 00392 00400 int 00401 bc_unlink(const char *path) { 00402 bc_file_t *cur_file; 00403 char path_bc[PATH_MAX]; 00404 00405 00406 /* Log the call */ 00407 log(LOG_BC_CALLS, "bc_unlink(path=%s)\n", path); 00408 00409 00410 /* Validate the inputs */ 00411 if (path == NULL) { 00412 log(LOG_BC_ERRORS, "Null arguments passed!\n"); 00413 exit(EXIT_FAILURE); 00414 } 00415 00416 00417 /* Generate the tmpFS file path for the path */ 00418 bc_generate_tmpFS_path(path_bc, path); 00419 00420 00421 /* Lock the BC File List */ 00422 dod(!pthread_mutex_lock(&bc_files.lock)); 00423 00424 /* Search for the BC File record for the file */ 00425 if ((cur_file = bc_file_search_path(path)) == NULL) { 00426 log(LOG_BC_INFO, "%s not present in BC!\n", path_bc); 00427 dod(!pthread_mutex_unlock(&bc_files.lock)); 00428 return -1; 00429 } 00430 00431 /* Lock the file record */ 00432 dod(!pthread_mutex_lock(&(cur_file->mutex))); 00433 00434 /* Check the state of the file */ 00435 if (cur_file->state == ST_STORED) { 00436 /* Dequeue the BC File record */ 00437 bc_file_dequeue(cur_file); 00438 log(LOG_BC_INFO, "Deleted %s from Buffer Cache\n", cur_file->path); 00439 free(cur_file); 00440 goto cleanup_file; 00441 00442 } else { 00443 /* Mark the file as deleted */ 00444 cur_file->deleted = 1; 00445 strcat(cur_file->path, "(deleted)"); 00446 log(LOG_BC_INFO, "Set deleted to TRUE in BC File record for %s\n", 00447 cur_file->path); 00448 00449 } 00450 00451 dod(!pthread_mutex_unlock(&(cur_file->mutex))); 00452 00453 cleanup_file: 00454 /* Delete the tmpFS copy of the file */ 00455 dod(!unlink(path_bc)); 00456 log(LOG_BC_INFO, "Deleted %s from tmpFS!\n", path_bc); 00457 00458 /* Unlock the BC File List */ 00459 dod(!pthread_mutex_unlock(&bc_files.lock)); 00460 00461 return 0; 00462 } 00463 00464 00465 00474 int 00475 bc_rename(const char *path, 00476 const char *path_new) { 00477 char path_bc[PATH_MAX], path_bc_new[PATH_MAX]; 00478 bc_file_t *cur_file, *renamed_file; 00479 inode_t inode; 00480 00481 00482 /* Log the call */ 00483 log(LOG_BC_CALLS, "bc_rename(path=%s, path_new=%s)\n", path, path_new); 00484 00485 00486 /* Validate the inputs */ 00487 if ((path == NULL) || (path_new == NULL)) { 00488 log(LOG_BC_ERRORS, "Null arguments passed!\n"); 00489 exit(EXIT_FAILURE); 00490 } 00491 00492 00493 /* Generate the tmpFS file path for the path */ 00494 bc_generate_tmpFS_path(path_bc, path); 00495 00496 00497 /* Lock the BC File List */ 00498 dod(!pthread_mutex_lock(&bc_files.lock)); 00499 00500 /* Search for the BC File record for the file */ 00501 if ((cur_file = bc_file_search_path(path)) == NULL) { 00502 log(LOG_BC_INFO, "%s not present in BC!\n", path_bc); 00503 assert(!pthread_mutex_unlock(&bc_files.lock)); 00504 return -1; 00505 } 00506 00507 /* Store a pointer to the BC File Record of the file to be renamed */ 00508 renamed_file = cur_file; 00509 log(LOG_BC_INFO, "File to rename: Path: %s, Inode No: %d, Descriptor: %d, " 00510 "RefCount: %d, State: %d, Dirty: %d, Deleted: %d\n", 00511 renamed_file->path, renamed_file->inode, renamed_file->fd, 00512 renamed_file->ref_count, renamed_file->state, 00513 renamed_file->dirty, renamed_file->deleted); 00514 00515 00516 /* Ensure that the renamed file does not exist in the Buffer Cache. If so, 00517 * its entry needs to be removed because the rename() function will 00518 * atomically remove it. */ 00519 if ((cur_file = bc_file_search_path(path_new)) != NULL) { 00520 log(LOG_BC_INFO, "File target name: Path: %s, Inode No: %d, " 00521 "Descriptor: %d, RefCount: %d, State: %d, Dirty: %d, " 00522 "Deleted: %d\n", cur_file->path, cur_file->inode, 00523 cur_file->fd, cur_file->ref_count, cur_file->state, 00524 cur_file->dirty, cur_file->deleted); 00525 00526 /* Check the state of the file */ 00527 dod(!pthread_mutex_lock(&(cur_file->mutex))); 00528 if (cur_file->state == ST_STORED) { 00529 /* Read the inode information for the file */ 00530 dod(!get_inode(cur_file->inode, &inode)); 00531 00532 /* Unlink the inode */ 00533 inode_unlink(inode); 00534 log(LOG_BC_INFO, "Freed inode %d\n", cur_file->inode); 00535 00536 00537 /* Dequeue the BC File record */ 00538 bc_file_dequeue(cur_file); 00539 log(LOG_BC_INFO, "Deleted BC File record for %s\n", cur_file->path); 00540 free(cur_file); 00541 goto cleanup_file; 00542 00543 } else { 00544 /* Mark the file as deleted */ 00545 cur_file->deleted = 1; 00546 strcat(cur_file->path, "(deleted)"); 00547 log(LOG_BC_INFO, "Set deleted to TRUE in BC File record for %s\n", 00548 cur_file->path); 00549 00550 } 00551 dod(!pthread_mutex_unlock(&(cur_file->mutex))); 00552 } 00553 00554 00555 cleanup_file: 00556 /* Update the name of the file in its BC File record */ 00557 dod(!pthread_mutex_lock(&(renamed_file->mutex))); 00558 strcpy(renamed_file->path, path_new); 00559 dod(!pthread_mutex_unlock(&(renamed_file->mutex))); 00560 log(LOG_BC_INFO, "Updated BC Record for original file to new name %s\n", 00561 renamed_file->path); 00562 00563 00564 /* Unlock the BC File List */ 00565 dod(!pthread_mutex_unlock(&bc_files.lock)); 00566 00567 00568 /* Generate the tmpFS file path for the path */ 00569 bc_generate_tmpFS_path(path_bc_new, path_new); 00570 00571 /* Rename the tmpFS copy of the file */ 00572 dod(!rename(path_bc, path_bc_new)); 00573 log(LOG_BC_INFO, "%s renamed to %s\n", path_bc, path_bc_new); 00574 00575 00576 return 0; 00577 } 00578 00579 00580 00589 void 00590 bc_clean() { 00591 bc_file_t *cur_file; 00592 00593 00594 /* Lock the BC Files list */ 00595 assert(!pthread_mutex_lock(&bc_files.lock)); 00596 00597 /* Check if there are any files that can be evicted using an LRU 00598 * heuristic. */ 00599 log(LOG_BC_INFO, "Buffer Cache full. Searching for a file to evict.\n"); 00600 dod((cur_file = bc_file_search_toevict()) != NULL); 00601 00602 /* Evict the file */ 00603 log(LOG_BC_INFO, "Evicting %s\n", cur_file->path); 00604 bc_evict_file(cur_file); 00605 00606 /* Unlock the BC Files list */ 00607 dod(!pthread_mutex_unlock(&bc_files.lock)); 00608 } 00609 00610 00611 00612 /* ===================================== */ 00613 /* -- Static Function Implementations -- */ 00614 /* ===================================== */ 00615 00624 static bc_file_t 00625 *bc_load_file(const char *path, 00626 inode_t *inode) { 00627 bc_file_t *new_file; 00628 00629 00630 /* Validate the inputs */ 00631 if ((path == NULL) || (inode == NULL)) { 00632 log(LOG_BC_ERRORS, "Null arguments passed!\n"); 00633 exit(EXIT_FAILURE); 00634 } 00635 00636 00637 /* Create a new cached file record */ 00638 new_file = bc_file_new(path, inode->inode_num); 00639 00640 /* Enqueue the new file in the BC Files list */ 00641 bc_file_enqueue(new_file, 1); 00642 00643 /* Enqueue a File Load request to load the new file from the EDI */ 00644 if (inode->band_id != 0) { 00645 bc_enqueue_request(new_file, OP_LOAD); 00646 log(LOG_BC_INFO, "Enqueued request to worker to load file from EDI.\n"); 00647 } else 00648 log(LOG_BC_INFO, "Newly created file. No need to call the worker.\n"); 00649 00650 return new_file; 00651 } 00652 00653 00654 00668 static void 00669 bc_prepare_tmpFS_file(const char *path, 00670 int *fd, 00671 int *fd_xattr) { 00672 char path_bc[PATH_MAX]; 00673 struct stat statbuf; 00674 00675 00676 /* Validate the inputs */ 00677 if ((path == NULL) || (fd == NULL) || (fd_xattr == NULL)) { 00678 log(LOG_BC_ERRORS, "Null arguments passed!\n"); 00679 exit(EXIT_FAILURE); 00680 } 00681 00682 00683 /* Open the Attribute Replication file */ 00684 xattr_path(path, path_bc); 00685 dod((*fd_xattr = open(path_bc, O_RDWR)) > 0); 00686 00687 /* Read the file stats */ 00688 dod(fgetxattr(*fd_xattr, X_STATS, &statbuf, sizeof(struct stat)) >= 0); 00689 00690 /* Generate the tmpFS file path */ 00691 bc_generate_tmpFS_path(path_bc, path); 00692 log(LOG_BC_INFO, "Generated tmpFS filename %s\n", path_bc); 00693 00694 00695 /* Create the file in tmpFS */ 00696 dod((*fd = open(path_bc, O_CREAT|O_RDWR, statbuf.st_mode)) > 0); 00697 00698 /* Delete the regular file */ 00699 dod(!unlink(path)); 00700 00701 /* Create a symbolic link to the tmpFS file */ 00702 dod(!symlink(path_bc, path)); 00703 log(LOG_BC_INFO, "Symlink %s created; pointing to %s on descriptor %d\n", 00704 path, path_bc, *fd); 00705 } 00706 00707 00708 00722 static void 00723 bc_evict_file(bc_file_t *file) { 00724 char path_tmpfs[PATH_MAX]; 00725 struct stat statbuf; 00726 00727 00728 /* Validate the inputs */ 00729 if ((file == NULL) || (bc_files.cached_files == 0)) { 00730 log(LOG_BC_ERRORS, "Null arguments passed!\n"); 00731 exit(EXIT_FAILURE); 00732 } 00733 00734 00735 /* Identify the file in the buffer using the file path */ 00736 if (!file->deleted) { 00737 dod(realpath(file->path, path_tmpfs) != NULL); 00738 00739 /* Read the stats of the file */ 00740 dod(!stat(file->path, &statbuf)); 00741 00742 /* Remove the symlink on the unshingled partition */ 00743 dod(!unlink(file->path)); 00744 00745 /* Recreate the file */ 00746 dod(!mknod(file->path, statbuf.st_mode, statbuf.st_dev)); 00747 00748 /* Remove the file from tmpFS */ 00749 dod(!unlink(path_tmpfs)); 00750 00751 00752 /* Update the file's metadata in the Attribute Replication file */ 00753 xattr_set(file->path, X_STATS, &statbuf, sizeof(struct stat), 0); 00754 } 00755 00756 00757 /* Remove the element from the list and free it */ 00758 bc_file_dequeue(file); 00759 free(file); 00760 } 00761 00762 00763 00773 static bc_file_t 00774 *bc_file_new(const char *path, 00775 int inode) { 00776 bc_file_t *new_file; 00777 00778 00779 /* Validate the inputs */ 00780 if (path == NULL) { 00781 log(LOG_BC_ERRORS, "Null arguments passed!\n"); 00782 exit(EXIT_FAILURE); 00783 } 00784 00785 00786 /* Create a new cached file record */ 00787 dod((new_file = malloc(sizeof(bc_file_t))) != NULL); 00788 00789 /* Initialize the new record and set its values */ 00790 memset(new_file, 0, sizeof(bc_file_t)); 00791 new_file->state = ST_LOADING; 00792 new_file->inode = inode; 00793 strcpy(new_file->path, path); 00794 new_file->fd = -1; 00795 new_file->fd_xattr = -1; 00796 new_file->deleted = 0; 00797 00798 00799 /* Initialize the synchronization primitives */ 00800 dod(!pthread_mutex_init(&(new_file->mutex), NULL)); 00801 dod(!pthread_cond_init(&(new_file->condvar), NULL)); 00802 00803 return new_file; 00804 } 00805 00806 00807 00819 static void 00820 bc_file_enqueue(bc_file_t *file, 00821 int insert_at_head) { 00822 /* Validate the inputs */ 00823 if (file == NULL) { 00824 log(LOG_BC_ERRORS, "Null arguments passed!\n"); 00825 exit(EXIT_FAILURE); 00826 } 00827 00828 00829 /* Insert the record into the BC files list */ 00830 if (bc_files.list == NULL) { 00831 bc_files.list = file; 00832 file->prev = file->next = file; 00833 } else { 00834 /* Insert the new element into the list just before the head */ 00835 bc_files.list->prev->next = file; 00836 file->prev = bc_files.list->prev; 00837 file->next = bc_files.list; 00838 bc_files.list->prev = file; 00839 00840 /* Reposition the list head, if needed */ 00841 if (insert_at_head) 00842 bc_files.list = file; 00843 } 00844 00845 /* Increment the record count */ 00846 bc_files.cached_files++; 00847 } 00848 00849 00850 00860 static void 00861 bc_file_dequeue(bc_file_t *file) { 00862 /* Validate the inputs */ 00863 if (file == NULL) { 00864 log(LOG_BC_ERRORS, "Null arguments passed!\n"); 00865 exit(EXIT_FAILURE); 00866 } 00867 00868 00869 /* Check if the list has only 1 record */ 00870 if (bc_files.cached_files == 1) { 00871 /* Since there is only one node, clear the list */ 00872 bc_files.list = NULL; 00873 } else { 00874 /* Fix the pointers for the previous and next nodes */ 00875 file->prev->next = file->next; 00876 file->next->prev = file->prev; 00877 00878 /* Adjust the list head, if needed */ 00879 if (file == bc_files.list) 00880 bc_files.list = file->next; 00881 } 00882 00883 /* Decrement the file count */ 00884 bc_files.cached_files--; 00885 } 00886 00887 00888 00900 bc_file_t 00901 *bc_file_search_path(const char *path) { 00902 int i; 00903 bc_file_t *file; 00904 00905 00906 /* Validate the inputs */ 00907 if (path == NULL) { 00908 log(LOG_BC_ERRORS, "Null arguments passed!\n"); 00909 exit(EXIT_FAILURE); 00910 } 00911 00912 00913 /* Iterate over the BC File List */ 00914 for(i=0, file=bc_files.list; i<bc_files.cached_files; file=file->next, i++) 00915 if (!(strcmp(file->path, path))) 00916 return file; 00917 00918 return NULL; 00919 } 00920 00921 00922 00938 static bc_file_t 00939 *bc_file_search_toevict() { 00940 int i; 00941 bc_file_t *cur_file; 00942 00943 00944 /* Iterate over the BC File list to find the record for the file */ 00945 for(i=0, cur_file=bc_files.list; i<bc_files.cached_files; 00946 cur_file=cur_file->next, i++) { 00947 /* Lock the current file record */ 00948 dod(!pthread_mutex_lock(&(cur_file->mutex))); 00949 00950 /* Check the file's ref-count */ 00951 if (cur_file->ref_count == 0) { 00952 /* Check if the file has been stored */ 00953 if (cur_file->state == ST_STORED) { 00954 /* Return the current file as the eviction candidate */ 00955 log(LOG_BC_INFO, "Found %s for eviction! State: %d, " 00956 "Ref_count: %d\n", cur_file->path, 00957 cur_file->state, cur_file->ref_count); 00958 return cur_file; 00959 00960 } else if ((cur_file->state == ST_LOADED) || 00961 (cur_file->state == ST_STORING)) { 00962 /* Ensure that the file is LOADED or STORING. It could be 00963 * possible that the file has a request for STORING in the queue 00964 * that has not been processed yet. So the state could still be 00965 * LOADED 00966 * 00967 * @todo Verify the above. State is being changed from LOADED to 00968 * STORING synchronously in shingledfs_close(). So this might be 00969 * an incorrect assumption! 00970 */ 00971 00972 /* Increment the ref_count to force the worker to signal when 00973 * the file has been stored. */ 00974 cur_file->ref_count++; 00975 00976 /* Block waiting for the worker to finish writing the file */ 00977 dod(!pthread_cond_wait(&cur_file->condvar, &cur_file->mutex)); 00978 00979 /* Close the file */ 00980 close(cur_file->fd); 00981 close(cur_file->fd_xattr); 00982 00983 /* Decrement the ref_count to 0 */ 00984 cur_file->ref_count = 0; 00985 cur_file->fd = -1; 00986 cur_file->fd_xattr = -1; 00987 cur_file->state = ST_STORED; 00988 00989 /* Unlock the current file record */ 00990 dod(!pthread_mutex_unlock(&(cur_file->mutex))); 00991 00992 /* Return the current file as the eviction candidate */ 00993 return cur_file; 00994 } 00995 } 00996 00997 /* Unlock the current file record */ 00998 dod(!pthread_mutex_unlock(&(cur_file->mutex))); 00999 } 01000 01001 /* Return failure */ 01002 return NULL; 01003 } 01004 01005 01006 01015 static bc_request_t 01016 *bc_enqueue_request(bc_file_t *file, 01017 bc_operation_t op) { 01018 bc_request_t *request; 01019 01020 01021 /* Create a new cached file record */ 01022 dod((request = malloc(sizeof(bc_request_t))) != NULL); 01023 01024 /* Initialize the record */ 01025 request->file = file; 01026 request->op = op; 01027 01028 /* Lock the BC Request list */ 01029 dod(!pthread_mutex_lock(&(bc_requests.lock))); 01030 01031 /* Add the new request to the BC Request list */ 01032 request->next = bc_requests.list; 01033 bc_requests.list = request; 01034 01035 /* Unlock the BC Request list */ 01036 dod(!pthread_mutex_unlock(&(bc_requests.lock))); 01037 01038 /* Return the new request */ 01039 return request; 01040 } 01041 01042 01043 01053 static bc_request_t 01054 *bc_dequeue_request() { 01055 bc_request_t *request = NULL; 01056 01057 01058 /* Lock the BC Request list */ 01059 dod(!pthread_mutex_lock(&(bc_requests.lock))); 01060 01061 /* Get the request at the head of the BC Request list */ 01062 if (bc_requests.list != NULL) { 01063 request = bc_requests.list; 01064 bc_requests.list = bc_requests.list->next; 01065 request->next = NULL; 01066 } 01067 01068 /* Unlock the BC Request list */ 01069 dod(!pthread_mutex_unlock(&(bc_requests.lock))); 01070 01071 /* Return the new request */ 01072 return request; 01073 } 01074 01075 01076 01090 void 01091 bc_generate_tmpFS_path(char *path_out, 01092 const char *path_in) { 01093 int len; 01094 01095 01096 /* Validate the inputs */ 01097 if ((path_in == NULL) || (path_out == NULL)) { 01098 log(LOG_BC_ERRORS, "Null arguments passed!\n"); 01099 exit(EXIT_FAILURE); 01100 } 01101 01102 01103 /* Ensure that the path doesn't exceed PATH_MAX */ 01104 len = strlen(SFSDATA.path_unshingled); 01105 if ((strlen(SFSDATA.path_tmpfs) + len) >= PATH_MAX) { 01106 log(LOG_BC_ERRORS, "tmpFS for %s exceeds PATH_MAX!\n", path_in); 01107 exit(EXIT_FAILURE); 01108 } 01109 01110 /* Construct the tmpFS file path */ 01111 strcpy(path_out, SFSDATA.path_tmpfs); 01112 strcat(path_out, &(path_in[len])); 01113 len = strlen(SFSDATA.path_tmpfs); 01114 for(++len; len<strlen(path_out); len++) 01115 if (path_out[len] == '/') 01116 path_out[len] = '_'; 01117 } 01118 01119 01120 01127 static void 01128 *bc_worker() { 01129 bc_request_t *request; 01130 char *buf; 01131 inode_t inode; 01132 size_t blocks, size, written; 01133 ssize_t ret; 01134 struct stat statbuf; 01135 01136 01137 #ifdef GENERATE_LOGS 01138 char filename[50]; 01139 int len; 01140 time_t curtime = time(NULL); 01141 01142 01143 /* Initialize the logging interface */ 01144 len = strftime(filename, 50, "/tmp/smr%Y%m%d%H%M%S_worker.log", 01145 localtime(&curtime)); 01146 if (!len) { 01147 fprintf(stderr, "Error generating log file name: %s\n", filename); 01148 assert(0); 01149 } 01150 log_open(filename); 01151 #endif 01152 01153 01154 /* Log the call */ 01155 log(LOG_WRK_INFO, "Initializing...\n"); 01156 01157 01158 /* Allocate space for the buffer used to transfer data */ 01159 size = (size_t)SFSDATA.edi_blksize * (size_t)SFSDATA.edi_bandsize; 01160 dod((buf = malloc(size)) != NULL); 01161 01162 01163 /* Loop forever */ 01164 log(LOG_WRK_INFO, "Waiting for incoming requests...\n\n"); 01165 while (1) { 01166 /* Wait for BC Requests */ 01167 dod(!sem_wait(&bc_requests.count)); 01168 01169 01170 /* Dequeue the request */ 01171 if ((request = bc_dequeue_request()) == NULL) { 01172 log(LOG_WRK_ERRORS, "Error dequeuing request. No request found!\n"); 01173 continue; 01174 } 01175 log(LOG_WRK_INFO, "New Request: Path: %s, Op: %d, Inode No: %d, " 01176 "fd: %d, fd_xattr: %d, RefCount: %d, State: %d, " 01177 "Dirty: %d, Deleted: %d\n", request->file->path, 01178 request->op, request->file->inode, request->file->fd, 01179 request->file->fd_xattr, request->file->ref_count, 01180 request->file->state, request->file->dirty, 01181 request->file->deleted); 01182 01183 01184 /* Identify the type of request */ 01185 switch(request->op) { 01186 case OP_LOAD: 01187 /* Read the stats of the file */ 01188 dod(fgetxattr(request->file->fd_xattr, X_STATS, &statbuf, 01189 sizeof(struct stat)) >= 0); 01190 01191 /* Read the file inode */ 01192 dod(!get_inode(request->file->inode, &inode)); 01193 dod(inode.band_id > 0); 01194 dod(inode.size >= 0); 01195 01196 /* Read data from the disk */ 01197 dod(!edi_read(SFSDATA.fd_edi, inode.band_id, inode.band_offset, buf, 01198 inode.size)); 01199 01200 /* Write the data to the file */ 01201 written = 0; 01202 size = statbuf.st_size; 01203 while ((ret = write(request->file->fd, buf + written, 01204 size)) != size) { 01205 /* Ensure that the write didn't fail */ 01206 dod(ret >= 0); 01207 01208 /* Update the size and buffer offset */ 01209 written += ret; 01210 size -= ret; 01211 log(LOG_WRK_INFO, "pwrite() returned %ld. Written: %ld. " 01212 "Size remaining: %ld\n", ret, written, size); 01213 } 01214 01215 01216 /* Update the state of the record */ 01217 dod(!pthread_mutex_lock(&(request->file->mutex))); 01218 request->file->state = ST_LOADED; 01219 dod(!pthread_mutex_unlock(&(request->file->mutex))); 01220 log(LOG_WRK_INFO, "Loaded %d bytes.Updated state to LOADED!\n", 01221 size); 01222 01223 01224 /* Awaken the threads waiting on the request */ 01225 log(LOG_WRK_INFO, "Waking up thread(s) waiting on file\n"); 01226 assert(!pthread_cond_broadcast(&(request->file->condvar))); 01227 break; 01228 01229 /* Reset the file descriptor to be -1, since the operation failed */ 01230 dod(!pthread_mutex_lock(&(request->file->mutex))); 01231 dod(!close(request->file->fd)); 01232 dod(!close(request->file->fd_xattr)); 01233 request->file->fd = -1; 01234 request->file->fd_xattr = -1; 01235 dod(!pthread_mutex_unlock(&(request->file->mutex))); 01236 01237 break; 01238 01239 01240 case OP_STORE: 01241 /* Lock the File Record */ 01242 dod(!pthread_mutex_lock(&(request->file->mutex))); 01243 01244 /* Read the file inode */ 01245 dod(!get_inode(request->file->inode, &inode)); 01246 log(LOG_WRK_INFO, "Retrieved file inode %d: Band: %d, RBA: %d, " 01247 "Extent: %d, Inode No.: %d, Append Log Entry " 01248 "No.: %d\n", request->file->inode, inode.band_id, 01249 inode.band_offset, inode.size, inode.inode_num, 01250 inode.entrynumber); 01251 01252 /* Check if the file has been deleted */ 01253 if (request->file->deleted) 01254 goto unlink_inode; 01255 01256 01257 /* Check if the file is dirty */ 01258 dod(request->file->dirty); 01259 log(LOG_WRK_INFO, "File is dirty!\n"); 01260 01261 /* Unlock the File Record */ 01262 dod(!pthread_mutex_unlock(&(request->file->mutex))); 01263 01264 01265 /* Calculate the no. of blocks to write */ 01266 dod(!fstat(request->file->fd, &statbuf)); 01267 blocks = statbuf.st_size; 01268 blocks = (blocks + SFSDATA.edi_blksize - 1) / SFSDATA.edi_blksize; 01269 log(LOG_WRK_INFO, "Need to write %d blocks!\n", blocks); 01270 01271 /* Retrieve an open band to write the file */ 01272 dod(get_openband(SFSDATA.fd_edi, &open_band, blocks) == 0); 01273 log(LOG_WRK_INFO, "Writing %d blocks (%ld bytes) to band %d at " 01274 "rba %d\n", blocks, statbuf.st_size, 01275 open_band.band, open_band.rba); 01276 01277 /* Mark any currently used band space by the file for cleaning */ 01278 if (inode.band_id > 0) 01279 mark_for_cleaning(inode); 01280 01281 /* Calculate the no. of bytes to write */ 01282 dod(blocks <= SFSDATA.edi_bandsize); 01283 size = SFSDATA.edi_blksize * blocks; 01284 memset(buf + (SFSDATA.edi_blksize * (blocks - 1)), 0, 01285 SFSDATA.edi_blksize); 01286 01287 /* Read the file data */ 01288 dod(pread(request->file->fd, buf, size, 0) == statbuf.st_size); 01289 log(LOG_WRK_INFO, "Read %ld bytes from file\n", statbuf.st_size); 01290 01291 /* Write the file data to the EDI */ 01292 dod(edi_write(SFSDATA.fd_edi, open_band.band, open_band.rba, buf, 01293 blocks) == 0); 01294 log(LOG_WRK_INFO, "Wrote %ld bytes from tmpFS to EDI in Band %ld, " 01295 "RBA %ld for %d blocks!\n", statbuf.st_size, 01296 open_band.band, open_band.rba, blocks); 01297 01298 /* Update the inode */ 01299 inode_update(&inode, open_band.band, open_band.rba, blocks); 01300 01301 01302 /* Lock the File Record */ 01303 dod(!pthread_mutex_lock(&(request->file->mutex))); 01304 01305 01306 /* Check if the file was deleted while it was being written out */ 01307 if (request->file->deleted) { 01308 unlink_inode: 01309 /* Unlink the inode */ 01310 inode_unlink(inode); 01311 } 01312 01313 01314 /* Mark the file as clean */ 01315 request->file->dirty = 0; 01316 01317 /* Copy the file's stats to disk */ 01318 if (!request->file->deleted) { 01319 dod(!fsetxattr(request->file->fd_xattr, X_STATS, &statbuf, 01320 sizeof(struct stat), 0)); 01321 } 01322 01323 /* Determine the cleanup strategy based on the ref-count */ 01324 if (request->file->ref_count == 0) { 01325 /* Mark the state as STORED and close the open files */ 01326 close(request->file->fd); 01327 close(request->file->fd_xattr); 01328 request->file->fd = -1; 01329 request->file->fd_xattr = -1; 01330 log(LOG_WRK_INFO, "Closed %s on tmpFS.\n", request->file->path); 01331 request->file->state = ST_STORED; 01332 01333 } else { 01334 /* Mark the file state as LOADED and wake up any threads */ 01335 request->file->state = ST_LOADED; 01336 dod(!pthread_cond_broadcast(&(request->file->condvar))); 01337 log(LOG_WRK_INFO, "Waking up thread(s) waiting on file\n"); 01338 01339 } 01340 01341 /* Unlock the File Record */ 01342 dod(!pthread_mutex_unlock(&(request->file->mutex))); 01343 01344 break; 01345 01346 01347 default: 01348 /* Report the error for an unknown request type */ 01349 log(LOG_WRK_ERRORS, "Unknown request type %d for request %s\n", 01350 request->op, request->file->path); 01351 dod(0); 01352 break; 01353 } 01354 01355 01356 /* Free the request struct */ 01357 free(request); 01358 log(LOG_WRK_INFO, "Request completed!\n\n"); 01359 } 01360 }