以5.1.0为例。
¶ 简单的key
test1.c
:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/rhashtable.h>
("GPL");
MODULE_LICENSE
struct hash_entry {
struct rhash_head node;
;
u32 key;
u64 value};
void hash_entry_free(void *ptr, void *arg) {
(ptr);
kfree}
static int __init start_init(void)
{
;
u32 istruct hash_entry *entry;
struct rhashtable_params param = {
.key_len = sizeof(u32),
.key_offset = offsetof(struct hash_entry, key),
.head_offset = offsetof(struct hash_entry, node),
.automatic_shrinking = true,
};
struct rhashtable rht;
int ret = rhashtable_init(&rht, ¶m);
("rhashtable_init returns %d\n", ret);
printkif (ret < 0)
return ret;
for (i = 0; i < (1 << 26); ++i) {
= kzalloc(sizeof(struct hash_entry), GFP_KERNEL);
entry if (entry == NULL) {
("kzalloc returns NULL\n");
printkgoto err_exit;
}
->key = i;
entry->value = (u64)i * i;
entry//printk("Inserting %u %llu\n", entry->key, entry->value);
= rhashtable_insert_fast(&rht, &entry->node, param);
ret if (ret < 0) {
(entry);
kfree("rhashtable_insert_fast returns %d\n", ret);
printkgoto err_exit;
}
}
for (i = 0; i < (1 << 26); ++i) {
// 如果可以保证entry不被删掉,那么可以用rhashtable_lookup_fast
= rhashtable_lookup_fast(&rht, &i, param);
entry if (entry == NULL) {
("rhashtable_lookup_fast returns NULL\n");
printkgoto err_exit;
}
if (entry->value != (u64)i * i) {
("%u %llu\n", i, entry->value);
printkgoto err_exit;
}
// 如果另一个线程可能会删掉entry,那么需要一直拿着read lock直到不再需要entry
();
rcu_read_lock= rhashtable_lookup(&rht, &i, param);
entry if (entry == NULL) {
("rhashtable_lookup returns NULL\n");
printkgoto err_exit;
}
if (entry->value != (u64)i * i) {
("%u %llu\n", i, entry->value);
printkgoto err_exit;
}
();
rcu_read_unlock}
:
err_exit(&rht, hash_entry_free, NULL);
rhashtable_free_and_destroyreturn 0;
}
static void __exit end_exit(void)
{
}
(start_init)
module_init(end_exit) module_exit
obj-m += test1.o
all:
$(MAKE) -C /lib/modules/$(shell uname -r)/build M=`pwd`
clean:
$(MAKE) -C /lib/modules/$(shell uname -r)/build M=`pwd` clean
make
sudo insmod test1.ko
sudo rmmod test1
dmesg | less
[ 340.782852] rhashtable_init returns 0
正常完成。
¶ 间接存储的key
比方说entry里只存key的指针之类的。这里方便起见,将key存到KV pair里。
由于key是间接存储的,rhashtable
不知道怎么把key读出来,所以需要提供obj_hashfn
用于从entry中算出key的哈希值,还需要提供obj_cmpfn
用于确定这个entry的key是不是跟给定的key一致。注意,这里仍然需要提供key_len
,我也不知道为什么。。。
test1.c
:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/rhashtable.h>
#include <linux/sched.h>
("GPL");
MODULE_LICENSE
struct kvpair {
;
u64 key;
u64 value};
struct hash_entry {
struct rhash_head node;
struct kvpair kv;
};
void hash_entry_free(void *ptr, void *arg)
{
(ptr);
kfree}
(const void *data, u32 len, u32 seed)
u32 hashfn{
return jhash(data, len, seed);
}
(const void *data, u32 len, u32 seed)
u32 hash_entry_hashfn{
struct hash_entry *entry = (struct hash_entry *)data;
return jhash(&entry->kv.key, sizeof(entry->kv.key), seed);
}
// Function to compare key with object
int key_entry_cmp(struct rhashtable_compare_arg *arg, const void *obj)
{
struct hash_entry *entry = (struct hash_entry *)obj;
return *(u64 *)arg->key - entry->kv.key;
}
static int __init start_init(void)
{
;
u64 istruct hash_entry *entry;
struct rhashtable_params param = {
.key_len = sizeof(u64),
.head_offset = offsetof(struct hash_entry, node),
.hashfn = hashfn,
.obj_hashfn = hash_entry_hashfn,
.obj_cmpfn = key_entry_cmp,
.automatic_shrinking = true,
};
struct rhashtable rht;
int ret = rhashtable_init(&rht, ¶m);
("rhashtable_init returns %d\n", ret);
printkif (ret < 0)
return ret;
for (i = 0; i < (1 << 20); ++i) {
//entry = kmalloc(sizeof(struct hash_entry), GFP_KERNEL);
= kzalloc(sizeof(struct hash_entry), GFP_KERNEL);
entry if (entry == NULL) {
("kzalloc returns NULL\n");
printkgoto err_exit;
}
->kv.key = i;
entry->kv.value = i * i;
entry//printk("Inserting %llu %llu\n", entry->key, entry->value);
while (1) {
// rhashtable_insert_fast 不检查这个元素是否已经存在,而是直接插入
= rhashtable_insert_fast(&rht, &entry->node, param);
ret // 如果插入太快,尤其是多线程插入的时候,可能会返回-EBUSY。好像是因为需要触发table resizing,但是同一时间只能有一个table resizing任务,所以就返回-EBUSY。此时需要schedule一下然后重试。
if (ret != -EBUSY)
break;
();
schedule}
if (ret < 0) {
(entry);
kfree("rhashtable_insert_fast returns %d\n", ret);
printkgoto err_exit;
}
// hashtable_lookup_insert_key 会检查元素是否已经存在,如果已经存在就返回-EEXIST,不存在则插入并返回0
= rhashtable_lookup_insert_key(&rht, &i, &entry->node, param);
ret (ret != -EEXIST);
BUG_ON}
for (i = 0; i < (1 << 20); ++i) {
// 如果可以保证entry不被删掉,那么可以用rhashtable_lookup_fast
= rhashtable_lookup_fast(&rht, &i, param);
entry if (entry == NULL) {
("rhashtable_lookup_fast returns NULL\n");
printkgoto err_exit;
}
if (entry->kv.value != i * i) {
("%llu %llu\n", i, entry->kv.value);
printkgoto err_exit;
}
// 如果另一个线程可能会删掉entry,那么需要一直拿着read lock直到不再需要entry
();
rcu_read_lock= rhashtable_lookup(&rht, &i, param);
entry if (entry == NULL) {
("rhashtable_lookup returns NULL\n");
printkgoto err_exit;
}
if (entry->kv.value != (u64)i * i) {
("%llu %llu\n", i, entry->kv.value);
printkgoto err_exit;
}
();
rcu_read_unlock}
:
err_exit(&rht, hash_entry_free, NULL);
rhashtable_free_and_destroyreturn 0;
}
static void __exit end_exit(void)
{
}
(start_init)
module_init(end_exit) module_exit
Makefile
和编译运行的步骤跟上面一样。