"Fossies" - the Fresh Open Source Software Archive

Member "glusterfs-8.2/libglusterfs/src/gidcache.c" (16 Sep 2020, 6171 Bytes) of package /linux/misc/glusterfs-8.2.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "gidcache.c" see the Fossies "Dox" file reference documentation.

    1 /*
    2   Copyright (c) 2008-2012 Red Hat, Inc. <http://www.redhat.com>
    3   This file is part of GlusterFS.
    4 
    5   This file is licensed to you under your choice of the GNU Lesser
    6   General Public License, version 3 or any later version (LGPLv3 or
    7   later), or the GNU General Public License, version 2 (GPLv2), in all
    8   cases as published by the Free Software Foundation.
    9 */
   10 
   11 #include "glusterfs/gidcache.h"
   12 #include "glusterfs/mem-pool.h"
   13 
   14 /*
   15  * We treat this as a very simple set-associative LRU cache, with entries aged
   16  * out after a configurable interval.  Hardly rocket science, but lots of
   17  * details to worry about.
   18  */
   19 #define BUCKET_START(p, n) ((p) + ((n)*AUX_GID_CACHE_ASSOC))
   20 
   21 /*
   22  * Initialize the cache.
   23  */
   24 int
   25 gid_cache_init(gid_cache_t *cache, uint32_t timeout)
   26 {
   27     if (!cache)
   28         return -1;
   29 
   30     LOCK_INIT(&cache->gc_lock);
   31     cache->gc_max_age = timeout;
   32     cache->gc_nbuckets = AUX_GID_CACHE_BUCKETS;
   33     memset(cache->gc_cache, 0, sizeof(gid_list_t) * AUX_GID_CACHE_SIZE);
   34 
   35     return 0;
   36 }
   37 
   38 /*
   39  * Reconfigure the cache timeout.
   40  */
   41 int
   42 gid_cache_reconf(gid_cache_t *cache, uint32_t timeout)
   43 {
   44     if (!cache)
   45         return -1;
   46 
   47     LOCK(&cache->gc_lock);
   48     cache->gc_max_age = timeout;
   49     UNLOCK(&cache->gc_lock);
   50 
   51     return 0;
   52 }
   53 
   54 /*
   55  * Look up an ID in the cache. If found, return the actual cache entry to avoid
   56  * an additional allocation and memory copy. The caller should copy the data and
   57  * release (unlock) the cache as soon as possible.
   58  */
   59 const gid_list_t *
   60 gid_cache_lookup(gid_cache_t *cache, uint64_t id, uint64_t uid, uint64_t gid)
   61 {
   62     int bucket;
   63     int i;
   64     time_t now;
   65     const gid_list_t *agl;
   66 
   67     now = time(NULL);
   68     LOCK(&cache->gc_lock);
   69     bucket = id % cache->gc_nbuckets;
   70     agl = BUCKET_START(cache->gc_cache, bucket);
   71     for (i = 0; i < AUX_GID_CACHE_ASSOC; i++, agl++) {
   72         if (!agl->gl_list)
   73             continue;
   74         if (agl->gl_id != id)
   75             continue;
   76 
   77         /*
   78           @uid and @gid reflect the latest UID/GID of the
   79            process performing the syscall (taken from frame->root).
   80 
   81            If the UID and GID has changed for the PID since the
   82            time we cached it, we should treat the cache as having
   83            stale values and query them freshly.
   84         */
   85         if (agl->gl_uid != uid || agl->gl_gid != gid)
   86             break;
   87 
   88         /*
   89          * We don't put new entries in the cache when expiration=0, but
   90          * there might be entries still in there if expiration was
   91          * changed very recently.  Writing the check this way ensures
   92          * that they're not used.
   93          */
   94         if (now < agl->gl_deadline) {
   95             return agl;
   96         }
   97 
   98         /*
   99          * We're not going to find any more UID matches, and reaping
  100          * is handled further down to maintain LRU order.
  101          */
  102         break;
  103     }
  104     UNLOCK(&cache->gc_lock);
  105     return NULL;
  106 }
  107 
  108 /*
  109  * Release an entry found via lookup.
  110  */
  111 void
  112 gid_cache_release(gid_cache_t *cache, const gid_list_t *agl)
  113 {
  114     UNLOCK(&cache->gc_lock);
  115 }
  116 
  117 /*
  118  * Add a new list entry to the cache. If an entry for this ID already exists,
  119  * update it.
  120  */
  121 int
  122 gid_cache_add(gid_cache_t *cache, gid_list_t *gl)
  123 {
  124     gid_list_t *agl;
  125     int bucket;
  126     int i;
  127     time_t now;
  128 
  129     if (!gl || !gl->gl_list)
  130         return -1;
  131 
  132     if (!cache->gc_max_age)
  133         return 0;
  134 
  135     now = time(NULL);
  136     LOCK(&cache->gc_lock);
  137 
  138     /*
  139      * Scan for the first free entry or one that matches this id. The id
  140      * check is added to address a bug where the cache might contain an
  141      * expired entry for this id. Since lookup occurs in LRU order and
  142      * does not reclaim entries, it will always return failure on discovery
  143      * of an expired entry. This leads to duplicate entries being added,
  144      * which still do not satisfy lookups until the expired entry (and
  145      * everything before it) is reclaimed.
  146      *
  147      * We address this through reuse of an entry already allocated to this
  148      * id, whether expired or not, since we have obviously already received
  149      * more recent data. The entry is repopulated with the new data and a new
  150      * deadline and is pushed forward to reside as the last populated entry in
  151      * the bucket.
  152      */
  153     bucket = gl->gl_id % cache->gc_nbuckets;
  154     agl = BUCKET_START(cache->gc_cache, bucket);
  155     for (i = 0; i < AUX_GID_CACHE_ASSOC; ++i, ++agl) {
  156         if (agl->gl_id == gl->gl_id)
  157             break;
  158         if (!agl->gl_list)
  159             break;
  160     }
  161 
  162     /*
  163      * The way we allocate free entries naturally places the newest
  164      * ones at the highest indices, so evicting the lowest makes
  165      * sense, but that also means we can't just replace it with the
  166      * one that caused the eviction.  That would cause us to thrash
  167      * the first entry while others remain idle.  Therefore, we
  168      * need to slide the other entries down and add the new one at
  169      * the end just as if the *last* slot had been free.
  170      *
  171      * Deadline expiration is also handled here, since the oldest
  172      * expired entry will be in the first position.  This does mean
  173      * the bucket can stay full of expired entries if we're idle
  174      * but, if the small amount of extra memory or scan time before
  175      * we decide to evict someone ever become issues, we could
  176      * easily add a reaper thread.
  177      */
  178 
  179     if (i >= AUX_GID_CACHE_ASSOC) {
  180         /* cache full, evict the first (LRU) entry */
  181         i = 0;
  182         agl = BUCKET_START(cache->gc_cache, bucket);
  183         GF_FREE(agl->gl_list);
  184     } else if (agl->gl_list) {
  185         /* evict the old entry we plan to reuse */
  186         GF_FREE(agl->gl_list);
  187     }
  188 
  189     /*
  190      * If we have evicted an entry, slide the subsequent populated entries
  191      * back and populate the last entry.
  192      */
  193     for (; i < AUX_GID_CACHE_ASSOC - 1; i++) {
  194         if (!agl[1].gl_list)
  195             break;
  196         agl[0] = agl[1];
  197         agl++;
  198     }
  199 
  200     agl->gl_id = gl->gl_id;
  201     agl->gl_uid = gl->gl_uid;
  202     agl->gl_gid = gl->gl_gid;
  203     agl->gl_count = gl->gl_count;
  204     agl->gl_list = gl->gl_list;
  205     agl->gl_deadline = now + cache->gc_max_age;
  206 
  207     UNLOCK(&cache->gc_lock);
  208 
  209     return 1;
  210 }