HelenOS sources

root/uspace/srv/vfs/vfs_node.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. vfs_nodes_init
  2. _vfs_node_addref
  3. vfs_node_addref
  4. vfs_node_delref
  5. vfs_node_forget
  6. vfs_node_get
  7. vfs_node_peek
  8. vfs_node_put
  9. refcnt_visitor
  10. vfs_nodes_refcount_sum_get
  11. vfs_open_node_remote
  12. nodes_key_hash
  13. nodes_hash
  14. nodes_key_equal
  15. node_triplet
  16. vfs_node_has_children

/*
 * Copyright (c) 2008 Jakub Jermar
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * - Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 * - Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 * - The name of the author may not be used to endorse or promote products
 *   derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/** @addtogroup vfs
 * @{
 */

/**
 * @file        vfs_node.c
 * @brief       Various operations on VFS nodes have their home in this file.
 */

#include "vfs.h"
#include <stdlib.h>
#include <str.h>
#include <fibril_synch.h>
#include <adt/hash_table.h>
#include <adt/hash.h>
#include <assert.h>
#include <async.h>
#include <errno.h>
#include <macros.h>

/** Mutex protecting the VFS node hash table. */
FIBRIL_MUTEX_INITIALIZE(nodes_mutex);

#define NODES_BUCKETS_LOG       8
#define NODES_BUCKETS           (1 << NODES_BUCKETS_LOG)

/** VFS node hash table containing all active, in-memory VFS nodes. */
hash_table_t nodes;

#define KEY_FS_HANDLE   0
#define KEY_DEV_HANDLE  1
#define KEY_INDEX       2

static size_t nodes_key_hash(const void *);
static size_t nodes_hash(const ht_link_t *);
static bool nodes_key_equal(const void *, const ht_link_t *);
static vfs_triplet_t node_triplet(vfs_node_t *node);

/** VFS node hash table operations. */
const hash_table_ops_t nodes_ops = {
        .hash = nodes_hash,
        .key_hash = nodes_key_hash,
        .key_equal = nodes_key_equal,
        .equal = NULL,
        .remove_callback = NULL,
};

/** Initialize the VFS node hash table.
 *
 * @return              Return true on success, false on failure.
 */
bool vfs_nodes_init(void)
{
        return hash_table_create(&nodes, 0, 0, &nodes_ops);
}

static inline void _vfs_node_addref(vfs_node_t *node)
{
        node->refcnt++;
}

/** Increment reference count of a VFS node.
 *
 * @param node          VFS node that will have its refcnt incremented.
 */
void vfs_node_addref(vfs_node_t *node)
{
        fibril_mutex_lock(&nodes_mutex);
        _vfs_node_addref(node);
        fibril_mutex_unlock(&nodes_mutex);
}

/** Decrement reference count of a VFS node.
 *
 * This function handles the case when the reference count drops to zero.
 *
 * @param node          VFS node that will have its refcnt decremented.
 */
void vfs_node_delref(vfs_node_t *node)
{
        bool free_node = false;

        fibril_mutex_lock(&nodes_mutex);

        node->refcnt--;
        if (node->refcnt == 0) {
                /*
                 * We are dropping the last reference to this node.
                 * Remove it from the VFS node hash table.
                 */

                hash_table_remove_item(&nodes, &node->nh_link);
                free_node = true;
        }

        fibril_mutex_unlock(&nodes_mutex);

        if (free_node) {
                /*
                 * VFS_OUT_DESTROY will free up the file's resources if there
                 * are no more hard links.
                 */

                async_exch_t *exch = vfs_exchange_grab(node->fs_handle);
                async_msg_2(exch, VFS_OUT_DESTROY, (sysarg_t) node->service_id,
                    (sysarg_t)node->index);
                vfs_exchange_release(exch);

                free(node);
        }
}

/** Forget node.
 *
 * This function will remove the node from the node hash table and deallocate
 * its memory, regardless of the node's reference count.
 *
 * @param node  Node to be forgotten.
 */
void vfs_node_forget(vfs_node_t *node)
{
        fibril_mutex_lock(&nodes_mutex);
        hash_table_remove_item(&nodes, &node->nh_link);
        fibril_mutex_unlock(&nodes_mutex);
        free(node);
}

/** Find VFS node.
 *
 * This function will try to lookup the given triplet in the VFS node hash
 * table. In case the triplet is not found there, a new VFS node is created.
 * In any case, the VFS node will have its reference count incremented. Every
 * node returned by this call should be eventually put back by calling
 * vfs_node_put() on it.
 *
 * @param result        Populated lookup result structure.
 *
 * @return              VFS node corresponding to the given triplet.
 */
