nfs-ganesha 1.4

cache_inode_avl.c

Go to the documentation of this file.
00001 /*
00002  * vim:expandtab:shiftwidth=8:tabstop=8:
00003  *
00004  * Copyright (C) 2010, The Linux Box Corporation
00005  * Contributor : Matt Benjamin <matt@linuxbox.com>
00006  *
00007  * Some portions Copyright CEA/DAM/DIF  (2008)
00008  * contributeur : Philippe DENIEL   philippe.deniel@cea.fr
00009  *                Thomas LEIBOVICI  thomas.leibovici@cea.fr
00010  *
00011  *
00012  * This program is free software; you can redistribute it and/or
00013  * modify it under the terms of the GNU Lesser General Public
00014  * License as published by the Free Software Foundation; either
00015  * version 3 of the License, or (at your option) any later version.
00016  *
00017  * This program is distributed in the hope that it will be useful,
00018  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00019  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00020  * Lesser General Public License for more details.
00021  *
00022  * You should have received a copy of the GNU Lesser General Public
00023  * License along with this library; if not, write to the Free Software
00024  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
00025  *
00026  * -------------
00027  */
00028 
00029 #ifdef HAVE_CONFIG_H
00030 #include "config.h"
00031 #endif
00032 
00033 #ifdef _SOLARIS
00034 #include "solaris_port.h"
00035 #endif                          /* _SOLARIS */
00036 
00037 #include "log.h"
00038 #include "fsal.h"
00039 #include "cache_inode.h"
00040 #include "cache_inode_avl.h"
00041 #include "murmur3.h"
00042 
00043 #include <unistd.h>
00044 #include <sys/types.h>
00045 #include <sys/param.h>
00046 #include <time.h>
00047 #include <pthread.h>
00048 #include <assert.h>
00049 
00050 void cache_inode_avl_init(cache_entry_t *entry)
00051 {
00052     avltree_init(&entry->object.dir.avl.t, avl_dirent_hk_cmpf, 0 /* flags */);
00053     avltree_init(&entry->object.dir.avl.c, avl_dirent_hk_cmpf, 0 /* flags */);
00054 }
00055 
00056 static inline struct avltree_node *
00057 avltree_inline_lookup(
00058     const struct avltree_node *key,
00059     const struct avltree *tree)
00060 {
00061     struct avltree_node *node = tree->root;
00062     int is_left = 0, res = 0;
00063 
00064     while (node) {
00065         res = avl_dirent_hk_cmpf(node, key);
00066         if (res == 0)
00067             return node;
00068         if ((is_left = res > 0))
00069             node = node->left;
00070         else
00071             node = node->right;
00072     }
00073     return NULL;
00074 }
00075 
00076 void
00077 avl_dirent_set_deleted(cache_entry_t *entry, cache_inode_dir_entry_t *v)
00078 {
00079     struct avltree *t = &entry->object.dir.avl.t;
00080     struct avltree_node *node;
00081 
00082     assert(! (v->flags & DIR_ENTRY_FLAG_DELETED));
00083 
00084     node = avltree_inline_lookup(&v->node_hk, t);
00085     assert(node);
00086     avltree_remove(&v->node_hk, &entry->object.dir.avl.t);
00087 
00088 #if EXTRA_CHECK_DELETED_WORKED
00089     node = avltree_inline_lookup(&v->node_hk, c);
00090     assert(! node);
00091 #endif
00092 
00093     v->flags |= DIR_ENTRY_FLAG_DELETED;
00094     v->name.len = 0;
00095     v->entry.ptr = (void*)0xdeaddeaddeaddead;
00096     v->entry.gen = 0;
00097 
00098     avltree_insert(&v->node_hk, &entry->object.dir.avl.c);
00099 }
00100 
00101 void
00102 avl_dirent_clear_deleted(cache_entry_t *entry, cache_inode_dir_entry_t *v)
00103 {
00104     struct avltree *t = &entry->object.dir.avl.t;
00105     struct avltree *c = &entry->object.dir.avl.c;
00106     struct avltree_node *node;
00107 
00108     node = avltree_inline_lookup(&v->node_hk, c);
00109     assert(node);
00110     avltree_remove(&v->node_hk, c);
00111     memset(&v->node_hk, 0, sizeof(struct avltree_node));
00112 
00113     node = avltree_insert(&v->node_hk, t);
00114     assert(! node);
00115 
00116     v->flags &= ~DIR_ENTRY_FLAG_DELETED;
00117 }
00118 
00119 static inline int
00120 cache_inode_avl_insert_impl(cache_entry_t *entry, cache_inode_dir_entry_t *v,
00121                             int j, int j2)
00122 {
00123     int code = -1;
00124     struct avltree_node *node;
00125     cache_inode_dir_entry_t *v_exist = NULL;
00126     struct avltree *t = &entry->object.dir.avl.t;
00127     struct avltree *c = &entry->object.dir.avl.c;
00128 
00129     /* first check for a previously-deleted entry */
00130     node = avltree_inline_lookup(&v->node_hk, c);
00131 
00132     /* XXX we must not allow persist-cookies to overrun resource
00133      * management processes (ie, more coming in CIR/LRU) */
00134     if ((! node) && (avltree_size(c) > 65535)) {
00135         /* ie, recycle the smallest deleted entry */
00136         node = avltree_first(c);
00137     }
00138 
00139     if (node) {
00140         /* reuse the slot */
00141         v_exist = avltree_container_of(node, cache_inode_dir_entry_t,
00142                                        node_hk);
00143         FSAL_namecpy(&v_exist->name, &v->name);
00144         v_exist->entry = v->entry;
00145         avl_dirent_clear_deleted(entry, v_exist);
00146         v = v_exist;
00147         code = 1; /* tell client to dispose v */
00148     } else {
00149         /* try to insert active */
00150         node = avltree_insert(&v->node_hk, t);
00151         if (! node)
00152             code = 0;
00153     }
00154 
00155     switch (code) {
00156     case 0:
00157         /* success, note iterations */
00158         v->hk.p = j + j2;
00159         if (entry->object.dir.avl.collisions < v->hk.p)
00160             entry->object.dir.avl.collisions = v->hk.p;
00161 
00162         LogDebug(COMPONENT_CACHE_INODE,
00163                  "inserted new dirent on entry=%p cookie=%"PRIu64
00164                  " collisions %d",
00165                  entry, v->hk.k, entry->object.dir.avl.collisions);
00166         break;
00167     default:
00168         /* already inserted, or, keep trying at current j, j2 */
00169         break;
00170     }
00171     return (code);
00172 }
00173 
00174 #define MIN_COOKIE_VAL 3
00175 
00176 /*
00177  * Insert with quadatic, linear probing.  A unique k is assured for
00178  * any k whenever size(t) < max(uint64_t).
00179  *
00180  * First try quadratic probing, with coeff. 2 (since m = 2^n.)
00181  * A unique k is not assured, since the codomain is not prime.
00182  * If this fails, fall back to linear probing from hk.k+1.
00183  *
00184  * On return, the stored key is in v->hk.k, the iteration
00185  * count in v->hk.p.
00186  **/
00187 int cache_inode_avl_qp_insert(
00188     cache_entry_t *entry, cache_inode_dir_entry_t *v)
00189 {
00190     uint32_t hk[4];
00191     int j, j2, code = -1;
00192 
00193     /* don't permit illegal cookies */
00194     MurmurHash3_x64_128(v->name.name,  FSAL_MAX_NAME_LEN, 67, hk);
00195     memcpy(&v->hk.k, hk, 8);
00196 
00197     /* XXX would we really wait for UINT64_MAX?  if not, how many
00198      * probes should we attempt? */
00199 
00200     for (j = 0; j < UINT64_MAX; j++) {
00201         v->hk.k = (v->hk.k + (j * 2));
00202 
00203         /* reject values 0, 1 and 2 */
00204         if (v->hk.k < MIN_COOKIE_VAL)
00205             continue;
00206 
00207         code = cache_inode_avl_insert_impl(entry, v, j, 0);
00208         if (code >= 0)
00209             return (code);
00210     }
00211 
00212     LogCrit(COMPONENT_CACHE_INODE,
00213             "cache_inode_avl_qp_insert_s: could not insert at j=%d (%s)",
00214             j, v->name.name);
00215 
00216     memcpy(&v->hk.k, hk, 8);
00217     for (j2 = 1 /* tried j=0 */; j2 < UINT64_MAX; j2++) {
00218         v->hk.k = v->hk.k + j2;
00219         code = cache_inode_avl_insert_impl(entry, v, j, j2);
00220         if (code >= 0)
00221             return (code);
00222         j2++;
00223     }
00224 
00225     LogCrit(COMPONENT_CACHE_INODE,
00226             "cache_inode_avl_qp_insert_s: could not insert at j=%d (%s)",
00227             j, v->name.name);
00228 
00229     return (-1);
00230 }
00231 
00232 cache_inode_dir_entry_t *
00233 cache_inode_avl_lookup_k(cache_entry_t *entry, uint64_t k, uint32_t flags)
00234 {
00235     struct avltree *t = &entry->object.dir.avl.t;
00236     struct avltree *c = &entry->object.dir.avl.c;
00237     cache_inode_dir_entry_t dirent_key[1], *dirent = NULL;
00238     struct avltree_node *node, *node2;
00239 
00240     dirent_key->hk.k = k;
00241 
00242     node = avltree_inline_lookup(&dirent_key->node_hk, t);
00243     if (node) {
00244         if (flags & CACHE_INODE_FLAG_NEXT_ACTIVE)
00245             /* client wants the cookie -after- the last we sent, and
00246              * the Linux 3.0 and 3.1.0-rc7 clients misbehave if we
00247              * resend the last one */
00248             node = avltree_next(node);
00249             if (! node) {
00250                 LogFullDebug(COMPONENT_NFS_READDIR,
00251                              "seek to cookie=%"PRIu64" fail (no next entry)",
00252                              k);
00253                 goto out;
00254             }
00255     }
00256 
00257     /* Try the deleted AVL.  If a node with hk.k == v->hk.k is found,
00258      * return its least upper bound in -t-, if any. */
00259     if (! node) {
00260         node2 = avltree_inline_lookup(&dirent_key->node_hk, c);
00261         if (node2)
00262             node = avltree_sup(&dirent_key->node_hk, t);
00263         LogDebug(COMPONENT_NFS_READDIR,
00264                  "node %p found deleted supremum %p",
00265                  node2, node);
00266     }
00267 
00268     if (node)
00269         dirent = avltree_container_of(node, cache_inode_dir_entry_t, node_hk);
00270 
00271 out:
00272     return (dirent);
00273 }
00274 
00275 cache_inode_dir_entry_t *
00276 cache_inode_avl_qp_lookup_s(
00277     cache_entry_t *entry, cache_inode_dir_entry_t *v, int maxj)
00278 {
00279     struct avltree *t = &entry->object.dir.avl.t;
00280     struct avltree_node *node;
00281     cache_inode_dir_entry_t *v2;
00282     uint32_t hk[4];
00283     int j;
00284 
00285     MurmurHash3_x64_128(v->name.name,  FSAL_MAX_NAME_LEN, 67, hk);
00286     memcpy(&v->hk.k, hk, 8);
00287 
00288     for (j = 0; j < maxj; j++) {
00289         v->hk.k = (v->hk.k + (j * 2));
00290         node = avltree_lookup(&v->node_hk, t);
00291         if (node) {
00292             /* ensure that node is related to v */
00293             v2 = avltree_container_of(node, cache_inode_dir_entry_t, node_hk);
00294             if (! FSAL_namecmp(&v->name, &v2->name)) {
00295                 assert(!(v2->flags & DIR_ENTRY_FLAG_DELETED));
00296                 return (v2);
00297             }
00298         }
00299     }
00300 
00301     LogFullDebug(COMPONENT_CACHE_INODE,
00302                  "cache_inode_avl_qp_lookup_s: entry not found at j=%d (%s)",
00303                  j, v->name.name);
00304 
00305     return (NULL);
00306 }