Files @ 95e39e5412bd
Branch filter:

Location: vmkdrivers/vmkdrivers/src_9/drivers/net/nx_nic/nx_hash_table.c

unknown
ESXi-6.0-GA
/*
 * Copyright (C) 2003 - 2009 NetXen, Inc.
 * Copyright (C) 2009 - QLogic Corporation.
 * All rights reserved.
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * 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 General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
 * MA 02111-1307, USA.
 * 
 * The full GNU General Public License is included in this distribution
 * in the file called LICENSE.
 * 
 */
/*
 * NetXen:
 *
 * Provides a cache for destination lookup.
 */

#include <linux/types.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/vmalloc.h>
#include "unm_nic.h"
#include "unm_inc.h"
#include "nx_hash_table.h"
#include "nx_types.h"


/*
 *
 */
int nx_hash_tbl_create(nx_hash_tbl_t *tbl, int bucket_cnt,
		       nx_hash_tbl_ops_t *ops)
{
	int	alloc_size;
	int	i;

	nx_nic_print6(NULL, "Creating Hash table: Buckets[%u]\n", bucket_cnt);

	memset(tbl, 0, sizeof(nx_hash_tbl_t));

	tbl->bucket_cnt = bucket_cnt;

	mutex_init(&tbl->tbl_lock);
	/* Allocate and initialize the table buckets. */
	alloc_size = sizeof (nx_hbucket_head_t) * bucket_cnt;
	tbl->buckets = (nx_hbucket_head_t *)vmalloc(alloc_size);
	if (tbl->buckets == NULL) {
		nx_nic_print3(NULL, "Hash table alloc failed\n");
		return (-ENOMEM);
	}

	for (i = 0; i < bucket_cnt; i++) {
		INIT_NX_HBUCKET_HEAD(&tbl->buckets[i]);
	}

	tbl->ops = ops;

	mutex_lock(&tbl->tbl_lock);
	tbl->init_flag = 1;
	mutex_unlock(&tbl->tbl_lock);

	return (0);
}

/*
 *
 */
void nx_hash_tbl_destroy(nx_hash_tbl_t *tbl)
{
	int			i;
	nx_hash_tbl_node_t	*node;
	struct list_head 	*node1;
	struct list_head	*node2;

	mutex_lock(&tbl->tbl_lock);
	if(tbl->init_flag == 0) {
		mutex_unlock(&tbl->tbl_lock);
		return;
	}
	tbl->init_flag = 0;

	for (i = 0; i < tbl->bucket_cnt; i++) {
		spin_lock_bh(&tbl->buckets[i].lock);

		list_for_each_safe(node1, node2, &tbl->buckets[i].head) {
			node = list_entry(node1, nx_hash_tbl_node_t, list);
			list_del_init(&node->list);
			tbl->ops->destroy_cb(node);
		}

		spin_unlock_bh(&tbl->buckets[i].lock);
	}

	vfree(tbl->buckets);
	tbl->buckets = NULL;
	mutex_unlock(&tbl->tbl_lock);
}

/*
 *
 */
static inline nx_hbucket_head_t *get_bucket(nx_hash_tbl_t *tbl, void *key)
{
	uint32_t	hash;

	hash = tbl->ops->hash(key) & (tbl->bucket_cnt - 1);
	return (&tbl->buckets[hash]);
}

/*
 *
 */
static inline void list_insert_before(struct list_head *new,
				      struct list_head *before)
{
	new->next = before;
	new->prev = before->prev;
	new->prev->next = new;
	before->prev = new;
}

/*
 *
 */
int nx_hash_tbl_insert(nx_hash_tbl_t *tbl, nx_hash_tbl_node_t *node)
{
	nx_hash_tbl_node_t	*entry = NULL;
	nx_hbucket_head_t	*head;
	struct list_head 	*node1;
	struct list_head	*node2;
	int			rv;


	head = get_bucket(tbl, node->key);

	spin_lock_bh(&head->lock);

	list_for_each_safe(node1, node2, &head->head) {
		entry = list_entry(node1, nx_hash_tbl_node_t, list);
		rv = tbl->ops->compare_keys(node->key, entry->key);
		if (rv == 0) {
			rv = NX_HASH_TBL_NODE_EXISTS;
			goto done;
		}
		if (rv < 0) {
			entry = NULL;
		}
	}

	if (entry) {
		list_insert_before(&node->list, &entry->list);
	} else {
		list_add_tail(&node->list, &head->head);
	}
	rv = NX_HASH_TBL_NODE_INSERTED;

  done:
	spin_unlock_bh(&head->lock);
	return (rv);
}

/*
 *
 */
nx_hash_tbl_node_t *nx_hash_tbl_get(nx_hash_tbl_t *tbl, void *key)
{
	nx_hash_tbl_node_t	*entry = NULL;
	nx_hbucket_head_t	*head;
	struct list_head 	*node1;
	struct list_head	*node2;
	int			rv;

	head = get_bucket(tbl, key);

	spin_lock_bh(&head->lock);

	list_for_each_safe(node1, node2, &head->head) {
		entry = list_entry(node1, nx_hash_tbl_node_t, list);
		rv = tbl->ops->compare_keys(key, entry->key);
		if (rv == 0) {
			goto done;
		}
		entry = NULL;
	}

  done:
	spin_unlock_bh(&head->lock);

	return (entry);
}

/*
 *
 */
nx_hash_tbl_node_t *nx_hash_tbl_delete(nx_hash_tbl_t *tbl, void *key)
{
	nx_hash_tbl_node_t	*entry = NULL;
	nx_hbucket_head_t	*head;
	struct list_head 	*node1;
	struct list_head	*node2;
	int			rv;

	head = get_bucket(tbl, key);

	spin_lock_bh(&head->lock);

	list_for_each_safe(node1, node2, &head->head) {
		entry = list_entry(node1, nx_hash_tbl_node_t, list);
		rv = tbl->ops->compare_keys(key, entry->key);
		if (rv == 0) {
			list_del_init(&entry->list);
			goto done;
		}
		entry = NULL;
	}

  done:
	spin_unlock_bh(&head->lock);
	return (entry);
}

/*
 *
 */
int nx_cmp_ip_key(void *a1, void *a2)
{
        int     rv = 0;
        int     i;

	nx_host_key_t *key1 = (nx_host_key_t *)a1;
	nx_host_key_t *key2 = (nx_host_key_t *)a2;
        rv = key1->ip_version - key2->ip_version;
        if (rv) {
                return (rv);
        }
        if (key1->ip_version == NX_IP_VERSION_V4) {
                if (key1->daddr.v4 != key2->daddr.v4) {
                        return ((int)key1->daddr.v4 - (int)key2->daddr.v4);
                } 
		if (key1->sport != key2->sport) {
			return (key1->sport - key2->sport);
		}
		if (key1->dport != key2->dport) {
			return (key1->dport - key2->dport);
		}
                return 0;
        } else {
        	for (i = 0; i < 4; i++) {
                	rv = key1->daddr.v6[i] - key2->daddr.v6[i];
                	if (rv) {
                    		return (rv);
                	}
                	rv = key1->saddr.v6[i] - key2->saddr.v6[i];
                	if (rv) {
                    		return (rv);
                	}
            	}
	   	if (key1->sport != key2->sport) {
			return (key1->sport - key2->sport);
	  	}
		if (key1->dport != key2->dport) {
			return (key1->dport - key2->dport);
		}
                return 0;
        }
        return rv;
}