blob: b480e5f8d9980d39e9b677d5e77b18e0beaf70be [file] [log] [blame]
/*
* vim:noexpandtab:shiftwidth=8:tabstop=8:
*
* Copyright CEA/DAM/DIF (2008)
* contributeur : Philippe DENIEL philippe.deniel@cea.fr
* Thomas LEIBOVICI thomas.leibovici@cea.fr
*
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 3 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA
*
* ---------------------------------------
*/
/**
* @addtogroup cache_inode
* @{
*/
/**
* @file cache_inode_get.c
* @brief Get and eventually cache an entry.
*
* Get and eventually cache an entry.
*
*
*/
#include "config.h"
#include "log.h"
#include "hashtable.h"
#include "fsal.h"
#include "cache_inode.h"
#include "cache_inode_hash.h"
#include "cache_inode_lru.h"
#include "nfs_core.h" /* exportlist_t */
#include <unistd.h>
#include <sys/types.h>
#include <sys/param.h>
#include <time.h>
#include <pthread.h>
#include <assert.h>
#include "export_mgr.h"
/**
*
* @brief Check the active export mapping for this entry and update if
* necessary.
*
* If the entry does not have a mapping for the active export, add one.
*
* @param[in] entry The cache inode
* @param[in] export The active export
*
* @retval true if successful
* @retval false if new mapping was necessary and memory alloc failed
*
*/
bool check_mapping(cache_entry_t *entry,
struct gsh_export *export)
{
struct glist_head *glist;
struct entry_export_map *expmap;
bool try_write = false;
/* Fast path check to see if this export is already mapped */
if (atomic_fetch_voidptr(&entry->first_export) == export)
return true;
PTHREAD_RWLOCK_rdlock(&entry->attr_lock);
again:
(void)atomic_inc_uint64_t(&cache_stp->inode_mapping);
glist_for_each(glist, &entry->export_list) {
expmap = glist_entry(glist,
struct entry_export_map,
export_per_entry);
/* Found active export on list */
if (expmap->export == export) {
PTHREAD_RWLOCK_unlock(&entry->attr_lock);
return true;
}
}
if (!try_write) {
/* Now take write lock and try again in
* case another thread has raced with us.
*/
PTHREAD_RWLOCK_unlock(&entry->attr_lock);
PTHREAD_RWLOCK_wrlock(&entry->attr_lock);
try_write = true;
goto again;
}
/* We have the write lock and did not find
* this export on the list, add it.
*/
expmap = gsh_calloc(1, sizeof(*expmap));
if (expmap == NULL) {
PTHREAD_RWLOCK_unlock(&entry->attr_lock);
LogCrit(COMPONENT_CACHE_INODE,
"Out of memory");
return false;
}
PTHREAD_RWLOCK_wrlock(&export->lock);
/* If export_list is empty, store this export as first */
if (glist_empty(&entry->export_list))
atomic_store_voidptr(&entry->first_export, export);
expmap->export = export;
expmap->entry = entry;
glist_add_tail(&entry->export_list,
&expmap->export_per_entry);
glist_add_tail(&export->entry_list,
&expmap->entry_per_export);
PTHREAD_RWLOCK_unlock(&export->lock);
PTHREAD_RWLOCK_unlock(&entry->attr_lock);
return true;
}
/**
*
* @brief Cleans up the export mappings for this entry.
*
* @param[in] entry The cache inode
* @param[in] export The active export
*
*/
void clean_mapping(cache_entry_t *entry)
{
struct glist_head *glist;
struct glist_head *glistn;
PTHREAD_RWLOCK_wrlock(&entry->attr_lock);
/* Entry is unreachable and not referenced so no need to hold attr_lock
* to cleanup the export map.
*/
glist_for_each_safe(glist, glistn, &entry->export_list) {
struct entry_export_map *expmap;
expmap = glist_entry(glist,
struct entry_export_map,
export_per_entry);
PTHREAD_RWLOCK_wrlock(&expmap->export->lock);
/* Remove from list of exports for this entry */
glist_del(&expmap->export_per_entry);
/* Remove from list of entries for this export */
glist_del(&expmap->entry_per_export);
PTHREAD_RWLOCK_unlock(&expmap->export->lock);
gsh_free(expmap);
}
PTHREAD_RWLOCK_unlock(&entry->attr_lock);
}
/**
*
* @brief Gets an entry by using its fsdata as a key and caches it if needed.
*
* Gets an entry by using its fsdata as a key and caches it if needed.
*
* If a cache entry is returned, its refcount is incremented by one.
*
* @param[in] fsdata File system data
* @param[out] entry The entry
*
* @return CACHE_INODE_SUCCESS or errors.
*/
cache_inode_status_t
cache_inode_get(cache_inode_fsal_data_t *fsdata,
cache_entry_t **entry)
{
fsal_status_t fsal_status = { 0, 0 };
struct fsal_export *exp_hdl = NULL;
struct fsal_obj_handle *new_hdl;
cache_inode_status_t status = CACHE_INODE_SUCCESS;
cih_latch_t latch;
cache_inode_key_t key;
key.fsal = fsdata->export->fsal;
(void) cih_hash_key(&key, fsdata->export->fsal, &fsdata->fh_desc,
CIH_HASH_KEY_PROTOTYPE);
(void)atomic_inc_uint64_t(&cache_stp->inode_req);
/* Do lookup */
*entry =
cih_get_by_key_latched(&key, &latch,
CIH_GET_RLOCK | CIH_GET_UNLOCK_ON_MISS,
__func__, __LINE__);
if (*entry) {
/* take an extra reference within the critical section */
(void) cache_inode_lru_ref(*entry, LRU_REQ_INITIAL);
cih_latch_rele(&latch);
if (!check_mapping(*entry, op_ctx->export)) {
/* Return error instead of entry */
cache_inode_put(*entry);
*entry = NULL;
return CACHE_INODE_MALLOC_ERROR;
}
(void)atomic_inc_uint64_t(&cache_stp->inode_hit);
return CACHE_INODE_SUCCESS;
}
/* Cache miss, allocate a new entry */
exp_hdl = fsdata->export;
fsal_status =
exp_hdl->exp_ops.create_handle(exp_hdl, &fsdata->fh_desc,
&new_hdl);
if (FSAL_IS_ERROR(fsal_status)) {
status = cache_inode_error_convert(fsal_status);
LogDebug(COMPONENT_CACHE_INODE,
"could not get create_handle object");
*entry = NULL;
return status;
}
LogFullDebug(COMPONENT_CACHE_INODE, "Creating entry");
status = cache_inode_new_entry(new_hdl, CACHE_INODE_FLAG_NONE,
entry);
if (*entry == NULL)
return status;
/* If we have an entry, we succeeded. Don't propagate any
ENTRY_EXISTS errors upward. */
return CACHE_INODE_SUCCESS;
} /* cache_inode_get */
/**
* @brief Get an initial reference to a cache entry by its key.
*
* Lookup a cache entry by key, given an associated entry sharing the
* same export (e.g..
*
* @param[in] key [in] Cache key to use for lookup
* @param[in] flags flags
*
* @return Pointer to a ref'd entry if found, else NULL.
*/
cache_entry_t *
cache_inode_get_keyed(cache_inode_key_t *key,
uint32_t flags,
cache_inode_status_t *status)
{
cache_entry_t *entry = NULL;
cih_latch_t latch;
if (key->kv.addr == NULL) {
LogDebug(COMPONENT_CACHE_INODE,
"Attempt to use NULL key");
return NULL;
}
/* Check if the entry already exists */
entry =
cih_get_by_key_latched(key, &latch,
CIH_GET_RLOCK | CIH_GET_UNLOCK_ON_MISS,
__func__, __LINE__);
if (likely(entry)) {
/* Ref entry */
(void) cache_inode_lru_ref(entry, LRU_REQ_INITIAL);
/* Release the subtree hash table lock */
cih_latch_rele(&latch);
if (!check_mapping(entry, op_ctx->export)) {
/* Return error instead of entry */
cache_inode_put(entry);
return NULL;
}
goto out;
}
/* Cache miss, allocate a new entry */
if (!(flags & CIG_KEYED_FLAG_CACHED_ONLY)) {
struct fsal_obj_handle *new_hdl;
struct fsal_export *exp_hdl;
fsal_status_t fsal_status;
exp_hdl = op_ctx->fsal_export;
fsal_status =
exp_hdl->exp_ops.create_handle(exp_hdl, &key->kv,
&new_hdl);
if (unlikely(FSAL_IS_ERROR(fsal_status))) {
*status = cache_inode_error_convert(fsal_status);
LogDebug(COMPONENT_CACHE_INODE,
"could not get create_handle object %s",
cache_inode_err_str(*status));
goto out;
}
LogFullDebug(COMPONENT_CACHE_INODE, "Creating entry");
/* if all else fails, create a new entry */
*status =
cache_inode_new_entry(new_hdl, CACHE_INODE_FLAG_NONE,
&entry);
if (unlikely(!entry))
goto out;
*status = cache_inode_lock_trust_attrs(entry, false);
if (unlikely(*status != CACHE_INODE_SUCCESS)) {
cache_inode_put(entry);
entry = NULL;
goto out;
}
PTHREAD_RWLOCK_unlock(&entry->attr_lock);
} /* ! cached only */
out:
return entry;
}
/** @} */