ShingledFS 2.0
SMR-AwareFUSE-basedFileSystem

buffercache.c

Go to the documentation of this file.
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 }