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 00025 * 02110-1301 USA 00026 * 00027 * ------------- 00028 */ 00029 00030 #ifdef HAVE_CONFIG_H 00031 #include "config.h" 00032 #endif 00033 00034 #ifdef _SOLARIS 00035 #include "solaris_port.h" 00036 #endif /* _SOLARIS */ 00037 00038 #include <unistd.h> 00039 #include <sys/types.h> 00040 #include <sys/param.h> 00041 #include <time.h> 00042 #include <pthread.h> 00043 #include <assert.h> 00044 #include <stdint.h> 00045 #include "nlm_list.h" 00046 #include "fsal.h" 00047 #include "nfs_core.h" 00048 #include "log.h" 00049 #include "cache_inode.h" 00050 #include "generic_weakref.h" 00051 00066 #define CACHE_LINE_SIZE 64 00067 #define CACHE_PAD(_n) char __pad ## _n [CACHE_LINE_SIZE] 00068 00076 typedef struct gweakref_partition_ 00077 { 00078 pthread_rwlock_t lock; 00079 struct avltree t; 00080 uint64_t genctr; 00081 struct avltree_node **cache; 00082 CACHE_PAD(0); 00083 } gweakref_partition_t; 00084 00091 struct gweakref_table_ 00092 { 00093 CACHE_PAD(0); 00094 gweakref_partition_t *partition; 00095 CACHE_PAD(1); 00096 uint32_t npart; 00097 uint32_t cache_sz; 00098 }; 00099 00109 #define gwt_partition_of_addr_k(xt, k) \ 00110 (((xt)->partition)+(((uint64_t)k)%(xt)->npart)) 00111 00122 typedef struct gweakref_priv_ 00123 { 00124 struct avltree_node node_k; /*< The link to the rest of the tree */ 00125 gweakref_t k; /*< The (pointer, generation) pair */ 00126 } gweakref_priv_t; 00127 00143 static inline int wk_cmpf(const struct avltree_node *lhs, 00144 const struct avltree_node *rhs) 00145 { 00146 gweakref_priv_t *lk, *rk; 00147 00148 lk = avltree_container_of(lhs, gweakref_priv_t, node_k); 00149 rk = avltree_container_of(rhs, gweakref_priv_t, node_k); 00150 00151 if (lk->k.ptr < rk->k.ptr) 00152 return (-1); 00153 00154 if (lk->k.ptr == rk->k.ptr) 00155 return (0); 00156 00157 return (1); 00158 } 00159 00171 static inline int 00172 cache_offsetof(gweakref_table_t *wt, void *ptr) 00173 { 00174 return ((uintptr_t) ptr % wt->cache_sz); 00175 } 00176 00189 gweakref_table_t *gweakref_init(uint32_t npart, uint32_t cache_sz) 00190 { 00191 int ix = 0; 00192 pthread_rwlockattr_t rwlock_attr; 00193 gweakref_partition_t *wp = NULL; 00194 gweakref_table_t *wt = NULL; 00195 00196 wt = gsh_calloc(1, sizeof(gweakref_table_t)); 00197 if (!wt) 00198 goto out; 00199 00200 /* prior versions of Linux tirpc are subject to default prefer-reader 00201 * behavior (so have potential for writer starvation) */ 00202 pthread_rwlockattr_init(&rwlock_attr); 00203 #ifdef GLIBC 00204 pthread_rwlockattr_setkind_np( 00205 &rwlock_attr, 00206 PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP); 00207 #endif 00208 00209 /* npart should be a small integer */ 00210 wt->npart = npart; 00211 wt->partition = gsh_calloc(npart, sizeof(gweakref_partition_t)); 00212 for (ix = 0; ix < npart; ++ix) { 00213 wp = &wt->partition[ix]; 00214 pthread_rwlock_init(&wp->lock, &rwlock_attr); 00215 avltree_init(&wp->t, wk_cmpf, 0 /* must be 0 */); 00216 if (cache_sz > 0) { 00217 wt->cache_sz = cache_sz; 00218 wp->cache = gsh_calloc(cache_sz, sizeof(struct avltree_node *)); 00219 } 00220 wp->genctr = 0; 00221 } 00222 00223 out: 00224 return (wt); 00225 } 00226 00242 gweakref_t gweakref_insert(gweakref_table_t *wt, void *obj) 00243 { 00244 gweakref_t ret; 00245 gweakref_priv_t *ref; 00246 gweakref_partition_t *wp; 00247 struct avltree_node *node; 00248 00249 ref = gsh_calloc(1, sizeof(gweakref_priv_t)); 00250 ref->k.ptr = obj; 00251 00252 wp = (gweakref_partition_t *) gwt_partition_of_addr_k(wt, ref->k.ptr); 00253 00254 /* XXX initially wt had a single atomic counter, but for any address, 00255 * partition is fixed, and we must take the partition lock exclusive 00256 * in any case */ 00257 pthread_rwlock_wrlock(&wp->lock); 00258 00259 ref->k.gen = ++(wp->genctr); 00260 00261 node = avltree_insert(&ref->node_k, &wp->t); 00262 if (! node) { 00263 /* success */ 00264 ret = ref->k; 00265 } else { 00266 /* matching key existed */ 00267 ret.ptr = NULL; 00268 ret.gen = 0; 00269 } 00270 pthread_rwlock_unlock(&wp->lock); 00271 00272 return (ret); 00273 } 00274 00290 void *gweakref_lookupex(gweakref_table_t *wt, gweakref_t *ref, 00291 pthread_rwlock_t **lock) 00292 { 00293 struct avltree_node *node = NULL; 00294 gweakref_priv_t refk, *tref; 00295 gweakref_partition_t *wp; 00296 void *ret = NULL; 00297 00298 /* look up ref.ptr--return !NULL iff ref.ptr is found and 00299 * ref.gen == found.gen */ 00300 00301 refk.k = *ref; 00302 wp = gwt_partition_of_addr_k(wt, refk.k.ptr); 00303 pthread_rwlock_rdlock(&wp->lock); 00304 00305 /* check cache */ 00306 if (wp->cache) 00307 node = wp->cache[cache_offsetof(wt, refk.k.ptr)]; 00308 00309 if (! node) 00310 node = avltree_lookup(&refk.node_k, &wp->t); 00311 00312 if (node) { 00313 /* found it, maybe */ 00314 tref = avltree_container_of(node, gweakref_priv_t, node_k); 00315 if (tref->k.gen == ref->gen) { 00316 ret = ref->ptr; 00317 if (wp->cache) 00318 wp->cache[cache_offsetof(wt, refk.k.ptr)] = node; 00319 } 00320 } 00321 00322 if (ret) { 00323 *lock = &wp->lock; 00324 } else { 00325 pthread_rwlock_unlock(&wp->lock); 00326 } 00327 00328 return (ret); 00329 } 00330 00343 void *gweakref_lookup(gweakref_table_t *wt, gweakref_t *ref) 00344 { 00345 pthread_rwlock_t *treelock = NULL; 00346 void *result = NULL; 00347 00348 result = gweakref_lookupex(wt, ref, &treelock); 00349 00350 if (result) { 00351 pthread_rwlock_unlock(treelock); 00352 } 00353 00354 return result; 00355 } 00356 00357 00358 #define GWR_FLAG_NONE 0x0000 00359 #define GWR_FLAG_WLOCKED 0x0001 00360 00374 static inline void gweakref_delete_impl(gweakref_table_t *wt, gweakref_t *ref, 00375 uint32_t flags) 00376 { 00377 struct avltree_node *node; 00378 gweakref_priv_t refk, *tref; 00379 gweakref_partition_t *wp; 00380 00381 /* lookup up ref.ptr, delete iff ref.ptr is found and 00382 * ref.gen == found.gen */ 00383 00384 refk.k = *ref; 00385 wp = gwt_partition_of_addr_k(wt, refk.k.ptr); 00386 if (!(flags & GWR_FLAG_WLOCKED)) 00387 pthread_rwlock_wrlock(&wp->lock); 00388 node = avltree_lookup(&refk.node_k, &wp->t); 00389 if (node) { 00390 /* found it, maybe */ 00391 tref = avltree_container_of(node, gweakref_priv_t, node_k); 00392 /* XXX generation mismatch would be in error, we think */ 00393 if (tref->k.gen == ref->gen) { 00394 /* unhook it */ 00395 avltree_remove(node, &wp->t); 00396 gsh_free(tref); 00397 if (wp->cache) 00398 wp->cache[cache_offsetof(wt, refk.k.ptr)] = NULL; 00399 } 00400 } 00401 if (!(flags & GWR_FLAG_WLOCKED)) 00402 pthread_rwlock_unlock(&wp->lock); 00403 } 00404 00415 void gweakref_delete(gweakref_table_t *wt, gweakref_t *ref) 00416 { 00417 gweakref_delete_impl(wt, ref, GWR_FLAG_NONE); 00418 } 00419 00429 void gweakref_destroy(gweakref_table_t *wt) 00430 { 00431 struct avltree_node *node, *onode; 00432 gweakref_partition_t *wp; 00433 gweakref_priv_t *tref; 00434 int ix; 00435 00436 /* quiesce the server, then... */ 00437 00438 for (ix = 0; ix < wt->npart; ++ix) { 00439 wp = &wt->partition[ix]; 00440 onode = NULL; 00441 node = avltree_first(&wp->t); 00442 do { 00443 if (onode) { 00444 tref = avltree_container_of(onode, gweakref_priv_t, node_k); 00445 gsh_free(tref); 00446 } 00447 } while ((onode = node) && (node = avltree_next(node))); 00448 if (onode) { 00449 tref = avltree_container_of(onode, gweakref_priv_t, node_k); 00450 gsh_free(tref); 00451 } 00452 avltree_init(&wp->t, wk_cmpf, 0 /* must be 0 */); 00453 if (wp->cache) 00454 gsh_free(wp->cache); 00455 } 00456 gsh_free(wt->partition); 00457 gsh_free(wt); 00458 }