nfs-ganesha 1.4

generic_weakref.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
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 }