home *** CD-ROM | disk | FTP | other *** search
/ ftp.parl.clemson.edu / 2015-02-07.ftp.parl.clemson.edu.tar / ftp.parl.clemson.edu / pub / pvfs2 / orangefs-2.8.3-20110323.tar.gz / orangefs-2.8.3-20110323.tar / orangefs / src / client / sysint / ncache.c < prev    next >
C/C++ Source or Header  |  2006-09-16  |  16KB  |  538 lines

  1. /*
  2.  * Copyright ⌐ Acxiom Corporation, 2006
  3.  *
  4.  * See COPYING in top-level directory.
  5.  */
  6.   
  7. #include <assert.h>
  8.   
  9. #include "pvfs2-attr.h"
  10. #include "ncache.h"
  11. #include "tcache.h"
  12. #include "pint-util.h"
  13. #include "pvfs2-debug.h"
  14. #include "gossip.h"
  15. #include "pvfs2-internal.h"
  16. #include <string.h>
  17.   
  18. /** \file
  19.  *  \ingroup ncache
  20.  * Implementation of the Name Cache (ncache) component.
  21.  */
  22.   
  23. /* compile time defaults */
  24. enum {
  25. NCACHE_DEFAULT_TIMEOUT_MSECS  =  3000,
  26. NCACHE_DEFAULT_SOFT_LIMIT     =  5120,
  27. NCACHE_DEFAULT_HARD_LIMIT     = 10240,
  28. NCACHE_DEFAULT_RECLAIM_PERCENTAGE = 25,
  29. NCACHE_DEFAULT_REPLACE_ALGORITHM = LEAST_RECENTLY_USED,
  30. };
  31.  
  32. struct PINT_perf_key ncache_keys[] = 
  33. {
  34.    {"NCACHE_NUM_ENTRIES", PERF_NCACHE_NUM_ENTRIES, PINT_PERF_PRESERVE},
  35.    {"NCACHE_SOFT_LIMIT", PERF_NCACHE_SOFT_LIMIT, PINT_PERF_PRESERVE},
  36.    {"NCACHE_HARD_LIMIT", PERF_NCACHE_HARD_LIMIT, PINT_PERF_PRESERVE},
  37.    {"NCACHE_HITS", PERF_NCACHE_HITS, 0},
  38.    {"NCACHE_MISSES", PERF_NCACHE_MISSES, 0},
  39.    {"NCACHE_UPDATES", PERF_NCACHE_UPDATES, 0},
  40.    {"NCACHE_PURGES", PERF_NCACHE_PURGES, 0},
  41.    {"NCACHE_REPLACEMENTS", PERF_NCACHE_REPLACEMENTS, 0},
  42.    {"NCACHE_DELETIONS", PERF_NCACHE_DELETIONS, 0},
  43.    {"NCACHE_ENABLED", PERF_NCACHE_ENABLED, PINT_PERF_PRESERVE},
  44.    {NULL, 0, 0},
  45. };
  46.  
  47. /* data to be stored in a cached entry */
  48. struct ncache_payload
  49. {
  50.     PVFS_object_ref entry_ref;      /* PVFS2 object reference to entry */
  51.     PVFS_object_ref parent_ref;     /* PVFS2 object reference to parent */
  52.     int entry_status;               /* is the entry valid? */
  53.     char* entry_name;
  54. };
  55.  
  56. struct ncache_key
  57. {
  58.     PVFS_object_ref parent_ref;
  59.     const char* entry_name;
  60. };
  61.   
  62. static struct PINT_tcache* ncache = NULL;
  63. static gen_mutex_t ncache_mutex = GEN_MUTEX_INITIALIZER;
  64.   
  65. static int ncache_compare_key_entry(void* key, struct qhash_head* link);
  66. static int ncache_hash_key(void* key, int table_size);
  67. static int ncache_free_payload(void* payload);
  68. static struct PINT_perf_counter* ncache_pc = NULL;
  69.  
  70. /**
  71.  * Enables perf counter instrumentation of the ncache
  72.  */
  73. void PINT_ncache_enable_perf_counter(
  74.     struct PINT_perf_counter* pc)     /**< perf counter instance to use */
  75. {
  76.     gen_mutex_lock(&ncache_mutex);
  77.  
  78.     ncache_pc = pc;
  79.     assert(ncache_pc);
  80.  
  81.     /* set initial values */
  82.     PINT_perf_count(ncache_pc, PERF_NCACHE_SOFT_LIMIT,
  83.         ncache->soft_limit, PINT_PERF_SET);
  84.     PINT_perf_count(ncache_pc, PERF_NCACHE_HARD_LIMIT,
  85.         ncache->hard_limit, PINT_PERF_SET);
  86.     PINT_perf_count(ncache_pc, PERF_NCACHE_ENABLED,
  87.         ncache->enable, PINT_PERF_SET);
  88.  
  89.     gen_mutex_unlock(&ncache_mutex);
  90.  
  91.     return;
  92. }
  93.  
  94. /**
  95.  * Initializes the ncache 
  96.  * \return pointer to tcache on success, NULL on failure
  97.  */
  98. int PINT_ncache_initialize(void)
  99. {
  100.     int ret = -1;
  101.     unsigned int ncache_timeout_msecs;
  102.     char * ncache_timeout_str = NULL;
  103.   
  104.     gen_mutex_lock(&ncache_mutex);
  105.   
  106.     /* create tcache instance */
  107.     ncache = PINT_tcache_initialize(ncache_compare_key_entry,
  108.                                     ncache_hash_key,
  109.                                     ncache_free_payload,
  110.                                     -1 /* default tcache table size */);
  111.     if(!ncache)
  112.     {
  113.         gen_mutex_unlock(&ncache_mutex);
  114.         return(-PVFS_ENOMEM);
  115.     }
  116.   
  117.     /* fill in defaults that are specific to ncache */
  118.     ncache_timeout_str = getenv("PVFS2_NCACHE_TIMEOUT");
  119.     if (ncache_timeout_str != NULL) 
  120.         ncache_timeout_msecs = (unsigned int)strtoul(ncache_timeout_str,NULL,0);
  121.     else
  122.         ncache_timeout_msecs = NCACHE_DEFAULT_TIMEOUT_MSECS;
  123.  
  124.     ret = PINT_tcache_set_info(ncache, TCACHE_TIMEOUT_MSECS,
  125.                                ncache_timeout_msecs);
  126.                 
  127.     if(ret < 0)
  128.     {
  129.         PINT_tcache_finalize(ncache);
  130.         gen_mutex_unlock(&ncache_mutex);
  131.         return(ret);
  132.     }
  133.     ret = PINT_tcache_set_info(ncache, TCACHE_HARD_LIMIT, 
  134.                                NCACHE_DEFAULT_HARD_LIMIT);
  135.     if(ret < 0)
  136.     {
  137.         PINT_tcache_finalize(ncache);
  138.         gen_mutex_unlock(&ncache_mutex);
  139.         return(ret);
  140.     }
  141.     ret = PINT_tcache_set_info(ncache, TCACHE_SOFT_LIMIT, 
  142.                                NCACHE_DEFAULT_SOFT_LIMIT);
  143.     if(ret < 0)
  144.     {
  145.         PINT_tcache_finalize(ncache);
  146.         gen_mutex_unlock(&ncache_mutex);
  147.         return(ret);
  148.     }
  149.     ret = PINT_tcache_set_info(ncache, TCACHE_RECLAIM_PERCENTAGE,
  150.                                NCACHE_DEFAULT_RECLAIM_PERCENTAGE);
  151.     if(ret < 0)
  152.     {
  153.         PINT_tcache_finalize(ncache);
  154.         gen_mutex_unlock(&ncache_mutex);
  155.         return(ret);
  156.     }
  157.   
  158.     gen_mutex_unlock(&ncache_mutex);
  159.     return(0);
  160. }
  161.   
  162. /** Finalizes and destroys the ncache, frees all cached entries */
  163. void PINT_ncache_finalize(void)
  164. {
  165.     gen_mutex_lock(&ncache_mutex);
  166.  
  167.     assert(ncache != NULL);
  168.     PINT_tcache_finalize(ncache);
  169.     ncache = NULL;
  170.  
  171.     gen_mutex_unlock(&ncache_mutex);
  172.     return;
  173. }
  174.   
  175. /**
  176.  * Retrieves parameters from the ncache 
  177.  * @see PINT_tcache_options
  178.  * \return 0 on success, -PVFS_error on failure
  179.  */
  180. int PINT_ncache_get_info(
  181.     enum PINT_ncache_options option, /**< option to read */
  182.     unsigned int* arg)               /**< output value */
  183. {
  184.     int ret = -1;
  185.   
  186.     gen_mutex_lock(&ncache_mutex);
  187.     ret = PINT_tcache_get_info(ncache, option, arg);
  188.     gen_mutex_unlock(&ncache_mutex);
  189.   
  190.     return(ret);
  191. }
  192.   
  193. /**
  194.  * Sets optional parameters in the ncache
  195.  * @see PINT_tcache_options
  196.  * @return 0 on success, -PVFS_error on failure
  197.  */
  198. int PINT_ncache_set_info(
  199.     enum PINT_ncache_options option, /**< option to modify */
  200.     unsigned int arg)                /**< input value */
  201. {
  202.     int ret = -1;
  203.   
  204.     gen_mutex_lock(&ncache_mutex);
  205.     ret = PINT_tcache_set_info(ncache, option, arg);
  206.  
  207.     /* record any resulting parameter changes */
  208.     PINT_perf_count(ncache_pc, PERF_NCACHE_SOFT_LIMIT,
  209.         ncache->soft_limit, PINT_PERF_SET);
  210.     PINT_perf_count(ncache_pc, PERF_NCACHE_HARD_LIMIT,
  211.         ncache->hard_limit, PINT_PERF_SET);
  212.     PINT_perf_count(ncache_pc, PERF_NCACHE_ENABLED,
  213.         ncache->enable, PINT_PERF_SET);
  214.     PINT_perf_count(ncache_pc, PERF_NCACHE_NUM_ENTRIES,
  215.         ncache->num_entries, PINT_PERF_SET);
  216.  
  217.     gen_mutex_unlock(&ncache_mutex);
  218.  
  219.     return(ret);
  220. }
  221.   
  222. /** 
  223.  * Retrieves a _copy_ of a cached object reference, and reports the
  224.  * status to indicate if they are valid or  not
  225.  * @return 0 on success, -PVFS_error on failure
  226.  */
  227. int PINT_ncache_get_cached_entry(
  228.     const char* entry,                 /**< path of obect to look up*/
  229.     PVFS_object_ref* entry_ref,        /**< PVFS2 object looked up */
  230.     const PVFS_object_ref* parent_ref) /**< Parent of PVFS2 object */
  231. {
  232.     int ret = -1;
  233.     struct PINT_tcache_entry* tmp_entry;
  234.     struct ncache_payload* tmp_payload;
  235.     struct ncache_key entry_key;
  236.     int status;
  237.  
  238.     gossip_debug(GOSSIP_NCACHE_DEBUG, 
  239.                  "ncache: get_cached_entry(): [%s]\n",entry);
  240.   
  241.     entry_key.entry_name = entry;
  242.     entry_key.parent_ref.handle = parent_ref->handle;
  243.     entry_key.parent_ref.fs_id = parent_ref->fs_id;
  244.  
  245.     gen_mutex_lock(&ncache_mutex);
  246.  
  247.     /* lookup entry */
  248.     ret = PINT_tcache_lookup(ncache, (void *) &entry_key, &tmp_entry, &status);
  249.     if(ret < 0 || status != 0)
  250.     {
  251.         gossip_debug(GOSSIP_NCACHE_DEBUG, 
  252.             "ncache: miss: name=[%s]\n", entry_key.entry_name);
  253.         PINT_perf_count(ncache_pc, PERF_NCACHE_MISSES, 1, PINT_PERF_ADD);
  254.         gen_mutex_unlock(&ncache_mutex);
  255.         /* Return -PVFS_ENOENT if the entry has expired */
  256.         if(status != 0)
  257.         {   
  258.             return(-PVFS_ENOENT);
  259.         }
  260.         return(ret);
  261.     }
  262.     tmp_payload = tmp_entry->payload;
  263.   
  264.     gossip_debug(GOSSIP_NCACHE_DEBUG, "ncache: status=%d, entry_status=%d\n",
  265.                  status, tmp_payload->entry_status);
  266.  
  267.     /* copy out entry ref if valid */
  268.     if(tmp_payload->entry_status == 0 && 
  269.        tmp_payload->parent_ref.handle == parent_ref->handle)
  270.     {
  271.         gossip_debug(GOSSIP_NCACHE_DEBUG, "ncache: copying out ref.\n");
  272.         *entry_ref = tmp_payload->entry_ref;
  273.     }
  274.   
  275.     if(tmp_payload->entry_status == 0) 
  276.     {
  277.         /* return success if we got _anything_ out of the cache */
  278.         PINT_perf_count(ncache_pc, PERF_NCACHE_HITS, 1, PINT_PERF_ADD);
  279.         gen_mutex_unlock(&ncache_mutex);
  280.         return(0);
  281.     }
  282.  
  283.     gen_mutex_unlock(&ncache_mutex);
  284.   
  285.     PINT_perf_count(ncache_pc, PERF_NCACHE_MISSES, 1, PINT_PERF_ADD);
  286.     return(-PVFS_ETIME);
  287. }
  288.   
  289. /**
  290.  * Invalidates a cache entry (if present)
  291.  */
  292. void PINT_ncache_invalidate(
  293.     const char* entry,                  /**< path of obect */
  294.     const PVFS_object_ref* parent_ref)  /**< Parent of PVFS2 object */
  295. {
  296.     int ret = -1;
  297.     struct PINT_tcache_entry* tmp_entry;
  298.     struct ncache_key entry_key;
  299.     int tmp_status;
  300.   
  301.     gossip_debug(GOSSIP_NCACHE_DEBUG, "ncache: invalidate(): entry=%s\n",
  302.                  entry);
  303.   
  304.     gen_mutex_lock(&ncache_mutex);
  305.   
  306.     entry_key.entry_name = entry;
  307.     entry_key.parent_ref.handle = parent_ref->handle;
  308.     entry_key.parent_ref.fs_id = parent_ref->fs_id;
  309.  
  310.     /* find out if the entry is in the cache */
  311.     ret = PINT_tcache_lookup(ncache, 
  312.                              &entry_key,
  313.                              &tmp_entry,
  314.                              &tmp_status);
  315.     if(ret == 0)
  316.     {
  317.         PINT_tcache_delete(ncache, tmp_entry);
  318.         PINT_perf_count(ncache_pc, PERF_NCACHE_DELETIONS, 1,
  319.                         PINT_PERF_ADD);
  320.     }
  321.  
  322.     PINT_perf_count(ncache_pc, PERF_NCACHE_NUM_ENTRIES,
  323.                     ncache->num_entries, PINT_PERF_SET);
  324.  
  325.     gen_mutex_unlock(&ncache_mutex);
  326.     return;
  327. }
  328.   
  329. /** 
  330.  * Adds a name to the cache, or updates it if already present.  
  331.  * The given name is _copied_ into the cache.   
  332.  *
  333.  * \note NOTE: All previous information for the object will be discarded,
  334.  * even if there is still time remaining before it expires.
  335.  *
  336.  * \return 0 on success, -PVFS_error on failure
  337.  */
  338. int PINT_ncache_update(
  339.     const char* entry,                     /**< entry to update */
  340.     const PVFS_object_ref* entry_ref,      /**< entry ref to update */
  341.     const PVFS_object_ref* parent_ref)     /**< parent ref to update */
  342. {
  343.     int ret = -1;
  344.     struct PINT_tcache_entry* tmp_entry;
  345.     struct ncache_payload* tmp_payload;
  346.     struct ncache_key entry_key;
  347.     int status;
  348.     int purged;
  349.     unsigned int enabled;
  350.  
  351.     /* skip out immediately if the cache is disabled */
  352.     PINT_tcache_get_info(ncache, TCACHE_ENABLE, &enabled);
  353.     if(!enabled)
  354.     {
  355.         return(0);
  356.     }
  357.     
  358.     gossip_debug(GOSSIP_NCACHE_DEBUG, "ncache: update(): name [%s]\n",entry);
  359.   
  360.     if(!entry_ref->handle)
  361.     {
  362.         return(-PVFS_EINVAL);
  363.     }
  364.   
  365.     /* create new payload with updated information */
  366.     tmp_payload = (struct ncache_payload*) 
  367.                         calloc(1,sizeof(struct ncache_payload));
  368.     if(tmp_payload == NULL)
  369.     {
  370.         return(-PVFS_ENOMEM);
  371.     }
  372.  
  373.     tmp_payload->parent_ref.handle = parent_ref->handle;
  374.     tmp_payload->parent_ref.fs_id = parent_ref->fs_id;
  375.     tmp_payload->entry_ref.handle = entry_ref->handle;
  376.     tmp_payload->entry_ref.fs_id = entry_ref->fs_id;
  377.  
  378.     tmp_payload->entry_status = 0;
  379.     tmp_payload->entry_name = (char*) calloc(1, strlen(entry) + 1);
  380.     if(tmp_payload->entry_name == NULL)
  381.     {
  382.         free(tmp_payload);
  383.         return(-PVFS_ENOMEM);
  384.     }
  385.     memcpy(tmp_payload->entry_name, entry, strlen(entry) + 1);
  386.  
  387.     gen_mutex_lock(&ncache_mutex);
  388.  
  389.     entry_key.entry_name = entry;
  390.     entry_key.parent_ref.handle = parent_ref->handle;
  391.     entry_key.parent_ref.fs_id = parent_ref->fs_id;
  392.  
  393.     /* find out if the entry is already in the cache */
  394.     ret = PINT_tcache_lookup(ncache, 
  395.                              &entry_key,
  396.                              &tmp_entry,
  397.                              &status);
  398.     if(ret == 0)
  399.     {
  400.         /* found match in cache; destroy old payload, replace, and
  401.          * refresh time stamp
  402.          */
  403.         ncache_free_payload(tmp_entry->payload);
  404.         tmp_entry->payload = tmp_payload;
  405.         ret = PINT_tcache_refresh_entry(ncache, tmp_entry);
  406.         PINT_perf_count(ncache_pc, PERF_NCACHE_UPDATES, 1, PINT_PERF_ADD);
  407.     }
  408.     else
  409.     {
  410.         /* not found in cache; insert new payload*/
  411.         ret = PINT_tcache_insert_entry(ncache, 
  412.                                        &entry_key,
  413.                                        tmp_payload, 
  414.                                        &purged);
  415.         /* the purged variable indicates how many entries had to be purged
  416.          * from the tcache to make room for this new one
  417.          */
  418.         if(purged == 1)
  419.         {
  420.             /* since only one item was purged, we count this as one item being
  421.              * replaced rather than as a purge and an insert
  422.              */
  423.             PINT_perf_count(ncache_pc, PERF_NCACHE_REPLACEMENTS,purged,
  424.                 PINT_PERF_ADD);
  425.         }
  426.         else
  427.         {
  428.             /* otherwise we just purged as part of reclaimation */
  429.             /* if we didn't purge anything, then the "purged" variable will
  430.              * be zero and this counter call won't do anything.
  431.              */
  432.             PINT_perf_count(ncache_pc, PERF_NCACHE_PURGES, purged,
  433.                 PINT_PERF_ADD);
  434.         }
  435.     }
  436.     
  437.     PINT_perf_count(ncache_pc, PERF_NCACHE_NUM_ENTRIES,
  438.         ncache->num_entries, PINT_PERF_SET);
  439.  
  440.     gen_mutex_unlock(&ncache_mutex);
  441.   
  442.     /* cleanup if we did not succeed for some reason */
  443.     if(ret < 0)
  444.     {
  445.         ncache_free_payload(tmp_payload);
  446.     }
  447.   
  448.     gossip_debug(GOSSIP_NCACHE_DEBUG, "ncache: update(): return=%d\n", ret);
  449.     return(ret);
  450. }
  451.   
  452. /* ncache_compare_key_entry()
  453.  *
  454.  * compares an opaque key (char* in this case) against a payload to see
  455.  * if there is a match
  456.  *
  457.  * returns 1 on match, 0 otherwise
  458.  */
  459. static int ncache_compare_key_entry(void* key, struct qhash_head* link)
  460. {
  461.     struct ncache_key* real_key = (struct ncache_key*)key;
  462.     struct ncache_payload* tmp_payload = NULL;
  463.     struct PINT_tcache_entry* tmp_entry = NULL;
  464.   
  465.     tmp_entry = qhash_entry(link, struct PINT_tcache_entry, hash_link);
  466.     assert(tmp_entry);
  467.  
  468.     tmp_payload = (struct ncache_payload*)tmp_entry->payload;
  469.      /* If the following aren't equal, we know we don't have a match... Maybe
  470.      * these integer comparisons will be quicker than a strcmp each time?
  471.      *   - parent_ref.handle 
  472.      *   - parent_ref.fs_id
  473.      *   - entry_name length
  474.      */
  475.     if( real_key->parent_ref.handle  != tmp_payload->parent_ref.handle ||
  476.         real_key->parent_ref.fs_id   != tmp_payload->parent_ref.fs_id  ||
  477.         strlen(real_key->entry_name) != strlen(tmp_payload->entry_name) )
  478.     {
  479.         /* One of the above cases failed, so we know these aren't a match */
  480.         return(0);
  481.     }
  482.     
  483.     if( strcmp(real_key->entry_name, tmp_payload->entry_name) == 0 )
  484.     {
  485.         /* The strings matches */
  486.         return(1);
  487.     }
  488.  
  489.     return(0);
  490. }
  491.   
  492. /* ncache_hash_key()
  493.  *
  494.  * hash function for character pointers
  495.  *
  496.  * returns hash index 
  497.  */
  498. static int ncache_hash_key(void* key, int table_size)
  499. {
  500.     struct ncache_key* real_key = (struct ncache_key*) key;
  501.     int tmp_ret = 0;
  502.     unsigned int sum = 0, i = 0;
  503.  
  504.     while(real_key->entry_name[i] != '\0')
  505.     {
  506.         sum += (unsigned int) real_key->entry_name[i];
  507.         i++;
  508.     }
  509.     sum += real_key->parent_ref.handle + real_key->parent_ref.fs_id;
  510.     tmp_ret =  sum % table_size;
  511.     return(tmp_ret);
  512. }
  513.   
  514. /* ncache_free_payload()
  515.  *
  516.  * frees payload that has been stored in the ncache 
  517.  *
  518.  * returns 0 on success, -PVFS_error on failure
  519.  */
  520. static int ncache_free_payload(void* payload)
  521. {
  522.     struct ncache_payload* tmp_payload = (struct ncache_payload*)payload;
  523.   
  524.     free(tmp_payload->entry_name);
  525.     free(tmp_payload);
  526.     return(0);
  527. }
  528.   
  529. /*
  530.  * Local variables:
  531.  *  c-indent-level: 4
  532.  *  c-basic-offset: 4
  533.  * End:
  534.  *
  535.  * vim: ts=8 sts=4 sw=4 expandtab
  536.  */
  537.  
  538.