vfs_node_t *vfs_node_get(vfs_lookup_res_t *result)
{
        vfs_node_t *node;

        fibril_mutex_lock(&nodes_mutex);
        ht_link_t *tmp = hash_table_find(&nodes, &result->triplet);
        if (!tmp) {
                node = (vfs_node_t *) malloc(sizeof(vfs_node_t));
                if (!node) {
                        fibril_mutex_unlock(&nodes_mutex);
                        return NULL;
                }
                memset(node, 0, sizeof(vfs_node_t));
                node->fs_handle = result->triplet.fs_handle;
                node->service_id = result->triplet.service_id;
                node->index = result->triplet.index;
                node->size = result->size;
                node->type = result->type;
                fibril_rwlock_initialize(&node->contents_rwlock);
                hash_table_insert(&nodes, &node->nh_link);
        } else {
                node = hash_table_get_inst(tmp, vfs_node_t, nh_link);
        }

        _vfs_node_addref(node);
        fibril_mutex_unlock(&nodes_mutex);

        return node;
}

vfs_node_t *vfs_node_peek(vfs_lookup_res_t *result)
{
        vfs_node_t *node = NULL;

        fibril_mutex_lock(&nodes_mutex);
        ht_link_t *tmp = hash_table_find(&nodes, &result->triplet);
        if (tmp) {
                node = hash_table_get_inst(tmp, vfs_node_t, nh_link);
                _vfs_node_addref(node);
        }
        fibril_mutex_unlock(&nodes_mutex);

        return node;
}

/** Return VFS node when no longer needed by the caller.
 *
 * This function will remove the reference on the VFS node created by
 * vfs_node_get(). This function can only be called as a closing bracket to the
 * preceding vfs_node_get() call.
 *
 * @param node          VFS node being released.
 */
void vfs_node_put(vfs_node_t *node)
{
        vfs_node_delref(node);
}

struct refcnt_data {
        /** Sum of all reference counts for this file system instance. */
        unsigned refcnt;
        fs_handle_t fs_handle;
        service_id_t service_id;
};

static bool refcnt_visitor(ht_link_t *item, void *arg)
{
        vfs_node_t *node = hash_table_get_inst(item, vfs_node_t, nh_link);
        struct refcnt_data *rd = (void *) arg;

        if ((node->fs_handle == rd->fs_handle) &&
            (node->service_id == rd->service_id))
                rd->refcnt += node->refcnt;

        return true;
}

unsigned
vfs_nodes_refcount_sum_get(fs_handle_t fs_handle, service_id_t service_id)
{
        struct refcnt_data rd = {
                .refcnt = 0,
                .fs_handle = fs_handle,
                .service_id = service_id
        };

        fibril_mutex_lock(&nodes_mutex);
        hash_table_apply(&nodes, refcnt_visitor, &rd);
        fibril_mutex_unlock(&nodes_mutex);

        return rd.refcnt;
}

/** Perform a remote node open operation.
 *
 * @return EOK on success or an error code from errno.h.
 *
 */
errno_t vfs_open_node_remote(vfs_node_t *node)
{
        async_exch_t *exch = vfs_exchange_grab(node->fs_handle);

        ipc_call_t answer;
        aid_t req = async_send_2(exch, VFS_OUT_OPEN_NODE,
            (sysarg_t) node->service_id, (sysarg_t) node->index, &answer);

        vfs_exchange_release(exch);

        errno_t rc;
        async_wait_for(req, &rc);

        return rc;
}

static size_t nodes_key_hash(const void *key)
{
        const vfs_triplet_t *tri = key;
        size_t hash = hash_combine(tri->fs_handle, tri->index);
        return hash_combine(hash, tri->service_id);
}

static size_t nodes_hash(const ht_link_t *item)
{
        vfs_node_t *node = hash_table_get_inst(item, vfs_node_t, nh_link);
        vfs_triplet_t tri = node_triplet(node);
        return nodes_key_hash(&tri);
}

static bool nodes_key_equal(const void *key, const ht_link_t *item)
{
        const vfs_triplet_t *tri = key;
        vfs_node_t *node = hash_table_get_inst(item, vfs_node_t, nh_link);
        return node->fs_handle == tri->fs_handle &&
            node->service_id == tri->service_id && node->index == tri->index;
}

static inline vfs_triplet_t node_triplet(vfs_node_t *node)
{
        vfs_triplet_t tri = {
                .fs_handle = node->fs_handle,
                .service_id = node->service_id,
                .index = node->index
        };

        return tri;
}

bool vfs_node_has_children(vfs_node_t *node)
{
        async_exch_t *exch = vfs_exchange_grab(node->fs_handle);
        errno_t rc = async_req_2_0(exch, VFS_OUT_IS_EMPTY, node->service_id,
            node->index);
        vfs_exchange_release(exch);
        return rc == ENOTEMPTY;
}

/**
 * @}
 */

/* [<][>][^][v][top][bottom][index][help] */
HelenOS homepage, sources at GitHub