Files
android_kernel_samsung_sm86…/qdf/inc/qdf_ptr_hash.h
Jeff Johnson 4042de592d qcacmn: Fix QDF documentation
The kernel-doc script identified a large number of documentation
issues in the QDF. A series of patches has already fixed many of the
issues, so fix most of the remaining ones.

Note that the QDF IPA abstraction still has issues, but it is under
rework, so not trying to clean it up until after the rework is
complete.

Change-Id: I10c33e341cb6b46e0f8ada99069616d450c07189
CRs-Fixed: 3406197
2023-02-18 13:33:23 -08:00

343 lines
11 KiB
C

/*
* Copyright (c) 2019 The Linux Foundation. All rights reserved.
* Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
*
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all
* copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
/**
* DOC: qdf_ptr_hash.h
*
* A minimal hashtable implementation for doing fast lookups via pointer.
*
* qdf_ptr_hash also has the benefit of knowing its own size, allowing a pointer
* to the hashtable to be passed around and embedded in other structs. Since
* every hashtable is not necessarily of the same size, this allows using hash
* tables in a lot of new places which would be impossible with the current
* kernel hashtable implementation.
*
* Because the size of the hashtable varies with the number of bits used in the
* hash, declaring a qdf_ptr_hash is a bit different. If you want to embed a
* qdf_ptr_hash in another type, use a combination of qdf_ptr_hash_declare() and
* qdf_ptr_hash_ptr(). If you just want to declare and use a qdf_ptr_hash, use
* qdf_ptr_hash_declare_ptr() instead. Either method will ensure the appropriate
* number of bytes is accounted for using an internal union, and provides the
* consumer with a pointer to a qdf_ptr_hash type which can be used with all of
* the other qdf_ptr_hash APIs. Alternatively, you can skip these complexities
* by simply dynamically allocating the qdf_ptr_hash via qdf_ptr_hash_create().
*/
#ifndef __QDF_PTR_HASH_H
#define __QDF_PTR_HASH_H
#include "i_qdf_ptr_hash.h"
#include "qdf_mem.h"
#include "qdf_slist.h"
#include "qdf_types.h"
#include "qdf_util.h"
/**
* struct qdf_ptr_hash_bucket - a type representing a hash bucket
* @list: the list used for hash chaining
*/
struct qdf_ptr_hash_bucket {
struct qdf_slist list;
};
/**
* struct qdf_ptr_hash - a hash table type for doing fast lookups via pointer
* @bits: the number of bits to use when hashing keys
* @count: the number of buckets, always equal to 2^@bits
* @buckets: empty bucket array for accessing a variable length array of buckets
*/
struct qdf_ptr_hash {
int8_t bits;
int16_t count;
struct qdf_ptr_hash_bucket buckets[0];
};
/**
* struct qdf_ptr_hash_entry - entry type of membership in a qdf_ptr_hash
* @key: the value used as the key for insertion/lookup
* @node: the list node used for hash chaining
*/
struct qdf_ptr_hash_entry {
uintptr_t key;
struct qdf_slist_node node;
};
#define __qdf_ptr_hash_size(bits) (sizeof(struct qdf_ptr_hash) + \
sizeof(((struct qdf_ptr_hash *)0)->buckets[0]) * (1 << bits))
/**
* qdf_ptr_hash_declare() - declare a new qdf_ptr_hash
* @name: the C identifier to use for the new hash table
* @_bits: The number of bits to use for hashing
*
* Return: None
*/
#define qdf_ptr_hash_declare(name, _bits) \
union { \
struct qdf_ptr_hash ht; \
uint8_t __raw[__qdf_ptr_hash_size(_bits)]; \
} __##name = { .ht = { .bits = _bits, .count = (1 << _bits) } }
/**
* qdf_ptr_hash_ptr() - get a pointer to a declared qdf_ptr_hash
* @name: the C identifier of the declared qdf_ptr_hash
*
* Return: pointer to a qdf_ptr_hash
*/
#define qdf_ptr_hash_ptr(name) &__##name.ht
/**
* qdf_ptr_hash_declare_ptr() - declare a pointer to a new qdf_ptr_hash
* @name: the C identifier to use for the pointer to the new qdf_ptr_hash
* @bits: The number of bits to use for hashing
*
* Return: None
*/
#define qdf_ptr_hash_declare_ptr(name, bits) \
qdf_ptr_hash_declare(name, bits); \
struct qdf_ptr_hash *name = qdf_ptr_hash_ptr(name)
#define __qdf_ptr_hash_for_each_bucket(ht, bkt) \
for ((bkt) = (ht)->buckets; \
(bkt) < (ht)->buckets + (ht)->count; \
(bkt)++)
/**
* qdf_ptr_hash_init() - initialize a qdf_ptr_hash
* @ht: the hash table to initialize
*
* Return: None
*/
static inline void qdf_ptr_hash_init(struct qdf_ptr_hash *ht)
{
struct qdf_ptr_hash_bucket *bucket;
__qdf_ptr_hash_for_each_bucket(ht, bucket)
qdf_slist_init(&bucket->list);
}
/**
* qdf_ptr_hash_deinit() - de-initialize a qdf_ptr_hash
* @ht: the hash table to de-initialize
*
* Return: None
*/
static inline void qdf_ptr_hash_deinit(struct qdf_ptr_hash *ht)
{
struct qdf_ptr_hash_bucket *bucket;
__qdf_ptr_hash_for_each_bucket(ht, bucket)
qdf_slist_deinit(&bucket->list);
}
/**
* qdf_ptr_hash_create() - allocate and initialize a qdf_ptr_hash
* @bits: the number of bits to use for hashing
*
* Return: qdf_ptr_hash pointer on success, NULL on allocation failure
*/
static inline struct qdf_ptr_hash *qdf_ptr_hash_create(uint8_t bits)
{
struct qdf_ptr_hash *ht = qdf_mem_malloc(__qdf_ptr_hash_size(bits));
if (!ht)
return NULL;
ht->bits = bits;
ht->count = 1 << bits;
qdf_ptr_hash_init(ht);
return ht;
}
/**
* qdf_ptr_hash_destroy() - de-initialize and de-allocate a qdf_ptr_hash
* @ht: the qdf_ptr_hash to destroy
*
* Return: None
*/
static inline void qdf_ptr_hash_destroy(struct qdf_ptr_hash *ht)
{
qdf_ptr_hash_deinit(ht);
qdf_mem_free(ht);
}
/**
* qdf_ptr_hash_empty() - check if a qdf_ptr_hash has any entries
* @ht: the qdf_ptr_hash to check
*
* Return: true if @ht contains no entries
*/
static inline bool qdf_ptr_hash_empty(struct qdf_ptr_hash *ht)
{
struct qdf_ptr_hash_bucket *bucket;
__qdf_ptr_hash_for_each_bucket(ht, bucket)
if (!qdf_slist_empty(&bucket->list))
return false;
return true;
}
#ifdef ENABLE_QDF_PTR_HASH_DEBUG
/**
* qdf_ptr_hash_dup_check_in_bucket() - check if a hash_entry is duplicated
* in hash_bucket
* @bucket: qdf_ptr_hash_bucket pointer
* @cmp_entry: the hash_entry to be checked
*
* if the cmp_entry is found in bucket list, then trigger
* assert to report duplication.
*
* Return: None
*/
static inline void qdf_ptr_hash_dup_check_in_bucket(
struct qdf_ptr_hash_bucket *bucket,
struct qdf_ptr_hash_entry *cmp_entry)
{
struct qdf_ptr_hash_entry *tmp_entry;
qdf_slist_for_each(&bucket->list, tmp_entry, node)
qdf_assert_always(tmp_entry != cmp_entry);
}
#else
#define qdf_ptr_hash_dup_check_in_bucket(_bucket, _cmp_entry) /* no op */
#endif
static inline struct qdf_ptr_hash_bucket *
__qdf_ptr_hash_get_bucket(struct qdf_ptr_hash *ht, uintptr_t key)
{
return ht->buckets + __qdf_ptr_hash_key(key, ht->bits);
}
/**
* qdf_ptr_hash_add() - insert an entry into a qdf_ptr_hash
* @ht: the qdf_ptr_hash to insert into
* @key: the pointer to use as an insertion/lookup key
* @item: a pointer to a type that contains a qdf_ptr_hash_entry
* @entry_field: C identifier for the qdf_ptr_hash_entry field in @item
*
* Return: None
*/
#define qdf_ptr_hash_add(ht, key, item, entry_field) \
__qdf_ptr_hash_add(ht, (uintptr_t)key, &(item)->entry_field)
static inline void __qdf_ptr_hash_add(struct qdf_ptr_hash *ht, uintptr_t key,
struct qdf_ptr_hash_entry *entry)
{
entry->key = key;
/* check hash_enrty exist or not before push */
qdf_ptr_hash_dup_check_in_bucket(__qdf_ptr_hash_get_bucket(ht, key),
entry);
qdf_slist_push(&__qdf_ptr_hash_get_bucket(ht, key)->list, entry, node);
}
/**
* qdf_ptr_hash_remove() - remove an entry from a qdf_ptr_hash
* @ht: the qdf_ptr_hash to remove from
* @key: the pointer to use as a lookup key
* @cursor: a pointer to a type that contains a qdf_ptr_hash_entry
* @entry_field: C identifier for the qdf_ptr_hash_entry field in @cursor
*
* Return: removed item of type @cursor on success, NULL otherwise
*/
#define qdf_ptr_hash_remove(ht, key, cursor, entry_field) ({ \
struct qdf_ptr_hash_entry *_e = \
__qdf_ptr_hash_remove(ht, (uintptr_t)key); \
cursor = _e ? qdf_container_of(_e, typeof(*(cursor)), \
entry_field) : NULL; \
cursor; })
static inline struct qdf_ptr_hash_entry *
__qdf_ptr_hash_remove(struct qdf_ptr_hash *ht, uintptr_t key)
{
struct qdf_ptr_hash_bucket *bucket = __qdf_ptr_hash_get_bucket(ht, key);
struct qdf_ptr_hash_entry *prev;
struct qdf_ptr_hash_entry *entry;
qdf_slist_for_each_del(&bucket->list, prev, entry, node) {
if (entry->key == key) {
qdf_slist_remove(&bucket->list, prev, node);
/* check hash_enrty exist or not after remove */
qdf_ptr_hash_dup_check_in_bucket(bucket, entry);
entry->key = 0;
return entry;
}
}
return NULL;
}
#define __qdf_ptr_hash_for_each_in_bucket(bucket, cursor, entry_field) \
qdf_slist_for_each(&(bucket)->list, cursor, entry_field.node)
/**
* qdf_ptr_hash_for_each() - qdf_ptr_hash item iterator for all items
* @ht: the qdf_ptr_hash to iterate over
* @bucket: qdf_ptr_hash_bucket cursor pointer
* @cursor: a pointer to a type that contains a qdf_ptr_hash_entry
* @entry_field: C identifier for the qdf_ptr_hash_entry field in @cursor
*/
#define qdf_ptr_hash_for_each(ht, bucket, cursor, entry_field) \
__qdf_ptr_hash_for_each_bucket(ht, bucket) \
__qdf_ptr_hash_for_each_in_bucket(bucket, cursor, entry_field)
/**
* qdf_ptr_hash_for_each_by_hash() - qdf_ptr_hash item iterator for items which
* hash to the same value as @key
* @ht: the qdf_ptr_hash to iterate over
* @key: the pointer to use as a lookup key
* @cursor: a pointer to a type that contains a qdf_ptr_hash_entry
* @entry_field: C identifier for the qdf_ptr_hash_entry field in @cursor
*/
#define qdf_ptr_hash_for_each_by_hash(ht, key, cursor, entry_field) \
__qdf_ptr_hash_for_each_in_bucket( \
__qdf_ptr_hash_get_bucket(ht, (uintptr_t)key), \
cursor, entry_field)
/**
* qdf_ptr_hash_for_each_by_key() - qdf_ptr_hash item iterator for items whose
* keys equal @_key
* @ht: the qdf_ptr_hash to iterate over
* @_key: the pointer to use as a lookup key
* @cursor: a pointer to a type that contains a qdf_ptr_hash_entry
* @entry_field: C identifier for the qdf_ptr_hash_entry field in @cursor
*/
#define qdf_ptr_hash_for_each_by_key(ht, _key, cursor, entry_field) \
qdf_ptr_hash_for_each_by_hash(ht, _key, cursor, entry_field) \
if ((cursor)->entry_field.key == (uintptr_t)_key)
/**
* qdf_ptr_hash_get() - get the first item whose key matches @key
* @ht: the qdf_ptr_hash to look in
* @key: the pointer to use as a lookup key
* @cursor: a pointer to a type that contains a qdf_ptr_hash_entry
* @entry_field: C identifier for the qdf_ptr_hash_entry field in @cursor
*
* Return: first item matching @key of type @cursor on success, NULL otherwise
*/
#define qdf_ptr_hash_get(ht, key, cursor, entry_field) ({ \
cursor = NULL; \
qdf_ptr_hash_for_each_by_key(ht, key, cursor, entry_field) \
break; \
cursor; })
#endif /* __QDF_PTR_HASH_H */