nfs-ganesha 1.4
|
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 }